From 8845ec61a830634326d86f22aa514e37f0f84a98 Mon Sep 17 00:00:00 2001 From: gisungchoi Date: Mon, 29 Mar 2021 15:03:10 +0900 Subject: [PATCH 01/17] refactor : HttpRequest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -접근 제어자 변경 -메소드명 변경 -패키지명 변경 --- .../java/{webserver => http}/HttpMethod.java | 2 +- .../java/{webserver => http}/HttpRequest.java | 57 ++++++++----------- src/main/java/webserver/RequestHandler.java | 2 +- .../{webserver => http}/HttpRequestTest.java | 6 +- 4 files changed, 28 insertions(+), 39 deletions(-) rename src/main/java/{webserver => http}/HttpMethod.java (70%) rename src/main/java/{webserver => http}/HttpRequest.java (72%) rename src/test/java/{webserver => http}/HttpRequestTest.java (93%) diff --git a/src/main/java/webserver/HttpMethod.java b/src/main/java/http/HttpMethod.java similarity index 70% rename from src/main/java/webserver/HttpMethod.java rename to src/main/java/http/HttpMethod.java index cf5eff5..2e1c610 100644 --- a/src/main/java/webserver/HttpMethod.java +++ b/src/main/java/http/HttpMethod.java @@ -1,4 +1,4 @@ -package webserver; +package http; public enum HttpMethod { GET, diff --git a/src/main/java/webserver/HttpRequest.java b/src/main/java/http/HttpRequest.java similarity index 72% rename from src/main/java/webserver/HttpRequest.java rename to src/main/java/http/HttpRequest.java index f4278fc..307f6a2 100644 --- a/src/main/java/webserver/HttpRequest.java +++ b/src/main/java/http/HttpRequest.java @@ -1,4 +1,4 @@ -package webserver; +package http; import util.HttpRequestUtils; import util.IOUtils; @@ -22,15 +22,32 @@ public class HttpRequest { private String body; private HttpRequest() { - } - public String getUrl() { return url; } - public void addStartLine(String buffer) { + public static HttpRequest of(InputStream in) throws IOException { + HttpRequest httpRequest = new HttpRequest(); + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + String buffer; + + buffer = br.readLine(); + httpRequest.addStartLine(buffer); + while (!(buffer = br.readLine()).equals("")) { + httpRequest.addHeaders(buffer); + } + + String contentLength = httpRequest.header("Content-Length"); + if (contentLength != null) { + httpRequest.addBody(IOUtils.readData(br, Integer.parseInt(contentLength))); + } + + return httpRequest; + } + + private void addStartLine(String buffer) { String[] startLine = buffer.split(" "); method = HttpMethod.valueOf(startLine[0].toUpperCase()); url = startLine[1]; @@ -44,7 +61,7 @@ public void addStartLine(String buffer) { } } - public void addHeaders(String buffer) { + private void addHeaders(String buffer) { HttpRequestUtils.Pair pair = HttpRequestUtils.parseHeader(buffer); headers.put(pair.getKey(), pair.getValue()); String cookies; @@ -53,31 +70,6 @@ public void addHeaders(String buffer) { } } - public static HttpRequest of(InputStream in) { - HttpRequest httpRequest = new HttpRequest(); - BufferedReader br = new BufferedReader(new InputStreamReader(in)); - String buffer; - try { - buffer = br.readLine(); - httpRequest.addStartLine(buffer); - while (!(buffer = br.readLine()).equals("")) { - httpRequest.addHeaders(buffer); - } - - String contentLength = httpRequest.header("Content-Length"); - if (contentLength != null) { - httpRequest.addBody(IOUtils.readData(br, Integer.parseInt(contentLength))); - } - - - } catch (IOException e) { - e.printStackTrace(); - } - - - return httpRequest; - } - private void addBody(String readData) { body = readData; if (method == HttpMethod.POST) { @@ -96,7 +88,7 @@ public String cookie(String key) { return cookies.get(key); } - public HttpMethod getMethod() { + public HttpMethod method() { return method; } @@ -104,7 +96,4 @@ public String data(String key) { return data.get(key); } -// public boolean loginCookie(){ -// -// } } diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index bc57a83..37634a3 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -3,9 +3,9 @@ import java.io.*; import java.net.Socket; import java.nio.file.Files; -import java.util.List; import db.DataBase; +import http.HttpRequest; import model.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/test/java/webserver/HttpRequestTest.java b/src/test/java/http/HttpRequestTest.java similarity index 93% rename from src/test/java/webserver/HttpRequestTest.java rename to src/test/java/http/HttpRequestTest.java index f3b6cbb..bceb82c 100644 --- a/src/test/java/webserver/HttpRequestTest.java +++ b/src/test/java/http/HttpRequestTest.java @@ -1,4 +1,4 @@ -package webserver; +package http; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.AfterEach; @@ -24,7 +24,7 @@ public void afterEach() { @Test @DisplayName("리퀘스트 객체를 생성") - public void createRequest() { + public void createRequest() throws IOException { String request = "GET /index.html HTTP/1.1\r\n" + "Host: localhost:8080\r\n" + "Connection: keep-alive\r\n" + @@ -36,7 +36,7 @@ public void createRequest() { @Test @DisplayName("리퀘스트 쿼리 분석") - public void requestQuery() { + public void requestQuery() throws IOException { String request = "GET /user/create?userId=javajigi&password=password&name=%EB%B0%95%EC%9E%AC%EC%84%B1&email=javajigi%40slipp.net HTTP/1.1\r\n" + "Host: localhost:8080\r\n" + "Connection: keep-alive\r\n" + From 222894d9fd331ca5509e142662945e6627758fea Mon Sep 17 00:00:00 2001 From: K Date: Mon, 29 Mar 2021 16:31:15 +0900 Subject: [PATCH 02/17] test: HttpResponse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - HttpResponse 테스트를 위한 코드 --- src/main/java/http/HttpResponse.java | 11 +++++++ src/test/java/http/HttpResponseTest.java | 38 ++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 src/main/java/http/HttpResponse.java create mode 100644 src/test/java/http/HttpResponseTest.java diff --git a/src/main/java/http/HttpResponse.java b/src/main/java/http/HttpResponse.java new file mode 100644 index 0000000..d55281b --- /dev/null +++ b/src/main/java/http/HttpResponse.java @@ -0,0 +1,11 @@ +package http; + +import java.io.OutputStream; + +public class HttpResponse { + public HttpResponse(OutputStream os) { + } + + public void forward(String url) { + } +} diff --git a/src/test/java/http/HttpResponseTest.java b/src/test/java/http/HttpResponseTest.java new file mode 100644 index 0000000..2dd73c7 --- /dev/null +++ b/src/test/java/http/HttpResponseTest.java @@ -0,0 +1,38 @@ +package http; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; + +import org.assertj.core.api.SoftAssertions; + +class HttpResponseTest { + + private SoftAssertions softly; + private ByteArrayOutputStream os; + + @BeforeEach + public void beforeEach() { + softly = new SoftAssertions(); + os = new ByteArrayOutputStream(); + } + + @AfterEach + public void afterEach() { + softly.assertAll(); + } + + @Test + public void sendOk() { + HttpResponse response = new HttpResponse(os); + response.forward("/index.html"); + + softly.assertThat(os.toString()).contains("HTTP/1.1 200 OK\r\n"); + softly.assertThat(os.toString()).contains("Content-Type: text/html;charset=utf-8\r\n"); + softly.assertThat(os.toString()).contains("Content-Length: 150\r\n"); + softly.assertThat(os.toString()).contains("SLiPP Java Web Programming"); + } + +} From ebc07f7c12093071ec1985aaf28052774394467e Mon Sep 17 00:00:00 2001 From: gisungchoi Date: Mon, 29 Mar 2021 16:51:15 +0900 Subject: [PATCH 03/17] test : HttpResponse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -response의 값들이 잘 들어갔는지 검사 -static file 리턴 가능 --- src/main/java/http/HttpResponse.java | 32 +++++++++++++++++++++++- src/test/java/http/HttpResponseTest.java | 9 ++++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/main/java/http/HttpResponse.java b/src/main/java/http/HttpResponse.java index d55281b..9c58542 100644 --- a/src/main/java/http/HttpResponse.java +++ b/src/main/java/http/HttpResponse.java @@ -1,11 +1,41 @@ package http; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; import java.io.OutputStream; +import java.nio.file.Files; public class HttpResponse { + private DataOutputStream dos; + public HttpResponse(OutputStream os) { + dos = new DataOutputStream(os); + } + + public void forward(String url) throws IOException { + byte[] body = Files.readAllBytes(new File("./webapp" + url).toPath()); + + if (url.endsWith(".css")) { + response200Header(dos, body.length, "css"); + } else { + response200Header(dos, body.length, "html"); + } + + responseBody(dos, body); } - public void forward(String url) { + private void response200Header(DataOutputStream dos, int lengthOfBodyContent, String contentType) throws IOException { + dos.writeBytes("HTTP/1.1 200 OK \r\n"); + dos.writeBytes("Content-Type: text/" + contentType + ";charset=utf-8\r\n"); + dos.writeBytes("Content-Length: " + lengthOfBodyContent + "\r\n"); + dos.writeBytes("\r\n"); + + } + + private void responseBody(DataOutputStream dos, byte[] body) throws IOException { + dos.write(body, 0, body.length); + dos.flush(); + } } diff --git a/src/test/java/http/HttpResponseTest.java b/src/test/java/http/HttpResponseTest.java index 2dd73c7..fe657c9 100644 --- a/src/test/java/http/HttpResponseTest.java +++ b/src/test/java/http/HttpResponseTest.java @@ -2,9 +2,11 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; +import java.io.IOException; import org.assertj.core.api.SoftAssertions; @@ -25,14 +27,15 @@ public void afterEach() { } @Test - public void sendOk() { + public void sendOk() throws IOException { HttpResponse response = new HttpResponse(os); response.forward("/index.html"); - softly.assertThat(os.toString()).contains("HTTP/1.1 200 OK\r\n"); + softly.assertThat(os.toString()).contains("HTTP/1.1 200 OK \r\n"); softly.assertThat(os.toString()).contains("Content-Type: text/html;charset=utf-8\r\n"); - softly.assertThat(os.toString()).contains("Content-Length: 150\r\n"); + softly.assertThat(os.toString()).contains("Content-Length: 7049\r\n"); softly.assertThat(os.toString()).contains("SLiPP Java Web Programming"); } + } From 45b909171864dd770b24d1ee2b4e2b88745ba999 Mon Sep 17 00:00:00 2001 From: gisungchoi Date: Mon, 29 Mar 2021 17:02:18 +0900 Subject: [PATCH 04/17] test : read css MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -css 파일 읽기 가능 --- src/test/java/http/HttpResponseTest.java | 26 ++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/test/java/http/HttpResponseTest.java b/src/test/java/http/HttpResponseTest.java index fe657c9..f60dbc1 100644 --- a/src/test/java/http/HttpResponseTest.java +++ b/src/test/java/http/HttpResponseTest.java @@ -6,7 +6,9 @@ import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; +import java.nio.file.Files; import org.assertj.core.api.SoftAssertions; @@ -14,11 +16,13 @@ class HttpResponseTest { private SoftAssertions softly; private ByteArrayOutputStream os; + private HttpResponse response; @BeforeEach public void beforeEach() { softly = new SoftAssertions(); os = new ByteArrayOutputStream(); + response = new HttpResponse(os); } @AfterEach @@ -27,15 +31,29 @@ public void afterEach() { } @Test + @DisplayName("index.html 읽기") public void sendOk() throws IOException { - HttpResponse response = new HttpResponse(os); response.forward("/index.html"); - softly.assertThat(os.toString()).contains("HTTP/1.1 200 OK \r\n"); - softly.assertThat(os.toString()).contains("Content-Type: text/html;charset=utf-8\r\n"); - softly.assertThat(os.toString()).contains("Content-Length: 7049\r\n"); + assertResponseHeader("/index.html", "html"); softly.assertThat(os.toString()).contains("SLiPP Java Web Programming"); } + @Test + @DisplayName("css 파일 읽기") + public void sendOkCss() throws IOException { + response.forward("/css/styles.css"); + + assertResponseHeader("/css/styles.css", "css"); + softly.assertThat(os.toString()).contains(".navbar-default .dropdown-menu li > a {padding-left:30px;}"); + } + + private void assertResponseHeader(String url, String type) throws IOException { + byte[] body = Files.readAllBytes(new File("./webapp" + url).toPath()); + + softly.assertThat(os.toString()).contains("HTTP/1.1 200 OK \r\n"); + softly.assertThat(os.toString()).contains("Content-Type: text/" + type + ";charset=utf-8\r\n"); + softly.assertThat(os.toString()).contains("Content-Length: " + body.length + "\r\n"); + } } From 3494f03af287669903ecc056b715b9a8e62dd88d Mon Sep 17 00:00:00 2001 From: gisungchoi Date: Mon, 29 Mar 2021 17:12:05 +0900 Subject: [PATCH 05/17] test : HttpResponse redirect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -테스트 작성 --- src/main/java/http/HttpResponse.java | 4 ++++ src/test/java/http/HttpResponseTest.java | 20 +++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/main/java/http/HttpResponse.java b/src/main/java/http/HttpResponse.java index 9c58542..becdda8 100644 --- a/src/main/java/http/HttpResponse.java +++ b/src/main/java/http/HttpResponse.java @@ -38,4 +38,8 @@ private void responseBody(DataOutputStream dos, byte[] body) throws IOException dos.flush(); } + + public void redirect(String url) { + + } } diff --git a/src/test/java/http/HttpResponseTest.java b/src/test/java/http/HttpResponseTest.java index f60dbc1..b730a2d 100644 --- a/src/test/java/http/HttpResponseTest.java +++ b/src/test/java/http/HttpResponseTest.java @@ -48,12 +48,26 @@ public void sendOkCss() throws IOException { softly.assertThat(os.toString()).contains(".navbar-default .dropdown-menu li > a {padding-left:30px;}"); } + @Test + @DisplayName("redirect to /index.html") + public void sendRedirect() { + String url = "/index.html"; + response.redirect(url); + + softly.assertThat(os.toString()) + .contains("HTTP/1.1 302 FOUND \r\n") + .contains("Location: " + url + "\r\n") + .contains("\r\n"); + } + + private void assertResponseHeader(String url, String type) throws IOException { byte[] body = Files.readAllBytes(new File("./webapp" + url).toPath()); - softly.assertThat(os.toString()).contains("HTTP/1.1 200 OK \r\n"); - softly.assertThat(os.toString()).contains("Content-Type: text/" + type + ";charset=utf-8\r\n"); - softly.assertThat(os.toString()).contains("Content-Length: " + body.length + "\r\n"); + softly.assertThat(os.toString()) + .contains("HTTP/1.1 200 OK \r\n") + .contains("Content-Type: text/" + type + ";charset=utf-8\r\n") + .contains("Content-Length: " + body.length + "\r\n"); } } From 7501bb7ebba485ba26fd9db5369a3e0a189baf7b Mon Sep 17 00:00:00 2001 From: gisungchoi Date: Mon, 29 Mar 2021 17:15:31 +0900 Subject: [PATCH 06/17] =?UTF-8?q?feat:=20sendRedirect=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -redirect 동작 확인 --- src/main/java/http/HttpResponse.java | 6 ++++-- src/test/java/http/HttpResponseTest.java | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/http/HttpResponse.java b/src/main/java/http/HttpResponse.java index becdda8..c31843b 100644 --- a/src/main/java/http/HttpResponse.java +++ b/src/main/java/http/HttpResponse.java @@ -39,7 +39,9 @@ private void responseBody(DataOutputStream dos, byte[] body) throws IOException } - public void redirect(String url) { - + public void redirect(String url) throws IOException { + dos.writeBytes("HTTP/1.1 302 FOUND \r\n"); + dos.writeBytes("Location: " + url + "\r\n"); + dos.writeBytes("\r\n"); } } diff --git a/src/test/java/http/HttpResponseTest.java b/src/test/java/http/HttpResponseTest.java index b730a2d..306a54c 100644 --- a/src/test/java/http/HttpResponseTest.java +++ b/src/test/java/http/HttpResponseTest.java @@ -50,7 +50,7 @@ public void sendOkCss() throws IOException { @Test @DisplayName("redirect to /index.html") - public void sendRedirect() { + public void sendRedirect() throws IOException { String url = "/index.html"; response.redirect(url); From 0c496fbdc97aef4f60cdaa8acc34f3e8a98dd6cc Mon Sep 17 00:00:00 2001 From: gisungchoi Date: Mon, 29 Mar 2021 17:22:59 +0900 Subject: [PATCH 07/17] test : HttpResponse Cookie MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -cookie 값이 있는지 test한다. --- src/main/java/http/HttpResponse.java | 4 ++++ src/test/java/http/HttpResponseTest.java | 19 +++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/main/java/http/HttpResponse.java b/src/main/java/http/HttpResponse.java index c31843b..7978aff 100644 --- a/src/main/java/http/HttpResponse.java +++ b/src/main/java/http/HttpResponse.java @@ -44,4 +44,8 @@ public void redirect(String url) throws IOException { dos.writeBytes("Location: " + url + "\r\n"); dos.writeBytes("\r\n"); } + + public void addHeader(String header, String value) { + + } } diff --git a/src/test/java/http/HttpResponseTest.java b/src/test/java/http/HttpResponseTest.java index 306a54c..3d38090 100644 --- a/src/test/java/http/HttpResponseTest.java +++ b/src/test/java/http/HttpResponseTest.java @@ -54,13 +54,28 @@ public void sendRedirect() throws IOException { String url = "/index.html"; response.redirect(url); + assertRedirectResponseHeader(url); + } + + @Test + @DisplayName("redirect cookie test") + public void sendRedirectWithCookie() throws IOException { + String url = "/index.html"; + response.addHeader("Set-Cookie", "logined=true"); + response.redirect(url); + + assertRedirectResponseHeader(url); + softly.assertThat(os.toString()) + .contains("Set-Cookie: logined=true"); + } + + private void assertRedirectResponseHeader(String url) { softly.assertThat(os.toString()) .contains("HTTP/1.1 302 FOUND \r\n") .contains("Location: " + url + "\r\n") - .contains("\r\n"); + .contains("\r\n\r\n"); } - private void assertResponseHeader(String url, String type) throws IOException { byte[] body = Files.readAllBytes(new File("./webapp" + url).toPath()); From 6bd7c82425e6ec32b05e7d2154567bf7364bee0a Mon Sep 17 00:00:00 2001 From: gisungchoi Date: Mon, 29 Mar 2021 17:37:06 +0900 Subject: [PATCH 08/17] feat : redirect with cookie MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -cookie값 헤더에 추가 --- src/main/java/http/HttpResponse.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/http/HttpResponse.java b/src/main/java/http/HttpResponse.java index 7978aff..d1d6a44 100644 --- a/src/main/java/http/HttpResponse.java +++ b/src/main/java/http/HttpResponse.java @@ -5,9 +5,12 @@ import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; +import java.util.HashMap; +import java.util.Map; public class HttpResponse { private DataOutputStream dos; + private Map headers = new HashMap<>(); public HttpResponse(OutputStream os) { dos = new DataOutputStream(os); @@ -42,10 +45,14 @@ private void responseBody(DataOutputStream dos, byte[] body) throws IOException public void redirect(String url) throws IOException { dos.writeBytes("HTTP/1.1 302 FOUND \r\n"); dos.writeBytes("Location: " + url + "\r\n"); + for (Map.Entry header : headers.entrySet()) { + dos.writeBytes(header.getKey() + ": " + header.getValue() + "\r\n"); + } + dos.writeBytes("\r\n"); } public void addHeader(String header, String value) { - + headers.put(header, value); } } From 570fee73a70742c1afdbfd6bd026f9c04e60c3a1 Mon Sep 17 00:00:00 2001 From: gisungchoi Date: Mon, 29 Mar 2021 18:03:44 +0900 Subject: [PATCH 09/17] refactor : HttpResponse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -controller의 중복 제거 -response에서 header를 map으로 관리 --- src/main/java/http/HttpResponse.java | 40 ++++++------ src/main/java/webserver/RequestHandler.java | 71 ++++----------------- 2 files changed, 33 insertions(+), 78 deletions(-) diff --git a/src/main/java/http/HttpResponse.java b/src/main/java/http/HttpResponse.java index d1d6a44..0bb4f94 100644 --- a/src/main/java/http/HttpResponse.java +++ b/src/main/java/http/HttpResponse.java @@ -9,7 +9,10 @@ import java.util.Map; public class HttpResponse { + private DataOutputStream dos; + + private String startLine; private Map headers = new HashMap<>(); public HttpResponse(OutputStream os) { @@ -17,42 +20,41 @@ public HttpResponse(OutputStream os) { } public void forward(String url) throws IOException { + startLine = "HTTP/1.1 200 OK \r\n"; byte[] body = Files.readAllBytes(new File("./webapp" + url).toPath()); if (url.endsWith(".css")) { - response200Header(dos, body.length, "css"); + addHeader("Content-Type", "text/css;charset=utf-8"); } else { - response200Header(dos, body.length, "html"); + addHeader("Content-Type", "text/html;charset=utf-8"); } + addHeader("Content-Length", String.valueOf(body.length)); - responseBody(dos, body); + processHeaders(); + responseBody(body); } - private void response200Header(DataOutputStream dos, int lengthOfBodyContent, String contentType) throws IOException { - dos.writeBytes("HTTP/1.1 200 OK \r\n"); - dos.writeBytes("Content-Type: text/" + contentType + ";charset=utf-8\r\n"); - dos.writeBytes("Content-Length: " + lengthOfBodyContent + "\r\n"); - dos.writeBytes("\r\n"); - - } - - private void responseBody(DataOutputStream dos, byte[] body) throws IOException { + private void responseBody(byte[] body) throws IOException { dos.write(body, 0, body.length); dos.flush(); - } public void redirect(String url) throws IOException { - dos.writeBytes("HTTP/1.1 302 FOUND \r\n"); - dos.writeBytes("Location: " + url + "\r\n"); + startLine = "HTTP/1.1 302 FOUND \r\n"; + addHeader("Location", url); + processHeaders(); + } + + public void addHeader(String header, String value) { + headers.put(header, value); + } + + private void processHeaders() throws IOException { + dos.writeBytes(startLine); for (Map.Entry header : headers.entrySet()) { dos.writeBytes(header.getKey() + ": " + header.getValue() + "\r\n"); } - dos.writeBytes("\r\n"); } - public void addHeader(String header, String value) { - headers.put(header, value); - } } diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index 37634a3..cada1c6 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -6,6 +6,7 @@ import db.DataBase; import http.HttpRequest; +import http.HttpResponse; import model.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,8 +29,7 @@ public void run() { HttpRequest httpRequest = HttpRequest.of(in); String url = httpRequest.getUrl(); - - DataOutputStream dos = new DataOutputStream(out); + HttpResponse httpResponse = new HttpResponse(out); if ("/user/create".equals(url)) { User user = new User( httpRequest.data("userId"), @@ -39,39 +39,32 @@ public void run() { ); DataBase.addUser(user); log.debug("user : {}", user); - response302Header(dos, "/index.html"); + httpResponse.redirect("/index.html"); } else if ("/user/login".equals(url)) { User user = DataBase.findUserById(httpRequest.data("userId")); if (user == null) { log.debug("Not Found"); - response302HeaderWithCookie(dos, "/user/login_failed.html", "logined=false", "/"); + httpResponse.addHeader("Set-Cookie", "logined=false; Path=/"); + httpResponse.redirect("/user/login_failed.html"); } else if (user.checkPassword(httpRequest.data("password"))) { log.debug("Login success"); - response302HeaderWithCookie(dos, "/index.html", "logined=true", "/"); + httpResponse.addHeader("Set-Cookie", "logined=true; Path=/"); + httpResponse.redirect("/index.html"); } else { log.debug("Password was not matched"); - response302HeaderWithCookie(dos, "/user/login_failed.html", "logined=false", "/"); + httpResponse.addHeader("Set-Cookie", "logined=false; Path=/"); + httpResponse.redirect("/user/login_failed.html"); } } else if ("/user/list".equals(url)) { log.debug("Cookie : {}", httpRequest.header("Cookie")); if ("true".equals(httpRequest.cookie("logined"))) { - byte[] body = Files.readAllBytes(new File("./webapp" + "/user/list.html").toPath()); - response200Header(dos, body.length, "html"); - responseBody(dos, body); + httpResponse.forward("/user/list.html"); } else { - response302Header(dos, "/user/login.html"); + httpResponse.redirect("/user/login.html"); } } else { log.debug("Cookie : {}", httpRequest.header("Cookie")); - byte[] body = Files.readAllBytes(new File("./webapp" + url).toPath()); - - if (url.endsWith(".css")){ - response200Header(dos, body.length, "css"); - }else { - response200Header(dos, body.length, "html"); - } - - responseBody(dos, body); + httpResponse.forward(url); } } catch (IOException e) { @@ -79,44 +72,4 @@ public void run() { } } - private void response302HeaderWithCookie(DataOutputStream dos, String url, String cookie, String cookiePath) { - try { - dos.writeBytes("HTTP/1.1 302 FOUND \r\n"); - dos.writeBytes("Location: " + url + "\r\n"); - dos.writeBytes("Set-Cookie: " + cookie + "; Path=" + cookiePath + "\r\n"); - dos.writeBytes("\r\n"); - } catch (IOException e) { - log.error(e.getMessage()); - } - } - - private void response302Header(DataOutputStream dos, String url) { - try { - dos.writeBytes("HTTP/1.1 302 FOUND \r\n"); - dos.writeBytes("Location: " + url + "\r\n"); - dos.writeBytes("\r\n"); - } catch (IOException e) { - log.error(e.getMessage()); - } - } - - private void response200Header(DataOutputStream dos, int lengthOfBodyContent, String contentType) { - try { - dos.writeBytes("HTTP/1.1 200 OK \r\n"); - dos.writeBytes("Content-Type: text/" + contentType + ";charset=utf-8\r\n"); - dos.writeBytes("Content-Length: " + lengthOfBodyContent + "\r\n"); - dos.writeBytes("\r\n"); - } 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()); - } - } } From 63aec98a4c22962e3bbfc43408ba71ba8b4cfa0a Mon Sep 17 00:00:00 2001 From: gisungchoi Date: Tue, 30 Mar 2021 11:08:29 +0900 Subject: [PATCH 10/17] test : ControllerKey MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -같은 httpMethod로 만든 controller key는 같아야한다. --- src/main/java/controller/ControllerKey.java | 10 +++++ .../java/controller/ControllerKeyTest.java | 45 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/main/java/controller/ControllerKey.java create mode 100644 src/test/java/controller/ControllerKeyTest.java diff --git a/src/main/java/controller/ControllerKey.java b/src/main/java/controller/ControllerKey.java new file mode 100644 index 0000000..85d75df --- /dev/null +++ b/src/main/java/controller/ControllerKey.java @@ -0,0 +1,10 @@ +package controller; + +import http.HttpMethod; + +public class ControllerKey { + + public ControllerKey(HttpMethod method, String url) { + + } +} diff --git a/src/test/java/controller/ControllerKeyTest.java b/src/test/java/controller/ControllerKeyTest.java new file mode 100644 index 0000000..f21de4d --- /dev/null +++ b/src/test/java/controller/ControllerKeyTest.java @@ -0,0 +1,45 @@ +package controller; + +import http.HttpMethod; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ControllerKeyTest { + + private SoftAssertions softly; + + @BeforeEach + public void beforeEach() { + softly = new SoftAssertions(); + } + + @AfterEach + public void afterEach() { + softly.assertAll(); + } + + @Test + @DisplayName("get /index.html") + void getIndex() { + String url = "/index.html"; + ControllerKey key1 = new ControllerKey(HttpMethod.GET, url); + ControllerKey key2 = new ControllerKey(HttpMethod.GET, url); + + softly.assertThat(key1).isEqualTo(key2); + softly.assertThat(key1.hashCode()).isEqualTo(key2.hashCode()); + } + + @Test + @DisplayName("post와 get은 달라야한다.") + void getAndPost() { + String url = "/index.html"; + ControllerKey key1 = new ControllerKey(HttpMethod.GET, url); + ControllerKey key2 = new ControllerKey(HttpMethod.POST, url); + + softly.assertThat(key1).isNotEqualTo(key2); + softly.assertThat(key1.hashCode()).isNotEqualTo(key2.hashCode()); + } +} \ No newline at end of file From 7729bf19b192d7359ce6db42273681b35cfe281f Mon Sep 17 00:00:00 2001 From: gisungchoi Date: Tue, 30 Mar 2021 11:14:30 +0900 Subject: [PATCH 11/17] feat : ControllerKey override equals hashCode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -동일성을 보장한다 --- src/main/java/controller/ControllerKey.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/java/controller/ControllerKey.java b/src/main/java/controller/ControllerKey.java index 85d75df..b2fe11d 100644 --- a/src/main/java/controller/ControllerKey.java +++ b/src/main/java/controller/ControllerKey.java @@ -2,9 +2,28 @@ import http.HttpMethod; +import java.util.Objects; + public class ControllerKey { + private HttpMethod method; + private String url; + public ControllerKey(HttpMethod method, String url) { + this.method = method; + this.url = url; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ControllerKey that = (ControllerKey) o; + return method == that.method && Objects.equals(url, that.url); + } + @Override + public int hashCode() { + return Objects.hash(method, url); } } From 66a923720b378e3972ac3ddfe06a263651efeb11 Mon Sep 17 00:00:00 2001 From: gisungchoi Date: Tue, 30 Mar 2021 11:57:14 +0900 Subject: [PATCH 12/17] refactor : Use Controller in RequestHandler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -컨트롤러 다형성를 이용해서 리퀘스트 핸들러를 리팩토링 --- src/main/java/controller/Controller.java | 10 ++++ .../java/controller/CreateUserController.java | 22 +++++++ .../java/controller/DefaultController.java | 14 +++++ .../java/controller/ListUserController.java | 17 ++++++ src/main/java/controller/LoginController.java | 25 ++++++++ src/main/java/webserver/RequestHandler.java | 57 +++++-------------- src/main/java/webserver/WebServer.java | 19 +++++-- 7 files changed, 116 insertions(+), 48 deletions(-) create mode 100644 src/main/java/controller/Controller.java create mode 100644 src/main/java/controller/CreateUserController.java create mode 100644 src/main/java/controller/DefaultController.java create mode 100644 src/main/java/controller/ListUserController.java create mode 100644 src/main/java/controller/LoginController.java diff --git a/src/main/java/controller/Controller.java b/src/main/java/controller/Controller.java new file mode 100644 index 0000000..4a517ef --- /dev/null +++ b/src/main/java/controller/Controller.java @@ -0,0 +1,10 @@ +package controller; + +import http.HttpRequest; +import http.HttpResponse; + +import java.io.IOException; + +public interface Controller { + void service(HttpRequest httpRequest, HttpResponse httpResponse) throws IOException; +} diff --git a/src/main/java/controller/CreateUserController.java b/src/main/java/controller/CreateUserController.java new file mode 100644 index 0000000..7e7d16b --- /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 java.io.IOException; + +public class CreateUserController implements Controller { + @Override + public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws IOException { + User user = new User( + httpRequest.data("userId"), + httpRequest.data("password"), + httpRequest.data("name"), + httpRequest.data("email") + ); + DataBase.addUser(user); + httpResponse.redirect("/index.html"); + } +} diff --git a/src/main/java/controller/DefaultController.java b/src/main/java/controller/DefaultController.java new file mode 100644 index 0000000..7204764 --- /dev/null +++ b/src/main/java/controller/DefaultController.java @@ -0,0 +1,14 @@ +package controller; + +import http.HttpRequest; +import http.HttpResponse; + +import java.io.IOException; + +public class DefaultController implements Controller { + + @Override + public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws IOException { + httpResponse.forward(httpRequest.getUrl()); + } +} diff --git a/src/main/java/controller/ListUserController.java b/src/main/java/controller/ListUserController.java new file mode 100644 index 0000000..df23944 --- /dev/null +++ b/src/main/java/controller/ListUserController.java @@ -0,0 +1,17 @@ +package controller; + +import http.HttpRequest; +import http.HttpResponse; + +import java.io.IOException; + +public class ListUserController implements Controller{ + @Override + public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws IOException { + if ("true".equals(httpRequest.cookie("logined"))) { + httpResponse.forward("/user/list.html"); + } else { + httpResponse.redirect("/user/login.html"); + } + } +} diff --git a/src/main/java/controller/LoginController.java b/src/main/java/controller/LoginController.java new file mode 100644 index 0000000..edef9a1 --- /dev/null +++ b/src/main/java/controller/LoginController.java @@ -0,0 +1,25 @@ +package controller; + +import db.DataBase; +import http.HttpRequest; +import http.HttpResponse; +import model.User; + +import java.io.IOException; + +public class LoginController implements Controller{ + @Override + public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws IOException { + User user = DataBase.findUserById(httpRequest.data("userId")); + if (user == null) { + httpResponse.addHeader("Set-Cookie", "logined=false; Path=/"); + httpResponse.redirect("/user/login_failed.html"); + } else if (user.checkPassword(httpRequest.data("password"))) { + httpResponse.addHeader("Set-Cookie", "logined=true; Path=/"); + httpResponse.redirect("/index.html"); + } else { + httpResponse.addHeader("Set-Cookie", "logined=false; Path=/"); + httpResponse.redirect("/user/login_failed.html"); + } + } +} diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index cada1c6..87c9bb6 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -2,22 +2,27 @@ import java.io.*; import java.net.Socket; -import java.nio.file.Files; +import java.util.Map; -import db.DataBase; +import controller.Controller; +import controller.ControllerKey; import http.HttpRequest; import http.HttpResponse; -import model.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class RequestHandler extends Thread { private static final Logger log = LoggerFactory.getLogger(RequestHandler.class); + private Map controllerMap; + private Controller defaultController; private Socket connection; - public RequestHandler(Socket connectionSocket) { + + public RequestHandler(Socket connectionSocket, Map controllerMap, Controller defaultController) { this.connection = connectionSocket; + this.controllerMap = controllerMap; + this.defaultController = defaultController; } public void run() { @@ -25,47 +30,13 @@ public void run() { connection.getPort()); try (InputStream in = connection.getInputStream(); OutputStream out = connection.getOutputStream()) { - // TODO 사용자 요청에 대한 처리는 이 곳에 구현하면 된다. HttpRequest httpRequest = HttpRequest.of(in); - String url = httpRequest.getUrl(); - HttpResponse httpResponse = new HttpResponse(out); - if ("/user/create".equals(url)) { - User user = new User( - httpRequest.data("userId"), - httpRequest.data("password"), - httpRequest.data("name"), - httpRequest.data("email") - ); - DataBase.addUser(user); - log.debug("user : {}", user); - httpResponse.redirect("/index.html"); - } else if ("/user/login".equals(url)) { - User user = DataBase.findUserById(httpRequest.data("userId")); - if (user == null) { - log.debug("Not Found"); - httpResponse.addHeader("Set-Cookie", "logined=false; Path=/"); - httpResponse.redirect("/user/login_failed.html"); - } else if (user.checkPassword(httpRequest.data("password"))) { - log.debug("Login success"); - httpResponse.addHeader("Set-Cookie", "logined=true; Path=/"); - httpResponse.redirect("/index.html"); - } else { - log.debug("Password was not matched"); - httpResponse.addHeader("Set-Cookie", "logined=false; Path=/"); - httpResponse.redirect("/user/login_failed.html"); - } - } else if ("/user/list".equals(url)) { - log.debug("Cookie : {}", httpRequest.header("Cookie")); - if ("true".equals(httpRequest.cookie("logined"))) { - httpResponse.forward("/user/list.html"); - } else { - httpResponse.redirect("/user/login.html"); - } - } else { - log.debug("Cookie : {}", httpRequest.header("Cookie")); - httpResponse.forward(url); - } + + ControllerKey controllerKey = new ControllerKey(httpRequest.method(), httpRequest.getUrl()); + Controller controller = controllerMap.getOrDefault(controllerKey, defaultController); + + controller.service(httpRequest, httpResponse); } catch (IOException e) { log.error(e.getMessage()); diff --git a/src/main/java/webserver/WebServer.java b/src/main/java/webserver/WebServer.java index 986e1be..46f7819 100644 --- a/src/main/java/webserver/WebServer.java +++ b/src/main/java/webserver/WebServer.java @@ -2,10 +2,11 @@ import java.net.ServerSocket; import java.net.Socket; -import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; +import java.util.Map; -import model.User; +import controller.*; +import http.HttpMethod; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,6 +14,15 @@ public class WebServer { private static final Logger log = LoggerFactory.getLogger(WebServer.class); private static final int DEFAULT_PORT = 8080; + private static final Controller defaultController = new DefaultController(); + private static final Map controllerMap = new HashMap<>(); + + static { + controllerMap.put(new ControllerKey(HttpMethod.POST, "/user/create"), new CreateUserController()); + controllerMap.put(new ControllerKey(HttpMethod.POST, "/user/login"), new LoginController()); + controllerMap.put(new ControllerKey(HttpMethod.GET, "/user/list"), new ListUserController()); + } + public static void main(String args[]) throws Exception { int port = 0; if (args == null || args.length == 0) { @@ -22,14 +32,13 @@ public static void main(String args[]) throws Exception { } // 서버소켓을 생성한다. 웹서버는 기본적으로 8080번 포트를 사용한다. - try (ServerSocket listenSocket = new ServerSocket(port)) { log.info("Web Application Server started {} port.", port); // 클라이언트가 연결될때까지 대기한다. Socket connection; while ((connection = listenSocket.accept()) != null) { - RequestHandler requestHandler = new RequestHandler(connection); + RequestHandler requestHandler = new RequestHandler(connection, controllerMap, defaultController); requestHandler.start(); } } From e84df0d09bd523174b8ead8f21e16708c4dbb122 Mon Sep 17 00:00:00 2001 From: ChoiGiSung <60220562+ChoiGiSung@users.noreply.github.com> Date: Tue, 30 Mar 2021 12:38:23 +0900 Subject: [PATCH 13/17] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index bc9c93b..5d52e80 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # 자바 웹 서버 +https://github.com/ChoiGiSung/java-was/wiki + ## 진행 방법 * 요구사항에 대한 구현을 완료한 후 자신의 **github 아이디**에 해당하는 브랜치에 Pull Request(이하 PR)를 통해 코드 리뷰 요청을 한다. From 168ef41f03a98481d1e55c44779606676df657c0 Mon Sep 17 00:00:00 2001 From: PizzaCola-K Date: Fri, 2 Apr 2021 11:30:24 +0900 Subject: [PATCH 14/17] refactor: Remove else MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 작성한 코드에서 else를 모두 제거 하였습니다. --- src/main/java/controller/ListUserController.java | 6 +++--- src/main/java/controller/LoginController.java | 15 +++++++-------- src/main/java/http/HttpResponse.java | 7 ++++--- src/main/java/webserver/WebServer.java | 16 +++++++--------- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/main/java/controller/ListUserController.java b/src/main/java/controller/ListUserController.java index df23944..80ff873 100644 --- a/src/main/java/controller/ListUserController.java +++ b/src/main/java/controller/ListUserController.java @@ -5,13 +5,13 @@ import java.io.IOException; -public class ListUserController implements Controller{ +public class ListUserController implements Controller { @Override public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws IOException { if ("true".equals(httpRequest.cookie("logined"))) { httpResponse.forward("/user/list.html"); - } else { - httpResponse.redirect("/user/login.html"); + return; } + httpResponse.redirect("/user/login.html"); } } diff --git a/src/main/java/controller/LoginController.java b/src/main/java/controller/LoginController.java index edef9a1..6fde938 100644 --- a/src/main/java/controller/LoginController.java +++ b/src/main/java/controller/LoginController.java @@ -7,19 +7,18 @@ import java.io.IOException; -public class LoginController implements Controller{ +public class LoginController implements Controller { @Override public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws IOException { User user = DataBase.findUserById(httpRequest.data("userId")); - if (user == null) { - httpResponse.addHeader("Set-Cookie", "logined=false; Path=/"); - httpResponse.redirect("/user/login_failed.html"); - } else if (user.checkPassword(httpRequest.data("password"))) { + + if (user != null && user.checkPassword(httpRequest.data("password"))) { httpResponse.addHeader("Set-Cookie", "logined=true; Path=/"); httpResponse.redirect("/index.html"); - } else { - httpResponse.addHeader("Set-Cookie", "logined=false; Path=/"); - httpResponse.redirect("/user/login_failed.html"); + return; } + + httpResponse.addHeader("Set-Cookie", "logined=false; Path=/"); + httpResponse.redirect("/user/login_failed.html"); } } diff --git a/src/main/java/http/HttpResponse.java b/src/main/java/http/HttpResponse.java index 0bb4f94..8f945e6 100644 --- a/src/main/java/http/HttpResponse.java +++ b/src/main/java/http/HttpResponse.java @@ -23,11 +23,12 @@ public void forward(String url) throws IOException { startLine = "HTTP/1.1 200 OK \r\n"; byte[] body = Files.readAllBytes(new File("./webapp" + url).toPath()); + String contentTypeValue = "text/html;charset=utf-8"; if (url.endsWith(".css")) { - addHeader("Content-Type", "text/css;charset=utf-8"); - } else { - addHeader("Content-Type", "text/html;charset=utf-8"); + contentTypeValue = "text/css;charset=utf-8"; } + + addHeader("Content-Type", contentTypeValue); addHeader("Content-Length", String.valueOf(body.length)); processHeaders(); diff --git a/src/main/java/webserver/WebServer.java b/src/main/java/webserver/WebServer.java index 46f7819..d343c58 100644 --- a/src/main/java/webserver/WebServer.java +++ b/src/main/java/webserver/WebServer.java @@ -1,15 +1,15 @@ package webserver; -import java.net.ServerSocket; -import java.net.Socket; -import java.util.HashMap; -import java.util.Map; - import controller.*; import http.HttpMethod; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.HashMap; +import java.util.Map; + public class WebServer { private static final Logger log = LoggerFactory.getLogger(WebServer.class); private static final int DEFAULT_PORT = 8080; @@ -24,10 +24,8 @@ public class WebServer { } public static void main(String args[]) throws Exception { - int port = 0; - if (args == null || args.length == 0) { - port = DEFAULT_PORT; - } else { + int port = DEFAULT_PORT; + if (args != null && args.length > 0) { port = Integer.parseInt(args[0]); } From aa77c93233e42fc5eec07ba6992ec60fe7022665 Mon Sep 17 00:00:00 2001 From: "DESKTOP-FL6VBV6\\chlrl" Date: Fri, 2 Apr 2021 17:27:05 +0900 Subject: [PATCH 15/17] test : RequestMapping test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -reflections library를 이용하는 test --- src/main/java/annotation/RequestMapping.java | 16 +++++++++++++ .../java/controller/CreateUserController.java | 3 +++ .../java/controller/ListUserController.java | 3 +++ src/main/java/controller/LoginController.java | 3 +++ src/test/java/annotation/ReflectionTest.java | 24 +++++++++++++++++++ 5 files changed, 49 insertions(+) create mode 100644 src/main/java/annotation/RequestMapping.java create mode 100644 src/test/java/annotation/ReflectionTest.java diff --git a/src/main/java/annotation/RequestMapping.java b/src/main/java/annotation/RequestMapping.java new file mode 100644 index 0000000..975b6bd --- /dev/null +++ b/src/main/java/annotation/RequestMapping.java @@ -0,0 +1,16 @@ +package annotation; + +import http.HttpMethod; +import http.HttpRequest; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface RequestMapping { + String path(); + HttpMethod method(); +} diff --git a/src/main/java/controller/CreateUserController.java b/src/main/java/controller/CreateUserController.java index 7e7d16b..a9175b4 100644 --- a/src/main/java/controller/CreateUserController.java +++ b/src/main/java/controller/CreateUserController.java @@ -1,12 +1,15 @@ package controller; +import annotation.RequestMapping; import db.DataBase; +import http.HttpMethod; import http.HttpRequest; import http.HttpResponse; import model.User; import java.io.IOException; +@RequestMapping(path = "/user/create", method = HttpMethod.POST) public class CreateUserController implements Controller { @Override public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws IOException { diff --git a/src/main/java/controller/ListUserController.java b/src/main/java/controller/ListUserController.java index 80ff873..8726e8d 100644 --- a/src/main/java/controller/ListUserController.java +++ b/src/main/java/controller/ListUserController.java @@ -1,10 +1,13 @@ package controller; +import annotation.RequestMapping; +import http.HttpMethod; import http.HttpRequest; import http.HttpResponse; import java.io.IOException; +@RequestMapping(path = "/user/list", method = HttpMethod.GET) public class ListUserController implements Controller { @Override public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws IOException { diff --git a/src/main/java/controller/LoginController.java b/src/main/java/controller/LoginController.java index 6fde938..bf1fd3d 100644 --- a/src/main/java/controller/LoginController.java +++ b/src/main/java/controller/LoginController.java @@ -1,12 +1,15 @@ package controller; +import annotation.RequestMapping; import db.DataBase; +import http.HttpMethod; import http.HttpRequest; import http.HttpResponse; import model.User; import java.io.IOException; +@RequestMapping(path = "/user/login", method = HttpMethod.POST) public class LoginController implements Controller { @Override public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws IOException { diff --git a/src/test/java/annotation/ReflectionTest.java b/src/test/java/annotation/ReflectionTest.java new file mode 100644 index 0000000..14e2070 --- /dev/null +++ b/src/test/java/annotation/ReflectionTest.java @@ -0,0 +1,24 @@ +package annotation; + +import org.junit.jupiter.api.Test; +import org.reflections.Reflections; + +import java.lang.annotation.Annotation; +import java.util.Set; + +public class ReflectionTest { + + @Test + void getAnnotation() throws ClassNotFoundException { + Class aClass = Class.forName("annotation.RequestMapping"); + + + Reflections reflections = new Reflections("controller"); + Set> getAno = reflections.getTypesAnnotatedWith(RequestMapping.class); + + for (Class aClass1 : getAno) { + RequestMapping declaredAnnotation = aClass1.getDeclaredAnnotation(RequestMapping.class); + System.out.println(declaredAnnotation); + } + } +} From 1473b041bb11c18eed97a1a6570d573b84ea156c Mon Sep 17 00:00:00 2001 From: "DESKTOP-FL6VBV6\\chlrl" Date: Fri, 2 Apr 2021 17:34:26 +0900 Subject: [PATCH 16/17] feat : RequestMapping using reflactions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -리플렉션을 활용해 컨트롤러 등록 --- src/main/java/webserver/WebServer.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/main/java/webserver/WebServer.java b/src/main/java/webserver/WebServer.java index d343c58..ccaeb0a 100644 --- a/src/main/java/webserver/WebServer.java +++ b/src/main/java/webserver/WebServer.java @@ -1,7 +1,9 @@ package webserver; +import annotation.RequestMapping; import controller.*; import http.HttpMethod; +import org.reflections.Reflections; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -9,6 +11,7 @@ import java.net.Socket; import java.util.HashMap; import java.util.Map; +import java.util.Set; public class WebServer { private static final Logger log = LoggerFactory.getLogger(WebServer.class); @@ -18,9 +21,18 @@ public class WebServer { private static final Map controllerMap = new HashMap<>(); static { - controllerMap.put(new ControllerKey(HttpMethod.POST, "/user/create"), new CreateUserController()); - controllerMap.put(new ControllerKey(HttpMethod.POST, "/user/login"), new LoginController()); - controllerMap.put(new ControllerKey(HttpMethod.GET, "/user/list"), new ListUserController()); + + Reflections reflections = new Reflections("controller"); + Set> requestMappingControllers = reflections.getTypesAnnotatedWith(RequestMapping.class); + for (Class controller : requestMappingControllers) { + RequestMapping annotation = controller.getAnnotation(RequestMapping.class); + try { + controllerMap.put(new ControllerKey(annotation.method(), annotation.path()), (Controller) controller.newInstance()); + } catch (InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + + } } public static void main(String args[]) throws Exception { From ef37ef5b47f949c8a0418eca64d03b53b198fc05 Mon Sep 17 00:00:00 2001 From: "DESKTOP-FL6VBV6\\chlrl" Date: Fri, 2 Apr 2021 17:45:12 +0900 Subject: [PATCH 17/17] refactor : HandlerAdapter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -핸들러를 구성하는 맵을 만드는 어댑터를 추가 --- src/main/java/webserver/HandlerAdapter.java | 34 +++++++++++++++++++++ src/main/java/webserver/RequestHandler.java | 11 +++---- src/main/java/webserver/WebServer.java | 21 ++----------- 3 files changed, 41 insertions(+), 25 deletions(-) create mode 100644 src/main/java/webserver/HandlerAdapter.java diff --git a/src/main/java/webserver/HandlerAdapter.java b/src/main/java/webserver/HandlerAdapter.java new file mode 100644 index 0000000..df86aab --- /dev/null +++ b/src/main/java/webserver/HandlerAdapter.java @@ -0,0 +1,34 @@ +package webserver; + +import annotation.RequestMapping; +import controller.Controller; +import controller.ControllerKey; +import controller.DefaultController; +import org.reflections.Reflections; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class HandlerAdapter { + private static final Controller defaultController = new DefaultController(); + private static final Map controllerMap = new HashMap<>(); + + public HandlerAdapter() { + Reflections reflections = new Reflections("controller"); + Set> requestMappingControllers = reflections.getTypesAnnotatedWith(RequestMapping.class); + for (Class controller : requestMappingControllers) { + RequestMapping annotation = controller.getAnnotation(RequestMapping.class); + try { + controllerMap.put(new ControllerKey(annotation.method(), annotation.path()), (Controller) controller.newInstance()); + } catch (InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + + } + } + + public Controller controller(ControllerKey key){ + return controllerMap.getOrDefault(key,defaultController); + } +} diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index 87c9bb6..3628e7e 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -14,15 +14,13 @@ public class RequestHandler extends Thread { private static final Logger log = LoggerFactory.getLogger(RequestHandler.class); - private Map controllerMap; - private Controller defaultController; private Socket connection; + private HandlerAdapter adapter; - public RequestHandler(Socket connectionSocket, Map controllerMap, Controller defaultController) { + public RequestHandler(Socket connectionSocket,HandlerAdapter adapter) { this.connection = connectionSocket; - this.controllerMap = controllerMap; - this.defaultController = defaultController; + this.adapter = adapter; } public void run() { @@ -33,8 +31,7 @@ public void run() { HttpRequest httpRequest = HttpRequest.of(in); HttpResponse httpResponse = new HttpResponse(out); - ControllerKey controllerKey = new ControllerKey(httpRequest.method(), httpRequest.getUrl()); - Controller controller = controllerMap.getOrDefault(controllerKey, defaultController); + Controller controller = adapter.controller(new ControllerKey(httpRequest.method(), httpRequest.getUrl())); controller.service(httpRequest, httpResponse); diff --git a/src/main/java/webserver/WebServer.java b/src/main/java/webserver/WebServer.java index ccaeb0a..b072bd4 100644 --- a/src/main/java/webserver/WebServer.java +++ b/src/main/java/webserver/WebServer.java @@ -17,29 +17,14 @@ public class WebServer { private static final Logger log = LoggerFactory.getLogger(WebServer.class); private static final int DEFAULT_PORT = 8080; - private static final Controller defaultController = new DefaultController(); - private static final Map controllerMap = new HashMap<>(); - - static { - - Reflections reflections = new Reflections("controller"); - Set> requestMappingControllers = reflections.getTypesAnnotatedWith(RequestMapping.class); - for (Class controller : requestMappingControllers) { - RequestMapping annotation = controller.getAnnotation(RequestMapping.class); - try { - controllerMap.put(new ControllerKey(annotation.method(), annotation.path()), (Controller) controller.newInstance()); - } catch (InstantiationException | IllegalAccessException e) { - e.printStackTrace(); - } - - } - } public static void main(String args[]) throws Exception { int port = DEFAULT_PORT; if (args != null && args.length > 0) { port = Integer.parseInt(args[0]); } + HandlerAdapter adapter = new HandlerAdapter(); + // 서버소켓을 생성한다. 웹서버는 기본적으로 8080번 포트를 사용한다. try (ServerSocket listenSocket = new ServerSocket(port)) { @@ -48,7 +33,7 @@ public static void main(String args[]) throws Exception { // 클라이언트가 연결될때까지 대기한다. Socket connection; while ((connection = listenSocket.accept()) != null) { - RequestHandler requestHandler = new RequestHandler(connection, controllerMap, defaultController); + RequestHandler requestHandler = new RequestHandler(connection,adapter); requestHandler.start(); } }