diff --git a/src/main/java/controller/AbstractController.java b/src/main/java/controller/AbstractController.java new file mode 100644 index 0000000..eed12b9 --- /dev/null +++ b/src/main/java/controller/AbstractController.java @@ -0,0 +1,20 @@ +package controller; + +import http.HttpRequest; +import http.HttpResponse; + +public abstract class AbstractController implements Controller{ + @Override + public void service(HttpRequest request, HttpResponse response) { + + } + + public void doPost(HttpRequest request, HttpResponse response) { + + } + + public void doGet(HttpRequest request, HttpResponse response) { + + } + +} diff --git a/src/main/java/controller/Controller.java b/src/main/java/controller/Controller.java new file mode 100644 index 0000000..3d23ddc --- /dev/null +++ b/src/main/java/controller/Controller.java @@ -0,0 +1,8 @@ +package controller; + +import http.HttpRequest; +import http.HttpResponse; + +public interface Controller { + void service(HttpRequest request, HttpResponse response); +} diff --git a/src/main/java/controller/CreateUserController.java b/src/main/java/controller/CreateUserController.java new file mode 100644 index 0000000..3a5fb5e --- /dev/null +++ b/src/main/java/controller/CreateUserController.java @@ -0,0 +1,22 @@ +package controller; + +import db.DataBase; +import http.HttpRequest; +import http.HttpResponse; +import model.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CreateUserController extends AbstractController{ + private static final Logger log = LoggerFactory.getLogger(CreateUserController.class); + @Override + public void doPost(HttpRequest request, HttpResponse response) { + User user = new User(request.getParameter("userId"), request.getParameter("password"), + request.getParameter("name"), request.getParameter("email")); + + DataBase.addUser(user); + log.debug("user: {}", user); + + response.sendRedirect("/index.html"); + } +} diff --git a/src/main/java/controller/ListUserController.java b/src/main/java/controller/ListUserController.java new file mode 100644 index 0000000..cbac2d7 --- /dev/null +++ b/src/main/java/controller/ListUserController.java @@ -0,0 +1,49 @@ +package controller; + +import db.DataBase; +import http.HttpRequest; +import http.HttpResponse; +import model.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import util.HttpRequestUtils; + +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Map; + +public class ListUserController extends AbstractController{ + private final Logger log = LoggerFactory.getLogger(ListUserController.class); + @Override + public void doPost(HttpRequest request, HttpResponse response) { + if (isLogined(request.getHeader("Cookie"))) { + Collection users = DataBase.findAll(); + StringBuilder sb = new StringBuilder(); + sb.append(""); + sb.append(""); + sb.append(""); + for (User u : users) { + sb.append(""); + sb.append(""); + sb.append(""); + sb.append(""); + sb.append(""); + sb.append(""); + } + sb.append(""); + sb.append("
# 사용자 아이디 이름 이메일
#" + u.getUserId() + "" + u.getName() + "" + u.getEmail() + "
"); + + response.forwardBody(sb.toString().getBytes(StandardCharsets.UTF_8)); + } else { + response.sendRedirect("/user/login.html"); + } + } + + public boolean isLogined(String s) { + Map cookies = HttpRequestUtils.parseCookies(s); + if(cookies.get("logined") != null && cookies.get("logined").equals("true")) { + return true; + } + return false; + } +} diff --git a/src/main/java/controller/LoginController.java b/src/main/java/controller/LoginController.java new file mode 100644 index 0000000..325d115 --- /dev/null +++ b/src/main/java/controller/LoginController.java @@ -0,0 +1,28 @@ +package controller; + +import db.DataBase; +import http.HttpRequest; +import http.HttpResponse; +import model.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LoginController extends AbstractController{ + private final Logger log = LoggerFactory.getLogger(LoginController.class); + @Override + public void doPost(HttpRequest request, HttpResponse response) { + User user = DataBase.findUserById(request.getParameter("userId")); + log.debug("로그인유저: {}", user ); + + if (user == null) { + response.forward("/user/login_failed.html"); + return; + } + if (user.getPassword().equals(request.getParameter("password"))){ + response.addHeader("Set-Cookie", "logined=true"); + response.sendRedirect("/index.html"); + } else { + response.sendRedirect("/user/login_failed.html"); + } + } +} diff --git a/src/main/java/http/HttpRequest.java b/src/main/java/http/HttpRequest.java new file mode 100644 index 0000000..2dfe721 --- /dev/null +++ b/src/main/java/http/HttpRequest.java @@ -0,0 +1,86 @@ +package http; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import util.HttpRequestUtils; +import util.IOUtils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +public class HttpRequest { + private static final Logger log = LoggerFactory.getLogger(HttpRequest.class); + private String method; + private String path; + private Map header = new HashMap<>(); + private Map parameter = new HashMap<>(); + + public HttpRequest(InputStream in) { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8")); + String line = br.readLine(); + parseRequestLine(line); + + + if (line == null) { + return; + } + + line = br.readLine(); + while (!line.equals("")) { + log.debug("헤더: {}", line); + String[] split = line.split(":"); + header.put(split[0].trim(), split[1].trim()); + line = br.readLine(); + } + + if (method.equals("POST")){ + String body = URLDecoder.decode(IOUtils.readData(br, Integer.parseInt(header.get("Content-Length"))), StandardCharsets.UTF_8); + parameter = HttpRequestUtils.parseQueryString(body); + } + } catch (IOException e) { + log.error(e.getMessage()); + } + } + + public void parseRequestLine(String line){ + log.debug("라인: {}", line); + String[] parsedLines = line.split(" "); + method = parsedLines[0]; + if (method.equals("POST")){ + path = parsedLines[1]; + return; + } + + int index = parsedLines[1].indexOf("?"); + if (index == -1) { + path = parsedLines[1]; + } else { + path = parsedLines[1].substring(0, index); + parameter = HttpRequestUtils.parseQueryString(parsedLines[1].substring(index+1)); + } +// log.debug("유저아이디: {}, 비밀번호: {}, 유저이름: {}", parameter.get("userId"), parameter.get("password"), parameter.get("name")); + } + + public String getMethod() { + return method; + } + + public String getPath() { + return path; + } + + public String getHeader(String s) { + return header.get(s); + } + + public String getParameter(String s) { + return parameter.get(s); + } +} diff --git a/src/main/java/http/HttpResponse.java b/src/main/java/http/HttpResponse.java new file mode 100644 index 0000000..12f4f0c --- /dev/null +++ b/src/main/java/http/HttpResponse.java @@ -0,0 +1,94 @@ +package http; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class HttpResponse { + private static final Logger log = LoggerFactory.getLogger(HttpResponse.class); + private DataOutputStream dos = null; + private Map header = new HashMap(); + + public HttpResponse(OutputStream out) { + dos = new DataOutputStream(out); + } + public void forward(String url) { + try { + byte[] body = Files.readAllBytes(new File("./webapp" + url).toPath()); + if (url.endsWith(".css")) { + header.put("Content-Type", "text/css"); + } else if (url.endsWith(".js")){ + header.put("Content-Type", "application/javascript"); + } else { + header.put("Content-Type", "text/html;charset=uft-8"); + } + header.put("Content-Length", body.length + ""); + response200Header(body.length); + responseBody(body); + } catch (IOException e) { + log.debug(e.getMessage()); + } + } + + public void forwardBody(byte[] body) { + header.put("Content-Type", "text/html;charset=uft-8"); + header.put("Content-Length", body.length + ""); + response200Header(body.length); + responseBody(body); + } + + public void response200Header(int lengthOfBody) { + try { + dos.writeBytes("HTTP/1.1 200 OK \r\n"); + writeHeaders(); + dos.writeBytes("\r\n"); + } catch (IOException e) { + log.error(e.getMessage()); + } + } + + public void responseBody(byte[] body) { + try { + dos.write(body, 0, body.length); + dos.writeBytes("\r\n"); + dos.flush(); + } catch (IOException e) { + log.error(e.getMessage()); + } + } + + private void writeHeaders() { + try { + Set keys = header.keySet(); + for (String key : keys) { + dos.writeBytes(key + ": " + header.get(key) + "\r\n"); + } + } catch (IOException e){ + log.error(e.getMessage()); + } + } + + public void sendRedirect(String url) { + try { + dos.writeBytes("HTTP/1.1 302 Redirect \r\n"); + writeHeaders(); + dos.writeBytes("Location: " + url + "\r\n"); + } catch (IOException e) { + log.error(e.getMessage()); + } + } + + + public void addHeader(String key, String value) { + header.put(key, value); + } + +} diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index 90195ec..abace9c 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -1,14 +1,15 @@ package webserver; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; - +import controller.Controller; +import http.HttpRequest; +import http.HttpResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.*; +import java.net.Socket; + + public class RequestHandler extends Thread { private static final Logger log = LoggerFactory.getLogger(RequestHandler.class); @@ -23,33 +24,26 @@ public void run() { connection.getPort()); try (InputStream in = connection.getInputStream(); OutputStream out = connection.getOutputStream()) { - // TODO 사용자 요청에 대한 처리는 이 곳에 구현하면 된다. - DataOutputStream dos = new DataOutputStream(out); - byte[] body = "Hello World".getBytes(); - response200Header(dos, body.length); - responseBody(dos, body); - } catch (IOException e) { - log.error(e.getMessage()); - } - } + HttpRequest httpRequest = new HttpRequest(in); + HttpResponse httpResponse = new HttpResponse(out); - private void response200Header(DataOutputStream dos, int lengthOfBodyContent) { - try { - dos.writeBytes("HTTP/1.1 200 OK \r\n"); - dos.writeBytes("Content-Type: text/html;charset=utf-8\r\n"); - dos.writeBytes("Content-Length: " + lengthOfBodyContent + "\r\n"); - dos.writeBytes("\r\n"); + + Controller controller = RequestMapping.getController(httpRequest.getPath()); + if (controller == null) { + String path = getDefaultPath(httpRequest.getPath()); + httpResponse.forward(path); + } else { + controller.service(httpRequest, httpResponse); + } } catch (IOException e) { log.error(e.getMessage()); } } - private void responseBody(DataOutputStream dos, byte[] body) { - try { - dos.write(body, 0, body.length); - dos.flush(); - } catch (IOException e) { - log.error(e.getMessage()); + private String getDefaultPath(String path) { + if (path.equals("/")) { + return "/index.html"; } + return path; } } diff --git a/src/main/java/webserver/RequestMapping.java b/src/main/java/webserver/RequestMapping.java new file mode 100644 index 0000000..320e576 --- /dev/null +++ b/src/main/java/webserver/RequestMapping.java @@ -0,0 +1,23 @@ +package webserver; + +import controller.Controller; +import controller.CreateUserController; +import controller.ListUserController; +import controller.LoginController; + +import java.util.HashMap; +import java.util.Map; + +public class RequestMapping { + private static Map controllers = new HashMap(); + + static { + controllers.put("/user/create", new CreateUserController()); + controllers.put("/user/login", new LoginController()); + controllers.put("/user/list", new ListUserController()); + } + + public static Controller getController (String requestUrl) { + return controllers.get(requestUrl); + } +} diff --git a/src/test/java/http/HttpRequestTest.java b/src/test/java/http/HttpRequestTest.java new file mode 100644 index 0000000..bd07044 --- /dev/null +++ b/src/test/java/http/HttpRequestTest.java @@ -0,0 +1,34 @@ +package http; +import java.io.*; + +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.*; + +public class HttpRequestTest { + private String testDirectory = "./src/test/resources/"; + + @Test + public void request_GET() throws Exception{ + InputStream in = new FileInputStream(new File(testDirectory + "Http_GET.txt")); + HttpRequest request = new HttpRequest(in); + + + assertThat("GET").isEqualTo(request.getMethod()); + assertThat("/user/create").isEqualTo(request.getPath()); + assertThat("keep-alive").isEqualTo(request.getHeader("Connection")); + assertThat("javajigi").isEqualTo(request.getParameter("userId")); + + } + + @Test + public void request_POST() throws Exception{ + InputStream in = new FileInputStream(new File(testDirectory + "Http_POST.txt")); + HttpRequest request = new HttpRequest(in); + + assertThat("POST").isEqualTo(request.getMethod()); + assertThat("/user/create").isEqualTo(request.getPath()); + assertThat("keep-alive").isEqualTo(request.getHeader("Connection")); + assertThat("javajigi").isEqualTo(request.getParameter("userId")); + + } +} diff --git a/src/test/java/http/HttpResponseTest.java b/src/test/java/http/HttpResponseTest.java new file mode 100644 index 0000000..2982e85 --- /dev/null +++ b/src/test/java/http/HttpResponseTest.java @@ -0,0 +1,40 @@ +package http; + +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStream; + +public class HttpResponseTest { + private String testDirectory = "./src/test/resources/"; + + @Test + public void responseForward() throws Exception{ + //http_forward.txt 결과는 응답 body에 index.html이 포함되어 있어야 한다. + HttpResponse response = new HttpResponse(createOutputStream("Http_Forward.txt")); + response.forward("/index.html"); + } + + @Test + public void responseRedirect() throws Exception{ + // http_redirect.txt 결과는 응답 header에 + // location 정보가 /index.html로 포함되어 있어야 한다 + HttpResponse response = new HttpResponse(createOutputStream("Http_Forward.txt")); + response.sendRedirect("/index.html"); + } + + @Test + public void responseCookies() throws Exception{ + //http_cookie.txt 결과는 응답 header에 set-cookie 값으로 + //logined=true 값이 포함되어 있어야 한다. + HttpResponse response = new HttpResponse(createOutputStream("Http_Forward.txt")); + response.addHeader("Set-Cookie", "logined=true"); + response.sendRedirect("/index.html"); + } + + private OutputStream createOutputStream(String filename) throws FileNotFoundException { + return new FileOutputStream(new File(testDirectory + filename)); + } +} diff --git a/src/test/resources/Http_GET.txt b/src/test/resources/Http_GET.txt new file mode 100644 index 0000000..0313030 --- /dev/null +++ b/src/test/resources/Http_GET.txt @@ -0,0 +1,5 @@ +GET /user/create?userId=javajigi&password=password&name=tester HTTP/1.1 +Host: localhost:8080 +Connection: keep-alive +Accept: */* + diff --git a/src/test/resources/Http_POST.txt b/src/test/resources/Http_POST.txt new file mode 100644 index 0000000..be3664b --- /dev/null +++ b/src/test/resources/Http_POST.txt @@ -0,0 +1,8 @@ +POST /user/create HTTP/1.1 +Host: localhost:8080 +Connection: keep-alive +Content-Length: 46 +Content-Type: application/x-www-form-urlencoded +Accept: */* + +userId=javajigi&password=password&name=tester diff --git a/webapp/user/form.html b/webapp/user/form.html index 96fe1bd..587a9d6 100644 --- a/webapp/user/form.html +++ b/webapp/user/form.html @@ -75,7 +75,7 @@
-
+
@@ -104,4 +104,4 @@ - \ No newline at end of file +