From fed02f6f5f4308400e55c160d9495cad010f5bfb Mon Sep 17 00:00:00 2001 From: Gyeongho Yang Date: Thu, 5 Sep 2024 11:11:09 +0900 Subject: [PATCH 01/49] fix: remove implementation logback-classic on gradle (#501) --- study/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/study/build.gradle b/study/build.gradle index 5c69542f84..87a1f0313c 100644 --- a/study/build.gradle +++ b/study/build.gradle @@ -19,7 +19,6 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-webflux' - implementation 'ch.qos.logback:logback-classic:1.5.7' implementation 'org.apache.commons:commons-lang3:3.14.0' implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1' implementation 'pl.allegro.tech.boot:handlebars-spring-boot-starter:0.4.1' From 7e9135698878932274ddc1f523ba817ed9c56c70 Mon Sep 17 00:00:00 2001 From: Gyeongho Yang Date: Thu, 5 Sep 2024 13:51:07 +0900 Subject: [PATCH 02/49] fix: add threads min-spare configuration on properties (#502) --- study/src/main/resources/application.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/study/src/main/resources/application.yml b/study/src/main/resources/application.yml index 4e8655a962..e3503a5fb9 100644 --- a/study/src/main/resources/application.yml +++ b/study/src/main/resources/application.yml @@ -6,4 +6,5 @@ server: accept-count: 1 max-connections: 1 threads: + min-spare: 2 max: 2 From 44661a3ff115669f6e180485ecb9c13d2387c55c Mon Sep 17 00:00:00 2001 From: le2sky Date: Wed, 4 Sep 2024 07:58:38 +0900 Subject: [PATCH 03/49] =?UTF-8?q?feat:=20index.html=20=EC=9D=91=EB=8B=B5?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 42 +++++++++++++++---- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index bb14184757..d99633ff33 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -5,8 +5,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.net.Socket; +import java.nio.charset.StandardCharsets; public class Http11Processor implements Runnable, Processor { @@ -28,15 +33,9 @@ public void run() { public void process(final Socket connection) { try (final var inputStream = connection.getInputStream(); final var outputStream = connection.getOutputStream()) { - - final var responseBody = "Hello world!"; - - final var response = String.join("\r\n", - "HTTP/1.1 200 OK ", - "Content-Type: text/html;charset=utf-8 ", - "Content-Length: " + responseBody.getBytes().length + " ", - "", - responseBody); + final var clientReader = new BufferedReader(new InputStreamReader(inputStream)); + final var httpRequestHeader = clientReader.readLine(); + final var response = createHttpResponse(loadStaticFile(httpRequestHeader)); outputStream.write(response.getBytes()); outputStream.flush(); @@ -44,4 +43,29 @@ public void process(final Socket connection) { log.error(e.getMessage(), e); } } + + private byte[] loadStaticFile(String httpRequestHeader) throws IOException { + final var startLine = httpRequestHeader.split(" "); + if ("/".equals(startLine[1])) { + return "Hello world!".getBytes(); + } + + final var requestResource = startLine[1].split("/"); + final var resourceName = requestResource[requestResource.length - 1]; + final var resourcePath = getClass().getClassLoader().getResource("static/" + resourceName).getPath(); + final var bufferedInputStream = new BufferedInputStream(new FileInputStream(resourcePath)); + final var responseBody = bufferedInputStream.readAllBytes(); + bufferedInputStream.close(); + + return responseBody; + } + + private String createHttpResponse(byte[] responseBody) { + return String.join("\r\n", + "HTTP/1.1 200 OK ", + "Content-Type: text/html;charset=utf-8 ", + "Content-Length: " + responseBody.length + " ", + "", + new String(responseBody, StandardCharsets.UTF_8)); + } } From d11ca3bfd7d7def7261a5ff6dae6f819e9513026 Mon Sep 17 00:00:00 2001 From: le2sky Date: Wed, 4 Sep 2024 08:06:47 +0900 Subject: [PATCH 04/49] =?UTF-8?q?refactor:=20=EC=9D=91=EB=8B=B5=EA=B0=92?= =?UTF-8?q?=EA=B3=BC=20contentType=20=EC=9E=90=EB=A3=8C=EA=B5=AC=EC=A1=B0?= =?UTF-8?q?=EB=A1=9C=20=ED=8F=AC=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index d99633ff33..044f350a2b 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -34,8 +34,8 @@ public void process(final Socket connection) { try (final var inputStream = connection.getInputStream(); final var outputStream = connection.getOutputStream()) { final var clientReader = new BufferedReader(new InputStreamReader(inputStream)); - final var httpRequestHeader = clientReader.readLine(); - final var response = createHttpResponse(loadStaticFile(httpRequestHeader)); + final var startLine = clientReader.readLine(); + final var response = createHttpResponse(loadStaticFile(startLine)); outputStream.write(response.getBytes()); outputStream.flush(); @@ -44,28 +44,32 @@ public void process(final Socket connection) { } } - private byte[] loadStaticFile(String httpRequestHeader) throws IOException { - final var startLine = httpRequestHeader.split(" "); - if ("/".equals(startLine[1])) { - return "Hello world!".getBytes(); + private HttpResponseData loadStaticFile(String startLine) throws IOException { + final var startLines = startLine.split(" "); + if ("/".equals(startLines[1])) { + return new HttpResponseData("Hello world!".getBytes(), "text/html;charset=utf-8"); } - - final var requestResource = startLine[1].split("/"); + final var requestResource = startLines[1].split("/"); final var resourceName = requestResource[requestResource.length - 1]; final var resourcePath = getClass().getClassLoader().getResource("static/" + resourceName).getPath(); final var bufferedInputStream = new BufferedInputStream(new FileInputStream(resourcePath)); final var responseBody = bufferedInputStream.readAllBytes(); bufferedInputStream.close(); - return responseBody; + return new HttpResponseData(responseBody, "text/html;charset=utf-8"); } - private String createHttpResponse(byte[] responseBody) { + private String createHttpResponse(HttpResponseData httpResponseData) { return String.join("\r\n", "HTTP/1.1 200 OK ", - "Content-Type: text/html;charset=utf-8 ", - "Content-Length: " + responseBody.length + " ", + "Content-Type: " + httpResponseData.contentType + " ", + "Content-Length: " + httpResponseData.responseBody.length + " ", "", - new String(responseBody, StandardCharsets.UTF_8)); + new String(httpResponseData.responseBody, StandardCharsets.UTF_8)); + } + + private static record HttpResponseData(byte[] responseBody, String contentType) { } + + ; } From 7a15bf7b06406803368837f48ad4d9e3d48e8f1d Mon Sep 17 00:00:00 2001 From: le2sky Date: Wed, 4 Sep 2024 08:59:52 +0900 Subject: [PATCH 05/49] =?UTF-8?q?feat:=20css,=20javascript=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=A7=80=EC=9B=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 55 ++++++++++++++++--- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 044f350a2b..769f402f5c 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -12,6 +12,8 @@ import java.io.InputStreamReader; import java.net.Socket; import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; public class Http11Processor implements Runnable, Processor { @@ -35,7 +37,8 @@ public void process(final Socket connection) { final var outputStream = connection.getOutputStream()) { final var clientReader = new BufferedReader(new InputStreamReader(inputStream)); final var startLine = clientReader.readLine(); - final var response = createHttpResponse(loadStaticFile(startLine)); + final var headers = createHeaders(clientReader); + final var response = createHttpResponse(loadStaticFile(new HttpRequestData(startLine, headers))); outputStream.write(response.getBytes()); outputStream.flush(); @@ -44,19 +47,52 @@ public void process(final Socket connection) { } } - private HttpResponseData loadStaticFile(String startLine) throws IOException { - final var startLines = startLine.split(" "); + private Map createHeaders(BufferedReader clientReader) throws IOException { + Map headers = new HashMap<>(); + while (true) { + final var readLine = clientReader.readLine(); + if (readLine.isBlank()) { + break; + } + + final var header = readLine.split(":"); + headers.put(header[0].trim(), header[1].trim()); + } + + return headers; + } + + private HttpResponseData loadStaticFile(HttpRequestData httpRequestData) throws IOException { + final var startLines = httpRequestData.startLine.split(" "); + log.info("{}", httpRequestData.headers); + if ("/".equals(startLines[1])) { return new HttpResponseData("Hello world!".getBytes(), "text/html;charset=utf-8"); } - final var requestResource = startLines[1].split("/"); - final var resourceName = requestResource[requestResource.length - 1]; - final var resourcePath = getClass().getClassLoader().getResource("static/" + resourceName).getPath(); + + final var headers = httpRequestData.headers(); + var acceptHeader = headers.getOrDefault("Accept", "text/html;charset=utf-8"); + var contentType = acceptHeader; + + if (acceptHeader.startsWith("text/html")) { + contentType = "text/html;charset=utf-8"; + } + + if (acceptHeader.startsWith("text/css")) { + contentType = "text/css;charset=utf-8"; + } + + if (acceptHeader.startsWith("text/javascript")) { + contentType = "text/javascript;charset=utf-8"; + } + + final var requestResource = startLines[1]; + final var resourcePath = getClass().getClassLoader().getResource("static/" + requestResource).getPath(); final var bufferedInputStream = new BufferedInputStream(new FileInputStream(resourcePath)); final var responseBody = bufferedInputStream.readAllBytes(); bufferedInputStream.close(); - return new HttpResponseData(responseBody, "text/html;charset=utf-8"); + return new HttpResponseData(responseBody, contentType); } private String createHttpResponse(HttpResponseData httpResponseData) { @@ -68,8 +104,9 @@ private String createHttpResponse(HttpResponseData httpResponseData) { new String(httpResponseData.responseBody, StandardCharsets.UTF_8)); } - private static record HttpResponseData(byte[] responseBody, String contentType) { + private record HttpRequestData(String startLine, Map headers) { } - ; + private record HttpResponseData(byte[] responseBody, String contentType) { + } } From 436a244a910aab6ac097b0fe621e748ca3ddc8dd Mon Sep 17 00:00:00 2001 From: le2sky Date: Wed, 4 Sep 2024 21:39:19 +0900 Subject: [PATCH 06/49] =?UTF-8?q?feat:=20login.html=20=EC=9D=91=EB=8B=B5?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 769f402f5c..49532516b1 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -1,6 +1,8 @@ package org.apache.coyote.http11; +import com.techcourse.db.InMemoryUserRepository; import com.techcourse.exception.UncheckedServletException; +import com.techcourse.model.User; import org.apache.coyote.Processor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -14,6 +16,7 @@ import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; +import java.util.Optional; public class Http11Processor implements Runnable, Processor { @@ -83,11 +86,23 @@ private HttpResponseData loadStaticFile(HttpRequestData httpRequestData) throws } if (acceptHeader.startsWith("text/javascript")) { - contentType = "text/javascript;charset=utf-8"; + contentType = "text/html;charset=utf-8"; } final var requestResource = startLines[1]; - final var resourcePath = getClass().getClassLoader().getResource("static/" + requestResource).getPath(); + var resourceUrl = getClass().getClassLoader().getResource("static/" + requestResource); + var resourcePath = ""; + + // 존재하지 않는 리소스 요청 + if (resourceUrl == null) { + if (requestResource.contains("login")) { + processLogin(httpRequestData); + resourcePath = getClass().getClassLoader().getResource("static/login.html").getPath(); + } + } else { + resourcePath = resourceUrl.getPath(); + } + final var bufferedInputStream = new BufferedInputStream(new FileInputStream(resourcePath)); final var responseBody = bufferedInputStream.readAllBytes(); bufferedInputStream.close(); @@ -95,6 +110,37 @@ private HttpResponseData loadStaticFile(HttpRequestData httpRequestData) throws return new HttpResponseData(responseBody, contentType); } + private void processLogin(HttpRequestData httpRequestData) { + final var startLine = httpRequestData.startLine; + final var split = startLine.split(" "); + final var resourcePath = split[1]; + + final var isQueryStringExists = resourcePath.contains("?"); + final var queryStringMap = new HashMap(); + if (isQueryStringExists) { + int indexOf = resourcePath.indexOf("?"); + final var queryParameterString = resourcePath.substring(indexOf + 1); + final var values = queryParameterString.split("&"); + for (final var value : values) { + final var keyValue = value.split("="); + queryStringMap.put(keyValue[0], keyValue[1]); + } + } + + checkUser(queryStringMap); + } + + private void checkUser(Map queryStringMap) { + Optional user = InMemoryUserRepository.findByAccount(queryStringMap.getOrDefault("account", "")); + + if (user.isPresent()) { + boolean isSame = user.get().checkPassword(queryStringMap.getOrDefault("password", "")); + if (isSame) { + log.info("{}", user.get()); + } + } + } + private String createHttpResponse(HttpResponseData httpResponseData) { return String.join("\r\n", "HTTP/1.1 200 OK ", From bd9e29a6e537b3dacca8b00fe75fe8d0749ecd9b Mon Sep 17 00:00:00 2001 From: le2sky Date: Wed, 4 Sep 2024 22:34:17 +0900 Subject: [PATCH 07/49] =?UTF-8?q?docs:=20todo=20list=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/org/apache/coyote/http11/Http11Processor.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 49532516b1..81a100d280 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -50,6 +50,7 @@ public void process(final Socket connection) { } } + // TODO: header를 생성하는 부분 private Map createHeaders(BufferedReader clientReader) throws IOException { Map headers = new HashMap<>(); while (true) { @@ -77,6 +78,7 @@ private HttpResponseData loadStaticFile(HttpRequestData httpRequestData) throws var acceptHeader = headers.getOrDefault("Accept", "text/html;charset=utf-8"); var contentType = acceptHeader; + // TODO: acceptHeader를 읽어 적절한 contentType을 생성하는 부분 if (acceptHeader.startsWith("text/html")) { contentType = "text/html;charset=utf-8"; } @@ -93,7 +95,7 @@ private HttpResponseData loadStaticFile(HttpRequestData httpRequestData) throws var resourceUrl = getClass().getClassLoader().getResource("static/" + requestResource); var resourcePath = ""; - // 존재하지 않는 리소스 요청 + // TODO: 존재하지 않는 정적 리소스 요청이라면 동적으로 생성하는 부분 if (resourceUrl == null) { if (requestResource.contains("login")) { processLogin(httpRequestData); @@ -115,6 +117,7 @@ private void processLogin(HttpRequestData httpRequestData) { final var split = startLine.split(" "); final var resourcePath = split[1]; + // TODO: 쿼리 스트링을 읽는 부분 final var isQueryStringExists = resourcePath.contains("?"); final var queryStringMap = new HashMap(); if (isQueryStringExists) { @@ -127,6 +130,7 @@ private void processLogin(HttpRequestData httpRequestData) { } } + // TODO: path에 해당되는 처리기를 찾는 부분 checkUser(queryStringMap); } From 6d95afc22fac3e9e80431d1885692e181b78bdab Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 5 Sep 2024 11:18:09 +0900 Subject: [PATCH 08/49] =?UTF-8?q?refactor:=20query=20parameter=20=EC=B1=85?= =?UTF-8?q?=EC=9E=84=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tomcat/build.gradle | 1 + .../apache/coyote/common/QueryParameter.java | 57 ++++++++++++++++++ .../apache/coyote/http11/Http11Processor.java | 26 +++----- .../coyote/common/QueryParameterTest.java | 60 +++++++++++++++++++ 4 files changed, 127 insertions(+), 17 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/common/QueryParameter.java create mode 100644 tomcat/src/test/java/org/apache/coyote/common/QueryParameterTest.java diff --git a/tomcat/build.gradle b/tomcat/build.gradle index 21063b298f..ccb3e2d92d 100644 --- a/tomcat/build.gradle +++ b/tomcat/build.gradle @@ -22,6 +22,7 @@ dependencies { testImplementation 'org.mockito:mockito-core:5.12.0' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.2' testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.2' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.2' } test { diff --git a/tomcat/src/main/java/org/apache/coyote/common/QueryParameter.java b/tomcat/src/main/java/org/apache/coyote/common/QueryParameter.java new file mode 100644 index 0000000000..aa8b1a01aa --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/common/QueryParameter.java @@ -0,0 +1,57 @@ +package org.apache.coyote.common; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +public class QueryParameter { + + private static final String PAIR_DELIMITER = "="; + private static final String PARAMETER_DELIMITER = "&"; + + private final Map queryParameter = new HashMap<>(); + + public QueryParameter(String queryParameter) { + if (queryParameter == null) { + queryParameter = ""; + } + + parseQueryParameter(queryParameter); + } + + private void parseQueryParameter(String queryParameter) { + String[] pairs = queryParameter.split(PARAMETER_DELIMITER); + + for (String pair : pairs) { + if (pair.contains(PAIR_DELIMITER)) { + String[] keyValuePair = pair.split(PAIR_DELIMITER); + putIfValidPair(keyValuePair); + } + } + } + + private void putIfValidPair(String[] keyValuePair) { + if (keyValuePair.length != 2) { + return; + } + String key = keyValuePair[0]; + String value = keyValuePair[1]; + + queryParameter.put(key, value); + } + + public Optional get(String key) { + Objects.requireNonNull(key); + + return Optional.ofNullable(queryParameter.get(key)); + } + + public boolean isEmpty() { + return queryParameter.isEmpty(); + } + + public int getSize() { + return queryParameter.size(); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 81a100d280..717ce3d8fa 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -4,6 +4,7 @@ import com.techcourse.exception.UncheckedServletException; import com.techcourse.model.User; import org.apache.coyote.Processor; +import org.apache.coyote.common.QueryParameter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,6 +14,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @@ -117,28 +119,18 @@ private void processLogin(HttpRequestData httpRequestData) { final var split = startLine.split(" "); final var resourcePath = split[1]; - // TODO: 쿼리 스트링을 읽는 부분 - final var isQueryStringExists = resourcePath.contains("?"); - final var queryStringMap = new HashMap(); - if (isQueryStringExists) { - int indexOf = resourcePath.indexOf("?"); - final var queryParameterString = resourcePath.substring(indexOf + 1); - final var values = queryParameterString.split("&"); - for (final var value : values) { - final var keyValue = value.split("="); - queryStringMap.put(keyValue[0], keyValue[1]); - } - } + URI uri = URI.create(resourcePath); + String query = uri.getQuery(); - // TODO: path에 해당되는 처리기를 찾는 부분 - checkUser(queryStringMap); + checkUser(new QueryParameter(query)); } - private void checkUser(Map queryStringMap) { - Optional user = InMemoryUserRepository.findByAccount(queryStringMap.getOrDefault("account", "")); + private void checkUser(QueryParameter queryParameter) { + String password = queryParameter.get("password").orElse(""); + Optional user = queryParameter.get("account").flatMap(InMemoryUserRepository::findByAccount); if (user.isPresent()) { - boolean isSame = user.get().checkPassword(queryStringMap.getOrDefault("password", "")); + boolean isSame = user.get().checkPassword(password); if (isSame) { log.info("{}", user.get()); } diff --git a/tomcat/src/test/java/org/apache/coyote/common/QueryParameterTest.java b/tomcat/src/test/java/org/apache/coyote/common/QueryParameterTest.java new file mode 100644 index 0000000000..c9206e43a3 --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/common/QueryParameterTest.java @@ -0,0 +1,60 @@ +package org.apache.coyote.common; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class QueryParameterTest { + + @Test + @DisplayName("쿼리 파라미터 문자열은 null이 될 수 있다.") + void createWithNull() { + QueryParameter queryParameter = new QueryParameter(null); + + assertThat(queryParameter.isEmpty()).isTrue(); + } + + @Test + @DisplayName("키값 페어는 a=b 형식만 인식한다.") + void checkFormat() { + QueryParameter queryParameter = new QueryParameter("a-2"); + + assertThat(queryParameter.isEmpty()).isTrue(); + } + + @Test + @DisplayName("단일 키값 페어를 조회한다.") + void single() { + QueryParameter queryParameter = new QueryParameter("a=2"); + + Optional result = queryParameter.get("a"); + + assertThat(result).hasValue("2"); + } + + @Test + @DisplayName("조회 키는 null이 될 수 없다.") + void keyNonNull() { + QueryParameter queryParameter = new QueryParameter("a=2"); + + assertThatThrownBy(() -> queryParameter.get(null)) + .isInstanceOf(NullPointerException.class); + } + + @ParameterizedTest + @CsvSource(value = {"=&:0", "a:0", "a=1:1", "a=2&b=2:2", "a=2&a=2:1", "&a=1&:1", "a=1&b=2&c=3:3"}, delimiter = ':') + @DisplayName("여러 표현식을 처리한다.") + void parametrized(String source, int expectedSize) { + QueryParameter queryParameter = new QueryParameter(source); + + int result = queryParameter.getSize(); + + assertThat(result).isEqualTo(expectedSize); + } +} From 172d7c5165206bbb796e34d6576d302667ed56a6 Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 5 Sep 2024 11:23:28 +0900 Subject: [PATCH 09/49] =?UTF-8?q?refactor:=20process=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EA=B0=80=EB=8F=85=EC=84=B1=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 717ce3d8fa..0384a546f0 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -12,7 +12,10 @@ import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; import java.net.Socket; import java.net.URI; import java.nio.charset.StandardCharsets; @@ -26,7 +29,7 @@ public class Http11Processor implements Runnable, Processor { private final Socket connection; - public Http11Processor(final Socket connection) { + public Http11Processor(Socket connection) { this.connection = connection; } @@ -37,21 +40,29 @@ public void run() { } @Override - public void process(final Socket connection) { - try (final var inputStream = connection.getInputStream(); - final var outputStream = connection.getOutputStream()) { - final var clientReader = new BufferedReader(new InputStreamReader(inputStream)); - final var startLine = clientReader.readLine(); - final var headers = createHeaders(clientReader); - final var response = createHttpResponse(loadStaticFile(new HttpRequestData(startLine, headers))); - - outputStream.write(response.getBytes()); + public void process(Socket connection) { + try (InputStream inputStream = connection.getInputStream(); + OutputStream outputStream = connection.getOutputStream()) { + Reader inputStreamReader = new InputStreamReader(inputStream); + BufferedReader clientReader = new BufferedReader(inputStreamReader); + + HttpRequestData httpRequestData = createHttpRequestData(clientReader); + String httpResponse = createHttpResponseMessage(responseResource(httpRequestData)); + + outputStream.write(httpResponse.getBytes()); outputStream.flush(); } catch (IOException | UncheckedServletException e) { log.error(e.getMessage(), e); } } + private HttpRequestData createHttpRequestData(BufferedReader clientReader) throws IOException { + String requestStartLine = clientReader.readLine(); + Map requestHeader = createHeaders(clientReader); + + return new HttpRequestData(requestStartLine, requestHeader); + } + // TODO: header를 생성하는 부분 private Map createHeaders(BufferedReader clientReader) throws IOException { Map headers = new HashMap<>(); @@ -68,7 +79,7 @@ private Map createHeaders(BufferedReader clientReader) throws IO return headers; } - private HttpResponseData loadStaticFile(HttpRequestData httpRequestData) throws IOException { + private HttpResponseData responseResource(HttpRequestData httpRequestData) throws IOException { final var startLines = httpRequestData.startLine.split(" "); log.info("{}", httpRequestData.headers); @@ -137,7 +148,7 @@ private void checkUser(QueryParameter queryParameter) { } } - private String createHttpResponse(HttpResponseData httpResponseData) { + private String createHttpResponseMessage(HttpResponseData httpResponseData) { return String.join("\r\n", "HTTP/1.1 200 OK ", "Content-Type: " + httpResponseData.contentType + " ", From 3a925600ff6ff10619f639cad17b594a4c9f9d47 Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 5 Sep 2024 11:40:45 +0900 Subject: [PATCH 10/49] =?UTF-8?q?fix:=20javascript=20contentType=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/org/apache/coyote/http11/Http11Processor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 0384a546f0..0dbad8852d 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -101,7 +101,7 @@ private HttpResponseData responseResource(HttpRequestData httpRequestData) throw } if (acceptHeader.startsWith("text/javascript")) { - contentType = "text/html;charset=utf-8"; + contentType = "text/javascript;charset=utf-8"; } final var requestResource = startLines[1]; From 314b36afecd5414954ffa0932163dab76740d4a4 Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 5 Sep 2024 12:59:33 +0900 Subject: [PATCH 11/49] =?UTF-8?q?test:=20=EB=88=84=EB=9D=BD=EB=90=9C=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/common/QueryParameterTest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tomcat/src/test/java/org/apache/coyote/common/QueryParameterTest.java b/tomcat/src/test/java/org/apache/coyote/common/QueryParameterTest.java index c9206e43a3..72153131dc 100644 --- a/tomcat/src/test/java/org/apache/coyote/common/QueryParameterTest.java +++ b/tomcat/src/test/java/org/apache/coyote/common/QueryParameterTest.java @@ -1,10 +1,12 @@ package org.apache.coyote.common; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import java.util.List; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; @@ -38,6 +40,20 @@ void single() { assertThat(result).hasValue("2"); } + @Test + @DisplayName("여러 키값 페어를 읽는다.") + void multi() { + List strings = List.of("a=1", "b=2", "c=3"); + String input = String.join("&", strings); + QueryParameter queryParameter = new QueryParameter(input); + + Assertions.assertAll( + () -> assertThat(queryParameter.get("a")).hasValue("1"), + () -> assertThat(queryParameter.get("b")).hasValue("2"), + () -> assertThat(queryParameter.get("c")).hasValue("3") + ); + } + @Test @DisplayName("조회 키는 null이 될 수 없다.") void keyNonNull() { From 8107ba5c038b3e3f7000256c68cb33bf89e38572 Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 5 Sep 2024 14:43:38 +0900 Subject: [PATCH 12/49] =?UTF-8?q?refactor:=20header=20=EC=B1=85=EC=9E=84?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/apache/coyote/common/Header.java | 48 ++++++++++++++ .../apache/coyote/http11/Http11Processor.java | 62 +++++++----------- .../org/apache/coyote/common/HeaderTest.java | 63 +++++++++++++++++++ 3 files changed, 134 insertions(+), 39 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/common/Header.java create mode 100644 tomcat/src/test/java/org/apache/coyote/common/HeaderTest.java diff --git a/tomcat/src/main/java/org/apache/coyote/common/Header.java b/tomcat/src/main/java/org/apache/coyote/common/Header.java new file mode 100644 index 0000000000..e3f704a2c1 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/common/Header.java @@ -0,0 +1,48 @@ +package org.apache.coyote.common; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +public class Header { + + private static final String PAIR_DELIMITER = "="; + + private final Map header = new HashMap<>(); + + public Header(List header) { + Objects.requireNonNull(header); + + parseHeader(header); + } + + private void parseHeader(List headers) { + for (String pair : headers) { + if (pair.contains(PAIR_DELIMITER)) { + String[] split = pair.split(PAIR_DELIMITER); + putIfValidPair(split); + } + } + } + + private void putIfValidPair(String[] keyValuePair) { + if (keyValuePair.length != 2) { + return; + } + String key = keyValuePair[0].trim(); + String value = keyValuePair[1].trim(); + + header.put(key, value); + } + + public Optional get(String key) { + Objects.requireNonNull(key); + + return Optional.ofNullable(header.get(key)); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 0dbad8852d..b673cce516 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -4,6 +4,7 @@ import com.techcourse.exception.UncheckedServletException; import com.techcourse.model.User; import org.apache.coyote.Processor; +import org.apache.coyote.common.Header; import org.apache.coyote.common.QueryParameter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,12 +16,11 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; -import java.io.Reader; import java.net.Socket; import java.net.URI; import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; public class Http11Processor implements Runnable, Processor { @@ -43,10 +43,7 @@ public void run() { public void process(Socket connection) { try (InputStream inputStream = connection.getInputStream(); OutputStream outputStream = connection.getOutputStream()) { - Reader inputStreamReader = new InputStreamReader(inputStream); - BufferedReader clientReader = new BufferedReader(inputStreamReader); - - HttpRequestData httpRequestData = createHttpRequestData(clientReader); + HttpRequestData httpRequestData = createHttpRequestData(inputStream); String httpResponse = createHttpResponseMessage(responseResource(httpRequestData)); outputStream.write(httpResponse.getBytes()); @@ -56,61 +53,48 @@ public void process(Socket connection) { } } - private HttpRequestData createHttpRequestData(BufferedReader clientReader) throws IOException { - String requestStartLine = clientReader.readLine(); - Map requestHeader = createHeaders(clientReader); - - return new HttpRequestData(requestStartLine, requestHeader); - } - - // TODO: header를 생성하는 부분 - private Map createHeaders(BufferedReader clientReader) throws IOException { - Map headers = new HashMap<>(); - while (true) { - final var readLine = clientReader.readLine(); - if (readLine.isBlank()) { - break; - } + private HttpRequestData createHttpRequestData(InputStream inputStream) throws IOException { + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + String requestLine = bufferedReader.readLine(); + List headerTokens = new ArrayList<>(); - final var header = readLine.split(":"); - headers.put(header[0].trim(), header[1].trim()); + String nowLine = bufferedReader.readLine(); + while (!nowLine.isBlank()) { + headerTokens.add(nowLine); + nowLine = bufferedReader.readLine(); } - return headers; + return new HttpRequestData(requestLine, new Header(headerTokens)); } private HttpResponseData responseResource(HttpRequestData httpRequestData) throws IOException { - final var startLines = httpRequestData.startLine.split(" "); - log.info("{}", httpRequestData.headers); + final var uri = URI.create(httpRequestData.startLine.split(" ")[1]); - if ("/".equals(startLines[1])) { + if ("/".equals(uri.getPath())) { return new HttpResponseData("Hello world!".getBytes(), "text/html;charset=utf-8"); } - final var headers = httpRequestData.headers(); - var acceptHeader = headers.getOrDefault("Accept", "text/html;charset=utf-8"); - var contentType = acceptHeader; - + var contentType = ""; + String path = uri.getPath(); // TODO: acceptHeader를 읽어 적절한 contentType을 생성하는 부분 - if (acceptHeader.startsWith("text/html")) { + if (path.endsWith(".html")) { contentType = "text/html;charset=utf-8"; } - if (acceptHeader.startsWith("text/css")) { + if (path.endsWith(".css")) { contentType = "text/css;charset=utf-8"; } - if (acceptHeader.startsWith("text/javascript")) { + if (path.endsWith(".js")) { contentType = "text/javascript;charset=utf-8"; } - final var requestResource = startLines[1]; - var resourceUrl = getClass().getClassLoader().getResource("static/" + requestResource); + var resourceUrl = getClass().getClassLoader().getResource("static/" + path); var resourcePath = ""; // TODO: 존재하지 않는 정적 리소스 요청이라면 동적으로 생성하는 부분 if (resourceUrl == null) { - if (requestResource.contains("login")) { + if (path.contains("login")) { processLogin(httpRequestData); resourcePath = getClass().getClassLoader().getResource("static/login.html").getPath(); } @@ -157,7 +141,7 @@ private String createHttpResponseMessage(HttpResponseData httpResponseData) { new String(httpResponseData.responseBody, StandardCharsets.UTF_8)); } - private record HttpRequestData(String startLine, Map headers) { + private record HttpRequestData(String startLine, Header header) { } private record HttpResponseData(byte[] responseBody, String contentType) { diff --git a/tomcat/src/test/java/org/apache/coyote/common/HeaderTest.java b/tomcat/src/test/java/org/apache/coyote/common/HeaderTest.java new file mode 100644 index 0000000000..26ca958eda --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/common/HeaderTest.java @@ -0,0 +1,63 @@ +package org.apache.coyote.common; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class HeaderTest { + + @Test + @DisplayName("헤더 문자열 리스트는 null이 될 수 없다.") + void createWithNull() { + assertThatThrownBy(() -> new Header(null)) + .isInstanceOf(NullPointerException.class); + } + + @Test + @DisplayName("헤더는 a=b 형식만 인식한다.") + void singleFormat() { + Header header = new Header(List.of("a-2")); + + Optional result = header.get("a"); + + assertThat(result).isEmpty(); + } + + @Test + @DisplayName("단일 헤더를 조회한다.") + void single() { + Header header = new Header(List.of("a=2")); + + Optional result = header.get("a"); + + assertThat(result).hasValue("2"); + } + + @Test + @DisplayName("여러 헤더를 읽는다.") + void multi() { + List headers = List.of("a=1", "b=2", "c=3"); + Header header = new Header(headers); + + Assertions.assertAll( + () -> assertThat(header.get("a")).hasValue("1"), + () -> assertThat(header.get("b")).hasValue("2"), + () -> assertThat(header.get("c")).hasValue("3") + ); + } + + @Test + @DisplayName("조회 키는 null이 될 수 없다.") + void keyNonNull() { + Header header = new Header(List.of("a=2")); + + assertThatThrownBy(() -> header.get(null)) + .isInstanceOf(NullPointerException.class); + } +} From 8c996268965aaf4207e8b7766a357a1dc248a7e5 Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 5 Sep 2024 14:45:32 +0900 Subject: [PATCH 13/49] =?UTF-8?q?refactor:=20=ED=8C=A8=ED=82=A4=EC=A7=80?= =?UTF-8?q?=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/apache/coyote/{common => http11}/Header.java | 7 ++----- .../java/org/apache/coyote/http11/Http11Processor.java | 2 -- .../apache/coyote/{common => http11}/QueryParameter.java | 4 ++-- .../org/apache/coyote/{common => http11}/HeaderTest.java | 2 +- .../coyote/{common => http11}/QueryParameterTest.java | 2 +- 5 files changed, 6 insertions(+), 11 deletions(-) rename tomcat/src/main/java/org/apache/coyote/{common => http11}/Header.java (90%) rename tomcat/src/main/java/org/apache/coyote/{common => http11}/QueryParameter.java (95%) rename tomcat/src/test/java/org/apache/coyote/{common => http11}/HeaderTest.java (98%) rename tomcat/src/test/java/org/apache/coyote/{common => http11}/QueryParameterTest.java (98%) diff --git a/tomcat/src/main/java/org/apache/coyote/common/Header.java b/tomcat/src/main/java/org/apache/coyote/http11/Header.java similarity index 90% rename from tomcat/src/main/java/org/apache/coyote/common/Header.java rename to tomcat/src/main/java/org/apache/coyote/http11/Header.java index e3f704a2c1..4b2ded6621 100644 --- a/tomcat/src/main/java/org/apache/coyote/common/Header.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Header.java @@ -1,7 +1,4 @@ -package org.apache.coyote.common; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +package org.apache.coyote.http11; import java.util.HashMap; import java.util.List; @@ -9,7 +6,7 @@ import java.util.Objects; import java.util.Optional; -public class Header { +class Header { private static final String PAIR_DELIMITER = "="; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index b673cce516..881a547ade 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -4,8 +4,6 @@ import com.techcourse.exception.UncheckedServletException; import com.techcourse.model.User; import org.apache.coyote.Processor; -import org.apache.coyote.common.Header; -import org.apache.coyote.common.QueryParameter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/tomcat/src/main/java/org/apache/coyote/common/QueryParameter.java b/tomcat/src/main/java/org/apache/coyote/http11/QueryParameter.java similarity index 95% rename from tomcat/src/main/java/org/apache/coyote/common/QueryParameter.java rename to tomcat/src/main/java/org/apache/coyote/http11/QueryParameter.java index aa8b1a01aa..048cfe393f 100644 --- a/tomcat/src/main/java/org/apache/coyote/common/QueryParameter.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/QueryParameter.java @@ -1,11 +1,11 @@ -package org.apache.coyote.common; +package org.apache.coyote.http11; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Optional; -public class QueryParameter { +class QueryParameter { private static final String PAIR_DELIMITER = "="; private static final String PARAMETER_DELIMITER = "&"; diff --git a/tomcat/src/test/java/org/apache/coyote/common/HeaderTest.java b/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java similarity index 98% rename from tomcat/src/test/java/org/apache/coyote/common/HeaderTest.java rename to tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java index 26ca958eda..7dfee38190 100644 --- a/tomcat/src/test/java/org/apache/coyote/common/HeaderTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java @@ -1,4 +1,4 @@ -package org.apache.coyote.common; +package org.apache.coyote.http11; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; diff --git a/tomcat/src/test/java/org/apache/coyote/common/QueryParameterTest.java b/tomcat/src/test/java/org/apache/coyote/http11/QueryParameterTest.java similarity index 98% rename from tomcat/src/test/java/org/apache/coyote/common/QueryParameterTest.java rename to tomcat/src/test/java/org/apache/coyote/http11/QueryParameterTest.java index 72153131dc..640d9c8424 100644 --- a/tomcat/src/test/java/org/apache/coyote/common/QueryParameterTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/QueryParameterTest.java @@ -1,4 +1,4 @@ -package org.apache.coyote.common; +package org.apache.coyote.http11; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; From 281d9d33905b9ed9716afd2112cf07987b14f36d Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 5 Sep 2024 15:32:22 +0900 Subject: [PATCH 14/49] =?UTF-8?q?fix:=20header=20pair=20=EA=B5=AC=EB=B6=84?= =?UTF-8?q?=EC=9E=90=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tomcat/src/main/java/org/apache/coyote/http11/Header.java | 2 +- .../test/java/org/apache/coyote/http11/HeaderTest.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Header.java b/tomcat/src/main/java/org/apache/coyote/http11/Header.java index 4b2ded6621..5bf283db2c 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Header.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Header.java @@ -8,7 +8,7 @@ class Header { - private static final String PAIR_DELIMITER = "="; + private static final String PAIR_DELIMITER = ":"; private final Map header = new HashMap<>(); diff --git a/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java b/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java index 7dfee38190..d59885128f 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java @@ -20,7 +20,7 @@ void createWithNull() { } @Test - @DisplayName("헤더는 a=b 형식만 인식한다.") + @DisplayName("헤더는 a:b 형식만 인식한다.") void singleFormat() { Header header = new Header(List.of("a-2")); @@ -32,7 +32,7 @@ void singleFormat() { @Test @DisplayName("단일 헤더를 조회한다.") void single() { - Header header = new Header(List.of("a=2")); + Header header = new Header(List.of("a:2")); Optional result = header.get("a"); @@ -42,7 +42,7 @@ void single() { @Test @DisplayName("여러 헤더를 읽는다.") void multi() { - List headers = List.of("a=1", "b=2", "c=3"); + List headers = List.of("a:1", "b:2", "c:3"); Header header = new Header(headers); Assertions.assertAll( @@ -55,7 +55,7 @@ void multi() { @Test @DisplayName("조회 키는 null이 될 수 없다.") void keyNonNull() { - Header header = new Header(List.of("a=2")); + Header header = new Header(List.of("a:2")); assertThatThrownBy(() -> header.get(null)) .isInstanceOf(NullPointerException.class); From ccecee0f0733cad177a60fb2e1dabd041dca859d Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 5 Sep 2024 16:16:43 +0900 Subject: [PATCH 15/49] =?UTF-8?q?refactor:=20=EC=A0=95=EC=A0=81=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=ED=95=B8=EB=93=A4=EB=A7=81=20=EC=B1=85?= =?UTF-8?q?=EC=9E=84=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 71 +++++++------------ .../org/apache/coyote/http11/HttpRequest.java | 10 +++ .../apache/coyote/http11/HttpResponse.java | 4 ++ .../apache/coyote/http11/ResourceHandler.java | 10 +++ .../coyote/http11/StaticResourceHandler.java | 55 ++++++++++++++ .../http11/StaticResourceHandlerTest.java | 35 +++++++++ tomcat/src/test/resources/static/sample.txt | 0 7 files changed, 141 insertions(+), 44 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/ResourceHandler.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/StaticResourceHandler.java create mode 100644 tomcat/src/test/java/org/apache/coyote/http11/StaticResourceHandlerTest.java create mode 100644 tomcat/src/test/resources/static/sample.txt diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 881a547ade..99ad9ea1a3 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -41,7 +41,7 @@ public void run() { public void process(Socket connection) { try (InputStream inputStream = connection.getInputStream(); OutputStream outputStream = connection.getOutputStream()) { - HttpRequestData httpRequestData = createHttpRequestData(inputStream); + HttpRequest httpRequestData = createHttpRequestData(inputStream); String httpResponse = createHttpResponseMessage(responseResource(httpRequestData)); outputStream.write(httpResponse.getBytes()); @@ -51,64 +51,53 @@ public void process(Socket connection) { } } - private HttpRequestData createHttpRequestData(InputStream inputStream) throws IOException { + private HttpRequest createHttpRequestData(InputStream inputStream) throws IOException { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String requestLine = bufferedReader.readLine(); List headerTokens = new ArrayList<>(); String nowLine = bufferedReader.readLine(); while (!nowLine.isBlank()) { + if (nowLine.startsWith("Accept")) { + log.info(nowLine); + } + headerTokens.add(nowLine); nowLine = bufferedReader.readLine(); } - return new HttpRequestData(requestLine, new Header(headerTokens)); + return new HttpRequest(requestLine, new Header(headerTokens)); } - private HttpResponseData responseResource(HttpRequestData httpRequestData) throws IOException { - final var uri = URI.create(httpRequestData.startLine.split(" ")[1]); + private HttpResponse responseResource(HttpRequest httpRequestData) throws IOException { + final var uri = httpRequestData.getUri(); if ("/".equals(uri.getPath())) { - return new HttpResponseData("Hello world!".getBytes(), "text/html;charset=utf-8"); - } - - var contentType = ""; - String path = uri.getPath(); - // TODO: acceptHeader를 읽어 적절한 contentType을 생성하는 부분 - if (path.endsWith(".html")) { - contentType = "text/html;charset=utf-8"; - } - - if (path.endsWith(".css")) { - contentType = "text/css;charset=utf-8"; - } - - if (path.endsWith(".js")) { - contentType = "text/javascript;charset=utf-8"; + return new HttpResponse("Hello world!".getBytes(), "text/html;charset=utf-8"); } - var resourceUrl = getClass().getClassLoader().getResource("static/" + path); - var resourcePath = ""; + StaticResourceHandler staticResourceHandler = new StaticResourceHandler(); // TODO: 존재하지 않는 정적 리소스 요청이라면 동적으로 생성하는 부분 - if (resourceUrl == null) { - if (path.contains("login")) { + if (!staticResourceHandler.canHandle(httpRequestData)) { + if (uri.getPath().contains("login")) { processLogin(httpRequestData); - resourcePath = getClass().getClassLoader().getResource("static/login.html").getPath(); + String resourcePath = getClass().getClassLoader().getResource("static/login.html").getPath(); + final var bufferedInputStream = new BufferedInputStream(new FileInputStream(resourcePath)); + final var responseBody = bufferedInputStream.readAllBytes(); + bufferedInputStream.close(); + + return new HttpResponse(responseBody, "text/html;charset=utf8"); } } else { - resourcePath = resourceUrl.getPath(); + return staticResourceHandler.handle(httpRequestData); } - final var bufferedInputStream = new BufferedInputStream(new FileInputStream(resourcePath)); - final var responseBody = bufferedInputStream.readAllBytes(); - bufferedInputStream.close(); - - return new HttpResponseData(responseBody, contentType); + return null; } - private void processLogin(HttpRequestData httpRequestData) { - final var startLine = httpRequestData.startLine; + private void processLogin(HttpRequest httpRequestData) { + final var startLine = httpRequestData.startLine(); final var split = startLine.split(" "); final var resourcePath = split[1]; @@ -130,18 +119,12 @@ private void checkUser(QueryParameter queryParameter) { } } - private String createHttpResponseMessage(HttpResponseData httpResponseData) { + private String createHttpResponseMessage(HttpResponse httpResponseData) { return String.join("\r\n", "HTTP/1.1 200 OK ", - "Content-Type: " + httpResponseData.contentType + " ", - "Content-Length: " + httpResponseData.responseBody.length + " ", + "Content-Type: " + httpResponseData.contentType() + " ", + "Content-Length: " + httpResponseData.responseBody().length + " ", "", - new String(httpResponseData.responseBody, StandardCharsets.UTF_8)); - } - - private record HttpRequestData(String startLine, Header header) { - } - - private record HttpResponseData(byte[] responseBody, String contentType) { + new String(httpResponseData.responseBody(), StandardCharsets.UTF_8)); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java new file mode 100644 index 0000000000..2d2a06f364 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java @@ -0,0 +1,10 @@ +package org.apache.coyote.http11; + +import java.net.URI; + +record HttpRequest(String startLine, Header header) { + + public URI getUri() { + return URI.create(startLine.split(" ")[1]); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java new file mode 100644 index 0000000000..9f8886f834 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java @@ -0,0 +1,4 @@ +package org.apache.coyote.http11; + +record HttpResponse(byte[] responseBody, String contentType) { +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/ResourceHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/ResourceHandler.java new file mode 100644 index 0000000000..5f6cedc974 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/ResourceHandler.java @@ -0,0 +1,10 @@ +package org.apache.coyote.http11; + +import java.net.URI; + +interface ResourceHandler { + + boolean canHandle(HttpRequest httpRequest); + + HttpResponse handle(HttpRequest httpRequest); +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/StaticResourceHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/StaticResourceHandler.java new file mode 100644 index 0000000000..ca759a0ddf --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/StaticResourceHandler.java @@ -0,0 +1,55 @@ +package org.apache.coyote.http11; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.URI; +import java.net.URL; + +class StaticResourceHandler implements ResourceHandler { + + @Override + public boolean canHandle(HttpRequest httpRequest) { + URL resource = getClass().getClassLoader().getResource("static/" + httpRequest.getUri()); + + return resource != null; + } + + @Override + public HttpResponse handle(HttpRequest httpRequest) { + URI uri = httpRequest.getUri(); + String path = uri.getPath(); + String acceptHeader = httpRequest.header() + .get("Accept") + .orElseThrow(IllegalArgumentException::new); + String resourcePath = getClass().getClassLoader().getResource("static/" + path).getPath(); + String contentType = determineContentType(resourcePath, acceptHeader); + + return new HttpResponse(readStaticResource(resourcePath), contentType); + } + + private String determineContentType(String resourcePath, String acceptHeader) { + String encodedContentType = "%s;charset=utf-8"; + if (acceptHeader.startsWith("text/html") && resourcePath.endsWith(".html")) { + return String.format(encodedContentType, "text/html"); + } + + if (acceptHeader.startsWith("text/css") && resourcePath.endsWith(".css")) { + return String.format(encodedContentType, "text/css"); + } + + if (acceptHeader.startsWith("text/javascript") && resourcePath.endsWith(".js")) { + return String.format(encodedContentType, "text/javascript"); + } + + throw new IllegalStateException(); + } + + private byte[] readStaticResource(String resourcePath) { + try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(resourcePath))) { + return bufferedInputStream.readAllBytes(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/StaticResourceHandlerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/StaticResourceHandlerTest.java new file mode 100644 index 0000000000..d668322bca --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/StaticResourceHandlerTest.java @@ -0,0 +1,35 @@ +package org.apache.coyote.http11; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; + +class StaticResourceHandlerTest { + + @Test + @DisplayName("static 하위에 존재하는 리소스라면 정적 리소스 핸들러가 처리할 수 있다.") + void canHandle() { + StaticResourceHandler staticResourceHandler = new StaticResourceHandler(); + + boolean result = staticResourceHandler.canHandle(createHttpRequest("GET /sample.txt HTTP/1.1")); + + assertThat(result).isTrue(); + } + + @Test + @DisplayName("static 하위에 존재하지 않는 리소스라면 정적 리소스 핸들러가 처리할 수 없다.") + void cantHandle() { + StaticResourceHandler staticResourceHandler = new StaticResourceHandler(); + + boolean result = staticResourceHandler.canHandle(createHttpRequest("GET /unknown.txt HTTP/1.1")); + + assertThat(result).isFalse(); + } + + private HttpRequest createHttpRequest(String startLine) { + return new HttpRequest(startLine, new Header(Collections.emptyList())); + } +} diff --git a/tomcat/src/test/resources/static/sample.txt b/tomcat/src/test/resources/static/sample.txt new file mode 100644 index 0000000000..e69de29bb2 From 2506893e642421f5dd6893becdf2b8aec625c1e2 Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 5 Sep 2024 16:33:24 +0900 Subject: [PATCH 16/49] =?UTF-8?q?refactor:=20login=20=EC=B1=85=EC=9E=84=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 47 ++-------------- .../apache/coyote/http11/LoginHandler.java | 56 +++++++++++++++++++ .../coyote/http11/LoginHandlerTest.java | 35 ++++++++++++ 3 files changed, 95 insertions(+), 43 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/LoginHandler.java create mode 100644 tomcat/src/test/java/org/apache/coyote/http11/LoginHandlerTest.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 99ad9ea1a3..6ef50b64f8 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -1,25 +1,19 @@ package org.apache.coyote.http11; -import com.techcourse.db.InMemoryUserRepository; import com.techcourse.exception.UncheckedServletException; -import com.techcourse.model.User; import org.apache.coyote.Processor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.BufferedInputStream; import java.io.BufferedReader; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; -import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.Optional; public class Http11Processor implements Runnable, Processor { @@ -77,45 +71,12 @@ private HttpResponse responseResource(HttpRequest httpRequestData) throws IOExce } StaticResourceHandler staticResourceHandler = new StaticResourceHandler(); + LoginHandler loginHandler = new LoginHandler(); - // TODO: 존재하지 않는 정적 리소스 요청이라면 동적으로 생성하는 부분 - if (!staticResourceHandler.canHandle(httpRequestData)) { - if (uri.getPath().contains("login")) { - processLogin(httpRequestData); - String resourcePath = getClass().getClassLoader().getResource("static/login.html").getPath(); - final var bufferedInputStream = new BufferedInputStream(new FileInputStream(resourcePath)); - final var responseBody = bufferedInputStream.readAllBytes(); - bufferedInputStream.close(); - - return new HttpResponse(responseBody, "text/html;charset=utf8"); - } - } else { + if (staticResourceHandler.canHandle(httpRequestData)) { return staticResourceHandler.handle(httpRequestData); - } - - return null; - } - - private void processLogin(HttpRequest httpRequestData) { - final var startLine = httpRequestData.startLine(); - final var split = startLine.split(" "); - final var resourcePath = split[1]; - - URI uri = URI.create(resourcePath); - String query = uri.getQuery(); - - checkUser(new QueryParameter(query)); - } - - private void checkUser(QueryParameter queryParameter) { - String password = queryParameter.get("password").orElse(""); - Optional user = queryParameter.get("account").flatMap(InMemoryUserRepository::findByAccount); - - if (user.isPresent()) { - boolean isSame = user.get().checkPassword(password); - if (isSame) { - log.info("{}", user.get()); - } + } else { + return loginHandler.handle(httpRequestData); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/LoginHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/LoginHandler.java new file mode 100644 index 0000000000..5617709f68 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/LoginHandler.java @@ -0,0 +1,56 @@ +package org.apache.coyote.http11; + +import com.techcourse.db.InMemoryUserRepository; +import com.techcourse.model.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.URI; +import java.util.Optional; + +class LoginHandler implements ResourceHandler { + + private static final Logger log = LoggerFactory.getLogger(LoginHandler.class); + + @Override + public boolean canHandle(HttpRequest httpRequest) { + URI uri = httpRequest.getUri(); + String path = uri.getPath(); + + return "/login".equals(path); + } + + @Override + public HttpResponse handle(HttpRequest httpRequest) { + QueryParameter queryParameter = new QueryParameter(httpRequest.getUri().getQuery()); + checkUser(queryParameter); + + String resourcePath = getClass().getClassLoader().getResource("static/login.html").getPath(); + byte[] bytes = readStaticResource(resourcePath); + + return new HttpResponse(bytes, "text/html;charset=utf8"); + } + + private void checkUser(QueryParameter queryParameter) { + String password = queryParameter.get("password").orElse(""); + Optional user = queryParameter.get("account").flatMap(InMemoryUserRepository::findByAccount); + + if (user.isPresent()) { + boolean isSame = user.get().checkPassword(password); + if (isSame) { + log.info("{}", user.get()); + } + } + } + + private byte[] readStaticResource(String resourcePath) { + try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(resourcePath))) { + return bufferedInputStream.readAllBytes(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/LoginHandlerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/LoginHandlerTest.java new file mode 100644 index 0000000000..0e124c3e1f --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/LoginHandlerTest.java @@ -0,0 +1,35 @@ +package org.apache.coyote.http11; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; + +class LoginHandlerTest { + + @Test + @DisplayName("로그인 관련 요청을 처리할 수 있다.") + void canHandle() { + LoginHandler loginHandler = new LoginHandler(); + + boolean result = loginHandler.canHandle(createHttpRequest("GET /login HTTP/1.1")); + + assertThat(result).isTrue(); + } + + @Test + @DisplayName("로그인 관련 요청이 아니라면 처리할 수 없다.") + void cantHandle() { + LoginHandler loginHandler = new LoginHandler(); + + boolean result = loginHandler.canHandle(createHttpRequest("GET /login1?a=1 HTTP/1.1")); + + assertThat(result).isFalse(); + } + + private HttpRequest createHttpRequest(String startLine) { + return new HttpRequest(startLine, new Header(Collections.emptyList())); + } +} From c0b8dfc266b4303482d33926a20a15ddadffe12a Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 5 Sep 2024 16:56:38 +0900 Subject: [PATCH 17/49] =?UTF-8?q?refactor:=20handler=20=EC=B6=94=EC=83=81?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/AbstractHandler.java | 50 +++++++++++++++++++ .../apache/coyote/http11/LoginHandler.java | 20 ++------ .../coyote/http11/StaticResourceHandler.java | 39 ++------------- 3 files changed, 56 insertions(+), 53 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java new file mode 100644 index 0000000000..964841403c --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java @@ -0,0 +1,50 @@ +package org.apache.coyote.http11; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; + +abstract class AbstractHandler { + + protected abstract boolean canHandle(HttpRequest httpRequest); + + protected abstract String forward(HttpRequest httpRequest); + + public HttpResponse handle(HttpRequest httpRequest) { + String acceptHeader = httpRequest.header() + .get("Accept") + .orElseThrow(IllegalArgumentException::new); + + String result = forward(httpRequest); + + String resourcePath = getClass().getClassLoader().getResource(result).getPath(); + String contentType = determineContentType(resourcePath, acceptHeader); + + return new HttpResponse(readStaticResource(resourcePath), contentType); + } + + private String determineContentType(String resourcePath, String acceptHeader) { + String encodedContentType = "%s;charset=utf-8"; + if (acceptHeader.startsWith("text/html") && resourcePath.endsWith(".html")) { + return String.format(encodedContentType, "text/html"); + } + + if (acceptHeader.startsWith("text/css") && resourcePath.endsWith(".css")) { + return String.format(encodedContentType, "text/css"); + } + + if (acceptHeader.startsWith("text/javascript") && resourcePath.endsWith(".js")) { + return String.format(encodedContentType, "text/javascript"); + } + + throw new IllegalStateException(); + } + + private byte[] readStaticResource(String resourcePath) { + try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(resourcePath))) { + return bufferedInputStream.readAllBytes(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/LoginHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/LoginHandler.java index 5617709f68..79aa0d88e9 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/LoginHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/LoginHandler.java @@ -5,13 +5,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.BufferedInputStream; -import java.io.FileInputStream; -import java.io.IOException; import java.net.URI; import java.util.Optional; -class LoginHandler implements ResourceHandler { +class LoginHandler extends AbstractHandler { private static final Logger log = LoggerFactory.getLogger(LoginHandler.class); @@ -24,14 +21,11 @@ public boolean canHandle(HttpRequest httpRequest) { } @Override - public HttpResponse handle(HttpRequest httpRequest) { + protected String forward(HttpRequest httpRequest) { QueryParameter queryParameter = new QueryParameter(httpRequest.getUri().getQuery()); checkUser(queryParameter); - String resourcePath = getClass().getClassLoader().getResource("static/login.html").getPath(); - byte[] bytes = readStaticResource(resourcePath); - - return new HttpResponse(bytes, "text/html;charset=utf8"); + return "static/login.html"; } private void checkUser(QueryParameter queryParameter) { @@ -45,12 +39,4 @@ private void checkUser(QueryParameter queryParameter) { } } } - - private byte[] readStaticResource(String resourcePath) { - try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(resourcePath))) { - return bufferedInputStream.readAllBytes(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/StaticResourceHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/StaticResourceHandler.java index ca759a0ddf..1cc24fc956 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/StaticResourceHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/StaticResourceHandler.java @@ -1,12 +1,9 @@ package org.apache.coyote.http11; -import java.io.BufferedInputStream; -import java.io.FileInputStream; -import java.io.IOException; import java.net.URI; import java.net.URL; -class StaticResourceHandler implements ResourceHandler { +class StaticResourceHandler extends AbstractHandler { @Override public boolean canHandle(HttpRequest httpRequest) { @@ -16,40 +13,10 @@ public boolean canHandle(HttpRequest httpRequest) { } @Override - public HttpResponse handle(HttpRequest httpRequest) { + public String forward(HttpRequest httpRequest) { URI uri = httpRequest.getUri(); String path = uri.getPath(); - String acceptHeader = httpRequest.header() - .get("Accept") - .orElseThrow(IllegalArgumentException::new); - String resourcePath = getClass().getClassLoader().getResource("static/" + path).getPath(); - String contentType = determineContentType(resourcePath, acceptHeader); - return new HttpResponse(readStaticResource(resourcePath), contentType); - } - - private String determineContentType(String resourcePath, String acceptHeader) { - String encodedContentType = "%s;charset=utf-8"; - if (acceptHeader.startsWith("text/html") && resourcePath.endsWith(".html")) { - return String.format(encodedContentType, "text/html"); - } - - if (acceptHeader.startsWith("text/css") && resourcePath.endsWith(".css")) { - return String.format(encodedContentType, "text/css"); - } - - if (acceptHeader.startsWith("text/javascript") && resourcePath.endsWith(".js")) { - return String.format(encodedContentType, "text/javascript"); - } - - throw new IllegalStateException(); - } - - private byte[] readStaticResource(String resourcePath) { - try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(resourcePath))) { - return bufferedInputStream.readAllBytes(); - } catch (IOException e) { - throw new RuntimeException(e); - } + return "static/" + path; } } From 8780caf82cd87620fa4378bc259d66a914c41c1d Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 5 Sep 2024 17:14:41 +0900 Subject: [PATCH 18/49] =?UTF-8?q?refactor:=20=EC=B6=94=EC=83=81=ED=99=94?= =?UTF-8?q?=EB=90=9C=20=ED=95=B8=EB=93=A4=EB=9F=AC=20=EC=82=AC=EC=9A=A9?= =?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 --- .../java/org/apache/coyote/http11/Header.java | 2 +- .../apache/coyote/http11/Http11Processor.java | 27 ++++++------- .../org/apache/coyote/http11/HttpRequest.java | 2 +- .../apache/coyote/http11/HttpResponse.java | 2 +- .../apache/coyote/http11/QueryParameter.java | 2 +- .../apache/coyote/http11/ResourceHandler.java | 10 ----- .../http11/{ => handler}/AbstractHandler.java | 9 +++-- .../coyote/http11/handler/IndexHandler.java | 27 +++++++++++++ .../http11/{ => handler}/LoginHandler.java | 6 ++- .../{ => handler}/StaticResourceHandler.java | 6 ++- .../http11/handler/IndexHandlerTest.java | 38 +++++++++++++++++++ .../{ => handler}/LoginHandlerTest.java | 5 ++- .../StaticResourceHandlerTest.java | 5 ++- 13 files changed, 105 insertions(+), 36 deletions(-) delete mode 100644 tomcat/src/main/java/org/apache/coyote/http11/ResourceHandler.java rename tomcat/src/main/java/org/apache/coyote/http11/{ => handler}/AbstractHandler.java (87%) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/handler/IndexHandler.java rename tomcat/src/main/java/org/apache/coyote/http11/{ => handler}/LoginHandler.java (85%) rename tomcat/src/main/java/org/apache/coyote/http11/{ => handler}/StaticResourceHandler.java (74%) create mode 100644 tomcat/src/test/java/org/apache/coyote/http11/handler/IndexHandlerTest.java rename tomcat/src/test/java/org/apache/coyote/http11/{ => handler}/LoginHandlerTest.java (84%) rename tomcat/src/test/java/org/apache/coyote/http11/{ => handler}/StaticResourceHandlerTest.java (85%) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Header.java b/tomcat/src/main/java/org/apache/coyote/http11/Header.java index 5bf283db2c..ec9a7a1fd2 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Header.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Header.java @@ -6,7 +6,7 @@ import java.util.Objects; import java.util.Optional; -class Header { +public class Header { private static final String PAIR_DELIMITER = ":"; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 6ef50b64f8..c3599cd6e8 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -2,6 +2,10 @@ import com.techcourse.exception.UncheckedServletException; import org.apache.coyote.Processor; +import org.apache.coyote.http11.handler.AbstractHandler; +import org.apache.coyote.http11.handler.IndexHandler; +import org.apache.coyote.http11.handler.LoginHandler; +import org.apache.coyote.http11.handler.StaticResourceHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -63,21 +67,18 @@ private HttpRequest createHttpRequestData(InputStream inputStream) throws IOExce return new HttpRequest(requestLine, new Header(headerTokens)); } - private HttpResponse responseResource(HttpRequest httpRequestData) throws IOException { - final var uri = httpRequestData.getUri(); + private HttpResponse responseResource(HttpRequest httpRequest) throws IOException { + AbstractHandler indexHandler = new IndexHandler(); + AbstractHandler staticResourceHandler = new StaticResourceHandler(); + AbstractHandler loginHandler = new LoginHandler(); - if ("/".equals(uri.getPath())) { - return new HttpResponse("Hello world!".getBytes(), "text/html;charset=utf-8"); - } - - StaticResourceHandler staticResourceHandler = new StaticResourceHandler(); - LoginHandler loginHandler = new LoginHandler(); + List handlers = List.of(indexHandler, staticResourceHandler, loginHandler); + AbstractHandler targetHandler = handlers.stream() + .filter(it -> it.canHandle(httpRequest)) + .findFirst() + .orElseThrow(IllegalArgumentException::new); - if (staticResourceHandler.canHandle(httpRequestData)) { - return staticResourceHandler.handle(httpRequestData); - } else { - return loginHandler.handle(httpRequestData); - } + return targetHandler.handle(httpRequest); } private String createHttpResponseMessage(HttpResponse httpResponseData) { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java index 2d2a06f364..74a83731f6 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java @@ -2,7 +2,7 @@ import java.net.URI; -record HttpRequest(String startLine, Header header) { +public record HttpRequest(String startLine, Header header) { public URI getUri() { return URI.create(startLine.split(" ")[1]); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java index 9f8886f834..5c36cbb76e 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java @@ -1,4 +1,4 @@ package org.apache.coyote.http11; -record HttpResponse(byte[] responseBody, String contentType) { +public record HttpResponse(byte[] responseBody, String contentType) { } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/QueryParameter.java b/tomcat/src/main/java/org/apache/coyote/http11/QueryParameter.java index 048cfe393f..29d0189c8b 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/QueryParameter.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/QueryParameter.java @@ -5,7 +5,7 @@ import java.util.Objects; import java.util.Optional; -class QueryParameter { +public class QueryParameter { private static final String PAIR_DELIMITER = "="; private static final String PARAMETER_DELIMITER = "&"; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/ResourceHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/ResourceHandler.java deleted file mode 100644 index 5f6cedc974..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/ResourceHandler.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.apache.coyote.http11; - -import java.net.URI; - -interface ResourceHandler { - - boolean canHandle(HttpRequest httpRequest); - - HttpResponse handle(HttpRequest httpRequest); -} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java similarity index 87% rename from tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java rename to tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java index 964841403c..2b8066db05 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java @@ -1,12 +1,15 @@ -package org.apache.coyote.http11; +package org.apache.coyote.http11.handler; + +import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.HttpResponse; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; -abstract class AbstractHandler { +public abstract class AbstractHandler { - protected abstract boolean canHandle(HttpRequest httpRequest); + public abstract boolean canHandle(HttpRequest httpRequest); protected abstract String forward(HttpRequest httpRequest); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/IndexHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/IndexHandler.java new file mode 100644 index 0000000000..068087d2c5 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/IndexHandler.java @@ -0,0 +1,27 @@ +package org.apache.coyote.http11.handler; + +import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.HttpResponse; + +import java.net.URI; + +public class IndexHandler extends AbstractHandler { + + @Override + public boolean canHandle(HttpRequest httpRequest) { + URI uri = httpRequest.getUri(); + String path = uri.getPath(); + + return "/".equals(path); + } + + @Override + protected String forward(HttpRequest httpRequest) { + return null; + } + + @Override + public HttpResponse handle(HttpRequest httpRequest) { + return new HttpResponse("Hello world!".getBytes(), "text/html;charset=utf-8"); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/LoginHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/LoginHandler.java similarity index 85% rename from tomcat/src/main/java/org/apache/coyote/http11/LoginHandler.java rename to tomcat/src/main/java/org/apache/coyote/http11/handler/LoginHandler.java index 79aa0d88e9..a7ccfdef47 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/LoginHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/LoginHandler.java @@ -1,14 +1,16 @@ -package org.apache.coyote.http11; +package org.apache.coyote.http11.handler; import com.techcourse.db.InMemoryUserRepository; import com.techcourse.model.User; +import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.QueryParameter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.URI; import java.util.Optional; -class LoginHandler extends AbstractHandler { +public class LoginHandler extends AbstractHandler { private static final Logger log = LoggerFactory.getLogger(LoginHandler.class); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/StaticResourceHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/StaticResourceHandler.java similarity index 74% rename from tomcat/src/main/java/org/apache/coyote/http11/StaticResourceHandler.java rename to tomcat/src/main/java/org/apache/coyote/http11/handler/StaticResourceHandler.java index 1cc24fc956..58dd7a7bc6 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/StaticResourceHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/StaticResourceHandler.java @@ -1,9 +1,11 @@ -package org.apache.coyote.http11; +package org.apache.coyote.http11.handler; + +import org.apache.coyote.http11.HttpRequest; import java.net.URI; import java.net.URL; -class StaticResourceHandler extends AbstractHandler { +public class StaticResourceHandler extends AbstractHandler { @Override public boolean canHandle(HttpRequest httpRequest) { diff --git a/tomcat/src/test/java/org/apache/coyote/http11/handler/IndexHandlerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/handler/IndexHandlerTest.java new file mode 100644 index 0000000000..86120d8f1b --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/handler/IndexHandlerTest.java @@ -0,0 +1,38 @@ +package org.apache.coyote.http11.handler; + +import org.apache.coyote.http11.Header; +import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.handler.IndexHandler; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; + +class IndexHandlerTest { + + @Test + @DisplayName("루트 경로 요청을 처리할 수 있다.") + void canHandle() { + IndexHandler indexHandler = new IndexHandler(); + + boolean result = indexHandler.canHandle(createHttpRequest("GET / HTTP/1.1")); + + assertThat(result).isTrue(); + } + + @Test + @DisplayName("루트 경로가 아닌 요청은 처리할 수 없다.") + void cantHandle() { + IndexHandler indexHandler = new IndexHandler(); + + boolean result = indexHandler.canHandle(createHttpRequest("GET /login HTTP/1.1")); + + assertThat(result).isFalse(); + } + + private HttpRequest createHttpRequest(String startLine) { + return new HttpRequest(startLine, new Header(Collections.emptyList())); + } +} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/LoginHandlerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/handler/LoginHandlerTest.java similarity index 84% rename from tomcat/src/test/java/org/apache/coyote/http11/LoginHandlerTest.java rename to tomcat/src/test/java/org/apache/coyote/http11/handler/LoginHandlerTest.java index 0e124c3e1f..4295a3f7e6 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/LoginHandlerTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/handler/LoginHandlerTest.java @@ -1,5 +1,8 @@ -package org.apache.coyote.http11; +package org.apache.coyote.http11.handler; +import org.apache.coyote.http11.Header; +import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.handler.LoginHandler; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/tomcat/src/test/java/org/apache/coyote/http11/StaticResourceHandlerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/handler/StaticResourceHandlerTest.java similarity index 85% rename from tomcat/src/test/java/org/apache/coyote/http11/StaticResourceHandlerTest.java rename to tomcat/src/test/java/org/apache/coyote/http11/handler/StaticResourceHandlerTest.java index d668322bca..470245287a 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/StaticResourceHandlerTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/handler/StaticResourceHandlerTest.java @@ -1,5 +1,8 @@ -package org.apache.coyote.http11; +package org.apache.coyote.http11.handler; +import org.apache.coyote.http11.Header; +import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.handler.StaticResourceHandler; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; From b7acd8245eceaa3ee0be2004671ae77249c3330d Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 5 Sep 2024 17:24:56 +0900 Subject: [PATCH 19/49] =?UTF-8?q?fix:=20*/*=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coyote/http11/handler/AbstractHandler.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java index 2b8066db05..bf6e2c780c 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java @@ -28,6 +28,20 @@ public HttpResponse handle(HttpRequest httpRequest) { private String determineContentType(String resourcePath, String acceptHeader) { String encodedContentType = "%s;charset=utf-8"; + if (acceptHeader.startsWith("*/*")) { + if (resourcePath.endsWith(".html")) { + return String.format(encodedContentType, "text/html"); + } + + if (resourcePath.endsWith(".css")) { + return String.format(encodedContentType, "text/css"); + } + + if (resourcePath.endsWith(".js")) { + return String.format(encodedContentType, "text/javascript"); + } + } + if (acceptHeader.startsWith("text/html") && resourcePath.endsWith(".html")) { return String.format(encodedContentType, "text/html"); } From f852512c767f02e9a3cd4923f64dbe50a1cc5777 Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 5 Sep 2024 18:55:07 +0900 Subject: [PATCH 20/49] =?UTF-8?q?fix:=20hello=20handler=20hello.html=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/apache/coyote/http11/Http11Processor.java | 6 +++--- .../apache/coyote/http11/handler/AbstractHandler.java | 2 +- .../handler/{IndexHandler.java => HelloHandler.java} | 10 ++-------- tomcat/src/main/resources/static/hello.html | 1 + .../{IndexHandlerTest.java => HelloHandlerTest.java} | 11 +++++------ 5 files changed, 12 insertions(+), 18 deletions(-) rename tomcat/src/main/java/org/apache/coyote/http11/handler/{IndexHandler.java => HelloHandler.java} (57%) create mode 100644 tomcat/src/main/resources/static/hello.html rename tomcat/src/test/java/org/apache/coyote/http11/handler/{IndexHandlerTest.java => HelloHandlerTest.java} (71%) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index c3599cd6e8..d9c2d0e82f 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -3,7 +3,7 @@ import com.techcourse.exception.UncheckedServletException; import org.apache.coyote.Processor; import org.apache.coyote.http11.handler.AbstractHandler; -import org.apache.coyote.http11.handler.IndexHandler; +import org.apache.coyote.http11.handler.HelloHandler; import org.apache.coyote.http11.handler.LoginHandler; import org.apache.coyote.http11.handler.StaticResourceHandler; import org.slf4j.Logger; @@ -68,11 +68,11 @@ private HttpRequest createHttpRequestData(InputStream inputStream) throws IOExce } private HttpResponse responseResource(HttpRequest httpRequest) throws IOException { - AbstractHandler indexHandler = new IndexHandler(); + AbstractHandler helloHandler = new HelloHandler(); AbstractHandler staticResourceHandler = new StaticResourceHandler(); AbstractHandler loginHandler = new LoginHandler(); - List handlers = List.of(indexHandler, staticResourceHandler, loginHandler); + List handlers = List.of(helloHandler, staticResourceHandler, loginHandler); AbstractHandler targetHandler = handlers.stream() .filter(it -> it.canHandle(httpRequest)) .findFirst() diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java index bf6e2c780c..cd41cbe116 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java @@ -16,7 +16,7 @@ public abstract class AbstractHandler { public HttpResponse handle(HttpRequest httpRequest) { String acceptHeader = httpRequest.header() .get("Accept") - .orElseThrow(IllegalArgumentException::new); + .orElse("*/*"); String result = forward(httpRequest); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/IndexHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/HelloHandler.java similarity index 57% rename from tomcat/src/main/java/org/apache/coyote/http11/handler/IndexHandler.java rename to tomcat/src/main/java/org/apache/coyote/http11/handler/HelloHandler.java index 068087d2c5..6cb2ff2512 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/IndexHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/HelloHandler.java @@ -1,11 +1,10 @@ package org.apache.coyote.http11.handler; import org.apache.coyote.http11.HttpRequest; -import org.apache.coyote.http11.HttpResponse; import java.net.URI; -public class IndexHandler extends AbstractHandler { +public class HelloHandler extends AbstractHandler { @Override public boolean canHandle(HttpRequest httpRequest) { @@ -17,11 +16,6 @@ public boolean canHandle(HttpRequest httpRequest) { @Override protected String forward(HttpRequest httpRequest) { - return null; - } - - @Override - public HttpResponse handle(HttpRequest httpRequest) { - return new HttpResponse("Hello world!".getBytes(), "text/html;charset=utf-8"); + return "static/hello.html"; } } diff --git a/tomcat/src/main/resources/static/hello.html b/tomcat/src/main/resources/static/hello.html new file mode 100644 index 0000000000..6769dd60bd --- /dev/null +++ b/tomcat/src/main/resources/static/hello.html @@ -0,0 +1 @@ +Hello world! \ No newline at end of file diff --git a/tomcat/src/test/java/org/apache/coyote/http11/handler/IndexHandlerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/handler/HelloHandlerTest.java similarity index 71% rename from tomcat/src/test/java/org/apache/coyote/http11/handler/IndexHandlerTest.java rename to tomcat/src/test/java/org/apache/coyote/http11/handler/HelloHandlerTest.java index 86120d8f1b..89f0249269 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/handler/IndexHandlerTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/handler/HelloHandlerTest.java @@ -2,7 +2,6 @@ import org.apache.coyote.http11.Header; import org.apache.coyote.http11.HttpRequest; -import org.apache.coyote.http11.handler.IndexHandler; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -10,14 +9,14 @@ import static org.assertj.core.api.Assertions.assertThat; -class IndexHandlerTest { +class HelloHandlerTest { @Test @DisplayName("루트 경로 요청을 처리할 수 있다.") void canHandle() { - IndexHandler indexHandler = new IndexHandler(); + HelloHandler helloHandler = new HelloHandler(); - boolean result = indexHandler.canHandle(createHttpRequest("GET / HTTP/1.1")); + boolean result = helloHandler.canHandle(createHttpRequest("GET / HTTP/1.1")); assertThat(result).isTrue(); } @@ -25,9 +24,9 @@ void canHandle() { @Test @DisplayName("루트 경로가 아닌 요청은 처리할 수 없다.") void cantHandle() { - IndexHandler indexHandler = new IndexHandler(); + HelloHandler helloHandler = new HelloHandler(); - boolean result = indexHandler.canHandle(createHttpRequest("GET /login HTTP/1.1")); + boolean result = helloHandler.canHandle(createHttpRequest("GET /login HTTP/1.1")); assertThat(result).isFalse(); } From 704823cc89c7627eb99761b8a0a714ac2bfbcf71 Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 5 Sep 2024 18:56:30 +0900 Subject: [PATCH 21/49] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EB=A1=9C=EA=B7=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/org/apache/coyote/http11/Http11Processor.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index d9c2d0e82f..823d518cae 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -56,10 +56,6 @@ private HttpRequest createHttpRequestData(InputStream inputStream) throws IOExce String nowLine = bufferedReader.readLine(); while (!nowLine.isBlank()) { - if (nowLine.startsWith("Accept")) { - log.info(nowLine); - } - headerTokens.add(nowLine); nowLine = bufferedReader.readLine(); } From 3b5537683c7e77d620f28f1b35ae463fcd4de1ff Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 5 Sep 2024 18:57:20 +0900 Subject: [PATCH 22/49] =?UTF-8?q?chore:=20import=20=EC=B5=9C=EC=A0=81?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/apache/coyote/http11/handler/LoginHandlerTest.java | 1 - .../apache/coyote/http11/handler/StaticResourceHandlerTest.java | 1 - 2 files changed, 2 deletions(-) diff --git a/tomcat/src/test/java/org/apache/coyote/http11/handler/LoginHandlerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/handler/LoginHandlerTest.java index 4295a3f7e6..4b57450393 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/handler/LoginHandlerTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/handler/LoginHandlerTest.java @@ -2,7 +2,6 @@ import org.apache.coyote.http11.Header; import org.apache.coyote.http11.HttpRequest; -import org.apache.coyote.http11.handler.LoginHandler; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/tomcat/src/test/java/org/apache/coyote/http11/handler/StaticResourceHandlerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/handler/StaticResourceHandlerTest.java index 470245287a..ca020e415e 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/handler/StaticResourceHandlerTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/handler/StaticResourceHandlerTest.java @@ -2,7 +2,6 @@ import org.apache.coyote.http11.Header; import org.apache.coyote.http11.HttpRequest; -import org.apache.coyote.http11.handler.StaticResourceHandler; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; From 23b30982bc84b9a0ac4fe576dfc2ef539e0a2517 Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 5 Sep 2024 19:04:19 +0900 Subject: [PATCH 23/49] =?UTF-8?q?refactor:=20response=20=EB=82=B4=EB=B6=80?= =?UTF-8?q?=EB=A1=9C=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EC=B1=85=EC=9E=84=20=EC=9C=84=EC=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 21 +++++-------------- .../apache/coyote/http11/HttpResponse.java | 13 ++++++++++++ 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 823d518cae..bd7a4033cb 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -15,7 +15,6 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -39,17 +38,16 @@ public void run() { public void process(Socket connection) { try (InputStream inputStream = connection.getInputStream(); OutputStream outputStream = connection.getOutputStream()) { - HttpRequest httpRequestData = createHttpRequestData(inputStream); - String httpResponse = createHttpResponseMessage(responseResource(httpRequestData)); - - outputStream.write(httpResponse.getBytes()); + HttpRequest httpRequest = createHttpRequest(inputStream); + HttpResponse httpResponse = respondResource(httpRequest); + outputStream.write(httpResponse.serialize()); outputStream.flush(); } catch (IOException | UncheckedServletException e) { log.error(e.getMessage(), e); } } - private HttpRequest createHttpRequestData(InputStream inputStream) throws IOException { + private HttpRequest createHttpRequest(InputStream inputStream) throws IOException { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String requestLine = bufferedReader.readLine(); List headerTokens = new ArrayList<>(); @@ -63,7 +61,7 @@ private HttpRequest createHttpRequestData(InputStream inputStream) throws IOExce return new HttpRequest(requestLine, new Header(headerTokens)); } - private HttpResponse responseResource(HttpRequest httpRequest) throws IOException { + private HttpResponse respondResource(HttpRequest httpRequest) throws IOException { AbstractHandler helloHandler = new HelloHandler(); AbstractHandler staticResourceHandler = new StaticResourceHandler(); AbstractHandler loginHandler = new LoginHandler(); @@ -76,13 +74,4 @@ private HttpResponse responseResource(HttpRequest httpRequest) throws IOExceptio return targetHandler.handle(httpRequest); } - - private String createHttpResponseMessage(HttpResponse httpResponseData) { - return String.join("\r\n", - "HTTP/1.1 200 OK ", - "Content-Type: " + httpResponseData.contentType() + " ", - "Content-Length: " + httpResponseData.responseBody().length + " ", - "", - new String(httpResponseData.responseBody(), StandardCharsets.UTF_8)); - } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java index 5c36cbb76e..c15462a767 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java @@ -1,4 +1,17 @@ package org.apache.coyote.http11; +import java.nio.charset.StandardCharsets; + public record HttpResponse(byte[] responseBody, String contentType) { + + public byte[] serialize() { + String message = String.join("\r\n", + "HTTP/1.1 200 OK ", + "Content-Type: " + contentType + " ", + "Content-Length: " + responseBody.length + " ", + "", + new String(responseBody, StandardCharsets.UTF_8)); + + return message.getBytes(); + } } From e36a27b5b172f31325c2968f4ff35af1a227e8c0 Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 5 Sep 2024 21:09:21 +0900 Subject: [PATCH 24/49] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20red?= =?UTF-8?q?irection=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/apache/coyote/http11/Header.java | 5 +++ .../apache/coyote/http11/HttpResponse.java | 35 ++++++++++++++----- .../org/apache/coyote/http11/HttpStatus.java | 21 +++++++++++ .../http11/handler/AbstractHandler.java | 22 ++++++++++-- .../coyote/http11/handler/HelloHandler.java | 2 +- .../coyote/http11/handler/LoginHandler.java | 33 ++++++++--------- .../http11/handler/StaticResourceHandler.java | 4 +-- .../apache/coyote/http11/HttpStatusTest.java | 19 ++++++++++ 8 files changed, 111 insertions(+), 30 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpStatus.java create mode 100644 tomcat/src/test/java/org/apache/coyote/http11/HttpStatusTest.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Header.java b/tomcat/src/main/java/org/apache/coyote/http11/Header.java index ec9a7a1fd2..0db39e55ca 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Header.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Header.java @@ -1,5 +1,6 @@ package org.apache.coyote.http11; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -42,4 +43,8 @@ public Optional get(String key) { return Optional.ofNullable(header.get(key)); } + + public Map getHeader() { + return Collections.unmodifiableMap(header); + } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java index c15462a767..06eb119130 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java @@ -1,17 +1,36 @@ package org.apache.coyote.http11; import java.nio.charset.StandardCharsets; +import java.util.Map; -public record HttpResponse(byte[] responseBody, String contentType) { +public record HttpResponse( + HttpStatus httpStatus, + Header header, + byte[] responseBody +) { - public byte[] serialize() { - String message = String.join("\r\n", - "HTTP/1.1 200 OK ", - "Content-Type: " + contentType + " ", - "Content-Length: " + responseBody.length + " ", - "", - new String(responseBody, StandardCharsets.UTF_8)); + private static final String RESPONSE_HEADER_FORMAT = "%s: %s \r\n"; + public byte[] serialize() { + String message = String.join("\r\n", startLine(), getHeaders(), getBody()); return message.getBytes(); } + + private String startLine() { + return "HTTP/1.1 " + httpStatus.getDescription() + " "; + } + + private CharSequence getHeaders() { + StringBuilder stringBuilder = new StringBuilder(); + Map headerMap = header.getHeader(); + headerMap.forEach((key, value) -> stringBuilder.append(String.format(RESPONSE_HEADER_FORMAT, key, value))); + String format = String.format(RESPONSE_HEADER_FORMAT, "Content-Length", responseBody.length); + stringBuilder.append(format); + + return stringBuilder; + } + + private String getBody() { + return new String(responseBody, StandardCharsets.UTF_8); + } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpStatus.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpStatus.java new file mode 100644 index 0000000000..354d1e8860 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpStatus.java @@ -0,0 +1,21 @@ +package org.apache.coyote.http11; + +public enum HttpStatus { + + OK(200, "OK"), + FOUND(302, "Found") + + ; + + private final int code; + private final String name; + + HttpStatus(int code, String name) { + this.code = code; + this.name = name; + } + + public String getDescription() { + return code + " " + name; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java index cd41cbe116..483d165620 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java @@ -1,11 +1,15 @@ package org.apache.coyote.http11.handler; +import org.apache.coyote.http11.Header; import org.apache.coyote.http11.HttpRequest; import org.apache.coyote.http11.HttpResponse; +import org.apache.coyote.http11.HttpStatus; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; public abstract class AbstractHandler { @@ -20,10 +24,24 @@ public HttpResponse handle(HttpRequest httpRequest) { String result = forward(httpRequest); - String resourcePath = getClass().getClassLoader().getResource(result).getPath(); + List headerTokens = new ArrayList<>(); + boolean isRedirect = false; + if (result.startsWith("redirect:")) { + result = result.split(":")[1]; + headerTokens.add("Location:" + result); + isRedirect = true; + } + + String resourcePath = getClass().getClassLoader().getResource("static/" + result).getPath(); String contentType = determineContentType(resourcePath, acceptHeader); + headerTokens.add("Content-Type: " + contentType); + Header header = new Header(headerTokens); + + if (isRedirect) { + return new HttpResponse(HttpStatus.FOUND, header, new byte[]{}); + } - return new HttpResponse(readStaticResource(resourcePath), contentType); + return new HttpResponse(HttpStatus.OK, header, readStaticResource(resourcePath)); } private String determineContentType(String resourcePath, String acceptHeader) { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/HelloHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/HelloHandler.java index 6cb2ff2512..519399c9ae 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/HelloHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/HelloHandler.java @@ -16,6 +16,6 @@ public boolean canHandle(HttpRequest httpRequest) { @Override protected String forward(HttpRequest httpRequest) { - return "static/hello.html"; + return "hello.html"; } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/LoginHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/LoginHandler.java index a7ccfdef47..8206182fdc 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/LoginHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/LoginHandler.java @@ -1,19 +1,13 @@ package org.apache.coyote.http11.handler; import com.techcourse.db.InMemoryUserRepository; -import com.techcourse.model.User; import org.apache.coyote.http11.HttpRequest; import org.apache.coyote.http11.QueryParameter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.net.URI; -import java.util.Optional; public class LoginHandler extends AbstractHandler { - private static final Logger log = LoggerFactory.getLogger(LoginHandler.class); - @Override public boolean canHandle(HttpRequest httpRequest) { URI uri = httpRequest.getUri(); @@ -25,20 +19,27 @@ public boolean canHandle(HttpRequest httpRequest) { @Override protected String forward(HttpRequest httpRequest) { QueryParameter queryParameter = new QueryParameter(httpRequest.getUri().getQuery()); - checkUser(queryParameter); + if (queryParameter.isEmpty()) { + return "login.html"; + } - return "static/login.html"; + return authenticate(queryParameter); } - private void checkUser(QueryParameter queryParameter) { + private String authenticate(QueryParameter queryParameter) { + if (isLoggedIn(queryParameter)) { + return "redirect:index.html"; + } + + return "redirect:401.html"; + } + + private boolean isLoggedIn(QueryParameter queryParameter) { String password = queryParameter.get("password").orElse(""); - Optional user = queryParameter.get("account").flatMap(InMemoryUserRepository::findByAccount); - if (user.isPresent()) { - boolean isSame = user.get().checkPassword(password); - if (isSame) { - log.info("{}", user.get()); - } - } + return queryParameter.get("account") + .flatMap(InMemoryUserRepository::findByAccount) + .map(it -> it.checkPassword(password)) + .orElse(false); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/StaticResourceHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/StaticResourceHandler.java index 58dd7a7bc6..4490659b93 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/StaticResourceHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/StaticResourceHandler.java @@ -17,8 +17,6 @@ public boolean canHandle(HttpRequest httpRequest) { @Override public String forward(HttpRequest httpRequest) { URI uri = httpRequest.getUri(); - String path = uri.getPath(); - - return "static/" + path; + return uri.getPath(); } } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/HttpStatusTest.java b/tomcat/src/test/java/org/apache/coyote/http11/HttpStatusTest.java new file mode 100644 index 0000000000..460de30cb6 --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/HttpStatusTest.java @@ -0,0 +1,19 @@ +package org.apache.coyote.http11; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class HttpStatusTest { + + @Test + @DisplayName("응답 메시지에 들어갈 문자열을 생성한다.") + void message() { + Assertions.assertAll( + () -> assertThat(HttpStatus.OK.getDescription()).isEqualTo("200 OK"), + () -> assertThat(HttpStatus.FOUND.getDescription()).isEqualTo("302 Found") + ); + } +} From a43ebaefa08ca8763d9c5760471c5c59402d7a81 Mon Sep 17 00:00:00 2001 From: le2sky Date: Thu, 5 Sep 2024 21:10:48 +0900 Subject: [PATCH 25/49] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/org/apache/coyote/http11/HttpResponse.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java index 06eb119130..b788464301 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java @@ -12,11 +12,11 @@ public record HttpResponse( private static final String RESPONSE_HEADER_FORMAT = "%s: %s \r\n"; public byte[] serialize() { - String message = String.join("\r\n", startLine(), getHeaders(), getBody()); + String message = String.join("\r\n", getStartLine(), getHeaders(), getBody()); return message.getBytes(); } - private String startLine() { + private String getStartLine() { return "HTTP/1.1 " + httpStatus.getDescription() + " "; } From 6cc0cc5a68b4142c980286a08c57d56841773c78 Mon Sep 17 00:00:00 2001 From: le2sky Date: Fri, 6 Sep 2024 01:05:16 +0900 Subject: [PATCH 26/49] =?UTF-8?q?refactor:=20/login=20get=20post=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- study/src/test/java/study/IOStreamTest.java | 14 ++++++- .../apache/coyote/http11/Http11Processor.java | 35 ++++++++++++----- .../org/apache/coyote/http11/HttpMethod.java | 27 +++++++++++++ .../org/apache/coyote/http11/HttpRequest.java | 6 ++- .../apache/coyote/http11/HttpResponse.java | 3 +- .../http11/handler/GetLoginHandler.java | 21 ++++++++++ ...oginHandler.java => PostLoginHandler.java} | 14 ++----- tomcat/src/main/resources/static/login.html | 2 +- .../http11/handler/GetLoginHandlerTest.java | 38 +++++++++++++++++++ .../http11/handler/HelloHandlerTest.java | 3 +- .../http11/handler/LoginHandlerTest.java | 37 ------------------ .../http11/handler/PostLoginHandlerTest.java | 38 +++++++++++++++++++ .../handler/StaticResourceHandlerTest.java | 3 +- 13 files changed, 176 insertions(+), 65 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/handler/GetLoginHandler.java rename tomcat/src/main/java/org/apache/coyote/http11/handler/{LoginHandler.java => PostLoginHandler.java} (70%) create mode 100644 tomcat/src/test/java/org/apache/coyote/http11/handler/GetLoginHandlerTest.java delete mode 100644 tomcat/src/test/java/org/apache/coyote/http11/handler/LoginHandlerTest.java create mode 100644 tomcat/src/test/java/org/apache/coyote/http11/handler/PostLoginHandlerTest.java diff --git a/study/src/test/java/study/IOStreamTest.java b/study/src/test/java/study/IOStreamTest.java index 47a79356b6..ff5a81387d 100644 --- a/study/src/test/java/study/IOStreamTest.java +++ b/study/src/test/java/study/IOStreamTest.java @@ -7,6 +7,7 @@ import java.io.*; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.in; import static org.mockito.Mockito.*; /** @@ -205,9 +206,18 @@ class InputStreamReader_학습_테스트 { ""); final InputStream inputStream = new ByteArrayInputStream(emoji.getBytes()); - final StringBuilder actual = new StringBuilder(); + InputStreamReader inputStreamReader = new InputStreamReader(inputStream); - assertThat(actual).hasToString(emoji); + try (BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { + String s = bufferedReader.readLine(); + + System.out.println(s); + + final StringBuilder actual = new StringBuilder(); + assertThat(actual).hasToString(emoji); + } catch (IOException e) { + throw new RuntimeException(e); + } } } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index bd7a4033cb..b9ad8f243d 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -3,8 +3,9 @@ import com.techcourse.exception.UncheckedServletException; import org.apache.coyote.Processor; import org.apache.coyote.http11.handler.AbstractHandler; +import org.apache.coyote.http11.handler.GetLoginHandler; import org.apache.coyote.http11.handler.HelloHandler; -import org.apache.coyote.http11.handler.LoginHandler; +import org.apache.coyote.http11.handler.PostLoginHandler; import org.apache.coyote.http11.handler.StaticResourceHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,23 +51,39 @@ public void process(Socket connection) { private HttpRequest createHttpRequest(InputStream inputStream) throws IOException { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String requestLine = bufferedReader.readLine(); - List headerTokens = new ArrayList<>(); + Header header = createHeader(bufferedReader); + QueryParameter queryParameter = createQueryParameter(bufferedReader, header); + + return new HttpRequest(requestLine, header, queryParameter); + } - String nowLine = bufferedReader.readLine(); - while (!nowLine.isBlank()) { - headerTokens.add(nowLine); - nowLine = bufferedReader.readLine(); + private Header createHeader(BufferedReader bufferedReader) throws IOException { + List headerTokens = new ArrayList<>(); + String line = bufferedReader.readLine(); + while (!line.isBlank()) { + line = bufferedReader.readLine(); + headerTokens.add(line); } - return new HttpRequest(requestLine, new Header(headerTokens)); + return new Header(headerTokens); + } + + private QueryParameter createQueryParameter(BufferedReader bufferedReader, Header header) throws IOException { + int length = Integer.parseInt(header.get("Content-Length").orElse("0")); + char[] body = new char[length]; + bufferedReader.read(body); + String bodyString = new String(body); + + return new QueryParameter(bodyString); } private HttpResponse respondResource(HttpRequest httpRequest) throws IOException { AbstractHandler helloHandler = new HelloHandler(); AbstractHandler staticResourceHandler = new StaticResourceHandler(); - AbstractHandler loginHandler = new LoginHandler(); + AbstractHandler postLoginHandler = new PostLoginHandler(); + AbstractHandler getLoginHandler = new GetLoginHandler(); - List handlers = List.of(helloHandler, staticResourceHandler, loginHandler); + List handlers = List.of(helloHandler, staticResourceHandler, postLoginHandler, getLoginHandler); AbstractHandler targetHandler = handlers.stream() .filter(it -> it.canHandle(httpRequest)) .findFirst() diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java new file mode 100644 index 0000000000..b179b1d835 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java @@ -0,0 +1,27 @@ +package org.apache.coyote.http11; + +import java.util.Arrays; +import java.util.NoSuchElementException; + +public enum HttpMethod { + + GET, + POST, + + ; + + public static HttpMethod from(String value) { + return Arrays.stream(values()) + .filter(it -> it.name().equalsIgnoreCase(value)) + .findFirst() + .orElseThrow(NoSuchElementException::new); + } + + public boolean isGet() { + return this.equals(GET); + } + + public boolean isPost() { + return this.equals(POST); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java index 74a83731f6..b28eb0d1aa 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java @@ -2,7 +2,11 @@ import java.net.URI; -public record HttpRequest(String startLine, Header header) { +public record HttpRequest(String startLine, Header header, QueryParameter body) { + + public HttpMethod getMethod() { + return HttpMethod.from(startLine.split(" ")[0]); + } public URI getUri() { return URI.create(startLine.split(" ")[1]); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java index b788464301..0b0a16743a 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java @@ -1,6 +1,5 @@ package org.apache.coyote.http11; -import java.nio.charset.StandardCharsets; import java.util.Map; public record HttpResponse( @@ -31,6 +30,6 @@ private CharSequence getHeaders() { } private String getBody() { - return new String(responseBody, StandardCharsets.UTF_8); + return new String(responseBody); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/GetLoginHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/GetLoginHandler.java new file mode 100644 index 0000000000..70bf0cc5fc --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/GetLoginHandler.java @@ -0,0 +1,21 @@ +package org.apache.coyote.http11.handler; + +import org.apache.coyote.http11.HttpRequest; + +import java.net.URI; + +public class GetLoginHandler extends AbstractHandler { + + @Override + public boolean canHandle(HttpRequest httpRequest) { + URI uri = httpRequest.getUri(); + String path = uri.getPath(); + + return "/login".equals(path) && httpRequest.getMethod().isGet(); + } + + @Override + protected String forward(HttpRequest httpRequest) { + return "login.html"; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/LoginHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/PostLoginHandler.java similarity index 70% rename from tomcat/src/main/java/org/apache/coyote/http11/handler/LoginHandler.java rename to tomcat/src/main/java/org/apache/coyote/http11/handler/PostLoginHandler.java index 8206182fdc..9dca6be231 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/LoginHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/PostLoginHandler.java @@ -6,27 +6,19 @@ import java.net.URI; -public class LoginHandler extends AbstractHandler { +public class PostLoginHandler extends AbstractHandler { @Override public boolean canHandle(HttpRequest httpRequest) { URI uri = httpRequest.getUri(); String path = uri.getPath(); - return "/login".equals(path); + return "/login".equals(path) && httpRequest.getMethod().isPost(); } @Override protected String forward(HttpRequest httpRequest) { - QueryParameter queryParameter = new QueryParameter(httpRequest.getUri().getQuery()); - if (queryParameter.isEmpty()) { - return "login.html"; - } - - return authenticate(queryParameter); - } - - private String authenticate(QueryParameter queryParameter) { + QueryParameter queryParameter = httpRequest.body(); if (isLoggedIn(queryParameter)) { return "redirect:index.html"; } diff --git a/tomcat/src/main/resources/static/login.html b/tomcat/src/main/resources/static/login.html index f4ed9de875..bc933357f2 100644 --- a/tomcat/src/main/resources/static/login.html +++ b/tomcat/src/main/resources/static/login.html @@ -20,7 +20,7 @@

로그인

-
+
diff --git a/tomcat/src/test/java/org/apache/coyote/http11/handler/GetLoginHandlerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/handler/GetLoginHandlerTest.java new file mode 100644 index 0000000000..9f074bbaf4 --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/handler/GetLoginHandlerTest.java @@ -0,0 +1,38 @@ +package org.apache.coyote.http11.handler; + +import org.apache.coyote.http11.Header; +import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.QueryParameter; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; + +class GetLoginHandlerTest { + + @Test + @DisplayName("로그인 관련 GET 요청을 처리할 수 있다.") + void canHandle() { + GetLoginHandler postLoginHandler = new GetLoginHandler(); + + boolean result = postLoginHandler.canHandle(createHttpRequest("GET /login HTTP/1.1")); + + assertThat(result).isTrue(); + } + + @Test + @DisplayName("로그인 관련 GET 요청이 아니라면 처리할 수 없다.") + void cantHandle() { + GetLoginHandler postLoginHandler = new GetLoginHandler(); + + boolean result = postLoginHandler.canHandle(createHttpRequest("POST /login HTTP/1.1")); + + assertThat(result).isFalse(); + } + + private HttpRequest createHttpRequest(String startLine) { + return new HttpRequest(startLine, new Header(Collections.emptyList()), new QueryParameter("")); + } +} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/handler/HelloHandlerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/handler/HelloHandlerTest.java index 89f0249269..b72e9ae693 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/handler/HelloHandlerTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/handler/HelloHandlerTest.java @@ -2,6 +2,7 @@ import org.apache.coyote.http11.Header; import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.QueryParameter; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -32,6 +33,6 @@ void cantHandle() { } private HttpRequest createHttpRequest(String startLine) { - return new HttpRequest(startLine, new Header(Collections.emptyList())); + return new HttpRequest(startLine, new Header(Collections.emptyList()), new QueryParameter("")); } } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/handler/LoginHandlerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/handler/LoginHandlerTest.java deleted file mode 100644 index 4b57450393..0000000000 --- a/tomcat/src/test/java/org/apache/coyote/http11/handler/LoginHandlerTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.apache.coyote.http11.handler; - -import org.apache.coyote.http11.Header; -import org.apache.coyote.http11.HttpRequest; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Collections; - -import static org.assertj.core.api.Assertions.assertThat; - -class LoginHandlerTest { - - @Test - @DisplayName("로그인 관련 요청을 처리할 수 있다.") - void canHandle() { - LoginHandler loginHandler = new LoginHandler(); - - boolean result = loginHandler.canHandle(createHttpRequest("GET /login HTTP/1.1")); - - assertThat(result).isTrue(); - } - - @Test - @DisplayName("로그인 관련 요청이 아니라면 처리할 수 없다.") - void cantHandle() { - LoginHandler loginHandler = new LoginHandler(); - - boolean result = loginHandler.canHandle(createHttpRequest("GET /login1?a=1 HTTP/1.1")); - - assertThat(result).isFalse(); - } - - private HttpRequest createHttpRequest(String startLine) { - return new HttpRequest(startLine, new Header(Collections.emptyList())); - } -} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/handler/PostLoginHandlerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/handler/PostLoginHandlerTest.java new file mode 100644 index 0000000000..2e10a583e7 --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/handler/PostLoginHandlerTest.java @@ -0,0 +1,38 @@ +package org.apache.coyote.http11.handler; + +import org.apache.coyote.http11.Header; +import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.QueryParameter; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; + +class PostLoginHandlerTest { + + @Test + @DisplayName("로그인 관련 POST 요청을 처리할 수 있다.") + void canHandle() { + PostLoginHandler postLoginHandler = new PostLoginHandler(); + + boolean result = postLoginHandler.canHandle(createHttpRequest("POST /login HTTP/1.1")); + + assertThat(result).isTrue(); + } + + @Test + @DisplayName("로그인 관련 POST 요청이 아니라면 처리할 수 없다.") + void cantHandle() { + PostLoginHandler postLoginHandler = new PostLoginHandler(); + + boolean result = postLoginHandler.canHandle(createHttpRequest("GET /login HTTP/1.1")); + + assertThat(result).isFalse(); + } + + private HttpRequest createHttpRequest(String startLine) { + return new HttpRequest(startLine, new Header(Collections.emptyList()), new QueryParameter("")); + } +} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/handler/StaticResourceHandlerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/handler/StaticResourceHandlerTest.java index ca020e415e..fcd13a12da 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/handler/StaticResourceHandlerTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/handler/StaticResourceHandlerTest.java @@ -2,6 +2,7 @@ import org.apache.coyote.http11.Header; import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.QueryParameter; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -32,6 +33,6 @@ void cantHandle() { } private HttpRequest createHttpRequest(String startLine) { - return new HttpRequest(startLine, new Header(Collections.emptyList())); + return new HttpRequest(startLine, new Header(Collections.emptyList()), new QueryParameter("")); } } From 45c7fd73a64274a831e986538cb0b22f8e659e7a Mon Sep 17 00:00:00 2001 From: le2sky Date: Fri, 6 Sep 2024 01:12:29 +0900 Subject: [PATCH 27/49] =?UTF-8?q?feat:=20register=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 12 +++++- .../http11/handler/GetRegisterHandler.java | 21 ++++++++++ .../http11/handler/GetLoginHandlerTest.java | 8 ++-- .../handler/GetRegisterHandlerTest.java | 38 +++++++++++++++++++ 4 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/handler/GetRegisterHandler.java create mode 100644 tomcat/src/test/java/org/apache/coyote/http11/handler/GetRegisterHandlerTest.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index b9ad8f243d..97cd2bc1ae 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -4,6 +4,7 @@ import org.apache.coyote.Processor; import org.apache.coyote.http11.handler.AbstractHandler; import org.apache.coyote.http11.handler.GetLoginHandler; +import org.apache.coyote.http11.handler.GetRegisterHandler; import org.apache.coyote.http11.handler.HelloHandler; import org.apache.coyote.http11.handler.PostLoginHandler; import org.apache.coyote.http11.handler.StaticResourceHandler; @@ -82,8 +83,15 @@ private HttpResponse respondResource(HttpRequest httpRequest) throws IOException AbstractHandler staticResourceHandler = new StaticResourceHandler(); AbstractHandler postLoginHandler = new PostLoginHandler(); AbstractHandler getLoginHandler = new GetLoginHandler(); - - List handlers = List.of(helloHandler, staticResourceHandler, postLoginHandler, getLoginHandler); + AbstractHandler getRegisterHandler = new GetRegisterHandler(); + + List handlers = List.of( + helloHandler, + staticResourceHandler, + postLoginHandler, + getLoginHandler, + getRegisterHandler + ); AbstractHandler targetHandler = handlers.stream() .filter(it -> it.canHandle(httpRequest)) .findFirst() diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/GetRegisterHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/GetRegisterHandler.java new file mode 100644 index 0000000000..7f48a859b2 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/GetRegisterHandler.java @@ -0,0 +1,21 @@ +package org.apache.coyote.http11.handler; + +import org.apache.coyote.http11.HttpRequest; + +import java.net.URI; + +public class GetRegisterHandler extends AbstractHandler { + + @Override + public boolean canHandle(HttpRequest httpRequest) { + URI uri = httpRequest.getUri(); + String path = uri.getPath(); + + return "/register".equals(path) && httpRequest.getMethod().isGet(); + } + + @Override + protected String forward(HttpRequest httpRequest) { + return "register.html"; + } +} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/handler/GetLoginHandlerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/handler/GetLoginHandlerTest.java index 9f074bbaf4..ed4986b364 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/handler/GetLoginHandlerTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/handler/GetLoginHandlerTest.java @@ -15,9 +15,9 @@ class GetLoginHandlerTest { @Test @DisplayName("로그인 관련 GET 요청을 처리할 수 있다.") void canHandle() { - GetLoginHandler postLoginHandler = new GetLoginHandler(); + GetLoginHandler getLoginHandler = new GetLoginHandler(); - boolean result = postLoginHandler.canHandle(createHttpRequest("GET /login HTTP/1.1")); + boolean result = getLoginHandler.canHandle(createHttpRequest("GET /login HTTP/1.1")); assertThat(result).isTrue(); } @@ -25,9 +25,9 @@ void canHandle() { @Test @DisplayName("로그인 관련 GET 요청이 아니라면 처리할 수 없다.") void cantHandle() { - GetLoginHandler postLoginHandler = new GetLoginHandler(); + GetLoginHandler getLoginHandler = new GetLoginHandler(); - boolean result = postLoginHandler.canHandle(createHttpRequest("POST /login HTTP/1.1")); + boolean result = getLoginHandler.canHandle(createHttpRequest("POST /login HTTP/1.1")); assertThat(result).isFalse(); } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/handler/GetRegisterHandlerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/handler/GetRegisterHandlerTest.java new file mode 100644 index 0000000000..472a854528 --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/handler/GetRegisterHandlerTest.java @@ -0,0 +1,38 @@ +package org.apache.coyote.http11.handler; + +import org.apache.coyote.http11.Header; +import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.QueryParameter; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; + +class GetRegisterHandlerTest { + + @Test + @DisplayName("회원가입 관련 GET 요청을 처리할 수 있다.") + void canHandle() { + GetRegisterHandler getRegisterHandler = new GetRegisterHandler(); + + boolean result = getRegisterHandler.canHandle(createHttpRequest("GET /register HTTP/1.1")); + + assertThat(result).isTrue(); + } + + @Test + @DisplayName("회원가입 관련 GET 요청이 아니라면 처리할 수 없다.") + void cantHandle() { + GetRegisterHandler getRegisterHandler = new GetRegisterHandler(); + + boolean result = getRegisterHandler.canHandle(createHttpRequest("POST /register HTTP/1.1")); + + assertThat(result).isFalse(); + } + + private HttpRequest createHttpRequest(String startLine) { + return new HttpRequest(startLine, new Header(Collections.emptyList()), new QueryParameter("")); + } +} From 9a84cdef7e94f11fcca595ab8f2d9e6b1e53f57d Mon Sep 17 00:00:00 2001 From: le2sky Date: Fri, 6 Sep 2024 01:26:55 +0900 Subject: [PATCH 28/49] =?UTF-8?q?feat:=20not=20found=20=ED=95=B8=EB=93=A4?= =?UTF-8?q?=EB=9F=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 4 +++- .../coyote/http11/handler/AbstractHandler.java | 4 ++++ .../coyote/http11/handler/NotFoundHandler.java | 16 ++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/handler/NotFoundHandler.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 97cd2bc1ae..23b20fd8b9 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -6,6 +6,7 @@ import org.apache.coyote.http11.handler.GetLoginHandler; import org.apache.coyote.http11.handler.GetRegisterHandler; import org.apache.coyote.http11.handler.HelloHandler; +import org.apache.coyote.http11.handler.NotFoundHandler; import org.apache.coyote.http11.handler.PostLoginHandler; import org.apache.coyote.http11.handler.StaticResourceHandler; import org.slf4j.Logger; @@ -84,6 +85,7 @@ private HttpResponse respondResource(HttpRequest httpRequest) throws IOException AbstractHandler postLoginHandler = new PostLoginHandler(); AbstractHandler getLoginHandler = new GetLoginHandler(); AbstractHandler getRegisterHandler = new GetRegisterHandler(); + AbstractHandler notFoundHandler = new NotFoundHandler(); List handlers = List.of( helloHandler, @@ -95,7 +97,7 @@ private HttpResponse respondResource(HttpRequest httpRequest) throws IOException AbstractHandler targetHandler = handlers.stream() .filter(it -> it.canHandle(httpRequest)) .findFirst() - .orElseThrow(IllegalArgumentException::new); + .orElse(notFoundHandler); return targetHandler.handle(httpRequest); } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java index 483d165620..6d06678092 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java @@ -72,6 +72,10 @@ private String determineContentType(String resourcePath, String acceptHeader) { return String.format(encodedContentType, "text/javascript"); } + if(resourcePath.endsWith(".svg")) { + return "image/svg+xml"; + } + throw new IllegalStateException(); } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/NotFoundHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/NotFoundHandler.java new file mode 100644 index 0000000000..65d6eee343 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/NotFoundHandler.java @@ -0,0 +1,16 @@ +package org.apache.coyote.http11.handler; + +import org.apache.coyote.http11.HttpRequest; + +public class NotFoundHandler extends AbstractHandler { + + @Override + public boolean canHandle(HttpRequest httpRequest) { + return true; + } + + @Override + protected String forward(HttpRequest httpRequest) { + return "404.html"; + } +} From 2614f3813f976e5bcf53d4e2a1c4cdbe11cd0b0d Mon Sep 17 00:00:00 2001 From: le2sky Date: Fri, 6 Sep 2024 01:50:17 +0900 Subject: [PATCH 29/49] =?UTF-8?q?refactor:=20content-type=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/apache/coyote/http11/ContentType.java | 28 ++++++++++++ .../http11/handler/AbstractHandler.java | 43 ++++--------------- 2 files changed, 36 insertions(+), 35 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/ContentType.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java b/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java new file mode 100644 index 0000000000..84b7a64ffc --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java @@ -0,0 +1,28 @@ +package org.apache.coyote.http11; + +public enum ContentType { + + HTML("text/html", ".html"), + JS("text/javascript", ".js"), + CSS("text/css", ".css"), + SVG("image/svg+xml", ".svg"), + PLAIN("text/plain", ""), + + ; + + private final String name; + private final String extension; + + ContentType(String name, String extension) { + this.name = name; + this.extension = extension; + } + + public String getName() { + return name; + } + + public String getExtension() { + return extension; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java index 6d06678092..2540708a6d 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java @@ -1,5 +1,6 @@ package org.apache.coyote.http11.handler; +import org.apache.coyote.http11.ContentType; import org.apache.coyote.http11.Header; import org.apache.coyote.http11.HttpRequest; import org.apache.coyote.http11.HttpResponse; @@ -18,10 +19,6 @@ public abstract class AbstractHandler { protected abstract String forward(HttpRequest httpRequest); public HttpResponse handle(HttpRequest httpRequest) { - String acceptHeader = httpRequest.header() - .get("Accept") - .orElse("*/*"); - String result = forward(httpRequest); List headerTokens = new ArrayList<>(); @@ -33,7 +30,7 @@ public HttpResponse handle(HttpRequest httpRequest) { } String resourcePath = getClass().getClassLoader().getResource("static/" + result).getPath(); - String contentType = determineContentType(resourcePath, acceptHeader); + String contentType = determineContentType(resourcePath); headerTokens.add("Content-Type: " + contentType); Header header = new Header(headerTokens); @@ -44,39 +41,15 @@ public HttpResponse handle(HttpRequest httpRequest) { return new HttpResponse(HttpStatus.OK, header, readStaticResource(resourcePath)); } - private String determineContentType(String resourcePath, String acceptHeader) { - String encodedContentType = "%s;charset=utf-8"; - if (acceptHeader.startsWith("*/*")) { - if (resourcePath.endsWith(".html")) { - return String.format(encodedContentType, "text/html"); - } - - if (resourcePath.endsWith(".css")) { - return String.format(encodedContentType, "text/css"); + private String determineContentType(String resourcePath) { + String encodedContentTypeFormat = "%s;charset=utf-8"; + for (ContentType contentType : ContentType.values()) { + if (resourcePath.endsWith(contentType.getExtension())) { + return String.format(encodedContentTypeFormat, contentType.getName()); } - - if (resourcePath.endsWith(".js")) { - return String.format(encodedContentType, "text/javascript"); - } - } - - if (acceptHeader.startsWith("text/html") && resourcePath.endsWith(".html")) { - return String.format(encodedContentType, "text/html"); - } - - if (acceptHeader.startsWith("text/css") && resourcePath.endsWith(".css")) { - return String.format(encodedContentType, "text/css"); - } - - if (acceptHeader.startsWith("text/javascript") && resourcePath.endsWith(".js")) { - return String.format(encodedContentType, "text/javascript"); - } - - if(resourcePath.endsWith(".svg")) { - return "image/svg+xml"; } - throw new IllegalStateException(); + return String.format(encodedContentTypeFormat, ContentType.PLAIN.getName()); } private byte[] readStaticResource(String resourcePath) { From 0a33edae95a667a1c591f1e7a3f7b2dc0201306b Mon Sep 17 00:00:00 2001 From: le2sky Date: Fri, 6 Sep 2024 02:16:09 +0900 Subject: [PATCH 30/49] =?UTF-8?q?refactor:=20forward=20=EA=B2=B0=EA=B3=BC?= =?UTF-8?q?=20=ED=8F=AC=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../http11/handler/AbstractHandler.java | 25 ++++++------------- .../coyote/http11/handler/ForwardResult.java | 4 +++ .../http11/handler/GetLoginHandler.java | 4 +-- .../http11/handler/GetRegisterHandler.java | 4 +-- .../coyote/http11/handler/HelloHandler.java | 4 +-- .../http11/handler/NotFoundHandler.java | 4 +-- .../http11/handler/PostLoginHandler.java | 6 ++--- .../http11/handler/StaticResourceHandler.java | 5 ++-- 8 files changed, 26 insertions(+), 30 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/handler/ForwardResult.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java index 2540708a6d..3b5506c595 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java @@ -16,29 +16,20 @@ public abstract class AbstractHandler { public abstract boolean canHandle(HttpRequest httpRequest); - protected abstract String forward(HttpRequest httpRequest); + protected abstract ForwardResult forward(HttpRequest httpRequest); public HttpResponse handle(HttpRequest httpRequest) { - String result = forward(httpRequest); - + ForwardResult forwardResult = forward(httpRequest); + String resourcePath = getClass().getClassLoader().getResource("static/" + forwardResult.path()).getPath(); List headerTokens = new ArrayList<>(); - boolean isRedirect = false; - if (result.startsWith("redirect:")) { - result = result.split(":")[1]; - headerTokens.add("Location:" + result); - isRedirect = true; - } - - String resourcePath = getClass().getClassLoader().getResource("static/" + result).getPath(); - String contentType = determineContentType(resourcePath); - headerTokens.add("Content-Type: " + contentType); - Header header = new Header(headerTokens); + headerTokens.add("Content-Type: " + determineContentType(resourcePath)); - if (isRedirect) { - return new HttpResponse(HttpStatus.FOUND, header, new byte[]{}); + if (forwardResult.isRedirect()) { + headerTokens.add("Location:" + forwardResult.path()); + return new HttpResponse(HttpStatus.FOUND, new Header(headerTokens), new byte[]{}); } - return new HttpResponse(HttpStatus.OK, header, readStaticResource(resourcePath)); + return new HttpResponse(HttpStatus.OK, new Header(headerTokens), readStaticResource(resourcePath)); } private String determineContentType(String resourcePath) { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/ForwardResult.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/ForwardResult.java new file mode 100644 index 0000000000..78b4e596c5 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/ForwardResult.java @@ -0,0 +1,4 @@ +package org.apache.coyote.http11.handler; + +record ForwardResult(boolean isRedirect, String path) { +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/GetLoginHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/GetLoginHandler.java index 70bf0cc5fc..47acee5d93 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/GetLoginHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/GetLoginHandler.java @@ -15,7 +15,7 @@ public boolean canHandle(HttpRequest httpRequest) { } @Override - protected String forward(HttpRequest httpRequest) { - return "login.html"; + protected ForwardResult forward(HttpRequest httpRequest) { + return new ForwardResult(false, "login.html"); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/GetRegisterHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/GetRegisterHandler.java index 7f48a859b2..f89068612f 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/GetRegisterHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/GetRegisterHandler.java @@ -15,7 +15,7 @@ public boolean canHandle(HttpRequest httpRequest) { } @Override - protected String forward(HttpRequest httpRequest) { - return "register.html"; + protected ForwardResult forward(HttpRequest httpRequest) { + return new ForwardResult(false, "register.html"); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/HelloHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/HelloHandler.java index 519399c9ae..0fd401b2ea 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/HelloHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/HelloHandler.java @@ -15,7 +15,7 @@ public boolean canHandle(HttpRequest httpRequest) { } @Override - protected String forward(HttpRequest httpRequest) { - return "hello.html"; + protected ForwardResult forward(HttpRequest httpRequest) { + return new ForwardResult(false, "hello.html"); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/NotFoundHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/NotFoundHandler.java index 65d6eee343..4b7080b4fe 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/NotFoundHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/NotFoundHandler.java @@ -10,7 +10,7 @@ public boolean canHandle(HttpRequest httpRequest) { } @Override - protected String forward(HttpRequest httpRequest) { - return "404.html"; + protected ForwardResult forward(HttpRequest httpRequest) { + return new ForwardResult(false, "404.html"); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/PostLoginHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/PostLoginHandler.java index 9dca6be231..cb4e015a3e 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/PostLoginHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/PostLoginHandler.java @@ -17,13 +17,13 @@ public boolean canHandle(HttpRequest httpRequest) { } @Override - protected String forward(HttpRequest httpRequest) { + protected ForwardResult forward(HttpRequest httpRequest) { QueryParameter queryParameter = httpRequest.body(); if (isLoggedIn(queryParameter)) { - return "redirect:index.html"; + return new ForwardResult(true, "index.html"); } - return "redirect:401.html"; + return new ForwardResult(true, "401.html"); } private boolean isLoggedIn(QueryParameter queryParameter) { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/StaticResourceHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/StaticResourceHandler.java index 4490659b93..09b5131e7e 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/StaticResourceHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/StaticResourceHandler.java @@ -15,8 +15,9 @@ public boolean canHandle(HttpRequest httpRequest) { } @Override - public String forward(HttpRequest httpRequest) { + public ForwardResult forward(HttpRequest httpRequest) { URI uri = httpRequest.getUri(); - return uri.getPath(); + + return new ForwardResult(false, uri.getPath()); } } From f3f408c13dba774acde9f1fca7616468bb671dc2 Mon Sep 17 00:00:00 2001 From: le2sky Date: Fri, 6 Sep 2024 09:20:26 +0900 Subject: [PATCH 31/49] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 3 ++ .../http11/handler/PostRegisterHandler.java | 31 +++++++++++++++ .../handler/PostRegisterHandlerTest.java | 38 +++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/handler/PostRegisterHandler.java create mode 100644 tomcat/src/test/java/org/apache/coyote/http11/handler/PostRegisterHandlerTest.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 23b20fd8b9..37ba0d5bbf 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -8,6 +8,7 @@ import org.apache.coyote.http11.handler.HelloHandler; import org.apache.coyote.http11.handler.NotFoundHandler; import org.apache.coyote.http11.handler.PostLoginHandler; +import org.apache.coyote.http11.handler.PostRegisterHandler; import org.apache.coyote.http11.handler.StaticResourceHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -83,6 +84,7 @@ private HttpResponse respondResource(HttpRequest httpRequest) throws IOException AbstractHandler helloHandler = new HelloHandler(); AbstractHandler staticResourceHandler = new StaticResourceHandler(); AbstractHandler postLoginHandler = new PostLoginHandler(); + AbstractHandler postRegisterHandler = new PostRegisterHandler(); AbstractHandler getLoginHandler = new GetLoginHandler(); AbstractHandler getRegisterHandler = new GetRegisterHandler(); AbstractHandler notFoundHandler = new NotFoundHandler(); @@ -91,6 +93,7 @@ private HttpResponse respondResource(HttpRequest httpRequest) throws IOException helloHandler, staticResourceHandler, postLoginHandler, + postRegisterHandler, getLoginHandler, getRegisterHandler ); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/PostRegisterHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/PostRegisterHandler.java new file mode 100644 index 0000000000..092d281f35 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/PostRegisterHandler.java @@ -0,0 +1,31 @@ +package org.apache.coyote.http11.handler; + +import com.techcourse.db.InMemoryUserRepository; +import com.techcourse.model.User; +import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.QueryParameter; + +import java.net.URI; + +public class PostRegisterHandler extends AbstractHandler { + + @Override + public boolean canHandle(HttpRequest httpRequest) { + URI uri = httpRequest.getUri(); + String path = uri.getPath(); + + return "/register".equals(path) && httpRequest.getMethod().isPost(); + } + + @Override + protected ForwardResult forward(HttpRequest httpRequest) { + QueryParameter body = httpRequest.body(); + String account = body.get("account").orElseThrow(); + String password = body.get("password").orElseThrow(); + String email = body.get("email").orElseThrow(); + + InMemoryUserRepository.save(new User(account, password, email)); + + return new ForwardResult(false, "index.html"); + } +} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/handler/PostRegisterHandlerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/handler/PostRegisterHandlerTest.java new file mode 100644 index 0000000000..e12a0f452b --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/handler/PostRegisterHandlerTest.java @@ -0,0 +1,38 @@ +package org.apache.coyote.http11.handler; + +import org.apache.coyote.http11.Header; +import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.QueryParameter; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; + +class PostRegisterHandlerTest { + + @Test + @DisplayName("회원가입 관련 POST 요청을 처리할 수 있다.") + void canHandle() { + PostRegisterHandler postRegisterHandler = new PostRegisterHandler(); + + boolean result = postRegisterHandler.canHandle(createHttpRequest("POST /register HTTP/1.1")); + + assertThat(result).isTrue(); + } + + @Test + @DisplayName("회원가입 관련 POST 요청이 아니라면 처리할 수 없다.") + void cantHandle() { + PostRegisterHandler postRegisterHandler = new PostRegisterHandler(); + + boolean result = postRegisterHandler.canHandle(createHttpRequest("GET /register HTTP/1.1")); + + assertThat(result).isFalse(); + } + + private HttpRequest createHttpRequest(String startLine) { + return new HttpRequest(startLine, new Header(Collections.emptyList()), new QueryParameter("")); + } +} From 236a94b255a1897da93eec4c6d4dc62a59260c1f Mon Sep 17 00:00:00 2001 From: le2sky Date: Fri, 6 Sep 2024 10:29:20 +0900 Subject: [PATCH 32/49] =?UTF-8?q?refactor:=20http=20header=20=EC=83=81?= =?UTF-8?q?=EC=88=98=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 2 +- .../apache/coyote/http11/HttpHeaderKey.java | 20 +++++++++++++++++++ .../apache/coyote/http11/HttpResponse.java | 2 +- .../http11/handler/AbstractHandler.java | 5 +++-- 4 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpHeaderKey.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 37ba0d5bbf..9bb7731003 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -72,7 +72,7 @@ private Header createHeader(BufferedReader bufferedReader) throws IOException { } private QueryParameter createQueryParameter(BufferedReader bufferedReader, Header header) throws IOException { - int length = Integer.parseInt(header.get("Content-Length").orElse("0")); + int length = Integer.parseInt(header.get(HttpHeaderKey.CONTENT_LENGTH.getName()).orElse("0")); char[] body = new char[length]; bufferedReader.read(body); String bodyString = new String(body); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpHeaderKey.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpHeaderKey.java new file mode 100644 index 0000000000..ca8b62be98 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpHeaderKey.java @@ -0,0 +1,20 @@ +package org.apache.coyote.http11; + +public enum HttpHeaderKey { + + CONTENT_LENGTH("Content-Length"), + CONTENT_TYPE("Content-Type"), + LOCATION("Location"), + + ; + + private final String name; + + HttpHeaderKey(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java index 0b0a16743a..06ff065f15 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java @@ -23,7 +23,7 @@ private CharSequence getHeaders() { StringBuilder stringBuilder = new StringBuilder(); Map headerMap = header.getHeader(); headerMap.forEach((key, value) -> stringBuilder.append(String.format(RESPONSE_HEADER_FORMAT, key, value))); - String format = String.format(RESPONSE_HEADER_FORMAT, "Content-Length", responseBody.length); + String format = String.format(RESPONSE_HEADER_FORMAT, HttpHeaderKey.CONTENT_LENGTH.getName(), responseBody.length); stringBuilder.append(format); return stringBuilder; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java index 3b5506c595..123ae82139 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java @@ -2,6 +2,7 @@ import org.apache.coyote.http11.ContentType; import org.apache.coyote.http11.Header; +import org.apache.coyote.http11.HttpHeaderKey; import org.apache.coyote.http11.HttpRequest; import org.apache.coyote.http11.HttpResponse; import org.apache.coyote.http11.HttpStatus; @@ -22,10 +23,10 @@ public HttpResponse handle(HttpRequest httpRequest) { ForwardResult forwardResult = forward(httpRequest); String resourcePath = getClass().getClassLoader().getResource("static/" + forwardResult.path()).getPath(); List headerTokens = new ArrayList<>(); - headerTokens.add("Content-Type: " + determineContentType(resourcePath)); + headerTokens.add(HttpHeaderKey.CONTENT_TYPE.getName() + ": " + determineContentType(resourcePath)); if (forwardResult.isRedirect()) { - headerTokens.add("Location:" + forwardResult.path()); + headerTokens.add(HttpHeaderKey.LOCATION.getName() + ": " + forwardResult.path()); return new HttpResponse(HttpStatus.FOUND, new Header(headerTokens), new byte[]{}); } From 421ddb621e6db2eeb09f72f02a55a29552bdc495 Mon Sep 17 00:00:00 2001 From: le2sky Date: Fri, 6 Sep 2024 10:47:05 +0900 Subject: [PATCH 33/49] =?UTF-8?q?feat:=20header=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/apache/coyote/http11/Header.java | 12 +++++++ .../org/apache/coyote/http11/HeaderTest.java | 34 ++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Header.java b/tomcat/src/main/java/org/apache/coyote/http11/Header.java index 0db39e55ca..5c771b324c 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Header.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Header.java @@ -35,9 +35,21 @@ private void putIfValidPair(String[] keyValuePair) { String key = keyValuePair[0].trim(); String value = keyValuePair[1].trim(); + append(key, value); + } + + public void append(HttpHeaderKey key, String value) { + append(key.getName(), value); + } + + public void append(String key, String value) { header.put(key, value); } + public Optional get(HttpHeaderKey key) { + return get(key.getName()); + } + public Optional get(String key) { Objects.requireNonNull(key); diff --git a/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java b/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java index d59885128f..0c240ec001 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java @@ -57,7 +57,39 @@ void multi() { void keyNonNull() { Header header = new Header(List.of("a:2")); - assertThatThrownBy(() -> header.get(null)) + assertThatThrownBy(() -> header.get((String) null)) .isInstanceOf(NullPointerException.class); } + + @Test + @DisplayName("header 상수를 조회 키로 사용할 수 있다.") + void enumKey() { + Header header = new Header(List.of("Location:/admin")); + + Optional result = header.get(HttpHeaderKey.LOCATION); + + assertThat(result).hasValue("/admin"); + } + + @Test + @DisplayName("정의된 헤더를 추가할 수 있다.") + void append() { + Header header = new Header(List.of()); + + header.append(HttpHeaderKey.LOCATION, "/admin"); + + Optional result = header.get(HttpHeaderKey.LOCATION); + assertThat(result).hasValue("/admin"); + } + + @Test + @DisplayName("사용자 정의 헤더를 추가할 수 있다.") + void appendCustom() { + Header header = new Header(List.of()); + + header.append("a", "b"); + + Optional result = header.get("a"); + assertThat(result).hasValue("b"); + } } From e24a8ef56da6034a5731557cfbeaabe11546605f Mon Sep 17 00:00:00 2001 From: le2sky Date: Fri, 6 Sep 2024 12:35:15 +0900 Subject: [PATCH 34/49] =?UTF-8?q?fix:=20null=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/apache/coyote/http11/Header.java | 4 ---- .../apache/coyote/http11/QueryParameter.java | 3 --- .../org/apache/coyote/http11/HeaderTest.java | 17 ----------------- .../coyote/http11/QueryParameterTest.java | 10 ---------- 4 files changed, 34 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Header.java b/tomcat/src/main/java/org/apache/coyote/http11/Header.java index 5c771b324c..21af7181f8 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Header.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Header.java @@ -14,8 +14,6 @@ public class Header { private final Map header = new HashMap<>(); public Header(List header) { - Objects.requireNonNull(header); - parseHeader(header); } @@ -51,8 +49,6 @@ public Optional get(HttpHeaderKey key) { } public Optional get(String key) { - Objects.requireNonNull(key); - return Optional.ofNullable(header.get(key)); } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/QueryParameter.java b/tomcat/src/main/java/org/apache/coyote/http11/QueryParameter.java index 29d0189c8b..052cb07319 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/QueryParameter.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/QueryParameter.java @@ -2,7 +2,6 @@ import java.util.HashMap; import java.util.Map; -import java.util.Objects; import java.util.Optional; public class QueryParameter { @@ -42,8 +41,6 @@ private void putIfValidPair(String[] keyValuePair) { } public Optional get(String key) { - Objects.requireNonNull(key); - return Optional.ofNullable(queryParameter.get(key)); } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java b/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java index 0c240ec001..e3abf7167b 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java @@ -8,17 +8,9 @@ import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; class HeaderTest { - @Test - @DisplayName("헤더 문자열 리스트는 null이 될 수 없다.") - void createWithNull() { - assertThatThrownBy(() -> new Header(null)) - .isInstanceOf(NullPointerException.class); - } - @Test @DisplayName("헤더는 a:b 형식만 인식한다.") void singleFormat() { @@ -52,15 +44,6 @@ void multi() { ); } - @Test - @DisplayName("조회 키는 null이 될 수 없다.") - void keyNonNull() { - Header header = new Header(List.of("a:2")); - - assertThatThrownBy(() -> header.get((String) null)) - .isInstanceOf(NullPointerException.class); - } - @Test @DisplayName("header 상수를 조회 키로 사용할 수 있다.") void enumKey() { diff --git a/tomcat/src/test/java/org/apache/coyote/http11/QueryParameterTest.java b/tomcat/src/test/java/org/apache/coyote/http11/QueryParameterTest.java index 640d9c8424..b14aa0a978 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/QueryParameterTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/QueryParameterTest.java @@ -10,7 +10,6 @@ import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; class QueryParameterTest { @@ -54,15 +53,6 @@ void multi() { ); } - @Test - @DisplayName("조회 키는 null이 될 수 없다.") - void keyNonNull() { - QueryParameter queryParameter = new QueryParameter("a=2"); - - assertThatThrownBy(() -> queryParameter.get(null)) - .isInstanceOf(NullPointerException.class); - } - @ParameterizedTest @CsvSource(value = {"=&:0", "a:0", "a=1:1", "a=2&b=2:2", "a=2&a=2:1", "&a=1&:1", "a=1&b=2&c=3:3"}, delimiter = ':') @DisplayName("여러 표현식을 처리한다.") From 7eb2844e25b95410fa17785ea65345e0edb563c9 Mon Sep 17 00:00:00 2001 From: le2sky Date: Fri, 6 Sep 2024 12:36:27 +0900 Subject: [PATCH 35/49] =?UTF-8?q?chore:=20import=EB=AC=B8=20=EC=B5=9C?= =?UTF-8?q?=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tomcat/src/main/java/org/apache/coyote/http11/Header.java | 1 - 1 file changed, 1 deletion(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Header.java b/tomcat/src/main/java/org/apache/coyote/http11/Header.java index 21af7181f8..c290d25676 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Header.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Header.java @@ -4,7 +4,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; public class Header { From cce15bec3690cbd7c98f73dbfbad7b2ce9d9912b Mon Sep 17 00:00:00 2001 From: le2sky Date: Fri, 6 Sep 2024 12:57:15 +0900 Subject: [PATCH 36/49] =?UTF-8?q?refactor:=20forward=20=EB=82=B4=EB=B6=80?= =?UTF-8?q?=EC=97=90=EC=84=9C=20status,=20header=20=EB=8B=A4=EB=A3=B0=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=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 --- .../org/apache/coyote/http11/HttpStatus.java | 7 ++++++- .../http11/handler/AbstractHandler.java | 20 ++++++++----------- .../coyote/http11/handler/ForwardResult.java | 15 +++++++++++++- .../http11/handler/GetLoginHandler.java | 3 ++- .../http11/handler/GetRegisterHandler.java | 3 ++- .../coyote/http11/handler/HelloHandler.java | 3 ++- .../http11/handler/NotFoundHandler.java | 3 ++- .../http11/handler/PostLoginHandler.java | 12 +++++++++-- .../http11/handler/PostRegisterHandler.java | 3 ++- .../http11/handler/StaticResourceHandler.java | 3 ++- .../org/apache/coyote/http11/HeaderTest.java | 5 +++-- 11 files changed, 53 insertions(+), 24 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpStatus.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpStatus.java index 354d1e8860..bf6f1eb8c9 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpStatus.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpStatus.java @@ -3,7 +3,8 @@ public enum HttpStatus { OK(200, "OK"), - FOUND(302, "Found") + FOUND(302, "Found"), + NOT_FOUND(404, "Not Found"), ; @@ -18,4 +19,8 @@ public enum HttpStatus { public String getDescription() { return code + " " + name; } + + public boolean isRedirection() { + return code >= 300 && code < 400; + } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java index 123ae82139..b8be7d7b9c 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java @@ -5,13 +5,10 @@ import org.apache.coyote.http11.HttpHeaderKey; import org.apache.coyote.http11.HttpRequest; import org.apache.coyote.http11.HttpResponse; -import org.apache.coyote.http11.HttpStatus; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; public abstract class AbstractHandler { @@ -20,17 +17,16 @@ public abstract class AbstractHandler { protected abstract ForwardResult forward(HttpRequest httpRequest); public HttpResponse handle(HttpRequest httpRequest) { - ForwardResult forwardResult = forward(httpRequest); - String resourcePath = getClass().getClassLoader().getResource("static/" + forwardResult.path()).getPath(); - List headerTokens = new ArrayList<>(); - headerTokens.add(HttpHeaderKey.CONTENT_TYPE.getName() + ": " + determineContentType(resourcePath)); - - if (forwardResult.isRedirect()) { - headerTokens.add(HttpHeaderKey.LOCATION.getName() + ": " + forwardResult.path()); - return new HttpResponse(HttpStatus.FOUND, new Header(headerTokens), new byte[]{}); + ForwardResult result = forward(httpRequest); + String resourcePath = getClass().getClassLoader().getResource("static/" + result.path()).getPath(); + Header header = result.header(); + header.append(HttpHeaderKey.CONTENT_TYPE, determineContentType(resourcePath)); + + if (result.httpStatus().isRedirection()) { + return new HttpResponse(result.httpStatus(), header, new byte[]{}); } - return new HttpResponse(HttpStatus.OK, new Header(headerTokens), readStaticResource(resourcePath)); + return new HttpResponse(result.httpStatus(), header, readStaticResource(resourcePath)); } private String determineContentType(String resourcePath) { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/ForwardResult.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/ForwardResult.java index 78b4e596c5..be3a42ea2c 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/ForwardResult.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/ForwardResult.java @@ -1,4 +1,17 @@ package org.apache.coyote.http11.handler; -record ForwardResult(boolean isRedirect, String path) { +import org.apache.coyote.http11.Header; +import org.apache.coyote.http11.HttpStatus; + +import java.util.Collections; + +record ForwardResult(String path, HttpStatus httpStatus, Header header) { + + ForwardResult(HttpStatus httpStatus, Header header) { + this("", httpStatus, header); + } + + ForwardResult(String path, HttpStatus httpStatus) { + this(path, httpStatus, new Header(Collections.emptyList())); + } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/GetLoginHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/GetLoginHandler.java index 47acee5d93..0a80e5e750 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/GetLoginHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/GetLoginHandler.java @@ -1,6 +1,7 @@ package org.apache.coyote.http11.handler; import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.HttpStatus; import java.net.URI; @@ -16,6 +17,6 @@ public boolean canHandle(HttpRequest httpRequest) { @Override protected ForwardResult forward(HttpRequest httpRequest) { - return new ForwardResult(false, "login.html"); + return new ForwardResult("login.html", HttpStatus.OK); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/GetRegisterHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/GetRegisterHandler.java index f89068612f..bc2a39e064 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/GetRegisterHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/GetRegisterHandler.java @@ -1,6 +1,7 @@ package org.apache.coyote.http11.handler; import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.HttpStatus; import java.net.URI; @@ -16,6 +17,6 @@ public boolean canHandle(HttpRequest httpRequest) { @Override protected ForwardResult forward(HttpRequest httpRequest) { - return new ForwardResult(false, "register.html"); + return new ForwardResult("register.html", HttpStatus.OK); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/HelloHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/HelloHandler.java index 0fd401b2ea..dc466a68fd 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/HelloHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/HelloHandler.java @@ -1,6 +1,7 @@ package org.apache.coyote.http11.handler; import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.HttpStatus; import java.net.URI; @@ -16,6 +17,6 @@ public boolean canHandle(HttpRequest httpRequest) { @Override protected ForwardResult forward(HttpRequest httpRequest) { - return new ForwardResult(false, "hello.html"); + return new ForwardResult("hello.html", HttpStatus.OK); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/NotFoundHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/NotFoundHandler.java index 4b7080b4fe..a0f7d7c455 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/NotFoundHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/NotFoundHandler.java @@ -1,6 +1,7 @@ package org.apache.coyote.http11.handler; import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.HttpStatus; public class NotFoundHandler extends AbstractHandler { @@ -11,6 +12,6 @@ public boolean canHandle(HttpRequest httpRequest) { @Override protected ForwardResult forward(HttpRequest httpRequest) { - return new ForwardResult(false, "404.html"); + return new ForwardResult("404.html", HttpStatus.NOT_FOUND); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/PostLoginHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/PostLoginHandler.java index cb4e015a3e..a52d9f6b32 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/PostLoginHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/PostLoginHandler.java @@ -1,10 +1,14 @@ package org.apache.coyote.http11.handler; import com.techcourse.db.InMemoryUserRepository; +import org.apache.coyote.http11.Header; +import org.apache.coyote.http11.HttpHeaderKey; import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.HttpStatus; import org.apache.coyote.http11.QueryParameter; import java.net.URI; +import java.util.Collections; public class PostLoginHandler extends AbstractHandler { @@ -19,11 +23,15 @@ public boolean canHandle(HttpRequest httpRequest) { @Override protected ForwardResult forward(HttpRequest httpRequest) { QueryParameter queryParameter = httpRequest.body(); + Header header = new Header(Collections.emptyList()); + String redirectionPath = "401.html"; + if (isLoggedIn(queryParameter)) { - return new ForwardResult(true, "index.html"); + redirectionPath = "index.html"; } - return new ForwardResult(true, "401.html"); + header.append(HttpHeaderKey.LOCATION, redirectionPath); + return new ForwardResult(HttpStatus.FOUND, header); } private boolean isLoggedIn(QueryParameter queryParameter) { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/PostRegisterHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/PostRegisterHandler.java index 092d281f35..bb99aa5b9c 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/PostRegisterHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/PostRegisterHandler.java @@ -3,6 +3,7 @@ import com.techcourse.db.InMemoryUserRepository; import com.techcourse.model.User; import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.HttpStatus; import org.apache.coyote.http11.QueryParameter; import java.net.URI; @@ -26,6 +27,6 @@ protected ForwardResult forward(HttpRequest httpRequest) { InMemoryUserRepository.save(new User(account, password, email)); - return new ForwardResult(false, "index.html"); + return new ForwardResult("index.html", HttpStatus.OK); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/StaticResourceHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/StaticResourceHandler.java index 09b5131e7e..5bd48bb8b4 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/StaticResourceHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/StaticResourceHandler.java @@ -1,6 +1,7 @@ package org.apache.coyote.http11.handler; import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.HttpStatus; import java.net.URI; import java.net.URL; @@ -18,6 +19,6 @@ public boolean canHandle(HttpRequest httpRequest) { public ForwardResult forward(HttpRequest httpRequest) { URI uri = httpRequest.getUri(); - return new ForwardResult(false, uri.getPath()); + return new ForwardResult(uri.getPath(), HttpStatus.OK); } } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java b/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java index e3abf7167b..8398affc60 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -57,7 +58,7 @@ void enumKey() { @Test @DisplayName("정의된 헤더를 추가할 수 있다.") void append() { - Header header = new Header(List.of()); + Header header = new Header(Collections.emptyList()); header.append(HttpHeaderKey.LOCATION, "/admin"); @@ -68,7 +69,7 @@ void append() { @Test @DisplayName("사용자 정의 헤더를 추가할 수 있다.") void appendCustom() { - Header header = new Header(List.of()); + Header header = new Header(Collections.emptyList()); header.append("a", "b"); From 5668186deed849584759720108277e94b644c1c5 Mon Sep 17 00:00:00 2001 From: le2sky Date: Fri, 6 Sep 2024 13:36:26 +0900 Subject: [PATCH 37/49] =?UTF-8?q?feat:=20cookie=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/apache/coyote/http11/Cookie.java | 46 ++++++++++++++++ .../java/org/apache/coyote/http11/Header.java | 4 +- .../org/apache/coyote/http11/CookieTest.java | 52 +++++++++++++++++++ 3 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/Cookie.java create mode 100644 tomcat/src/test/java/org/apache/coyote/http11/CookieTest.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Cookie.java b/tomcat/src/main/java/org/apache/coyote/http11/Cookie.java new file mode 100644 index 0000000000..24c818a68a --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/Cookie.java @@ -0,0 +1,46 @@ +package org.apache.coyote.http11; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class Cookie { + + private static final String PAIR_DELIMITER = "="; + private static final String COOKIE_DELIMITER = ";"; + + private final Map cookie = new HashMap<>(); + + public Cookie(String cookie) { + if (cookie == null) { + cookie = ""; + } + + parseCookie(cookie); + } + + private void parseCookie(String cookie) { + String[] pairs = cookie.split(COOKIE_DELIMITER); + + for (String pair : pairs) { + if (pair.contains(PAIR_DELIMITER)) { + String[] keyValuePair = pair.trim().split(PAIR_DELIMITER); + putIfValidPair(keyValuePair); + } + } + } + + private void putIfValidPair(String[] keyValuePair) { + if (keyValuePair.length != 2) { + return; + } + String key = keyValuePair[0].trim(); + String value = keyValuePair[1].trim(); + + cookie.put(key, value); + } + + public Optional get(String key) { + return Optional.ofNullable(cookie.get(key)); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Header.java b/tomcat/src/main/java/org/apache/coyote/http11/Header.java index c290d25676..f9858dacfc 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Header.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Header.java @@ -16,8 +16,8 @@ public Header(List header) { parseHeader(header); } - private void parseHeader(List headers) { - for (String pair : headers) { + private void parseHeader(List header) { + for (String pair : header) { if (pair.contains(PAIR_DELIMITER)) { String[] split = pair.split(PAIR_DELIMITER); putIfValidPair(split); diff --git a/tomcat/src/test/java/org/apache/coyote/http11/CookieTest.java b/tomcat/src/test/java/org/apache/coyote/http11/CookieTest.java new file mode 100644 index 0000000000..be659c88df --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/CookieTest.java @@ -0,0 +1,52 @@ +package org.apache.coyote.http11; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; + +class CookieTest { + + @Test + @DisplayName("쿠키 문자열은 null이 될 수 있다.") + void createWithNull() { + assertThatCode(() -> new Cookie(null)) + .doesNotThrowAnyException(); + } + + @Test + @DisplayName("쿠키는 a=2 형식만 인식된다.") + void singleFormat() { + Cookie cookie = new Cookie("a:2"); + + Optional result = cookie.get("a"); + + assertThat(result).isEmpty(); + } + + @Test + @DisplayName("단일 쿠키를 조회한다.") + void single() { + Cookie cookie = new Cookie("a=2"); + + Optional result = cookie.get("a"); + + assertThat(result).hasValue("2"); + } + + @Test + @DisplayName("여러 쿠키값을 읽는다.") + void multi() { + Cookie cookie = new Cookie("a=1; b=2; c=3"); + + Assertions.assertAll( + () -> assertThat(cookie.get("a")).hasValue("1"), + () -> assertThat(cookie.get("b")).hasValue("2"), + () -> assertThat(cookie.get("c")).hasValue("3") + ); + } +} From fc664f5ec8b037a94868f05e2349f603f173d255 Mon Sep 17 00:00:00 2001 From: le2sky Date: Fri, 6 Sep 2024 14:07:25 +0900 Subject: [PATCH 38/49] =?UTF-8?q?refactor:=20=ED=8C=A8=ED=82=A4=EC=A7=80?= =?UTF-8?q?=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../techcourse}/handler/GetLoginHandler.java | 4 +++- .../techcourse}/handler/GetRegisterHandler.java | 4 +++- .../techcourse}/handler/HelloHandler.java | 4 +++- .../techcourse}/handler/NotFoundHandler.java | 4 +++- .../techcourse}/handler/PostLoginHandler.java | 4 +++- .../handler/PostRegisterHandler.java | 4 +++- .../http11/{handler => }/AbstractHandler.java | 8 +------- .../org/apache/coyote/http11/ForwardResult.java | 14 ++++++++++++++ .../apache/coyote/http11/Http11Processor.java | 14 ++++++-------- .../{handler => }/StaticResourceHandler.java | 5 +---- .../coyote/http11/handler/ForwardResult.java | 17 ----------------- .../static/assets/img/error-404-monochrome.svg | 2 +- .../handler/GetLoginHandlerTest.java | 2 +- .../handler/GetRegisterHandlerTest.java | 2 +- .../techcourse}/handler/HelloHandlerTest.java | 2 +- .../handler/PostLoginHandlerTest.java | 2 +- .../handler/PostRegisterHandlerTest.java | 2 +- .../StaticResourceHandlerTest.java | 5 +---- 18 files changed, 47 insertions(+), 52 deletions(-) rename tomcat/src/main/java/{org/apache/coyote/http11 => com/techcourse}/handler/GetLoginHandler.java (80%) rename tomcat/src/main/java/{org/apache/coyote/http11 => com/techcourse}/handler/GetRegisterHandler.java (81%) rename tomcat/src/main/java/{org/apache/coyote/http11 => com/techcourse}/handler/HelloHandler.java (79%) rename tomcat/src/main/java/{org/apache/coyote/http11 => com/techcourse}/handler/NotFoundHandler.java (75%) rename tomcat/src/main/java/{org/apache/coyote/http11 => com/techcourse}/handler/PostLoginHandler.java (91%) rename tomcat/src/main/java/{org/apache/coyote/http11 => com/techcourse}/handler/PostRegisterHandler.java (88%) rename tomcat/src/main/java/org/apache/coyote/http11/{handler => }/AbstractHandler.java (86%) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/ForwardResult.java rename tomcat/src/main/java/org/apache/coyote/http11/{handler => }/StaticResourceHandler.java (79%) delete mode 100644 tomcat/src/main/java/org/apache/coyote/http11/handler/ForwardResult.java rename tomcat/src/test/java/{org/apache/coyote/http11 => com/techcourse}/handler/GetLoginHandlerTest.java (96%) rename tomcat/src/test/java/{org/apache/coyote/http11 => com/techcourse}/handler/GetRegisterHandlerTest.java (96%) rename tomcat/src/test/java/{org/apache/coyote/http11 => com/techcourse}/handler/HelloHandlerTest.java (96%) rename tomcat/src/test/java/{org/apache/coyote/http11 => com/techcourse}/handler/PostLoginHandlerTest.java (96%) rename tomcat/src/test/java/{org/apache/coyote/http11 => com/techcourse}/handler/PostRegisterHandlerTest.java (96%) rename tomcat/src/test/java/org/apache/coyote/http11/{handler => }/StaticResourceHandlerTest.java (86%) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/GetLoginHandler.java b/tomcat/src/main/java/com/techcourse/handler/GetLoginHandler.java similarity index 80% rename from tomcat/src/main/java/org/apache/coyote/http11/handler/GetLoginHandler.java rename to tomcat/src/main/java/com/techcourse/handler/GetLoginHandler.java index 0a80e5e750..604b2f1f3c 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/GetLoginHandler.java +++ b/tomcat/src/main/java/com/techcourse/handler/GetLoginHandler.java @@ -1,5 +1,7 @@ -package org.apache.coyote.http11.handler; +package com.techcourse.handler; +import org.apache.coyote.http11.AbstractHandler; +import org.apache.coyote.http11.ForwardResult; import org.apache.coyote.http11.HttpRequest; import org.apache.coyote.http11.HttpStatus; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/GetRegisterHandler.java b/tomcat/src/main/java/com/techcourse/handler/GetRegisterHandler.java similarity index 81% rename from tomcat/src/main/java/org/apache/coyote/http11/handler/GetRegisterHandler.java rename to tomcat/src/main/java/com/techcourse/handler/GetRegisterHandler.java index bc2a39e064..fe906329f5 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/GetRegisterHandler.java +++ b/tomcat/src/main/java/com/techcourse/handler/GetRegisterHandler.java @@ -1,5 +1,7 @@ -package org.apache.coyote.http11.handler; +package com.techcourse.handler; +import org.apache.coyote.http11.AbstractHandler; +import org.apache.coyote.http11.ForwardResult; import org.apache.coyote.http11.HttpRequest; import org.apache.coyote.http11.HttpStatus; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/HelloHandler.java b/tomcat/src/main/java/com/techcourse/handler/HelloHandler.java similarity index 79% rename from tomcat/src/main/java/org/apache/coyote/http11/handler/HelloHandler.java rename to tomcat/src/main/java/com/techcourse/handler/HelloHandler.java index dc466a68fd..90b77c0154 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/HelloHandler.java +++ b/tomcat/src/main/java/com/techcourse/handler/HelloHandler.java @@ -1,5 +1,7 @@ -package org.apache.coyote.http11.handler; +package com.techcourse.handler; +import org.apache.coyote.http11.AbstractHandler; +import org.apache.coyote.http11.ForwardResult; import org.apache.coyote.http11.HttpRequest; import org.apache.coyote.http11.HttpStatus; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/NotFoundHandler.java b/tomcat/src/main/java/com/techcourse/handler/NotFoundHandler.java similarity index 75% rename from tomcat/src/main/java/org/apache/coyote/http11/handler/NotFoundHandler.java rename to tomcat/src/main/java/com/techcourse/handler/NotFoundHandler.java index a0f7d7c455..d2ef69a2ff 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/NotFoundHandler.java +++ b/tomcat/src/main/java/com/techcourse/handler/NotFoundHandler.java @@ -1,5 +1,7 @@ -package org.apache.coyote.http11.handler; +package com.techcourse.handler; +import org.apache.coyote.http11.AbstractHandler; +import org.apache.coyote.http11.ForwardResult; import org.apache.coyote.http11.HttpRequest; import org.apache.coyote.http11.HttpStatus; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/PostLoginHandler.java b/tomcat/src/main/java/com/techcourse/handler/PostLoginHandler.java similarity index 91% rename from tomcat/src/main/java/org/apache/coyote/http11/handler/PostLoginHandler.java rename to tomcat/src/main/java/com/techcourse/handler/PostLoginHandler.java index a52d9f6b32..cd97809ffc 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/PostLoginHandler.java +++ b/tomcat/src/main/java/com/techcourse/handler/PostLoginHandler.java @@ -1,6 +1,8 @@ -package org.apache.coyote.http11.handler; +package com.techcourse.handler; import com.techcourse.db.InMemoryUserRepository; +import org.apache.coyote.http11.AbstractHandler; +import org.apache.coyote.http11.ForwardResult; import org.apache.coyote.http11.Header; import org.apache.coyote.http11.HttpHeaderKey; import org.apache.coyote.http11.HttpRequest; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/PostRegisterHandler.java b/tomcat/src/main/java/com/techcourse/handler/PostRegisterHandler.java similarity index 88% rename from tomcat/src/main/java/org/apache/coyote/http11/handler/PostRegisterHandler.java rename to tomcat/src/main/java/com/techcourse/handler/PostRegisterHandler.java index bb99aa5b9c..7ac9fee408 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/PostRegisterHandler.java +++ b/tomcat/src/main/java/com/techcourse/handler/PostRegisterHandler.java @@ -1,7 +1,9 @@ -package org.apache.coyote.http11.handler; +package com.techcourse.handler; import com.techcourse.db.InMemoryUserRepository; import com.techcourse.model.User; +import org.apache.coyote.http11.AbstractHandler; +import org.apache.coyote.http11.ForwardResult; import org.apache.coyote.http11.HttpRequest; import org.apache.coyote.http11.HttpStatus; import org.apache.coyote.http11.QueryParameter; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java similarity index 86% rename from tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java rename to tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java index b8be7d7b9c..378083f9ea 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/AbstractHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java @@ -1,10 +1,4 @@ -package org.apache.coyote.http11.handler; - -import org.apache.coyote.http11.ContentType; -import org.apache.coyote.http11.Header; -import org.apache.coyote.http11.HttpHeaderKey; -import org.apache.coyote.http11.HttpRequest; -import org.apache.coyote.http11.HttpResponse; +package org.apache.coyote.http11; import java.io.BufferedInputStream; import java.io.FileInputStream; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/ForwardResult.java b/tomcat/src/main/java/org/apache/coyote/http11/ForwardResult.java new file mode 100644 index 0000000000..c9134c68f0 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/ForwardResult.java @@ -0,0 +1,14 @@ +package org.apache.coyote.http11; + +import java.util.Collections; + +public record ForwardResult(String path, HttpStatus httpStatus, Header header) { + + public ForwardResult(HttpStatus httpStatus, Header header) { + this("", httpStatus, header); + } + + public ForwardResult(String path, HttpStatus httpStatus) { + this(path, httpStatus, new Header(Collections.emptyList())); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 9bb7731003..5707895334 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -1,15 +1,13 @@ package org.apache.coyote.http11; import com.techcourse.exception.UncheckedServletException; +import com.techcourse.handler.GetLoginHandler; +import com.techcourse.handler.GetRegisterHandler; +import com.techcourse.handler.HelloHandler; +import com.techcourse.handler.NotFoundHandler; +import com.techcourse.handler.PostLoginHandler; +import com.techcourse.handler.PostRegisterHandler; import org.apache.coyote.Processor; -import org.apache.coyote.http11.handler.AbstractHandler; -import org.apache.coyote.http11.handler.GetLoginHandler; -import org.apache.coyote.http11.handler.GetRegisterHandler; -import org.apache.coyote.http11.handler.HelloHandler; -import org.apache.coyote.http11.handler.NotFoundHandler; -import org.apache.coyote.http11.handler.PostLoginHandler; -import org.apache.coyote.http11.handler.PostRegisterHandler; -import org.apache.coyote.http11.handler.StaticResourceHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/StaticResourceHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/StaticResourceHandler.java similarity index 79% rename from tomcat/src/main/java/org/apache/coyote/http11/handler/StaticResourceHandler.java rename to tomcat/src/main/java/org/apache/coyote/http11/StaticResourceHandler.java index 5bd48bb8b4..4a1717f59b 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/StaticResourceHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/StaticResourceHandler.java @@ -1,7 +1,4 @@ -package org.apache.coyote.http11.handler; - -import org.apache.coyote.http11.HttpRequest; -import org.apache.coyote.http11.HttpStatus; +package org.apache.coyote.http11; import java.net.URI; import java.net.URL; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/ForwardResult.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/ForwardResult.java deleted file mode 100644 index be3a42ea2c..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/ForwardResult.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.apache.coyote.http11.handler; - -import org.apache.coyote.http11.Header; -import org.apache.coyote.http11.HttpStatus; - -import java.util.Collections; - -record ForwardResult(String path, HttpStatus httpStatus, Header header) { - - ForwardResult(HttpStatus httpStatus, Header header) { - this("", httpStatus, header); - } - - ForwardResult(String path, HttpStatus httpStatus) { - this(path, httpStatus, new Header(Collections.emptyList())); - } -} diff --git a/tomcat/src/main/resources/static/assets/img/error-404-monochrome.svg b/tomcat/src/main/resources/static/assets/img/error-404-monochrome.svg index f0d345f991..04464ecd0a 100644 --- a/tomcat/src/main/resources/static/assets/img/error-404-monochrome.svg +++ b/tomcat/src/main/resources/static/assets/img/error-404-monochrome.svg @@ -1 +1 @@ -error-404-monochrome \ No newline at end of file +error-404-monochrome \ No newline at end of file diff --git a/tomcat/src/test/java/org/apache/coyote/http11/handler/GetLoginHandlerTest.java b/tomcat/src/test/java/com/techcourse/handler/GetLoginHandlerTest.java similarity index 96% rename from tomcat/src/test/java/org/apache/coyote/http11/handler/GetLoginHandlerTest.java rename to tomcat/src/test/java/com/techcourse/handler/GetLoginHandlerTest.java index ed4986b364..e3d9676bcd 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/handler/GetLoginHandlerTest.java +++ b/tomcat/src/test/java/com/techcourse/handler/GetLoginHandlerTest.java @@ -1,4 +1,4 @@ -package org.apache.coyote.http11.handler; +package com.techcourse.handler; import org.apache.coyote.http11.Header; import org.apache.coyote.http11.HttpRequest; diff --git a/tomcat/src/test/java/org/apache/coyote/http11/handler/GetRegisterHandlerTest.java b/tomcat/src/test/java/com/techcourse/handler/GetRegisterHandlerTest.java similarity index 96% rename from tomcat/src/test/java/org/apache/coyote/http11/handler/GetRegisterHandlerTest.java rename to tomcat/src/test/java/com/techcourse/handler/GetRegisterHandlerTest.java index 472a854528..0908cb2993 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/handler/GetRegisterHandlerTest.java +++ b/tomcat/src/test/java/com/techcourse/handler/GetRegisterHandlerTest.java @@ -1,4 +1,4 @@ -package org.apache.coyote.http11.handler; +package com.techcourse.handler; import org.apache.coyote.http11.Header; import org.apache.coyote.http11.HttpRequest; diff --git a/tomcat/src/test/java/org/apache/coyote/http11/handler/HelloHandlerTest.java b/tomcat/src/test/java/com/techcourse/handler/HelloHandlerTest.java similarity index 96% rename from tomcat/src/test/java/org/apache/coyote/http11/handler/HelloHandlerTest.java rename to tomcat/src/test/java/com/techcourse/handler/HelloHandlerTest.java index b72e9ae693..c011e37602 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/handler/HelloHandlerTest.java +++ b/tomcat/src/test/java/com/techcourse/handler/HelloHandlerTest.java @@ -1,4 +1,4 @@ -package org.apache.coyote.http11.handler; +package com.techcourse.handler; import org.apache.coyote.http11.Header; import org.apache.coyote.http11.HttpRequest; diff --git a/tomcat/src/test/java/org/apache/coyote/http11/handler/PostLoginHandlerTest.java b/tomcat/src/test/java/com/techcourse/handler/PostLoginHandlerTest.java similarity index 96% rename from tomcat/src/test/java/org/apache/coyote/http11/handler/PostLoginHandlerTest.java rename to tomcat/src/test/java/com/techcourse/handler/PostLoginHandlerTest.java index 2e10a583e7..6cfb10f1b8 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/handler/PostLoginHandlerTest.java +++ b/tomcat/src/test/java/com/techcourse/handler/PostLoginHandlerTest.java @@ -1,4 +1,4 @@ -package org.apache.coyote.http11.handler; +package com.techcourse.handler; import org.apache.coyote.http11.Header; import org.apache.coyote.http11.HttpRequest; diff --git a/tomcat/src/test/java/org/apache/coyote/http11/handler/PostRegisterHandlerTest.java b/tomcat/src/test/java/com/techcourse/handler/PostRegisterHandlerTest.java similarity index 96% rename from tomcat/src/test/java/org/apache/coyote/http11/handler/PostRegisterHandlerTest.java rename to tomcat/src/test/java/com/techcourse/handler/PostRegisterHandlerTest.java index e12a0f452b..46956e6271 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/handler/PostRegisterHandlerTest.java +++ b/tomcat/src/test/java/com/techcourse/handler/PostRegisterHandlerTest.java @@ -1,4 +1,4 @@ -package org.apache.coyote.http11.handler; +package com.techcourse.handler; import org.apache.coyote.http11.Header; import org.apache.coyote.http11.HttpRequest; diff --git a/tomcat/src/test/java/org/apache/coyote/http11/handler/StaticResourceHandlerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/StaticResourceHandlerTest.java similarity index 86% rename from tomcat/src/test/java/org/apache/coyote/http11/handler/StaticResourceHandlerTest.java rename to tomcat/src/test/java/org/apache/coyote/http11/StaticResourceHandlerTest.java index fcd13a12da..412e1da887 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/handler/StaticResourceHandlerTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/StaticResourceHandlerTest.java @@ -1,8 +1,5 @@ -package org.apache.coyote.http11.handler; +package org.apache.coyote.http11; -import org.apache.coyote.http11.Header; -import org.apache.coyote.http11.HttpRequest; -import org.apache.coyote.http11.QueryParameter; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; From c3be4692df55cf1f0cd554537eef46b78f8b0c9b Mon Sep 17 00:00:00 2001 From: le2sky Date: Fri, 6 Sep 2024 14:38:59 +0900 Subject: [PATCH 39/49] =?UTF-8?q?feat:=20session=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../catalina/session/SimpleSession.java | 101 ++++++++++++++++++ .../session/SimpleSessionManager.java | 28 +++++ 2 files changed, 129 insertions(+) create mode 100644 tomcat/src/main/java/org/apache/catalina/session/SimpleSession.java create mode 100644 tomcat/src/main/java/org/apache/catalina/session/SimpleSessionManager.java diff --git a/tomcat/src/main/java/org/apache/catalina/session/SimpleSession.java b/tomcat/src/main/java/org/apache/catalina/session/SimpleSession.java new file mode 100644 index 0000000000..bcfca52a58 --- /dev/null +++ b/tomcat/src/main/java/org/apache/catalina/session/SimpleSession.java @@ -0,0 +1,101 @@ +package org.apache.catalina.session; + +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpSession; +import jakarta.servlet.http.HttpSessionContext; + +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class SimpleSession implements HttpSession { + + private final UUID sessionId = UUID.randomUUID(); + private final Map values = new HashMap<>(); + + @Override + public long getCreationTime() { + return 0; + } + + @Override + public String getId() { + return sessionId.toString(); + } + + @Override + public long getLastAccessedTime() { + return 0; + } + + @Override + public ServletContext getServletContext() { + return null; + } + + @Override + public void setMaxInactiveInterval(int interval) { + + } + + @Override + public int getMaxInactiveInterval() { + return 0; + } + + @Override + public HttpSessionContext getSessionContext() { + return null; + } + + @Override + public Object getAttribute(String name) { + return values.get(name); + } + + @Override + public Object getValue(String name) { + return null; + } + + @Override + public Enumeration getAttributeNames() { + return null; + } + + @Override + public String[] getValueNames() { + return new String[0]; + } + + @Override + public void setAttribute(String name, Object value) { + values.put(name, value); + } + + @Override + public void putValue(String name, Object value) { + + } + + @Override + public void removeAttribute(String name) { + + } + + @Override + public void removeValue(String name) { + + } + + @Override + public void invalidate() { + + } + + @Override + public boolean isNew() { + return false; + } +} diff --git a/tomcat/src/main/java/org/apache/catalina/session/SimpleSessionManager.java b/tomcat/src/main/java/org/apache/catalina/session/SimpleSessionManager.java new file mode 100644 index 0000000000..25ee6ffa41 --- /dev/null +++ b/tomcat/src/main/java/org/apache/catalina/session/SimpleSessionManager.java @@ -0,0 +1,28 @@ +package org.apache.catalina.session; + +import jakarta.servlet.http.HttpSession; +import org.apache.catalina.Manager; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class SimpleSessionManager implements Manager { + + private static final Map SESSIONS = new ConcurrentHashMap<>(); + + @Override + public void add(HttpSession session) { + SESSIONS.put(session.getId(), session); + } + + @Override + public HttpSession findSession(String id) throws IOException { + return SESSIONS.get(id); + } + + @Override + public void remove(HttpSession session) { + SESSIONS.remove(session.getId()); + } +} From 48bec18205e68374619074dfbf5617f52e5e33d8 Mon Sep 17 00:00:00 2001 From: le2sky Date: Fri, 6 Sep 2024 16:08:46 +0900 Subject: [PATCH 40/49] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=84=B8=EC=85=98=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../techcourse/handler/GetLoginHandler.java | 3 +- .../handler/GetRegisterHandler.java | 3 +- .../com/techcourse/handler/HelloHandler.java | 3 +- .../techcourse/handler/NotFoundHandler.java | 3 +- .../techcourse/handler/PostLoginHandler.java | 13 ++++++- .../handler/PostRegisterHandler.java | 3 +- .../apache/catalina/connector/Connector.java | 12 ++++--- .../org/apache/catalina/startup/Tomcat.java | 6 +++- .../apache/coyote/http11/AbstractHandler.java | 36 +++++++++++++++++-- .../apache/coyote/http11/Http11Processor.java | 7 ++-- .../apache/coyote/http11/HttpHeaderKey.java | 2 ++ .../coyote/http11/StaticResourceHandler.java | 4 ++- .../coyote/http11/Http11ProcessorTest.java | 6 ++-- 13 files changed, 82 insertions(+), 19 deletions(-) diff --git a/tomcat/src/main/java/com/techcourse/handler/GetLoginHandler.java b/tomcat/src/main/java/com/techcourse/handler/GetLoginHandler.java index 604b2f1f3c..18274c6f64 100644 --- a/tomcat/src/main/java/com/techcourse/handler/GetLoginHandler.java +++ b/tomcat/src/main/java/com/techcourse/handler/GetLoginHandler.java @@ -1,5 +1,6 @@ package com.techcourse.handler; +import org.apache.catalina.Manager; import org.apache.coyote.http11.AbstractHandler; import org.apache.coyote.http11.ForwardResult; import org.apache.coyote.http11.HttpRequest; @@ -18,7 +19,7 @@ public boolean canHandle(HttpRequest httpRequest) { } @Override - protected ForwardResult forward(HttpRequest httpRequest) { + protected ForwardResult forward(HttpRequest httpRequest, Manager sessionManager) { return new ForwardResult("login.html", HttpStatus.OK); } } diff --git a/tomcat/src/main/java/com/techcourse/handler/GetRegisterHandler.java b/tomcat/src/main/java/com/techcourse/handler/GetRegisterHandler.java index fe906329f5..5f05717cb7 100644 --- a/tomcat/src/main/java/com/techcourse/handler/GetRegisterHandler.java +++ b/tomcat/src/main/java/com/techcourse/handler/GetRegisterHandler.java @@ -1,5 +1,6 @@ package com.techcourse.handler; +import org.apache.catalina.Manager; import org.apache.coyote.http11.AbstractHandler; import org.apache.coyote.http11.ForwardResult; import org.apache.coyote.http11.HttpRequest; @@ -18,7 +19,7 @@ public boolean canHandle(HttpRequest httpRequest) { } @Override - protected ForwardResult forward(HttpRequest httpRequest) { + protected ForwardResult forward(HttpRequest httpRequest, Manager sessionManager) { return new ForwardResult("register.html", HttpStatus.OK); } } diff --git a/tomcat/src/main/java/com/techcourse/handler/HelloHandler.java b/tomcat/src/main/java/com/techcourse/handler/HelloHandler.java index 90b77c0154..df0c732af4 100644 --- a/tomcat/src/main/java/com/techcourse/handler/HelloHandler.java +++ b/tomcat/src/main/java/com/techcourse/handler/HelloHandler.java @@ -1,5 +1,6 @@ package com.techcourse.handler; +import org.apache.catalina.Manager; import org.apache.coyote.http11.AbstractHandler; import org.apache.coyote.http11.ForwardResult; import org.apache.coyote.http11.HttpRequest; @@ -18,7 +19,7 @@ public boolean canHandle(HttpRequest httpRequest) { } @Override - protected ForwardResult forward(HttpRequest httpRequest) { + protected ForwardResult forward(HttpRequest httpRequest, Manager sessionManager) { return new ForwardResult("hello.html", HttpStatus.OK); } } diff --git a/tomcat/src/main/java/com/techcourse/handler/NotFoundHandler.java b/tomcat/src/main/java/com/techcourse/handler/NotFoundHandler.java index d2ef69a2ff..7cbd73605e 100644 --- a/tomcat/src/main/java/com/techcourse/handler/NotFoundHandler.java +++ b/tomcat/src/main/java/com/techcourse/handler/NotFoundHandler.java @@ -1,5 +1,6 @@ package com.techcourse.handler; +import org.apache.catalina.Manager; import org.apache.coyote.http11.AbstractHandler; import org.apache.coyote.http11.ForwardResult; import org.apache.coyote.http11.HttpRequest; @@ -13,7 +14,7 @@ public boolean canHandle(HttpRequest httpRequest) { } @Override - protected ForwardResult forward(HttpRequest httpRequest) { + protected ForwardResult forward(HttpRequest httpRequest, Manager sessionManager) { return new ForwardResult("404.html", HttpStatus.NOT_FOUND); } } diff --git a/tomcat/src/main/java/com/techcourse/handler/PostLoginHandler.java b/tomcat/src/main/java/com/techcourse/handler/PostLoginHandler.java index cd97809ffc..88dbcafd4f 100644 --- a/tomcat/src/main/java/com/techcourse/handler/PostLoginHandler.java +++ b/tomcat/src/main/java/com/techcourse/handler/PostLoginHandler.java @@ -1,6 +1,9 @@ package com.techcourse.handler; import com.techcourse.db.InMemoryUserRepository; +import com.techcourse.model.User; +import jakarta.servlet.http.HttpSession; +import org.apache.catalina.Manager; import org.apache.coyote.http11.AbstractHandler; import org.apache.coyote.http11.ForwardResult; import org.apache.coyote.http11.Header; @@ -23,12 +26,15 @@ public boolean canHandle(HttpRequest httpRequest) { } @Override - protected ForwardResult forward(HttpRequest httpRequest) { + protected ForwardResult forward(HttpRequest httpRequest, Manager sessionManager) { QueryParameter queryParameter = httpRequest.body(); Header header = new Header(Collections.emptyList()); String redirectionPath = "401.html"; if (isLoggedIn(queryParameter)) { + HttpSession session = findSessionOrCreate(sessionManager, createCookie(httpRequest)); + session.setAttribute("user", getUser(queryParameter)); + header.append(HttpHeaderKey.SET_COOKIE, getSessionKey() + "=" + session.getId()); redirectionPath = "index.html"; } @@ -44,4 +50,9 @@ private boolean isLoggedIn(QueryParameter queryParameter) { .map(it -> it.checkPassword(password)) .orElse(false); } + + private User getUser(QueryParameter queryParameter) { + String account = queryParameter.get("account").orElseThrow(); + return InMemoryUserRepository.findByAccount(account).orElseThrow(); + } } diff --git a/tomcat/src/main/java/com/techcourse/handler/PostRegisterHandler.java b/tomcat/src/main/java/com/techcourse/handler/PostRegisterHandler.java index 7ac9fee408..3399705f95 100644 --- a/tomcat/src/main/java/com/techcourse/handler/PostRegisterHandler.java +++ b/tomcat/src/main/java/com/techcourse/handler/PostRegisterHandler.java @@ -2,6 +2,7 @@ import com.techcourse.db.InMemoryUserRepository; import com.techcourse.model.User; +import org.apache.catalina.Manager; import org.apache.coyote.http11.AbstractHandler; import org.apache.coyote.http11.ForwardResult; import org.apache.coyote.http11.HttpRequest; @@ -21,7 +22,7 @@ public boolean canHandle(HttpRequest httpRequest) { } @Override - protected ForwardResult forward(HttpRequest httpRequest) { + protected ForwardResult forward(HttpRequest httpRequest, Manager sessionManager) { QueryParameter body = httpRequest.body(); String account = body.get("account").orElseThrow(); String password = body.get("password").orElseThrow(); diff --git a/tomcat/src/main/java/org/apache/catalina/connector/Connector.java b/tomcat/src/main/java/org/apache/catalina/connector/Connector.java index 3b2c4dda7c..9fed635b96 100644 --- a/tomcat/src/main/java/org/apache/catalina/connector/Connector.java +++ b/tomcat/src/main/java/org/apache/catalina/connector/Connector.java @@ -1,5 +1,6 @@ package org.apache.catalina.connector; +import org.apache.catalina.Manager; import org.apache.coyote.http11.Http11Processor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,15 +18,18 @@ public class Connector implements Runnable { private static final int DEFAULT_ACCEPT_COUNT = 100; private final ServerSocket serverSocket; + private final Manager sessionManager; + private boolean stopped; - public Connector() { - this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT); + public Connector(Manager sessionManager) { + this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, sessionManager); } - public Connector(final int port, final int acceptCount) { + public Connector(final int port, final int acceptCount, final Manager sessionManager) { this.serverSocket = createServerSocket(port, acceptCount); this.stopped = false; + this.sessionManager = sessionManager; } private ServerSocket createServerSocket(final int port, final int acceptCount) { @@ -66,7 +70,7 @@ private void process(final Socket connection) { if (connection == null) { return; } - var processor = new Http11Processor(connection); + var processor = new Http11Processor(connection, sessionManager); new Thread(processor).start(); } diff --git a/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java b/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java index 205159e95b..8e1bb30c8a 100644 --- a/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java +++ b/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java @@ -1,6 +1,8 @@ package org.apache.catalina.startup; +import org.apache.catalina.Manager; import org.apache.catalina.connector.Connector; +import org.apache.catalina.session.SimpleSessionManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,8 +12,10 @@ public class Tomcat { private static final Logger log = LoggerFactory.getLogger(Tomcat.class); + private final Manager manager = new SimpleSessionManager(); + public void start() { - var connector = new Connector(); + var connector = new Connector(manager); connector.start(); try { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java index 378083f9ea..9f88e8f7ca 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java @@ -1,5 +1,9 @@ package org.apache.coyote.http11; +import jakarta.servlet.http.HttpSession; +import org.apache.catalina.Manager; +import org.apache.catalina.session.SimpleSession; + import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; @@ -8,10 +12,10 @@ public abstract class AbstractHandler { public abstract boolean canHandle(HttpRequest httpRequest); - protected abstract ForwardResult forward(HttpRequest httpRequest); + protected abstract ForwardResult forward(HttpRequest httpRequest, Manager sessionManager); - public HttpResponse handle(HttpRequest httpRequest) { - ForwardResult result = forward(httpRequest); + public HttpResponse handle(HttpRequest httpRequest, Manager sessionManager) { + ForwardResult result = forward(httpRequest, sessionManager); String resourcePath = getClass().getClassLoader().getResource("static/" + result.path()).getPath(); Header header = result.header(); header.append(HttpHeaderKey.CONTENT_TYPE, determineContentType(resourcePath)); @@ -41,4 +45,30 @@ private byte[] readStaticResource(String resourcePath) { throw new RuntimeException(e); } } + + protected Cookie createCookie(HttpRequest httpRequest) { + return httpRequest.header() + .get(HttpHeaderKey.COOKIE) + .map(Cookie::new) + .orElse(new Cookie("")); + } + + protected HttpSession findSessionOrCreate(Manager sessionManager, Cookie cookie) { + String id = cookie.get(getSessionKey()).orElse(""); + if (id.isBlank()) { + HttpSession session = new SimpleSession(); + sessionManager.add(session); + return session; + } + + try { + return sessionManager.findSession(id); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + protected String getSessionKey() { + return "JSESSIONID"; + } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 5707895334..1302027d65 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -7,6 +7,7 @@ import com.techcourse.handler.NotFoundHandler; import com.techcourse.handler.PostLoginHandler; import com.techcourse.handler.PostRegisterHandler; +import org.apache.catalina.Manager; import org.apache.coyote.Processor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,9 +26,11 @@ public class Http11Processor implements Runnable, Processor { private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); private final Socket connection; + private final Manager sessionManager; - public Http11Processor(Socket connection) { + public Http11Processor(Socket connection, Manager sessionManager) { this.connection = connection; + this.sessionManager = sessionManager; } @Override @@ -100,6 +103,6 @@ private HttpResponse respondResource(HttpRequest httpRequest) throws IOException .findFirst() .orElse(notFoundHandler); - return targetHandler.handle(httpRequest); + return targetHandler.handle(httpRequest, sessionManager); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpHeaderKey.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpHeaderKey.java index ca8b62be98..9fcfcca81a 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpHeaderKey.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpHeaderKey.java @@ -5,6 +5,8 @@ public enum HttpHeaderKey { CONTENT_LENGTH("Content-Length"), CONTENT_TYPE("Content-Type"), LOCATION("Location"), + COOKIE("Cookie"), + SET_COOKIE("Set-Cookie"), ; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/StaticResourceHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/StaticResourceHandler.java index 4a1717f59b..7847637e41 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/StaticResourceHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/StaticResourceHandler.java @@ -1,5 +1,7 @@ package org.apache.coyote.http11; +import org.apache.catalina.Manager; + import java.net.URI; import java.net.URL; @@ -13,7 +15,7 @@ public boolean canHandle(HttpRequest httpRequest) { } @Override - public ForwardResult forward(HttpRequest httpRequest) { + public ForwardResult forward(HttpRequest httpRequest, Manager sessionManager) { URI uri = httpRequest.getUri(); return new ForwardResult(uri.getPath(), HttpStatus.OK); diff --git a/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java b/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java index 2aba8c56e0..242c646ed1 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java @@ -1,6 +1,8 @@ package org.apache.coyote.http11; +import org.apache.catalina.Manager; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import support.StubSocket; import java.io.File; @@ -16,7 +18,7 @@ class Http11ProcessorTest { void process() { // given final var socket = new StubSocket(); - final var processor = new Http11Processor(socket); + final var processor = new Http11Processor(socket, Mockito.mock(Manager.class)); // when processor.process(socket); @@ -43,7 +45,7 @@ void index() throws IOException { ""); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final Http11Processor processor = new Http11Processor(socket, Mockito.mock(Manager.class)); // when processor.process(socket); From da8a395c560bac162f54dec0afac3509838d95a9 Mon Sep 17 00:00:00 2001 From: le2sky Date: Fri, 6 Sep 2024 16:10:46 +0900 Subject: [PATCH 41/49] =?UTF-8?q?style:=20enum=20=EA=B0=9C=ED=96=89=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tomcat/src/main/java/org/apache/coyote/http11/ContentType.java | 1 - tomcat/src/main/java/org/apache/coyote/http11/HttpHeaderKey.java | 1 - tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java | 1 - tomcat/src/main/java/org/apache/coyote/http11/HttpStatus.java | 1 - 4 files changed, 4 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java b/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java index 84b7a64ffc..96bb371d22 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java @@ -7,7 +7,6 @@ public enum ContentType { CSS("text/css", ".css"), SVG("image/svg+xml", ".svg"), PLAIN("text/plain", ""), - ; private final String name; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpHeaderKey.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpHeaderKey.java index 9fcfcca81a..5358b55d41 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpHeaderKey.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpHeaderKey.java @@ -7,7 +7,6 @@ public enum HttpHeaderKey { LOCATION("Location"), COOKIE("Cookie"), SET_COOKIE("Set-Cookie"), - ; private final String name; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java index b179b1d835..876eb75171 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java @@ -7,7 +7,6 @@ public enum HttpMethod { GET, POST, - ; public static HttpMethod from(String value) { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpStatus.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpStatus.java index bf6f1eb8c9..b59f9d4873 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpStatus.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpStatus.java @@ -5,7 +5,6 @@ public enum HttpStatus { OK(200, "OK"), FOUND(302, "Found"), NOT_FOUND(404, "Not Found"), - ; private final int code; From b11b42ab88b1b310fb6152932e86b9d0b2e8784b Mon Sep 17 00:00:00 2001 From: le2sky Date: Fri, 6 Sep 2024 16:11:26 +0900 Subject: [PATCH 42/49] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20throws=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/apache/catalina/session/SimpleSessionManager.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tomcat/src/main/java/org/apache/catalina/session/SimpleSessionManager.java b/tomcat/src/main/java/org/apache/catalina/session/SimpleSessionManager.java index 25ee6ffa41..9df39bc49f 100644 --- a/tomcat/src/main/java/org/apache/catalina/session/SimpleSessionManager.java +++ b/tomcat/src/main/java/org/apache/catalina/session/SimpleSessionManager.java @@ -3,7 +3,6 @@ import jakarta.servlet.http.HttpSession; import org.apache.catalina.Manager; -import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -17,7 +16,7 @@ public void add(HttpSession session) { } @Override - public HttpSession findSession(String id) throws IOException { + public HttpSession findSession(String id) { return SESSIONS.get(id); } From 2e0f82dae4c0f0e9392f1ba3d9300eace41360aa Mon Sep 17 00:00:00 2001 From: le2sky Date: Fri, 6 Sep 2024 16:57:11 +0900 Subject: [PATCH 43/49] =?UTF-8?q?chore:=20=ED=95=99=EC=8A=B5=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=ED=9D=94=EC=A0=81=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- study/src/test/java/study/IOStreamTest.java | 42 ++++++++++----------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/study/src/test/java/study/IOStreamTest.java b/study/src/test/java/study/IOStreamTest.java index ff5a81387d..4f1dccc73d 100644 --- a/study/src/test/java/study/IOStreamTest.java +++ b/study/src/test/java/study/IOStreamTest.java @@ -4,20 +4,27 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import java.io.*; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.in; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; /** * 자바는 스트림(Stream)으로부터 I/O를 사용한다. * 입출력(I/O)은 하나의 시스템에서 다른 시스템으로 데이터를 이동 시킬 때 사용한다. - * + *

* InputStream은 데이터를 읽고, OutputStream은 데이터를 쓴다. * FilterStream은 InputStream이나 OutputStream에 연결될 수 있다. * FilterStream은 읽거나 쓰는 데이터를 수정할 때 사용한다. (e.g. 암호화, 압축, 포맷 변환) - * + *

* Stream은 데이터를 바이트로 읽고 쓴다. * 바이트가 아닌 텍스트(문자)를 읽고 쓰려면 Reader와 Writer 클래스를 연결한다. * Reader, Writer는 다양한 문자 인코딩(e.g. UTF-8)을 처리할 수 있다. @@ -27,7 +34,7 @@ class IOStreamTest { /** * OutputStream 학습하기 - * + *

* 자바의 기본 출력 클래스는 java.io.OutputStream이다. * OutputStream의 write(int b) 메서드는 기반 메서드이다. * public abstract void write(int b) throws IOException; @@ -40,7 +47,7 @@ class OutputStream_학습_테스트 { * OutputStream의 서브 클래스(subclass)는 특정 매체에 데이터를 쓰기 위해 write(int b) 메서드를 사용한다. * 예를 들어, FilterOutputStream은 파일로 데이터를 쓸 때, * 또는 DataOutputStream은 자바의 primitive type data를 다른 매체로 데이터를 쓸 때 사용한다. - * + *

* write 메서드는 데이터를 바이트로 출력하기 때문에 비효율적이다. * write(byte[] data)write(byte b[], int off, int len) 메서드는 * 1바이트 이상을 한 번에 전송 할 수 있어 훨씬 효율적이다. @@ -64,7 +71,7 @@ class OutputStream_학습_테스트 { /** * 효율적인 전송을 위해 스트림에서 버퍼링을 사용 할 수 있다. * BufferedOutputStream 필터를 연결하면 버퍼링이 가능하다. - * + *

* 버퍼링을 사용하면 OutputStream을 사용할 때 flush를 사용하자. * flush() 메서드는 버퍼가 아직 가득 차지 않은 상황에서 강제로 버퍼의 내용을 전송한다. * Stream은 동기(synchronous)로 동작하기 때문에 버퍼가 찰 때까지 기다리면 @@ -104,12 +111,12 @@ class OutputStream_학습_테스트 { /** * InputStream 학습하기 - * + *

* 자바의 기본 입력 클래스는 java.io.InputStream이다. * InputStream은 다른 매체로부터 바이트로 데이터를 읽을 때 사용한다. * InputStream의 read() 메서드는 기반 메서드이다. * public abstract int read() throws IOException; - * + *

* InputStream의 서브 클래스(subclass)는 특정 매체에 데이터를 읽기 위해 read() 메서드를 사용한다. */ @Nested @@ -156,7 +163,7 @@ class InputStream_학습_테스트 { /** * FilterStream 학습하기 - * + *

* 필터는 필터 스트림, reader, writer로 나뉜다. * 필터는 바이트를 다른 데이터 형식으로 변환 할 때 사용한다. * reader, writer는 UTF-8, ISO 8859-1 같은 형식으로 인코딩된 텍스트를 처리하는 데 사용된다. @@ -206,18 +213,9 @@ class InputStreamReader_학습_테스트 { ""); final InputStream inputStream = new ByteArrayInputStream(emoji.getBytes()); - InputStreamReader inputStreamReader = new InputStreamReader(inputStream); + final StringBuilder actual = new StringBuilder(); - try (BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { - String s = bufferedReader.readLine(); - - System.out.println(s); - - final StringBuilder actual = new StringBuilder(); - assertThat(actual).hasToString(emoji); - } catch (IOException e) { - throw new RuntimeException(e); - } + assertThat(actual).hasToString(emoji); } } } From 5837dfd7e970e6ae51d467220bf654e145399923 Mon Sep 17 00:00:00 2001 From: le2sky Date: Fri, 6 Sep 2024 16:58:29 +0900 Subject: [PATCH 44/49] =?UTF-8?q?chore:=20p=20=ED=83=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- study/src/test/java/study/IOStreamTest.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/study/src/test/java/study/IOStreamTest.java b/study/src/test/java/study/IOStreamTest.java index 4f1dccc73d..0ab88bbb68 100644 --- a/study/src/test/java/study/IOStreamTest.java +++ b/study/src/test/java/study/IOStreamTest.java @@ -20,11 +20,11 @@ /** * 자바는 스트림(Stream)으로부터 I/O를 사용한다. * 입출력(I/O)은 하나의 시스템에서 다른 시스템으로 데이터를 이동 시킬 때 사용한다. - *

+ * * InputStream은 데이터를 읽고, OutputStream은 데이터를 쓴다. * FilterStream은 InputStream이나 OutputStream에 연결될 수 있다. * FilterStream은 읽거나 쓰는 데이터를 수정할 때 사용한다. (e.g. 암호화, 압축, 포맷 변환) - *

+ * * Stream은 데이터를 바이트로 읽고 쓴다. * 바이트가 아닌 텍스트(문자)를 읽고 쓰려면 Reader와 Writer 클래스를 연결한다. * Reader, Writer는 다양한 문자 인코딩(e.g. UTF-8)을 처리할 수 있다. @@ -34,7 +34,7 @@ class IOStreamTest { /** * OutputStream 학습하기 - *

+ * * 자바의 기본 출력 클래스는 java.io.OutputStream이다. * OutputStream의 write(int b) 메서드는 기반 메서드이다. * public abstract void write(int b) throws IOException; @@ -47,7 +47,7 @@ class OutputStream_학습_테스트 { * OutputStream의 서브 클래스(subclass)는 특정 매체에 데이터를 쓰기 위해 write(int b) 메서드를 사용한다. * 예를 들어, FilterOutputStream은 파일로 데이터를 쓸 때, * 또는 DataOutputStream은 자바의 primitive type data를 다른 매체로 데이터를 쓸 때 사용한다. - *

+ * * write 메서드는 데이터를 바이트로 출력하기 때문에 비효율적이다. * write(byte[] data)write(byte b[], int off, int len) 메서드는 * 1바이트 이상을 한 번에 전송 할 수 있어 훨씬 효율적이다. @@ -71,7 +71,7 @@ class OutputStream_학습_테스트 { /** * 효율적인 전송을 위해 스트림에서 버퍼링을 사용 할 수 있다. * BufferedOutputStream 필터를 연결하면 버퍼링이 가능하다. - *

+ * * 버퍼링을 사용하면 OutputStream을 사용할 때 flush를 사용하자. * flush() 메서드는 버퍼가 아직 가득 차지 않은 상황에서 강제로 버퍼의 내용을 전송한다. * Stream은 동기(synchronous)로 동작하기 때문에 버퍼가 찰 때까지 기다리면 @@ -111,12 +111,12 @@ class OutputStream_학습_테스트 { /** * InputStream 학습하기 - *

+ * * 자바의 기본 입력 클래스는 java.io.InputStream이다. * InputStream은 다른 매체로부터 바이트로 데이터를 읽을 때 사용한다. * InputStream의 read() 메서드는 기반 메서드이다. * public abstract int read() throws IOException; - *

+ * * InputStream의 서브 클래스(subclass)는 특정 매체에 데이터를 읽기 위해 read() 메서드를 사용한다. */ @Nested @@ -163,7 +163,7 @@ class InputStream_학습_테스트 { /** * FilterStream 학습하기 - *

+ * * 필터는 필터 스트림, reader, writer로 나뉜다. * 필터는 바이트를 다른 데이터 형식으로 변환 할 때 사용한다. * reader, writer는 UTF-8, ISO 8859-1 같은 형식으로 인코딩된 텍스트를 처리하는 데 사용된다. From d3b1cda96b9fdfb1a6cfd55bf45b1e1279f30cc4 Mon Sep 17 00:00:00 2001 From: le2sky Date: Sun, 8 Sep 2024 16:59:23 +0900 Subject: [PATCH 45/49] =?UTF-8?q?refactor:=20content-type=20=EB=82=B4?= =?UTF-8?q?=EB=B6=80=EB=A1=9C=20content-type=20=EA=B2=B0=EC=A0=95=20?= =?UTF-8?q?=EC=B1=85=EC=9E=84=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/AbstractHandler.java | 14 ++------- .../org/apache/coyote/http11/ContentType.java | 12 +++++++- .../apache/coyote/http11/ContentTypeTest.java | 29 +++++++++++++++++++ 3 files changed, 42 insertions(+), 13 deletions(-) create mode 100644 tomcat/src/test/java/org/apache/coyote/http11/ContentTypeTest.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java index 9f88e8f7ca..dccd2bd898 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java @@ -18,7 +18,8 @@ public HttpResponse handle(HttpRequest httpRequest, Manager sessionManager) { ForwardResult result = forward(httpRequest, sessionManager); String resourcePath = getClass().getClassLoader().getResource("static/" + result.path()).getPath(); Header header = result.header(); - header.append(HttpHeaderKey.CONTENT_TYPE, determineContentType(resourcePath)); + String contentType = ContentType.determineContentType(resourcePath).getName(); + header.append(HttpHeaderKey.CONTENT_TYPE, contentType); if (result.httpStatus().isRedirection()) { return new HttpResponse(result.httpStatus(), header, new byte[]{}); @@ -27,17 +28,6 @@ public HttpResponse handle(HttpRequest httpRequest, Manager sessionManager) { return new HttpResponse(result.httpStatus(), header, readStaticResource(resourcePath)); } - private String determineContentType(String resourcePath) { - String encodedContentTypeFormat = "%s;charset=utf-8"; - for (ContentType contentType : ContentType.values()) { - if (resourcePath.endsWith(contentType.getExtension())) { - return String.format(encodedContentTypeFormat, contentType.getName()); - } - } - - return String.format(encodedContentTypeFormat, ContentType.PLAIN.getName()); - } - private byte[] readStaticResource(String resourcePath) { try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(resourcePath))) { return bufferedInputStream.readAllBytes(); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java b/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java index 96bb371d22..ba31caadd4 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java @@ -17,8 +17,18 @@ public enum ContentType { this.extension = extension; } + public static ContentType determineContentType(String resourcePath) { + for (ContentType contentType : ContentType.values()) { + if (resourcePath.endsWith(contentType.getExtension())) { + return contentType; + } + } + + return ContentType.PLAIN; + } + public String getName() { - return name; + return name + ";charset=utf-8"; } public String getExtension() { diff --git a/tomcat/src/test/java/org/apache/coyote/http11/ContentTypeTest.java b/tomcat/src/test/java/org/apache/coyote/http11/ContentTypeTest.java new file mode 100644 index 0000000000..53fec2ef9c --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/ContentTypeTest.java @@ -0,0 +1,29 @@ +package org.apache.coyote.http11; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.Assertions.assertThat; + +class ContentTypeTest { + + @ParameterizedTest + @CsvSource( + value = { + "index.html:text/html", + "style.css:text/css", + "sample.js:text/javascript", + "image.svg:image/svg+xml", + "hi.unknown:text/plain" + }, + delimiter = ':' + ) + @DisplayName("주어진 확장자에 맞는 content-type을 반환한다.") + void determineContentType(String source, String expected) { + ContentType contentType = ContentType.determineContentType(source); + + assertThat(contentType.getName()).startsWith(expected); + assertThat(contentType.getName()).endsWith(";charset=utf-8"); + } +} From 8bead94ed4ecbf8468e8eba4c643c9bedaec9eb6 Mon Sep 17 00:00:00 2001 From: le2sky Date: Sun, 8 Sep 2024 17:13:12 +0900 Subject: [PATCH 46/49] =?UTF-8?q?refactor:=20header.empty=20=EC=A0=95?= =?UTF-8?q?=EC=A0=81=20=ED=8C=A9=ED=84=B0=EB=A6=AC=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/techcourse/handler/PostLoginHandler.java | 3 +-- .../main/java/org/apache/coyote/http11/ForwardResult.java | 4 +--- tomcat/src/main/java/org/apache/coyote/http11/Header.java | 4 ++++ .../java/com/techcourse/handler/GetLoginHandlerTest.java | 4 +--- .../java/com/techcourse/handler/GetRegisterHandlerTest.java | 4 +--- .../test/java/com/techcourse/handler/HelloHandlerTest.java | 4 +--- .../java/com/techcourse/handler/PostLoginHandlerTest.java | 4 +--- .../java/com/techcourse/handler/PostRegisterHandlerTest.java | 4 +--- .../src/test/java/org/apache/coyote/http11/HeaderTest.java | 5 ++--- .../org/apache/coyote/http11/StaticResourceHandlerTest.java | 4 +--- 10 files changed, 14 insertions(+), 26 deletions(-) diff --git a/tomcat/src/main/java/com/techcourse/handler/PostLoginHandler.java b/tomcat/src/main/java/com/techcourse/handler/PostLoginHandler.java index 88dbcafd4f..53afffd214 100644 --- a/tomcat/src/main/java/com/techcourse/handler/PostLoginHandler.java +++ b/tomcat/src/main/java/com/techcourse/handler/PostLoginHandler.java @@ -13,7 +13,6 @@ import org.apache.coyote.http11.QueryParameter; import java.net.URI; -import java.util.Collections; public class PostLoginHandler extends AbstractHandler { @@ -28,7 +27,7 @@ public boolean canHandle(HttpRequest httpRequest) { @Override protected ForwardResult forward(HttpRequest httpRequest, Manager sessionManager) { QueryParameter queryParameter = httpRequest.body(); - Header header = new Header(Collections.emptyList()); + Header header = Header.empty(); String redirectionPath = "401.html"; if (isLoggedIn(queryParameter)) { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/ForwardResult.java b/tomcat/src/main/java/org/apache/coyote/http11/ForwardResult.java index c9134c68f0..041519a4a5 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/ForwardResult.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/ForwardResult.java @@ -1,7 +1,5 @@ package org.apache.coyote.http11; -import java.util.Collections; - public record ForwardResult(String path, HttpStatus httpStatus, Header header) { public ForwardResult(HttpStatus httpStatus, Header header) { @@ -9,6 +7,6 @@ public ForwardResult(HttpStatus httpStatus, Header header) { } public ForwardResult(String path, HttpStatus httpStatus) { - this(path, httpStatus, new Header(Collections.emptyList())); + this(path, httpStatus, Header.empty()); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Header.java b/tomcat/src/main/java/org/apache/coyote/http11/Header.java index f9858dacfc..59e2ea1330 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Header.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Header.java @@ -12,6 +12,10 @@ public class Header { private final Map header = new HashMap<>(); + public static Header empty() { + return new Header(Collections.emptyList()); + } + public Header(List header) { parseHeader(header); } diff --git a/tomcat/src/test/java/com/techcourse/handler/GetLoginHandlerTest.java b/tomcat/src/test/java/com/techcourse/handler/GetLoginHandlerTest.java index e3d9676bcd..bb72c52050 100644 --- a/tomcat/src/test/java/com/techcourse/handler/GetLoginHandlerTest.java +++ b/tomcat/src/test/java/com/techcourse/handler/GetLoginHandlerTest.java @@ -6,8 +6,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import java.util.Collections; - import static org.assertj.core.api.Assertions.assertThat; class GetLoginHandlerTest { @@ -33,6 +31,6 @@ void cantHandle() { } private HttpRequest createHttpRequest(String startLine) { - return new HttpRequest(startLine, new Header(Collections.emptyList()), new QueryParameter("")); + return new HttpRequest(startLine, Header.empty(), new QueryParameter("")); } } diff --git a/tomcat/src/test/java/com/techcourse/handler/GetRegisterHandlerTest.java b/tomcat/src/test/java/com/techcourse/handler/GetRegisterHandlerTest.java index 0908cb2993..06925ae35b 100644 --- a/tomcat/src/test/java/com/techcourse/handler/GetRegisterHandlerTest.java +++ b/tomcat/src/test/java/com/techcourse/handler/GetRegisterHandlerTest.java @@ -6,8 +6,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import java.util.Collections; - import static org.assertj.core.api.Assertions.assertThat; class GetRegisterHandlerTest { @@ -33,6 +31,6 @@ void cantHandle() { } private HttpRequest createHttpRequest(String startLine) { - return new HttpRequest(startLine, new Header(Collections.emptyList()), new QueryParameter("")); + return new HttpRequest(startLine, Header.empty(), new QueryParameter("")); } } diff --git a/tomcat/src/test/java/com/techcourse/handler/HelloHandlerTest.java b/tomcat/src/test/java/com/techcourse/handler/HelloHandlerTest.java index c011e37602..2a362aa2b3 100644 --- a/tomcat/src/test/java/com/techcourse/handler/HelloHandlerTest.java +++ b/tomcat/src/test/java/com/techcourse/handler/HelloHandlerTest.java @@ -6,8 +6,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import java.util.Collections; - import static org.assertj.core.api.Assertions.assertThat; class HelloHandlerTest { @@ -33,6 +31,6 @@ void cantHandle() { } private HttpRequest createHttpRequest(String startLine) { - return new HttpRequest(startLine, new Header(Collections.emptyList()), new QueryParameter("")); + return new HttpRequest(startLine, Header.empty(), new QueryParameter("")); } } diff --git a/tomcat/src/test/java/com/techcourse/handler/PostLoginHandlerTest.java b/tomcat/src/test/java/com/techcourse/handler/PostLoginHandlerTest.java index 6cfb10f1b8..7af7f4b194 100644 --- a/tomcat/src/test/java/com/techcourse/handler/PostLoginHandlerTest.java +++ b/tomcat/src/test/java/com/techcourse/handler/PostLoginHandlerTest.java @@ -6,8 +6,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import java.util.Collections; - import static org.assertj.core.api.Assertions.assertThat; class PostLoginHandlerTest { @@ -33,6 +31,6 @@ void cantHandle() { } private HttpRequest createHttpRequest(String startLine) { - return new HttpRequest(startLine, new Header(Collections.emptyList()), new QueryParameter("")); + return new HttpRequest(startLine, Header.empty(), new QueryParameter("")); } } diff --git a/tomcat/src/test/java/com/techcourse/handler/PostRegisterHandlerTest.java b/tomcat/src/test/java/com/techcourse/handler/PostRegisterHandlerTest.java index 46956e6271..9926607c99 100644 --- a/tomcat/src/test/java/com/techcourse/handler/PostRegisterHandlerTest.java +++ b/tomcat/src/test/java/com/techcourse/handler/PostRegisterHandlerTest.java @@ -6,8 +6,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import java.util.Collections; - import static org.assertj.core.api.Assertions.assertThat; class PostRegisterHandlerTest { @@ -33,6 +31,6 @@ void cantHandle() { } private HttpRequest createHttpRequest(String startLine) { - return new HttpRequest(startLine, new Header(Collections.emptyList()), new QueryParameter("")); + return new HttpRequest(startLine, Header.empty(), new QueryParameter("")); } } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java b/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java index 8398affc60..2e784060e1 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/HeaderTest.java @@ -4,7 +4,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import java.util.Collections; import java.util.List; import java.util.Optional; @@ -58,7 +57,7 @@ void enumKey() { @Test @DisplayName("정의된 헤더를 추가할 수 있다.") void append() { - Header header = new Header(Collections.emptyList()); + Header header = Header.empty(); header.append(HttpHeaderKey.LOCATION, "/admin"); @@ -69,7 +68,7 @@ void append() { @Test @DisplayName("사용자 정의 헤더를 추가할 수 있다.") void appendCustom() { - Header header = new Header(Collections.emptyList()); + Header header = Header.empty(); header.append("a", "b"); diff --git a/tomcat/src/test/java/org/apache/coyote/http11/StaticResourceHandlerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/StaticResourceHandlerTest.java index 412e1da887..cb3d1cff6d 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/StaticResourceHandlerTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/StaticResourceHandlerTest.java @@ -3,8 +3,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import java.util.Collections; - import static org.assertj.core.api.Assertions.assertThat; class StaticResourceHandlerTest { @@ -30,6 +28,6 @@ void cantHandle() { } private HttpRequest createHttpRequest(String startLine) { - return new HttpRequest(startLine, new Header(Collections.emptyList()), new QueryParameter("")); + return new HttpRequest(startLine, Header.empty(), new QueryParameter("")); } } From 8e83204d60fab653b3cada5a273910cd551f4ec9 Mon Sep 17 00:00:00 2001 From: le2sky Date: Sun, 8 Sep 2024 17:22:47 +0900 Subject: [PATCH 47/49] =?UTF-8?q?refactor:=20http=20version=20=EC=83=81?= =?UTF-8?q?=EC=88=98=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/AbstractHandler.java | 9 +++++-- .../org/apache/coyote/http11/HttpRequest.java | 4 +++ .../apache/coyote/http11/HttpResponse.java | 3 ++- .../org/apache/coyote/http11/HttpVersion.java | 27 +++++++++++++++++++ 4 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpVersion.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java index dccd2bd898..1a4b5d08e8 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/AbstractHandler.java @@ -22,10 +22,15 @@ public HttpResponse handle(HttpRequest httpRequest, Manager sessionManager) { header.append(HttpHeaderKey.CONTENT_TYPE, contentType); if (result.httpStatus().isRedirection()) { - return new HttpResponse(result.httpStatus(), header, new byte[]{}); + return new HttpResponse(httpRequest.getHttpVersion(), result.httpStatus(), header, new byte[]{}); } - return new HttpResponse(result.httpStatus(), header, readStaticResource(resourcePath)); + return new HttpResponse( + httpRequest.getHttpVersion(), + result.httpStatus(), + header, + readStaticResource(resourcePath) + ); } private byte[] readStaticResource(String resourcePath) { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java index b28eb0d1aa..95e6f30e0d 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java @@ -11,4 +11,8 @@ public HttpMethod getMethod() { public URI getUri() { return URI.create(startLine.split(" ")[1]); } + + public HttpVersion getHttpVersion() { + return HttpVersion.from(startLine.split(" ")[2]); + } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java index 06ff065f15..b45fb685d0 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java @@ -3,6 +3,7 @@ import java.util.Map; public record HttpResponse( + HttpVersion httpVersion, HttpStatus httpStatus, Header header, byte[] responseBody @@ -16,7 +17,7 @@ public byte[] serialize() { } private String getStartLine() { - return "HTTP/1.1 " + httpStatus.getDescription() + " "; + return httpVersion.getVersionName() + " " + httpStatus.getDescription() + " "; } private CharSequence getHeaders() { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpVersion.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpVersion.java new file mode 100644 index 0000000000..1d147c3d80 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpVersion.java @@ -0,0 +1,27 @@ +package org.apache.coyote.http11; + +import java.util.Arrays; +import java.util.NoSuchElementException; + +enum HttpVersion { + + HTTP_1_1("HTTP/1.1"), + ; + + private final String versionName; + + HttpVersion(String versionName) { + this.versionName = versionName; + } + + public static HttpVersion from(String versionName) { + return Arrays.stream(values()) + .filter(it -> it.versionName.equals(versionName)) + .findFirst() + .orElseThrow(NoSuchElementException::new); + } + + public String getVersionName() { + return versionName; + } +} From 1f1e5f344cf071dd8ea3db46febe5df5ace8f191 Mon Sep 17 00:00:00 2001 From: le2sky Date: Sun, 8 Sep 2024 18:05:17 +0900 Subject: [PATCH 48/49] =?UTF-8?q?refactor:=20http=20request=20body?= =?UTF-8?q?=EB=A5=BC=20char[]=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/techcourse/handler/PostLoginHandler.java | 6 +++++- .../techcourse/handler/PostRegisterHandler.java | 14 +++++++++++--- .../org/apache/coyote/http11/ContentType.java | 15 +++++++++++++++ .../org/apache/coyote/http11/Http11Processor.java | 13 ++++++------- .../org/apache/coyote/http11/HttpRequest.java | 14 +++++++++++++- .../org/apache/coyote/http11/QueryParameter.java | 4 ++++ .../techcourse/handler/GetLoginHandlerTest.java | 3 +-- .../handler/GetRegisterHandlerTest.java | 3 +-- .../com/techcourse/handler/HelloHandlerTest.java | 3 +-- .../techcourse/handler/PostLoginHandlerTest.java | 3 +-- .../handler/PostRegisterHandlerTest.java | 3 +-- .../apache/coyote/http11/QueryParameterTest.java | 2 +- .../coyote/http11/StaticResourceHandlerTest.java | 2 +- 13 files changed, 61 insertions(+), 24 deletions(-) diff --git a/tomcat/src/main/java/com/techcourse/handler/PostLoginHandler.java b/tomcat/src/main/java/com/techcourse/handler/PostLoginHandler.java index 53afffd214..9a6e84b110 100644 --- a/tomcat/src/main/java/com/techcourse/handler/PostLoginHandler.java +++ b/tomcat/src/main/java/com/techcourse/handler/PostLoginHandler.java @@ -26,7 +26,11 @@ public boolean canHandle(HttpRequest httpRequest) { @Override protected ForwardResult forward(HttpRequest httpRequest, Manager sessionManager) { - QueryParameter queryParameter = httpRequest.body(); + if (httpRequest.hasNotApplicationXW3FormUrlEncodedBody()) { + throw new RuntimeException(); + } + + QueryParameter queryParameter = new QueryParameter(httpRequest.body()); Header header = Header.empty(); String redirectionPath = "401.html"; diff --git a/tomcat/src/main/java/com/techcourse/handler/PostRegisterHandler.java b/tomcat/src/main/java/com/techcourse/handler/PostRegisterHandler.java index 3399705f95..7f0bf64eb7 100644 --- a/tomcat/src/main/java/com/techcourse/handler/PostRegisterHandler.java +++ b/tomcat/src/main/java/com/techcourse/handler/PostRegisterHandler.java @@ -23,13 +23,21 @@ public boolean canHandle(HttpRequest httpRequest) { @Override protected ForwardResult forward(HttpRequest httpRequest, Manager sessionManager) { - QueryParameter body = httpRequest.body(); + if (httpRequest.hasNotApplicationXW3FormUrlEncodedBody()) { + throw new RuntimeException(); + } + + registerNewUser(httpRequest); + + return new ForwardResult("index.html", HttpStatus.OK); + } + + private void registerNewUser(HttpRequest httpRequest) { + QueryParameter body = new QueryParameter(httpRequest.body()); String account = body.get("account").orElseThrow(); String password = body.get("password").orElseThrow(); String email = body.get("email").orElseThrow(); InMemoryUserRepository.save(new User(account, password, email)); - - return new ForwardResult("index.html", HttpStatus.OK); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java b/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java index ba31caadd4..93f9261da9 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java @@ -1,5 +1,8 @@ package org.apache.coyote.http11; +import java.util.Arrays; +import java.util.NoSuchElementException; + public enum ContentType { HTML("text/html", ".html"), @@ -7,6 +10,7 @@ public enum ContentType { CSS("text/css", ".css"), SVG("image/svg+xml", ".svg"), PLAIN("text/plain", ""), + APPLICATION_X_WWW_FORM_URL_ENCODED("application/x-www-form-urlencoded", ""), ; private final String name; @@ -17,6 +21,13 @@ public enum ContentType { this.extension = extension; } + public static ContentType from(String contentTypeName) { + return Arrays.stream(values()) + .filter(it -> it.name.equals(contentTypeName)) + .findFirst() + .orElseThrow(NoSuchElementException::new); + } + public static ContentType determineContentType(String resourcePath) { for (ContentType contentType : ContentType.values()) { if (resourcePath.endsWith(contentType.getExtension())) { @@ -27,6 +38,10 @@ public static ContentType determineContentType(String resourcePath) { return ContentType.PLAIN; } + public boolean isApplicationXW3FormUrlEncoded() { + return this.equals(APPLICATION_X_WWW_FORM_URL_ENCODED); + } + public String getName() { return name + ";charset=utf-8"; } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 1302027d65..39c12e12d6 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -56,9 +56,9 @@ private HttpRequest createHttpRequest(InputStream inputStream) throws IOExceptio BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String requestLine = bufferedReader.readLine(); Header header = createHeader(bufferedReader); - QueryParameter queryParameter = createQueryParameter(bufferedReader, header); + char[] requestBody = createRequestBody(bufferedReader, header); - return new HttpRequest(requestLine, header, queryParameter); + return new HttpRequest(requestLine, header, requestBody); } private Header createHeader(BufferedReader bufferedReader) throws IOException { @@ -72,13 +72,12 @@ private Header createHeader(BufferedReader bufferedReader) throws IOException { return new Header(headerTokens); } - private QueryParameter createQueryParameter(BufferedReader bufferedReader, Header header) throws IOException { + private char[] createRequestBody(BufferedReader bufferedReader, Header header) throws IOException { int length = Integer.parseInt(header.get(HttpHeaderKey.CONTENT_LENGTH.getName()).orElse("0")); - char[] body = new char[length]; - bufferedReader.read(body); - String bodyString = new String(body); + char[] requestBody = new char[length]; + bufferedReader.read(requestBody); - return new QueryParameter(bodyString); + return requestBody; } private HttpResponse respondResource(HttpRequest httpRequest) throws IOException { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java index 95e6f30e0d..609a093390 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java @@ -2,7 +2,7 @@ import java.net.URI; -public record HttpRequest(String startLine, Header header, QueryParameter body) { +public record HttpRequest(String startLine, Header header, char[] body) { public HttpMethod getMethod() { return HttpMethod.from(startLine.split(" ")[0]); @@ -15,4 +15,16 @@ public URI getUri() { public HttpVersion getHttpVersion() { return HttpVersion.from(startLine.split(" ")[2]); } + + public QueryParameter getQueryParameter() { + return new QueryParameter(getUri().getQuery()); + } + + public boolean hasNotApplicationXW3FormUrlEncodedBody() { + String requestContentType = header.get(HttpHeaderKey.CONTENT_TYPE) + .orElse(ContentType.PLAIN.getName()); + ContentType contentType = ContentType.from(requestContentType); + + return !contentType.isApplicationXW3FormUrlEncoded(); + } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/QueryParameter.java b/tomcat/src/main/java/org/apache/coyote/http11/QueryParameter.java index 052cb07319..6acffc7aca 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/QueryParameter.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/QueryParameter.java @@ -11,6 +11,10 @@ public class QueryParameter { private final Map queryParameter = new HashMap<>(); + public QueryParameter(char[] body) { + this(new String(body)); + } + public QueryParameter(String queryParameter) { if (queryParameter == null) { queryParameter = ""; diff --git a/tomcat/src/test/java/com/techcourse/handler/GetLoginHandlerTest.java b/tomcat/src/test/java/com/techcourse/handler/GetLoginHandlerTest.java index bb72c52050..ae6206c19c 100644 --- a/tomcat/src/test/java/com/techcourse/handler/GetLoginHandlerTest.java +++ b/tomcat/src/test/java/com/techcourse/handler/GetLoginHandlerTest.java @@ -2,7 +2,6 @@ import org.apache.coyote.http11.Header; import org.apache.coyote.http11.HttpRequest; -import org.apache.coyote.http11.QueryParameter; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -31,6 +30,6 @@ void cantHandle() { } private HttpRequest createHttpRequest(String startLine) { - return new HttpRequest(startLine, Header.empty(), new QueryParameter("")); + return new HttpRequest(startLine, Header.empty(), "".toCharArray()); } } diff --git a/tomcat/src/test/java/com/techcourse/handler/GetRegisterHandlerTest.java b/tomcat/src/test/java/com/techcourse/handler/GetRegisterHandlerTest.java index 06925ae35b..9403d4771a 100644 --- a/tomcat/src/test/java/com/techcourse/handler/GetRegisterHandlerTest.java +++ b/tomcat/src/test/java/com/techcourse/handler/GetRegisterHandlerTest.java @@ -2,7 +2,6 @@ import org.apache.coyote.http11.Header; import org.apache.coyote.http11.HttpRequest; -import org.apache.coyote.http11.QueryParameter; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -31,6 +30,6 @@ void cantHandle() { } private HttpRequest createHttpRequest(String startLine) { - return new HttpRequest(startLine, Header.empty(), new QueryParameter("")); + return new HttpRequest(startLine, Header.empty(), "".toCharArray()); } } diff --git a/tomcat/src/test/java/com/techcourse/handler/HelloHandlerTest.java b/tomcat/src/test/java/com/techcourse/handler/HelloHandlerTest.java index 2a362aa2b3..65bb20ca2a 100644 --- a/tomcat/src/test/java/com/techcourse/handler/HelloHandlerTest.java +++ b/tomcat/src/test/java/com/techcourse/handler/HelloHandlerTest.java @@ -2,7 +2,6 @@ import org.apache.coyote.http11.Header; import org.apache.coyote.http11.HttpRequest; -import org.apache.coyote.http11.QueryParameter; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -31,6 +30,6 @@ void cantHandle() { } private HttpRequest createHttpRequest(String startLine) { - return new HttpRequest(startLine, Header.empty(), new QueryParameter("")); + return new HttpRequest(startLine, Header.empty(), "".toCharArray()); } } diff --git a/tomcat/src/test/java/com/techcourse/handler/PostLoginHandlerTest.java b/tomcat/src/test/java/com/techcourse/handler/PostLoginHandlerTest.java index 7af7f4b194..e98f832c5e 100644 --- a/tomcat/src/test/java/com/techcourse/handler/PostLoginHandlerTest.java +++ b/tomcat/src/test/java/com/techcourse/handler/PostLoginHandlerTest.java @@ -2,7 +2,6 @@ import org.apache.coyote.http11.Header; import org.apache.coyote.http11.HttpRequest; -import org.apache.coyote.http11.QueryParameter; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -31,6 +30,6 @@ void cantHandle() { } private HttpRequest createHttpRequest(String startLine) { - return new HttpRequest(startLine, Header.empty(), new QueryParameter("")); + return new HttpRequest(startLine, Header.empty(), "".toCharArray()); } } diff --git a/tomcat/src/test/java/com/techcourse/handler/PostRegisterHandlerTest.java b/tomcat/src/test/java/com/techcourse/handler/PostRegisterHandlerTest.java index 9926607c99..9f29fd37ce 100644 --- a/tomcat/src/test/java/com/techcourse/handler/PostRegisterHandlerTest.java +++ b/tomcat/src/test/java/com/techcourse/handler/PostRegisterHandlerTest.java @@ -2,7 +2,6 @@ import org.apache.coyote.http11.Header; import org.apache.coyote.http11.HttpRequest; -import org.apache.coyote.http11.QueryParameter; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -31,6 +30,6 @@ void cantHandle() { } private HttpRequest createHttpRequest(String startLine) { - return new HttpRequest(startLine, Header.empty(), new QueryParameter("")); + return new HttpRequest(startLine, Header.empty(), "".toCharArray()); } } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/QueryParameterTest.java b/tomcat/src/test/java/org/apache/coyote/http11/QueryParameterTest.java index b14aa0a978..dc65777052 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/QueryParameterTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/QueryParameterTest.java @@ -16,7 +16,7 @@ class QueryParameterTest { @Test @DisplayName("쿼리 파라미터 문자열은 null이 될 수 있다.") void createWithNull() { - QueryParameter queryParameter = new QueryParameter(null); + QueryParameter queryParameter = new QueryParameter((String) null); assertThat(queryParameter.isEmpty()).isTrue(); } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/StaticResourceHandlerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/StaticResourceHandlerTest.java index cb3d1cff6d..11f4140d80 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/StaticResourceHandlerTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/StaticResourceHandlerTest.java @@ -28,6 +28,6 @@ void cantHandle() { } private HttpRequest createHttpRequest(String startLine) { - return new HttpRequest(startLine, Header.empty(), new QueryParameter("")); + return new HttpRequest(startLine, Header.empty(), "".toCharArray()); } } From 3aab4c8d34580a1af489d9647fe8294772c89179 Mon Sep 17 00:00:00 2001 From: le2sky Date: Sun, 8 Sep 2024 18:05:50 +0900 Subject: [PATCH 49/49] =?UTF-8?q?fix:=20from=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EC=97=90=EC=84=9C=20case=20=EB=AC=B4=EC=8B=9C=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=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 --- tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java index 876eb75171..42d95eaa60 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java @@ -11,7 +11,7 @@ public enum HttpMethod { public static HttpMethod from(String value) { return Arrays.stream(values()) - .filter(it -> it.name().equalsIgnoreCase(value)) + .filter(it -> it.name().equals(value)) .findFirst() .orElseThrow(NoSuchElementException::new); }