From da100b107f1488ea3a78f57bf4f49ac3d129c968 Mon Sep 17 00:00:00 2001 From: mak-father <67811880+nathan29849@users.noreply.github.com> Date: Wed, 27 Jul 2022 11:05:51 +0900 Subject: [PATCH 01/14] Update feature_request.md --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index d3a68d0..cc200d1 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,4 +1,4 @@ - +--- name: Feature Request about: Suggest new Feature Request for this project title: "{ISSUE_TITLE}" From a71d643ef2d731a362a4011478ce39a31c313dd0 Mon Sep 17 00:00:00 2001 From: nathan29849 Date: Wed, 27 Jul 2022 17:56:52 +0900 Subject: [PATCH 02/14] =?UTF-8?q?refactor:=20[#12]=20HttpRequestHeaders=20?= =?UTF-8?q?Test=20=EB=B0=8F=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/http/request/HttpRequest.java | 15 ++++---- .../java/http/request/RequestHeaders.java | 21 ++++++++--- src/main/java/http/request/RequestLine.java | 4 ++- src/main/java/http/request/RequestURI.java | 2 +- src/main/java/util/HttpRequestUtils.java | 4 +-- .../java/http/request/RequestHeadersTest.java | 35 ++++++++++++++++++ .../java/http/request/RequestLineTest.java | 36 +++++++++++++++++++ src/test/java/util/HttpRequestUtilsTest.java | 2 +- 8 files changed, 101 insertions(+), 18 deletions(-) create mode 100644 src/test/java/http/request/RequestHeadersTest.java create mode 100644 src/test/java/http/request/RequestLineTest.java diff --git a/src/main/java/http/request/HttpRequest.java b/src/main/java/http/request/HttpRequest.java index 4d1ceee..5718aa2 100644 --- a/src/main/java/http/request/HttpRequest.java +++ b/src/main/java/http/request/HttpRequest.java @@ -27,19 +27,16 @@ private HttpRequest(RequestLine requestLine, RequestHeaders requestHeaders, Requ } public static HttpRequest from(InputStream in) throws IOException { - InputStreamReader inputStreamReader = new InputStreamReader(in, StandardCharsets.UTF_8); // 왜 안먹힐까? - BufferedReader bufferedReader = new BufferedReader(inputStreamReader); - String line = URLDecoder.decode(bufferedReader.readLine(), StandardCharsets.UTF_8); // 왜 한 번 더 해줘야 먹힐까? - 위에서 안먹히는 걸까? - - RequestLine requestLine = HttpRequestUtils.parseRequestLine(line); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); + String line = URLDecoder.decode(bufferedReader.readLine(), StandardCharsets.UTF_8); + RequestLine requestLine = RequestLine.from(line); RequestHeaders requestHeaders = new RequestHeaders(); while (!line.equals((""))) { line = URLDecoder.decode(bufferedReader.readLine(), StandardCharsets.UTF_8); - Pair pair = HttpRequestUtils.parseHeader(line); - requestHeaders.addHeader(pair); + requestHeaders.addHeader(line); } - if (requestHeaders.containsKey("Content-Length")) { - int contentLength = Integer.parseInt(requestHeaders.getHeader("Content-Length")); + if (requestHeaders.hasContentLength()) { + int contentLength = Integer.parseInt(requestHeaders.getContentLength()); String messageBody = URLDecoder.decode( IOUtils.readData(bufferedReader, contentLength), StandardCharsets.UTF_8 diff --git a/src/main/java/http/request/RequestHeaders.java b/src/main/java/http/request/RequestHeaders.java index c444ff7..2809c8f 100644 --- a/src/main/java/http/request/RequestHeaders.java +++ b/src/main/java/http/request/RequestHeaders.java @@ -2,16 +2,21 @@ import java.util.HashMap; import java.util.Map; +import util.HttpRequestUtils; import util.HttpRequestUtils.Pair; public class RequestHeaders { + private static final String CONTENT_LENGTH = "Content-Length"; + private static final String COOKIE = "Cookie"; + private final Map headers = new HashMap<>(); - public void addHeader(Pair pair) { - if (pair == null) { + public void addHeader(String line) { + if (line == null) { return; } + Pair pair = HttpRequestUtils.parseHeader(line); headers.put(pair.getKey(), pair.getValue()); } @@ -19,13 +24,21 @@ public String getHeader(String key) { return headers.get(key); } + public boolean hasContentLength() { + return headers.containsKey(CONTENT_LENGTH); + } + public boolean containsKey(String key) { return headers.containsKey(key); } + public String getContentLength() { + return headers.get(CONTENT_LENGTH); + } + public String getCookie() { - if (headers.containsKey("Cookie")) { - return headers.get("Cookie"); + if (headers.containsKey(COOKIE)) { + return headers.get(COOKIE); } return null; } diff --git a/src/main/java/http/request/RequestLine.java b/src/main/java/http/request/RequestLine.java index abca77a..4615d9d 100644 --- a/src/main/java/http/request/RequestLine.java +++ b/src/main/java/http/request/RequestLine.java @@ -1,6 +1,7 @@ package http.request; import http.HttpVersion; +import util.HttpRequestUtils; public class RequestLine { @@ -14,7 +15,8 @@ public RequestLine(HttpMethod httpMethod, RequestURI requestUri, HttpVersion htt this.httpVersion = httpVersion; } - public static RequestLine from(String[] tokens) { + public static RequestLine from(String line) { + String[] tokens = HttpRequestUtils.parseRequestLine(line); HttpMethod httpMethod = HttpMethod.valueOf(tokens[0]); RequestURI requestURI = RequestURI.from(tokens[1]); HttpVersion httpVersion = new HttpVersion(tokens[2]); diff --git a/src/main/java/http/request/RequestURI.java b/src/main/java/http/request/RequestURI.java index d4c910d..7ced690 100644 --- a/src/main/java/http/request/RequestURI.java +++ b/src/main/java/http/request/RequestURI.java @@ -33,7 +33,7 @@ public String getQueryString() { @Override public String toString() { if (queryString != null) { - return path + queryString; + return path + "?" + queryString; } return path; } diff --git a/src/main/java/util/HttpRequestUtils.java b/src/main/java/util/HttpRequestUtils.java index dccf3b5..bf67e4c 100644 --- a/src/main/java/util/HttpRequestUtils.java +++ b/src/main/java/util/HttpRequestUtils.java @@ -14,8 +14,8 @@ public class HttpRequestUtils { * @param queryString은 URL에서 ? 이후에 전달되는 field1=value1&field2=value2 형식임 * @return */ - public static RequestLine parseRequestLine(String requestLine) { - return RequestLine.from(requestLine.split(" ")); + public static String[] parseRequestLine(String line) { + return line.split(" "); } public static Map parseQueryString(String queryString) { diff --git a/src/test/java/http/request/RequestHeadersTest.java b/src/test/java/http/request/RequestHeadersTest.java new file mode 100644 index 0000000..b13a3aa --- /dev/null +++ b/src/test/java/http/request/RequestHeadersTest.java @@ -0,0 +1,35 @@ +package http.request; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +@DisplayName("RequestHeaders 클래스") +class RequestHeadersTest { + + @Nested + @DisplayName("addHeader 메소드는") + class Describe_addHeader { + + @Nested + @DisplayName("String(line)을 인자로 받으면") + class Context_with_String { + + @Test + @DisplayName("headers에 값을 key, value로 넣는다.") + void it_puts_in_headers() { + + RequestHeaders requestHeaders = new RequestHeaders(); + requestHeaders.addHeader("Host: localhost:8080"); + requestHeaders.addHeader("Connection: keep-alive"); + requestHeaders.addHeader("Accept: */*"); + + assertThat(requestHeaders.getHeader("Host")).isEqualTo("localhost:8080"); + assertThat(requestHeaders.getHeader("Connection")).isEqualTo("keep-alive"); + assertThat(requestHeaders.getHeader("Accept")).isEqualTo("*/*"); + } + } + } +} diff --git a/src/test/java/http/request/RequestLineTest.java b/src/test/java/http/request/RequestLineTest.java new file mode 100644 index 0000000..9e13ea7 --- /dev/null +++ b/src/test/java/http/request/RequestLineTest.java @@ -0,0 +1,36 @@ +package http.request; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +@DisplayName("RequestLine 클래스") +class RequestLineTest { + + @Nested + @DisplayName("from 메소드는") + class Describe_from{ + + @Nested + @DisplayName("파싱된 Request Line이 주어진다면") + class Context_with_parsed_RequestLine { + + @Test + @DisplayName("RequestLine 객체를 반환한다") + void it_returns_a_RequestLine() { + String rawHttpRequestLine = "GET /index.html?userId=3333 HTTP/1.1"; + RequestLine requestLine = RequestLine.from(rawHttpRequestLine); + + assertThat(requestLine.getHttpMethod()).isEqualTo(HttpMethod.GET); + assertThat(requestLine.getRequestUri()).hasToString("/index.html?userId=3333"); + assertThat(requestLine.getHttpVersion().getVersion()).isEqualTo("HTTP/1.1"); + + } + + } + } + + +} diff --git a/src/test/java/util/HttpRequestUtilsTest.java b/src/test/java/util/HttpRequestUtilsTest.java index 54cc874..dd32cbe 100644 --- a/src/test/java/util/HttpRequestUtilsTest.java +++ b/src/test/java/util/HttpRequestUtilsTest.java @@ -17,7 +17,7 @@ public class HttpRequestUtilsTest { @Test void RequestLine을_파싱하여_분류할_수_있다() { String line ="GET /index.html HTTP/1.1"; - RequestLine requestLine = HttpRequestUtils.parseRequestLine(line); + RequestLine requestLine = RequestLine.from(line); RequestURI requestUri = requestLine.getRequestUri(); HttpVersion httpVersion = requestLine.getHttpVersion(); From 0641a75e24ed2a179a1f11cfd29a0c429c407be3 Mon Sep 17 00:00:00 2001 From: nathan29849 Date: Wed, 27 Jul 2022 18:29:42 +0900 Subject: [PATCH 03/14] refactor: [#12] HttpRequestMessageBody Test --- src/main/java/handler/RequestHandler.java | 6 ++- src/main/java/http/request/HttpRequest.java | 11 +---- .../java/http/request/RequestHeaders.java | 15 +++---- .../java/http/request/RequestMessageBody.java | 12 ++++++ .../java/http/request/RequestHeadersTest.java | 2 +- .../http/request/RequestMessageBodyTest.java | 42 +++++++++++++++++++ 6 files changed, 67 insertions(+), 21 deletions(-) create mode 100644 src/test/java/http/request/RequestMessageBodyTest.java diff --git a/src/main/java/handler/RequestHandler.java b/src/main/java/handler/RequestHandler.java index 4ed8d09..2bba327 100644 --- a/src/main/java/handler/RequestHandler.java +++ b/src/main/java/handler/RequestHandler.java @@ -38,6 +38,7 @@ public void run() { RequestLine requestLine = httpRequest.getRequestLine(); log.info("RequestLine= {}", requestLine); RequestURI requestUri = requestLine.getRequestUri(); + log.info("RequestURI= {}", requestUri); String url = requestUri.getPath(); if (requestLine.getHttpMethod().equals(HttpMethod.GET)) { @@ -49,14 +50,15 @@ public void run() { RequestHeaders requestHeaders = httpRequest.getRequestHeaders(); String cookies = requestHeaders.getCookie(); log.debug("Cookie= {}", cookies); - if (cookies == null) { + if (!requestHeaders.hasCookie()) { httpResponse = HttpResponse.ok("/user/login.html"); } - if (cookies != null) { + if (requestHeaders.hasCookie()) { Map parseCookies = HttpRequestUtils.parseCookies(cookies); if (parseCookies.get("logined").equals("false")) { httpResponse = HttpResponse.ok("/user/login.html"); + httpResponse.flush(out); } httpResponse = HttpResponse.ok("/user/list.html", UserDataBase.findAll()); } diff --git a/src/main/java/http/request/HttpRequest.java b/src/main/java/http/request/HttpRequest.java index 5718aa2..70c3b5e 100644 --- a/src/main/java/http/request/HttpRequest.java +++ b/src/main/java/http/request/HttpRequest.java @@ -6,9 +6,6 @@ import java.io.InputStreamReader; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; -import util.HttpRequestUtils; -import util.HttpRequestUtils.Pair; -import util.IOUtils; public class HttpRequest { @@ -36,12 +33,8 @@ public static HttpRequest from(InputStream in) throws IOException { requestHeaders.addHeader(line); } if (requestHeaders.hasContentLength()) { - int contentLength = Integer.parseInt(requestHeaders.getContentLength()); - String messageBody = URLDecoder.decode( - IOUtils.readData(bufferedReader, contentLength), - StandardCharsets.UTF_8 - ); - RequestMessageBody requestMessageBody = new RequestMessageBody(messageBody); + RequestMessageBody requestMessageBody = RequestMessageBody + .from(bufferedReader, requestHeaders.getContentLength()); return new HttpRequest(requestLine, requestHeaders, requestMessageBody); } return new HttpRequest(requestLine, requestHeaders); diff --git a/src/main/java/http/request/RequestHeaders.java b/src/main/java/http/request/RequestHeaders.java index 2809c8f..b3d273c 100644 --- a/src/main/java/http/request/RequestHeaders.java +++ b/src/main/java/http/request/RequestHeaders.java @@ -13,7 +13,7 @@ public class RequestHeaders { private final Map headers = new HashMap<>(); public void addHeader(String line) { - if (line == null) { + if (line.isEmpty() || line.isBlank()) { return; } Pair pair = HttpRequestUtils.parseHeader(line); @@ -28,18 +28,15 @@ public boolean hasContentLength() { return headers.containsKey(CONTENT_LENGTH); } - public boolean containsKey(String key) { - return headers.containsKey(key); + public int getContentLength() { + return Integer.parseInt(headers.get(CONTENT_LENGTH)); } - public String getContentLength() { - return headers.get(CONTENT_LENGTH); + public boolean hasCookie() { + return headers.containsKey(COOKIE); } public String getCookie() { - if (headers.containsKey(COOKIE)) { - return headers.get(COOKIE); - } - return null; + return headers.get(COOKIE); } } diff --git a/src/main/java/http/request/RequestMessageBody.java b/src/main/java/http/request/RequestMessageBody.java index 750fca3..3f674a2 100644 --- a/src/main/java/http/request/RequestMessageBody.java +++ b/src/main/java/http/request/RequestMessageBody.java @@ -1,5 +1,11 @@ package http.request; +import java.io.BufferedReader; +import java.io.IOException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import util.IOUtils; + public class RequestMessageBody { private final String messageBody; @@ -8,6 +14,12 @@ public RequestMessageBody(String messageBody) { this.messageBody = messageBody; } + public static RequestMessageBody from(BufferedReader bufferedReader, int contentLength) + throws IOException { + String messageBody = URLDecoder.decode(IOUtils.readData(bufferedReader, contentLength), StandardCharsets.UTF_8); + return new RequestMessageBody(messageBody); + } + public String getMessageBody() { return messageBody; } diff --git a/src/test/java/http/request/RequestHeadersTest.java b/src/test/java/http/request/RequestHeadersTest.java index b13a3aa..6a06a2c 100644 --- a/src/test/java/http/request/RequestHeadersTest.java +++ b/src/test/java/http/request/RequestHeadersTest.java @@ -18,7 +18,7 @@ class Describe_addHeader { class Context_with_String { @Test - @DisplayName("headers에 값을 key, value로 넣는다.") + @DisplayName("headers에 값을 key, value로 넣는다") void it_puts_in_headers() { RequestHeaders requestHeaders = new RequestHeaders(); diff --git a/src/test/java/http/request/RequestMessageBodyTest.java b/src/test/java/http/request/RequestMessageBodyTest.java new file mode 100644 index 0000000..5f27700 --- /dev/null +++ b/src/test/java/http/request/RequestMessageBodyTest.java @@ -0,0 +1,42 @@ +package http.request; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +@DisplayName("RequestMessageBody 클래스") +class RequestMessageBodyTest { + + @Nested + @DisplayName("from 메소드는") + class Describe_from { + + @Nested + @DisplayName("BufferedReader 객체와 String(ContentLength)를 인자로 받으면") + class Context_with_BufferedRader_and_String { + + @Test + @DisplayName("새로운 RequestMessageBody 객체를 반환한다") + void it_returns_new_RequestMessageBody_instance() throws IOException { + //given + String rawMessageBody = "userId=nathan&password=123123&name=나단&email=phs5731@nav.com"; + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(rawMessageBody.getBytes()); + BufferedReader br = new BufferedReader(new InputStreamReader(byteArrayInputStream)); + + //when + RequestMessageBody requestMessageBody = RequestMessageBody.from(br, rawMessageBody.length()); + + //then + Assertions.assertThat(requestMessageBody.getMessageBody()) + .isEqualTo("userId=nathan&password=123123&name=나단&email=phs5731@nav.com"); + } + } + } + +} From ce1c7bc7667a829e9866456333b24338c403b022 Mon Sep 17 00:00:00 2001 From: nathan29849 Date: Wed, 27 Jul 2022 19:09:12 +0900 Subject: [PATCH 04/14] =?UTF-8?q?feat:=20[#12]=20HttpRequestTest=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/http/request/HttpRequestTest.java | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 src/test/java/http/request/HttpRequestTest.java diff --git a/src/test/java/http/request/HttpRequestTest.java b/src/test/java/http/request/HttpRequestTest.java new file mode 100644 index 0000000..3f8b9e3 --- /dev/null +++ b/src/test/java/http/request/HttpRequestTest.java @@ -0,0 +1,87 @@ +package http.request; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +@DisplayName("HttpRequest 클래스") +class HttpRequestTest { + + @Nested + @DisplayName("from 메소드는") + class Describe_from { + + @Nested + @DisplayName("[HTTP GET REQUEST] InputStream 객체를 인자로 받으면") + class Context_with_GET_InputStream_instance { + + @Test + @DisplayName("새로운 GET HttpRequest 객체를 반환한다.") + void it_returns_new_GET_HttpRequest_instance() throws IOException { + //given + String str = "GET /index.html HTTP/1.1\r\n" + + "Host: localhost:8080\r\n" + + "Connection: keep-alive\r\n" + + "Accept: */*\r\n" + + "\r\n"; + + //when + ByteArrayInputStream in = new ByteArrayInputStream(str.getBytes()); + HttpRequest httpRequest = HttpRequest.from(in); + RequestLine requestLine = httpRequest.getRequestLine(); + RequestHeaders requestHeaders = httpRequest.getRequestHeaders(); + + //then + assertThat(requestLine.getHttpMethod()).isEqualTo(HttpMethod.GET); + assertThat(requestLine.getRequestUri()).hasToString("/index.html"); + assertThat(requestHeaders.getHeader("Host")).isEqualTo("localhost:8080"); + assertThat(requestHeaders.getHeader("Connection")).isEqualTo("keep-alive"); + assertThat(requestHeaders.getHeader("Accept")).isEqualTo("*/*"); + assertThat(requestHeaders.hasContentLength()).isFalse(); + } + } + + @Nested + @DisplayName("[HTTP POST REQUEST] InputStream 객체를 인자로 받으면") + class Context_with_POST_InputStream_instance { + + @Test + @DisplayName("새로운 POST HttpRequest 객체를 반환한다.") + void it_returns_new_POST_HttpRequest_instance() throws IOException { + //given + String str = "POST /user/create HTTP/1.1\r\n" + + "Host: localhost:8080\r\n" + + "Connection: keep-alive\r\n" + + "Content-Length: 58\r\n" + + "Content-Type: application/x-www-form-urlencoded\r\n" + + "Accept: */*\r\n" + + "\r\n" + +"userId=nathan&password=123123&name=나단&email=nathan@dev.com"; + //TODO Content-Length, Content-Type 추가 해야한다. + //when + ByteArrayInputStream in = new ByteArrayInputStream(str.getBytes()); + HttpRequest httpRequest = HttpRequest.from(in); + RequestLine requestLine = httpRequest.getRequestLine(); + RequestHeaders requestHeaders = httpRequest.getRequestHeaders(); + RequestMessageBody requestMessageBody = httpRequest.getRequestMessageBody(); + + //then + assertThat(requestLine.getHttpMethod()).isEqualTo(HttpMethod.POST); + assertThat(requestLine.getRequestUri()).hasToString("/user/create"); + assertThat(requestHeaders.getHeader("Host")).isEqualTo("localhost:8080"); + assertThat(requestHeaders.getHeader("Connection")).isEqualTo("keep-alive"); + assertThat(requestHeaders.getHeader("Accept")).isEqualTo("*/*"); + assertThat(requestHeaders.hasContentLength()).isTrue(); + assertThat(requestHeaders.getContentLength()).isEqualTo(58); + assertThat(requestHeaders.getHeader("Content-Type")).isEqualTo( + "application/x-www-form-urlencoded"); + assertThat(requestMessageBody.getMessageBody()).isEqualTo( + "userId=nathan&password=123123&name=나단&email=nathan@dev.com"); + } + } + } +} From 523b4b1e43a64ac6d086b680d2275193e5c113a1 Mon Sep 17 00:00:00 2001 From: nathan29849 Date: Fri, 29 Jul 2022 16:16:06 +0900 Subject: [PATCH 05/14] =?UTF-8?q?feat:=20HttpResponse=20Test=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit related to: #13 --- src/main/java/http/response/HttpResponse.java | 10 -- .../http/response/ResponseMessageBody.java | 7 + .../java/http/response/HttpResponseTest.java | 131 ++++++++++++++++++ 3 files changed, 138 insertions(+), 10 deletions(-) create mode 100644 src/test/java/http/response/HttpResponseTest.java diff --git a/src/main/java/http/response/HttpResponse.java b/src/main/java/http/response/HttpResponse.java index 3e7b97e..8ba5562 100644 --- a/src/main/java/http/response/HttpResponse.java +++ b/src/main/java/http/response/HttpResponse.java @@ -106,16 +106,6 @@ public static HttpResponse found(String redirectURI, boolean cookie) { return new HttpResponse(statusLine, responseHeaders); } - public static HttpResponse notHTML(String extension, String viewName) throws IOException { - byte[] messageBody = Files.readAllBytes(new File("./webapp/" + viewName).toPath()); - StatusLine statusLine = new StatusLine(new HttpVersion("HTTP/1.1"), HttpResponseStatus.OK); - ResponseHeaders responseHeaders = new ResponseHeaders(); - responseHeaders.setAccept("text/"+extension+", */*; q=0.1"); - responseHeaders.setContentLength(messageBody.length); - ResponseMessageBody responseMessageBody = new ResponseMessageBody(messageBody); - return new HttpResponse(statusLine, responseHeaders, responseMessageBody); - } - public void flush(OutputStream out) throws IOException { DataOutputStream dos = new DataOutputStream(out); dos.writeBytes(statusLine.toString() + " \r\n"); diff --git a/src/main/java/http/response/ResponseMessageBody.java b/src/main/java/http/response/ResponseMessageBody.java index 7b49c64..1e7bb20 100644 --- a/src/main/java/http/response/ResponseMessageBody.java +++ b/src/main/java/http/response/ResponseMessageBody.java @@ -1,5 +1,7 @@ package http.response; +import java.nio.charset.StandardCharsets; + public class ResponseMessageBody { private final byte[] messageBody; @@ -11,4 +13,9 @@ public ResponseMessageBody(byte[] messageBody) { public byte[] getMessageBody() { return messageBody; } + + @Override + public String toString() { + return new String(messageBody, StandardCharsets.UTF_8); + } } diff --git a/src/test/java/http/response/HttpResponseTest.java b/src/test/java/http/response/HttpResponseTest.java new file mode 100644 index 0000000..046525e --- /dev/null +++ b/src/test/java/http/response/HttpResponseTest.java @@ -0,0 +1,131 @@ +package http.response; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +@DisplayName("HttpResponse 클래스") +class HttpResponseTest { + + @Nested + @DisplayName("[HTTP RESPONSE 200 OK] ok 메소드는") + class Describe_ok { + + @Nested + @DisplayName("아무것도 인자를 받지 않으면") + class Context_with_null { + + @Test + @DisplayName("default 200 OK HttpResponse 객체를 반환한다.") + void dit_returns_default_200_OK_HttpResponse_instance() { + //when + HttpResponse response = HttpResponse.ok(); + StatusLine statusLine = response.getStatusLine(); + ResponseHeaders responseHeaders = response.getResponseHeaders(); + + //then + assertThat(statusLine).hasToString("HTTP/1.1 200 OK"); + assertThat(responseHeaders.getHeader("Content-Type")).isEqualTo( + "text/plain;charset=utf-8"); + assertThat(responseHeaders.getHeader("Content-Length")).isEqualTo("11"); + } + + } + + @Nested + @DisplayName("String(viewName)을 하나만 받을 때") + class Context_with_a_String { + + @Test + @DisplayName("해당 viewName의 파일이 있다면, 그 파일을 담은 200 OK HttpResponse 객체를 반환한다.") + void it_returns_viewName_OK_HttpResponse_instance() throws IOException { + //given + String viewName = "/index.html"; + byte[] messageBody = Files.readAllBytes(new File("./webapp" + viewName).toPath()); + + //when + HttpResponse response = HttpResponse.ok(viewName); + StatusLine statusLine = response.getStatusLine(); + ResponseHeaders responseHeaders = response.getResponseHeaders(); + + //then + assertThat(statusLine).hasToString("HTTP/1.1 200 OK"); + assertThat(responseHeaders.getHeader("Content-Type")).isEqualTo( + "text/html;charset=utf-8"); + assertThat(responseHeaders.getHeader("Accept")).isEqualTo("text/html, */*; q=0.1"); + assertThat(responseHeaders.getHeader("Content-Length")).isEqualTo(String.valueOf(messageBody.length)); + } + + @Test + @DisplayName("해당 viewName의 파일이 없다면, NoSuchFileException을 던진다.") + void it_throws_NoSuchFileException() throws IOException { + //given + String viewName = "/indexing.html"; + + //then + assertThatThrownBy(() -> HttpResponse.ok(viewName)).isInstanceOf( + NoSuchFileException.class); + } + + } + } + + @Nested + @DisplayName("[HTTP RESPONSE 302 Found] found 메소드는") + class Discribe_found{ + + @Nested + @DisplayName("String(redirectURI)를 하나만 받으면") + class Context_with_a_String { + + @Test + @DisplayName("해당 인자를 Location으로 갖는 302 Found HttpResponse 객체를 반환한다.") + void it_returns_302_Found_HttpResponse_instance() { + //given + String redirectURI = "/index.html"; + + //when + HttpResponse response = HttpResponse.found(redirectURI); + StatusLine statusLine = response.getStatusLine(); + ResponseHeaders responseHeaders = response.getResponseHeaders(); + + + //then + assertThat(statusLine).hasToString("HTTP/1.1 302 Found"); + assertThat(responseHeaders.getHeader("Location")).isEqualTo("/index.html"); + } + } + + @Nested + @DisplayName("String(redirectURI)와 boolean(Cookie)를 받으면") + class Context_with_a_String_and_a_Cookie { + + @Test + @DisplayName("해당 인자를 Location으로 갖고, Cookie가 세팅된 302 Found HttpResponse 객체를 반환한다.") + void it_returns_302_Found_HttpResponse_instance_with_Cookie() { + //given + String redirectURI = "/index.html"; + boolean login_cookie = true; + + //when + HttpResponse response = HttpResponse.found(redirectURI, login_cookie); + StatusLine statusLine = response.getStatusLine(); + ResponseHeaders responseHeaders = response.getResponseHeaders(); + + + //then + assertThat(statusLine).hasToString("HTTP/1.1 302 Found"); + assertThat(responseHeaders.getHeader("Location")).isEqualTo("/index.html"); + assertThat(responseHeaders.getHeader("Set-Cookie")).isEqualTo("logined=true; path=/;"); + } + } + } +} From 86e511a73f18418bb0a4601f6cafe67322a1742e Mon Sep 17 00:00:00 2001 From: nathan29849 Date: Mon, 1 Aug 2022 19:50:33 +0900 Subject: [PATCH 06/14] =?UTF-8?q?refactor:=20MimeType=EC=9D=84=20Enum=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit related to : #14 --- src/main/java/handler/RequestHandler.java | 4 ++-- src/main/java/http/MimeType.java | 26 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 src/main/java/http/MimeType.java diff --git a/src/main/java/handler/RequestHandler.java b/src/main/java/handler/RequestHandler.java index 2bba327..cd00308 100644 --- a/src/main/java/handler/RequestHandler.java +++ b/src/main/java/handler/RequestHandler.java @@ -2,6 +2,7 @@ import db.URLDataBase; import db.UserDataBase; +import http.MimeType; import http.request.HttpMethod; import http.request.HttpRequest; import http.request.RequestHeaders; @@ -42,8 +43,7 @@ public void run() { String url = requestUri.getPath(); if (requestLine.getHttpMethod().equals(HttpMethod.GET)) { - if (URLDataBase.contains(url) || url.contains(".css") || url.contains(".js") - || url.contains(".woff") || url.contains(".ico")) { + if (URLDataBase.contains(url) || MimeType.isSupportedExtension(url)) { httpResponse = HttpResponse.ok(url); } if (url.equals("/user/list")) { diff --git a/src/main/java/http/MimeType.java b/src/main/java/http/MimeType.java new file mode 100644 index 0000000..af77626 --- /dev/null +++ b/src/main/java/http/MimeType.java @@ -0,0 +1,26 @@ +package http; + +import java.util.Arrays; + +public enum MimeType { + CSS(".css"), JS(".js"), WOFF(".woff"), ICO(".ico"), + TTF(".ttf"), EOT(".eot"), SVG(".svg"), OTF(".otf"); + + + final String extension; + + MimeType(String extension) { + this.extension = extension; + } + + public static boolean isSupportedExtension(String url) { + return searchExtension(url) != null; + } + + private static MimeType searchExtension(String url){ + return Arrays.stream(MimeType.values()) + .filter(m -> url.endsWith(m.extension)) + .findAny() + .orElseThrow(() -> new IllegalStateException("유효하지 않은 MimeType 입니다. ")); + } +} From 8803fae5c17525695aec1cdb00da730a8d4daf87 Mon Sep 17 00:00:00 2001 From: nathan29849 Date: Thu, 4 Aug 2022 14:49:28 +0900 Subject: [PATCH 07/14] =?UTF-8?q?refactor:=20=EC=9C=A0=EC=A0=80=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EB=B2=84=ED=8A=BC=20url=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(/user/list.html=20->=20/user/list)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit related to: #14 --- webapp/index.html | 2 +- webapp/user/form.html | 2 +- webapp/user/list.html | 2 +- webapp/user/login.html | 4 ++-- webapp/user/login_failed.html | 4 ++-- webapp/user/profile.html | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/webapp/index.html b/webapp/index.html index 226a521..c3baac6 100644 --- a/webapp/index.html +++ b/webapp/index.html @@ -44,7 +44,7 @@
  • Facebook
  • -
  • +
  • diff --git a/webapp/user/form.html b/webapp/user/form.html index 587a9d6..170f565 100644 --- a/webapp/user/form.html +++ b/webapp/user/form.html @@ -39,7 +39,7 @@
  • Facebook
  • -
  • +
  • diff --git a/webapp/user/list.html b/webapp/user/list.html index e98f978..e22d04b 100644 --- a/webapp/user/list.html +++ b/webapp/user/list.html @@ -39,7 +39,7 @@
  • Facebook
  • -
  • +
  • diff --git a/webapp/user/login.html b/webapp/user/login.html index 69be6c8..20c691e 100644 --- a/webapp/user/login.html +++ b/webapp/user/login.html @@ -39,7 +39,7 @@
  • Facebook
  • -
  • +
  • @@ -97,4 +97,4 @@ - \ No newline at end of file + diff --git a/webapp/user/login_failed.html b/webapp/user/login_failed.html index d115528..bae95ad 100644 --- a/webapp/user/login_failed.html +++ b/webapp/user/login_failed.html @@ -39,7 +39,7 @@
  • Facebook
  • -
  • +
  • @@ -98,4 +98,4 @@ - \ No newline at end of file + diff --git a/webapp/user/profile.html b/webapp/user/profile.html index dce83b1..9daf2f7 100644 --- a/webapp/user/profile.html +++ b/webapp/user/profile.html @@ -39,7 +39,7 @@
  • Facebook
  • -
  • +
  • @@ -101,4 +101,4 @@

    자바지기

    - \ No newline at end of file + From 79a457e42205750e83a68a818f1d6551c774bbdf Mon Sep 17 00:00:00 2001 From: nathan29849 Date: Thu, 4 Aug 2022 14:50:34 +0900 Subject: [PATCH 08/14] =?UTF-8?q?chore:=20controller=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20.gitignore=EC=97=90=EC=84=9C=20=EC=A0=9C=EA=B1=B0(?= =?UTF-8?q?=EC=9E=91=EC=97=85=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=A8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c35d488..75aa820 100644 --- a/.gitignore +++ b/.gitignore @@ -204,5 +204,5 @@ Network Trash Folder Temporary Items .apdisk -src/main/java/controller/ +#src/main/java/controller/ From da797f9edcc6f07b599e019457a20a0692a72408 Mon Sep 17 00:00:00 2001 From: nathan29849 Date: Thu, 4 Aug 2022 15:07:27 +0900 Subject: [PATCH 09/14] =?UTF-8?q?refactor:=20HttpResponse=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EB=82=B4=EC=97=90=EC=84=9C=20flush=20?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit related to : #14 --- src/main/java/http/response/HttpResponse.java | 66 +++++++++---------- .../java/http/response/ResponseHeaders.java | 5 +- .../java/http/response/HttpResponseTest.java | 33 +++++++--- 3 files changed, 56 insertions(+), 48 deletions(-) diff --git a/src/main/java/http/response/HttpResponse.java b/src/main/java/http/response/HttpResponse.java index 8ba5562..c36bc84 100644 --- a/src/main/java/http/response/HttpResponse.java +++ b/src/main/java/http/response/HttpResponse.java @@ -1,5 +1,6 @@ package http.response; +import http.Cookie; import http.HttpVersion; import java.io.DataOutputStream; import java.io.File; @@ -12,18 +13,13 @@ public class HttpResponse { - private final StatusLine statusLine; - private final ResponseHeaders responseHeaders; + private final DataOutputStream dos; + private StatusLine statusLine; + private final ResponseHeaders responseHeaders = new ResponseHeaders(); private ResponseMessageBody responseMessageBody; - public HttpResponse(StatusLine statusLine, ResponseHeaders responseHeaders) { - this.statusLine = statusLine; - this.responseHeaders = responseHeaders; - } - - public HttpResponse(StatusLine statusLine, ResponseHeaders responseHeaders, ResponseMessageBody responseMessageBody) { - this(statusLine, responseHeaders); - this.responseMessageBody = responseMessageBody; + public HttpResponse(OutputStream out) { + this.dos = new DataOutputStream(out); } public StatusLine getStatusLine() { @@ -34,31 +30,33 @@ public ResponseHeaders getResponseHeaders() { return responseHeaders; } - public static HttpResponse ok() { - StatusLine statusLine = new StatusLine(new HttpVersion("HTTP/1.1"), HttpResponseStatus.OK); + public ResponseMessageBody getResponseMessageBody() { + return responseMessageBody; + } + + public void ok() throws IOException { + statusLine = new StatusLine(new HttpVersion("HTTP/1.1"), HttpResponseStatus.OK); byte[] messageBody = "Hello World".getBytes(StandardCharsets.UTF_8); - ResponseHeaders responseHeaders = new ResponseHeaders(); responseHeaders.setContentType("text/plain;charset=utf-8"); responseHeaders.setContentLength(messageBody.length); - ResponseMessageBody responseMessageBody = new ResponseMessageBody(messageBody); - return new HttpResponse(statusLine, responseHeaders, responseMessageBody); + responseMessageBody = new ResponseMessageBody(messageBody); + flush(); } - public static HttpResponse ok(String viewName) throws IOException { + public void ok(String viewName) throws IOException { byte[] messageBody = Files.readAllBytes(new File("./webapp" + viewName).toPath()); - StatusLine statusLine = new StatusLine(new HttpVersion("HTTP/1.1"), HttpResponseStatus.OK); + statusLine = new StatusLine(new HttpVersion("HTTP/1.1"), HttpResponseStatus.OK); String[] urlTokens = viewName.split("\\."); String extension = urlTokens[urlTokens.length - 1]; - ResponseHeaders responseHeaders = new ResponseHeaders(); responseHeaders.setContentType("text/"+extension+";charset=utf-8"); responseHeaders.setAccept("text/"+extension+", */*; q=0.1"); responseHeaders.setContentLength(messageBody.length); - ResponseMessageBody responseMessageBody = new ResponseMessageBody(messageBody); - return new HttpResponse(statusLine, responseHeaders, responseMessageBody); + responseMessageBody = new ResponseMessageBody(messageBody); + flush(); } - public static HttpResponse ok(String viewName, List users) throws IOException { + public void ok(String viewName, List users) throws IOException { StringBuilder sb = new StringBuilder(); List fileLines = Files.readAllLines(new File("./webapp" + viewName).toPath()); for (String fileLine : fileLines) { @@ -80,34 +78,30 @@ public static HttpResponse ok(String viewName, List users) throws IOExcept sb.append(fileLine).append("\r\n"); } byte[] messageBody = sb.toString().getBytes(StandardCharsets.UTF_8); - StatusLine statusLine = new StatusLine(new HttpVersion("HTTP/1.1"), HttpResponseStatus.OK); - ResponseHeaders responseHeaders = new ResponseHeaders(); + statusLine = new StatusLine(new HttpVersion("HTTP/1.1"), HttpResponseStatus.OK); responseHeaders.setContentType("text/html;charset=utf-8"); responseHeaders.setContentLength(messageBody.length); - ResponseMessageBody responseMessageBody = new ResponseMessageBody(messageBody); - return new HttpResponse(statusLine, responseHeaders, responseMessageBody); + responseMessageBody = new ResponseMessageBody(messageBody); + flush(); } - public static HttpResponse found(String redirectURI) { - StatusLine statusLine = new StatusLine(new HttpVersion("HTTP/1.1"), HttpResponseStatus.FOUND); - ResponseHeaders responseHeaders = new ResponseHeaders(); + public void found(String redirectURI) throws IOException { + statusLine = new StatusLine(new HttpVersion("HTTP/1.1"), HttpResponseStatus.FOUND); responseHeaders.setContentType("text/html;charset=utf-8"); responseHeaders.setLocation(redirectURI); - responseHeaders.setCookie(false); - return new HttpResponse(statusLine, responseHeaders); + responseHeaders.setCookie(new Cookie("logined", "false")); + flush(); } - public static HttpResponse found(String redirectURI, boolean cookie) { - StatusLine statusLine = new StatusLine(new HttpVersion("HTTP/1.1"), HttpResponseStatus.FOUND); - ResponseHeaders responseHeaders = new ResponseHeaders(); + public void found(String redirectURI, Cookie cookie) throws IOException { + statusLine = new StatusLine(new HttpVersion("HTTP/1.1"), HttpResponseStatus.FOUND); responseHeaders.setContentType("text/html;charset=utf-8"); responseHeaders.setLocation(redirectURI); responseHeaders.setCookie(cookie); - return new HttpResponse(statusLine, responseHeaders); + flush(); } - public void flush(OutputStream out) throws IOException { - DataOutputStream dos = new DataOutputStream(out); + private void flush() throws IOException { dos.writeBytes(statusLine.toString() + " \r\n"); dos.writeBytes(responseHeaders.toString()); dos.writeBytes("\r\n"); diff --git a/src/main/java/http/response/ResponseHeaders.java b/src/main/java/http/response/ResponseHeaders.java index 65fd60b..ac7b6cd 100644 --- a/src/main/java/http/response/ResponseHeaders.java +++ b/src/main/java/http/response/ResponseHeaders.java @@ -1,5 +1,6 @@ package http.response; +import http.Cookie; import java.util.HashMap; import java.util.Map; @@ -27,8 +28,8 @@ public void setLocation(String redirectURI) { headers.put("Location", redirectURI); } - public void setCookie(boolean cookie) { - headers.put("Set-Cookie", "logined=" + cookie + "; path=/;"); + public void setCookie(Cookie cookie) { + headers.put("Set-Cookie", cookie + " path=/;"); } public void setAccept(String content) { diff --git a/src/test/java/http/response/HttpResponseTest.java b/src/test/java/http/response/HttpResponseTest.java index 046525e..12df118 100644 --- a/src/test/java/http/response/HttpResponseTest.java +++ b/src/test/java/http/response/HttpResponseTest.java @@ -3,9 +3,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import http.Cookie; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import org.junit.jupiter.api.DisplayName; @@ -25,9 +26,13 @@ class Context_with_null { @Test @DisplayName("default 200 OK HttpResponse 객체를 반환한다.") - void dit_returns_default_200_OK_HttpResponse_instance() { + void dit_returns_default_200_OK_HttpResponse_instance() throws IOException { + //given + ByteArrayOutputStream out = new ByteArrayOutputStream(); + HttpResponse response = new HttpResponse(out); + //when - HttpResponse response = HttpResponse.ok(); + response.ok(); StatusLine statusLine = response.getStatusLine(); ResponseHeaders responseHeaders = response.getResponseHeaders(); @@ -50,9 +55,11 @@ void it_returns_viewName_OK_HttpResponse_instance() throws IOException { //given String viewName = "/index.html"; byte[] messageBody = Files.readAllBytes(new File("./webapp" + viewName).toPath()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + HttpResponse response = new HttpResponse(out); //when - HttpResponse response = HttpResponse.ok(viewName); + response.ok(viewName); StatusLine statusLine = response.getStatusLine(); ResponseHeaders responseHeaders = response.getResponseHeaders(); @@ -69,9 +76,11 @@ void it_returns_viewName_OK_HttpResponse_instance() throws IOException { void it_throws_NoSuchFileException() throws IOException { //given String viewName = "/indexing.html"; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + HttpResponse response = new HttpResponse(out); //then - assertThatThrownBy(() -> HttpResponse.ok(viewName)).isInstanceOf( + assertThatThrownBy(() -> response.ok(viewName)).isInstanceOf( NoSuchFileException.class); } @@ -88,12 +97,14 @@ class Context_with_a_String { @Test @DisplayName("해당 인자를 Location으로 갖는 302 Found HttpResponse 객체를 반환한다.") - void it_returns_302_Found_HttpResponse_instance() { + void it_returns_302_Found_HttpResponse_instance() throws IOException { //given String redirectURI = "/index.html"; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + HttpResponse response = new HttpResponse(out); //when - HttpResponse response = HttpResponse.found(redirectURI); + response.found(redirectURI); StatusLine statusLine = response.getStatusLine(); ResponseHeaders responseHeaders = response.getResponseHeaders(); @@ -110,13 +121,15 @@ class Context_with_a_String_and_a_Cookie { @Test @DisplayName("해당 인자를 Location으로 갖고, Cookie가 세팅된 302 Found HttpResponse 객체를 반환한다.") - void it_returns_302_Found_HttpResponse_instance_with_Cookie() { + void it_returns_302_Found_HttpResponse_instance_with_Cookie() throws IOException { //given String redirectURI = "/index.html"; - boolean login_cookie = true; + Cookie cookie = new Cookie("logined", "true"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + HttpResponse response = new HttpResponse(out); //when - HttpResponse response = HttpResponse.found(redirectURI, login_cookie); + response.found(redirectURI, cookie); StatusLine statusLine = response.getStatusLine(); ResponseHeaders responseHeaders = response.getResponseHeaders(); From 8bc073b504563ab8fc0939f8ba581b5b5b279c0c Mon Sep 17 00:00:00 2001 From: nathan29849 Date: Thu, 4 Aug 2022 15:08:26 +0900 Subject: [PATCH 10/14] =?UTF-8?q?chore:=20import=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit related to : #14 --- src/main/java/db/UserDataBase.java | 2 -- src/main/java/util/HttpRequestUtils.java | 6 ++---- src/main/java/webserver/WebServer.java | 1 - 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/java/db/UserDataBase.java b/src/main/java/db/UserDataBase.java index 39ad114..d7d4c53 100644 --- a/src/main/java/db/UserDataBase.java +++ b/src/main/java/db/UserDataBase.java @@ -1,11 +1,9 @@ package db; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; - import model.User; public class UserDataBase { diff --git a/src/main/java/util/HttpRequestUtils.java b/src/main/java/util/HttpRequestUtils.java index bf67e4c..2ea983d 100644 --- a/src/main/java/util/HttpRequestUtils.java +++ b/src/main/java/util/HttpRequestUtils.java @@ -1,13 +1,11 @@ package util; -import http.request.RequestLine; +import com.google.common.base.Strings; +import com.google.common.collect.Maps; import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; -import com.google.common.base.Strings; -import com.google.common.collect.Maps; - public class HttpRequestUtils { /** diff --git a/src/main/java/webserver/WebServer.java b/src/main/java/webserver/WebServer.java index fcac018..242baea 100644 --- a/src/main/java/webserver/WebServer.java +++ b/src/main/java/webserver/WebServer.java @@ -3,7 +3,6 @@ import handler.RequestHandler; import java.net.ServerSocket; import java.net.Socket; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; From 4ff70bec393234daf3d8a9a5a6e1071a638c6e7d Mon Sep 17 00:00:00 2001 From: nathan29849 Date: Thu, 4 Aug 2022 15:09:21 +0900 Subject: [PATCH 11/14] =?UTF-8?q?refactor:=20RequestHandler=EB=A5=BC=20Con?= =?UTF-8?q?troller=EB=A1=9C=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20=EC=B6=94?= =?UTF-8?q?=EC=83=81=ED=99=94=20=EC=A7=84=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit related to : #14 --- .../java/controller/AbstractController.java | 25 +++++ src/main/java/controller/Controller.java | 10 ++ src/main/java/controller/HomeController.java | 24 ++++ src/main/java/controller/LoginController.java | 32 ++++++ .../java/controller/SignUpController.java | 31 ++++++ src/main/java/controller/UserController.java | 36 ++++++ src/main/java/handler/RequestHandler.java | 104 +++++------------- 7 files changed, 185 insertions(+), 77 deletions(-) create mode 100644 src/main/java/controller/AbstractController.java create mode 100644 src/main/java/controller/Controller.java create mode 100644 src/main/java/controller/HomeController.java create mode 100644 src/main/java/controller/LoginController.java create mode 100644 src/main/java/controller/SignUpController.java create mode 100644 src/main/java/controller/UserController.java diff --git a/src/main/java/controller/AbstractController.java b/src/main/java/controller/AbstractController.java new file mode 100644 index 0000000..e911c6e --- /dev/null +++ b/src/main/java/controller/AbstractController.java @@ -0,0 +1,25 @@ +package controller; + +import http.request.HttpRequest; +import http.request.RequestLine; +import http.response.HttpResponse; +import java.io.IOException; + +public abstract class AbstractController implements Controller{ + + @Override + public void service(HttpRequest request, HttpResponse response) throws IOException { + RequestLine requestLine = request.getRequestLine(); + if (requestLine.getHttpMethod().isGET()) { + doGet(request, response); + } + + if (requestLine.getHttpMethod().isPOST()) { + doPost(request, response); + } + } + + protected void doGet(HttpRequest request, HttpResponse response) throws IOException {} + + protected void doPost(HttpRequest request, HttpResponse response) throws IOException {} +} diff --git a/src/main/java/controller/Controller.java b/src/main/java/controller/Controller.java new file mode 100644 index 0000000..0f103bc --- /dev/null +++ b/src/main/java/controller/Controller.java @@ -0,0 +1,10 @@ +package controller; + +import http.request.HttpRequest; +import http.response.HttpResponse; +import java.io.IOException; + +public interface Controller { + + void service(HttpRequest request, HttpResponse response) throws IOException; +} diff --git a/src/main/java/controller/HomeController.java b/src/main/java/controller/HomeController.java new file mode 100644 index 0000000..06d6fea --- /dev/null +++ b/src/main/java/controller/HomeController.java @@ -0,0 +1,24 @@ +package controller; + +import db.URLDataBase; +import http.MimeType; +import http.request.HttpRequest; +import http.request.RequestLine; +import http.request.RequestURI; +import http.response.HttpResponse; +import java.io.IOException; + +public class HomeController extends AbstractController{ + + @Override + protected void doGet(HttpRequest request, HttpResponse response) throws IOException { + RequestLine requestLine = request.getRequestLine(); + RequestURI requestURI = requestLine.getRequestUri(); + String url = requestURI.getPath(); + if (URLDataBase.contains(url) || MimeType.isSupportedExtension(url)) { + response.ok(url); + return; + } + response.ok(); + } +} diff --git a/src/main/java/controller/LoginController.java b/src/main/java/controller/LoginController.java new file mode 100644 index 0000000..dafd1b5 --- /dev/null +++ b/src/main/java/controller/LoginController.java @@ -0,0 +1,32 @@ +package controller; + +import db.UserDataBase; +import http.Cookie; +import http.request.HttpRequest; +import http.response.HttpResponse; +import java.io.IOException; +import java.util.Map; +import model.User; +import util.HttpRequestUtils; + +public class LoginController extends AbstractController{ + + private static final String TRUE = "true"; + private static final String LOGIN = "logined"; + private static final String USER_ID = "userId"; + private static final String PASSWORD = "password"; + + @Override + protected void doPost(HttpRequest request, HttpResponse response) throws IOException { + String messageBody = request.getMessageBody(); + Map parsedMessageBody = HttpRequestUtils.parseQueryString(messageBody); + String userId = parsedMessageBody.get(USER_ID); + String password = parsedMessageBody.get(PASSWORD); + User savedUser = UserDataBase.findUserById(userId); + if (UserDataBase.login(savedUser, userId, password)) { + response.found("/index.html", new Cookie(LOGIN, TRUE)); + return; + } + response.found("/user/login_failed.html"); + } +} diff --git a/src/main/java/controller/SignUpController.java b/src/main/java/controller/SignUpController.java new file mode 100644 index 0000000..4b4a450 --- /dev/null +++ b/src/main/java/controller/SignUpController.java @@ -0,0 +1,31 @@ +package controller; + +import db.UserDataBase; +import http.request.HttpRequest; +import http.response.HttpResponse; +import java.io.IOException; +import java.util.Map; +import model.User; +import util.HttpRequestUtils; + +public class SignUpController extends AbstractController{ + + private static final String USER_ID = "userId"; + private static final String PASSWORD = "password"; + private static final String NAME = "name"; + private static final String EMAIL = "email"; + + @Override + protected void doPost(HttpRequest request, HttpResponse response) throws IOException { + String messageBody = request.getMessageBody(); + Map parsedMessageBody = HttpRequestUtils.parseQueryString(messageBody); + UserDataBase.addUser( + new User( + parsedMessageBody.get(USER_ID), + parsedMessageBody.get(PASSWORD), + parsedMessageBody.get(NAME), + parsedMessageBody.get(EMAIL)) + ); + response.found("/index.html"); + } +} diff --git a/src/main/java/controller/UserController.java b/src/main/java/controller/UserController.java new file mode 100644 index 0000000..cdab5bc --- /dev/null +++ b/src/main/java/controller/UserController.java @@ -0,0 +1,36 @@ +package controller; + +import db.UserDataBase; +import http.request.HttpRequest; +import http.request.RequestHeaders; +import http.response.HttpResponse; +import java.io.IOException; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import util.HttpRequestUtils; + +public class UserController extends AbstractController{ + + private static final Logger log = LoggerFactory.getLogger(UserController.class); + private static final String LOGIN = "logined"; + private static final String TRUE = "true"; + + @Override + protected void doGet(HttpRequest request, HttpResponse response) throws IOException { + RequestHeaders requestHeaders = request.getRequestHeaders(); + if (!requestHeaders.hasCookie()){ + log.debug("RequestHeaders has not Cookie!"); + response.ok("/user/login.html"); + return; + } + + String cookies = requestHeaders.getCookie(); + Map parsedCookies = HttpRequestUtils.parseCookies(cookies); + if (parsedCookies.get(LOGIN).equals(TRUE)) { + response.ok("/user/list.html", UserDataBase.findAll()); + return; + } + response.ok("/user/login.html"); + } +} diff --git a/src/main/java/handler/RequestHandler.java b/src/main/java/handler/RequestHandler.java index cd00308..90b4409 100644 --- a/src/main/java/handler/RequestHandler.java +++ b/src/main/java/handler/RequestHandler.java @@ -1,108 +1,58 @@ package handler; -import db.URLDataBase; -import db.UserDataBase; -import http.MimeType; -import http.request.HttpMethod; +import controller.Controller; +import controller.HomeController; +import controller.LoginController; +import controller.SignUpController; +import controller.UserController; import http.request.HttpRequest; -import http.request.RequestHeaders; -import http.request.RequestLine; -import http.request.RequestMessageBody; -import http.request.RequestURI; import http.response.HttpResponse; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; +import java.util.HashMap; import java.util.Map; -import model.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import util.HttpRequestUtils; public class RequestHandler extends Thread { + private static final Logger log = LoggerFactory.getLogger(RequestHandler.class); + private static final Map handlerMap = new HashMap<>(); + + static { + handlerMap.put("/index.html", new HomeController()); + handlerMap.put("/user/list", new UserController()); + handlerMap.put("/user/list.html", new UserController()); + handlerMap.put("/user/create", new SignUpController()); + handlerMap.put("/user/login", new LoginController()); + } private final Socket connection; + private Controller controller = new HomeController();; public RequestHandler(Socket connectionSocket) { this.connection = connectionSocket; } + @Override public void run() { log.debug("New Client Connect! Connected IP : {}, Port : {}", connection.getInetAddress(), connection.getPort()); try (InputStream in = connection.getInputStream(); OutputStream out = connection.getOutputStream()) { HttpRequest httpRequest = HttpRequest.from(in); - HttpResponse httpResponse = HttpResponse.ok(); - RequestLine requestLine = httpRequest.getRequestLine(); - log.info("RequestLine= {}", requestLine); - RequestURI requestUri = requestLine.getRequestUri(); - log.info("RequestURI= {}", requestUri); - String url = requestUri.getPath(); - - if (requestLine.getHttpMethod().equals(HttpMethod.GET)) { - if (URLDataBase.contains(url) || MimeType.isSupportedExtension(url)) { - httpResponse = HttpResponse.ok(url); - } - if (url.equals("/user/list")) { - RequestHeaders requestHeaders = httpRequest.getRequestHeaders(); - String cookies = requestHeaders.getCookie(); - log.debug("Cookie= {}", cookies); - if (!requestHeaders.hasCookie()) { - httpResponse = HttpResponse.ok("/user/login.html"); - } - - if (requestHeaders.hasCookie()) { - Map parseCookies = HttpRequestUtils.parseCookies(cookies); - if (parseCookies.get("logined").equals("false")) { - httpResponse = HttpResponse.ok("/user/login.html"); - httpResponse.flush(out); - } - httpResponse = HttpResponse.ok("/user/list.html", UserDataBase.findAll()); - } - } - httpResponse.flush(out); - } - - if (requestLine.getHttpMethod().equals(HttpMethod.POST)) { - RequestMessageBody requestMessageBody = httpRequest.getRequestMessageBody(); - String messageBody = requestMessageBody.getMessageBody(); - log.debug("HTTP Message Body = {}", messageBody ); - - Map parsedMessageBody = HttpRequestUtils.parseQueryString(messageBody); - String userId = parsedMessageBody.get("userId"); - String password = parsedMessageBody.get("password"); - if (url.equals("/user/create")) { - User user = new User( - userId, password, - parsedMessageBody.get("name"), - parsedMessageBody.get("email") - ); - UserDataBase.addUser(user); - log.debug("Create User ! = {}", user); - } - - if (url.equals("/user/login")) { - User savedUser = UserDataBase.findUserById(userId); - log.debug("saved User = {}", savedUser); - if (!UserDataBase.login(savedUser, userId, password)) { - httpResponse = HttpResponse.found("/user/login_failed.html"); - httpResponse.flush(out); - log.debug("Login Fail! userId = {}", userId); - return; - } - log.debug("Login Complete! userId = {}", userId); - httpResponse = HttpResponse.found("/index.html", true); - log.info("Response Headers = {}", httpResponse.getResponseHeaders()); - httpResponse.flush(out); - } - - httpResponse = HttpResponse.found("/index.html"); + HttpResponse httpResponse = new HttpResponse(out); + String url = httpRequest.getRequestURIPath(); + + if (handlerMap.containsKey(url)) { + controller = handlerMap.get(url); + log.debug("Controller = {}", controller.getClass()); + controller.service(httpRequest, httpResponse); + return; } - httpResponse.flush(out); - + controller.service(httpRequest, httpResponse); } catch (IOException e) { log.error(e.getMessage()); } From 1718de6098b8aead23387b707742d80629c3b486 Mon Sep 17 00:00:00 2001 From: nathan29849 Date: Thu, 4 Aug 2022 15:09:55 +0900 Subject: [PATCH 12/14] =?UTF-8?q?refactor:=20HttpRequest=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit related to : #14 --- src/main/java/http/Cookie.java | 17 +++++++++++++++++ src/main/java/http/MimeType.java | 7 +++---- src/main/java/http/request/HttpMethod.java | 10 +++++++++- src/main/java/http/request/HttpRequest.java | 8 ++++++-- src/main/java/http/request/RequestLine.java | 4 ++++ src/test/java/http/request/HttpRequestTest.java | 6 +++--- 6 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 src/main/java/http/Cookie.java diff --git a/src/main/java/http/Cookie.java b/src/main/java/http/Cookie.java new file mode 100644 index 0000000..fe96964 --- /dev/null +++ b/src/main/java/http/Cookie.java @@ -0,0 +1,17 @@ +package http; + +public class Cookie { + + private final String key; + private final String value; + + public Cookie(String key, String value) { + this.key = key; + this.value = value; + } + + @Override + public String toString() { + return key + "=" + value + ";"; + } +} diff --git a/src/main/java/http/MimeType.java b/src/main/java/http/MimeType.java index af77626..7fb80a2 100644 --- a/src/main/java/http/MimeType.java +++ b/src/main/java/http/MimeType.java @@ -3,11 +3,10 @@ import java.util.Arrays; public enum MimeType { - CSS(".css"), JS(".js"), WOFF(".woff"), ICO(".ico"), + HTML(".html"), CSS(".css"), JS(".js"), WOFF(".woff"), ICO(".ico"), TTF(".ttf"), EOT(".eot"), SVG(".svg"), OTF(".otf"); - - final String extension; + private final String extension; MimeType(String extension) { this.extension = extension; @@ -21,6 +20,6 @@ private static MimeType searchExtension(String url){ return Arrays.stream(MimeType.values()) .filter(m -> url.endsWith(m.extension)) .findAny() - .orElseThrow(() -> new IllegalStateException("유효하지 않은 MimeType 입니다. ")); + .orElse(null); } } diff --git a/src/main/java/http/request/HttpMethod.java b/src/main/java/http/request/HttpMethod.java index a673cdd..80435c1 100644 --- a/src/main/java/http/request/HttpMethod.java +++ b/src/main/java/http/request/HttpMethod.java @@ -1,5 +1,13 @@ package http.request; public enum HttpMethod { - GET, POST, PATCH, DELETE + GET, POST, PATCH, DELETE; + + public boolean isGET() { + return this == GET; + } + + public boolean isPOST() { + return this == POST; + } } diff --git a/src/main/java/http/request/HttpRequest.java b/src/main/java/http/request/HttpRequest.java index 70c3b5e..6fc83e8 100644 --- a/src/main/java/http/request/HttpRequest.java +++ b/src/main/java/http/request/HttpRequest.java @@ -44,11 +44,15 @@ public RequestLine getRequestLine() { return requestLine; } + public String getRequestURIPath() { + return requestLine.getPath(); + } + public RequestHeaders getRequestHeaders() { return requestHeaders; } - public RequestMessageBody getRequestMessageBody() { - return requestMessageBody; + public String getMessageBody() { + return requestMessageBody.getMessageBody(); } } diff --git a/src/main/java/http/request/RequestLine.java b/src/main/java/http/request/RequestLine.java index 4615d9d..2cf0405 100644 --- a/src/main/java/http/request/RequestLine.java +++ b/src/main/java/http/request/RequestLine.java @@ -35,6 +35,10 @@ public HttpVersion getHttpVersion() { return httpVersion; } + public String getPath() { + return requestUri.getPath(); + } + @Override public String toString() { return httpMethod.toString() + " " diff --git a/src/test/java/http/request/HttpRequestTest.java b/src/test/java/http/request/HttpRequestTest.java index 3f8b9e3..d847486 100644 --- a/src/test/java/http/request/HttpRequestTest.java +++ b/src/test/java/http/request/HttpRequestTest.java @@ -61,13 +61,13 @@ void it_returns_new_POST_HttpRequest_instance() throws IOException { + "Accept: */*\r\n" + "\r\n" +"userId=nathan&password=123123&name=나단&email=nathan@dev.com"; - //TODO Content-Length, Content-Type 추가 해야한다. + //when ByteArrayInputStream in = new ByteArrayInputStream(str.getBytes()); HttpRequest httpRequest = HttpRequest.from(in); RequestLine requestLine = httpRequest.getRequestLine(); RequestHeaders requestHeaders = httpRequest.getRequestHeaders(); - RequestMessageBody requestMessageBody = httpRequest.getRequestMessageBody(); + String requestMessageBody = httpRequest.getMessageBody(); //then assertThat(requestLine.getHttpMethod()).isEqualTo(HttpMethod.POST); @@ -79,7 +79,7 @@ void it_returns_new_POST_HttpRequest_instance() throws IOException { assertThat(requestHeaders.getContentLength()).isEqualTo(58); assertThat(requestHeaders.getHeader("Content-Type")).isEqualTo( "application/x-www-form-urlencoded"); - assertThat(requestMessageBody.getMessageBody()).isEqualTo( + assertThat(httpRequest.getMessageBody()).isEqualTo( "userId=nathan&password=123123&name=나단&email=nathan@dev.com"); } } From e3a6d49b55a28225322e1f086538c5181b6dae44 Mon Sep 17 00:00:00 2001 From: nathan29849 Date: Thu, 4 Aug 2022 15:26:09 +0900 Subject: [PATCH 13/14] =?UTF-8?q?refactor:=20HomeController=EC=9D=98=20url?= =?UTF-8?q?=20=EB=B0=9B=EB=8A=94=20=EB=B6=80=EB=B6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit related to: #14 --- src/main/java/controller/HomeController.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/controller/HomeController.java b/src/main/java/controller/HomeController.java index 06d6fea..15aab5c 100644 --- a/src/main/java/controller/HomeController.java +++ b/src/main/java/controller/HomeController.java @@ -12,9 +12,7 @@ public class HomeController extends AbstractController{ @Override protected void doGet(HttpRequest request, HttpResponse response) throws IOException { - RequestLine requestLine = request.getRequestLine(); - RequestURI requestURI = requestLine.getRequestUri(); - String url = requestURI.getPath(); + String url = request.getRequestURIPath(); if (URLDataBase.contains(url) || MimeType.isSupportedExtension(url)) { response.ok(url); return; From ee6a6b9a2b6d767ea3ed831de91c72b6ac275e58 Mon Sep 17 00:00:00 2001 From: nathan29849 Date: Fri, 5 Aug 2022 12:40:43 +0900 Subject: [PATCH 14/14] =?UTF-8?q?add:=20embedded=20tomcat=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=ED=99=9C?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit related to : #14 --- build.gradle | 14 +++++++++ src/main/java/servlet/HelloWorldServlet.java | 19 ++++++++++++ .../java/webserver/WebServerLauncher.java | 31 +++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 src/main/java/servlet/HelloWorldServlet.java create mode 100644 src/main/java/webserver/WebServerLauncher.java diff --git a/build.gradle b/build.gradle index 4345dbb..10f395e 100644 --- a/build.gradle +++ b/build.gradle @@ -34,6 +34,20 @@ dependencies { implementation group: 'org.apache.commons', name: 'commons-dbcp2', version: '2.9.0' // https://mvnrepository.com/artifact/com.h2database/h2 implementation group: 'com.h2database', name: 'h2', version: '2.1.210' + + // EMBED TOMCAT 관련 설정 + // tomcat-embed-core + implementation 'org.apache.tomcat.embed:tomcat-embed-core:10.1.0-M17' + // https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-logging-juli + implementation 'org.apache.tomcat.embed:tomcat-embed-logging-juli:9.0.0.M6' + // https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-jasper + implementation 'org.apache.tomcat.embed:tomcat-embed-jasper:10.1.0-M17' + // https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-jasper + implementation 'org.apache.tomcat:tomcat-jasper:10.1.0-M17' + // https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-el + implementation 'org.apache.tomcat.embed:tomcat-embed-el:10.1.0-M17' + // https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-jsp-api + implementation 'org.apache.tomcat:tomcat-jsp-api:10.1.0-M17' } test { diff --git a/src/main/java/servlet/HelloWorldServlet.java b/src/main/java/servlet/HelloWorldServlet.java new file mode 100644 index 0000000..3411a66 --- /dev/null +++ b/src/main/java/servlet/HelloWorldServlet.java @@ -0,0 +1,19 @@ +package servlet; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +@WebServlet("/hello") +public class HelloWorldServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + PrintWriter out = response.getWriter(); + out.print("Hello World!"); + } +} diff --git a/src/main/java/webserver/WebServerLauncher.java b/src/main/java/webserver/WebServerLauncher.java new file mode 100644 index 0000000..9c4b616 --- /dev/null +++ b/src/main/java/webserver/WebServerLauncher.java @@ -0,0 +1,31 @@ +package webserver; + +import java.io.File; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.connector.Connector; +import org.apache.catalina.startup.Tomcat; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class WebServerLauncher { + + private static final Logger log = LoggerFactory.getLogger(WebServerLauncher.class); + + public static void main(String[] args) throws LifecycleException { + String webappDirLocation = "webapp/"; + Tomcat tomcat = new Tomcat(); + tomcat.setPort(8080); +// String webPort = System.getenv("PORT"); +// if (webPort == null || webPort.isEmpty()) { +// webPort = "8080"; +// } + + Connector connector = tomcat.getConnector(); + connector.setURIEncoding("UTF-8"); + tomcat.addWebapp("/", new File(webappDirLocation).getAbsolutePath()); + log.info("configuring app with basedir: {}", new File("./" + webappDirLocation).getAbsolutePath()); + tomcat.start(); + tomcat.getServer().await(); + } + +}