From 8f80ba84101afd6ff1a823977d7445171c2c8174 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 4 Sep 2023 21:57:40 +0900 Subject: [PATCH 01/42] =?UTF-8?q?feat:=20HTTP=20=ED=99=9C=EC=9A=A9=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=A7=84=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/cachecontrol/CacheWebConfig.java | 10 ++++++++++ .../com/example/etag/EtagFilterConfiguration.java | 14 ++++++++++---- .../com/example/version/CacheBustingWebConfig.java | 5 ++++- study/src/main/resources/application.yml | 3 +++ 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java b/study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java index 305b1f1e1e..177d2786c1 100644 --- a/study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java +++ b/study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java @@ -1,13 +1,23 @@ package cache.com.example.cachecontrol; import org.springframework.context.annotation.Configuration; +import org.springframework.http.CacheControl; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.mvc.WebContentInterceptor; @Configuration public class CacheWebConfig implements WebMvcConfigurer { @Override public void addInterceptors(final InterceptorRegistry registry) { + final CacheControl cacheControl = CacheControl + .noCache() + .cachePrivate(); + + WebContentInterceptor webContentInterceptor = new WebContentInterceptor(); + webContentInterceptor.addCacheMapping(cacheControl, "/**"); + + registry.addInterceptor(webContentInterceptor); } } diff --git a/study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java b/study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java index 41ef7a3d9a..d83bc57da0 100644 --- a/study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java +++ b/study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java @@ -1,12 +1,18 @@ package cache.com.example.etag; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.filter.ShallowEtagHeaderFilter; @Configuration public class EtagFilterConfiguration { -// @Bean -// public FilterRegistrationBean shallowEtagHeaderFilter() { -// return null; -// } + @Bean + public FilterRegistrationBean shallowEtagHeaderFilter() { + final var filterRegistrationBean = new FilterRegistrationBean<>(new ShallowEtagHeaderFilter()); + filterRegistrationBean.addUrlPatterns("/etag"); + filterRegistrationBean.addUrlPatterns("/resources/*"); + return filterRegistrationBean; + } } diff --git a/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java b/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java index 6da6d2c795..57504d8dbd 100644 --- a/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java +++ b/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java @@ -1,7 +1,9 @@ package cache.com.example.version; +import java.util.concurrent.TimeUnit; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; +import org.springframework.http.CacheControl; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -20,6 +22,7 @@ public CacheBustingWebConfig(ResourceVersion version) { @Override public void addResourceHandlers(final ResourceHandlerRegistry registry) { registry.addResourceHandler(PREFIX_STATIC_RESOURCES + "/" + version.getVersion() + "/**") - .addResourceLocations("classpath:/static/"); + .addResourceLocations("classpath:/static/") + .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS).cachePublic()); } } diff --git a/study/src/main/resources/application.yml b/study/src/main/resources/application.yml index 4e8655a962..385c11d5f1 100644 --- a/study/src/main/resources/application.yml +++ b/study/src/main/resources/application.yml @@ -7,3 +7,6 @@ server: max-connections: 1 threads: max: 2 + compression: + enabled: true + min-response-size: 10 From 5cde35312bab4aec8b26c7091206e7acaae1a078 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 4 Sep 2023 23:42:23 +0900 Subject: [PATCH 02/42] =?UTF-8?q?feat:=20HttpVersion=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coyote/http11/request/HttpVersion.java | 32 ++++++++++++++++ .../http11/request/HttpVersionTest.java | 38 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/request/HttpVersion.java create mode 100644 tomcat/src/test/java/org/apache/coyote/http11/request/HttpVersionTest.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpVersion.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpVersion.java new file mode 100644 index 0000000000..a4128a888e --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpVersion.java @@ -0,0 +1,32 @@ +package org.apache.coyote.http11.request; + +import java.util.Arrays; +import org.apache.coyote.http11.exception.Http11Exception; + +/** + * HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT + * . + */ +public enum HttpVersion { + + HTTP_1_0("HTTP/1.0"), + HTTP_1_1("HTTP/1.1"), + ; + + private final String field; + + HttpVersion(final String field) { + this.field = field; + } + + public static HttpVersion from(final String field) { + return Arrays.stream(values()) + .filter(httpVersion -> httpVersion.field.equals(field)) + .findAny() + .orElseThrow(() -> new Http11Exception("입력한 Http Version을 지원할 수 없습니다.")); + } + + public String getField() { + return field; + } +} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/request/HttpVersionTest.java b/tomcat/src/test/java/org/apache/coyote/http11/request/HttpVersionTest.java new file mode 100644 index 0000000000..d3a125e3d2 --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/request/HttpVersionTest.java @@ -0,0 +1,38 @@ +package org.apache.coyote.http11.request; + +import static org.apache.coyote.http11.request.HttpVersion.from; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.apache.coyote.http11.exception.Http11Exception; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class HttpVersionTest { + + @Test + void HttpVersion_문자열에_해당하는_HttpVersion이_없는_경우_예외를_던진다() { + // given + final String version = "HTTP/5.5"; + + // expect + assertThatThrownBy(() -> HttpVersion.from(version)) + .isInstanceOf(Http11Exception.class) + .hasMessage("입력한 Http Version을 지원할 수 없습니다."); + } + + @Test + void HttpVersion_문자열을_입력받아_HttpVersion을_반환한다() { + // given + final String version = "HTTP/1.1"; + + // when + final HttpVersion httpVersion = from(version); + + // then + assertThat(httpVersion).isEqualTo(HttpVersion.HTTP_1_1); + } +} From d36d48db631bd5052b2ef2d85dc5a5cebaac0c2d Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 4 Sep 2023 23:45:05 +0900 Subject: [PATCH 03/42] =?UTF-8?q?feat:=20Http11Exception=EC=9D=84=20?= =?UTF-8?q?=EC=A0=9C=EC=99=B8=ED=95=9C=20CustomException=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../http11/{exception => common}/Http11Exception.java | 2 +- .../java/org/apache/coyote/http11/common/HttpMethod.java | 3 +-- .../coyote/http11/{request => common}/HttpVersion.java | 6 ++---- .../http11/exception/InvalidHttpMethodException.java | 8 -------- .../http11/exception/InvalidRequestLineException.java | 8 -------- .../org/apache/coyote/http11/request/RequestLine.java | 4 ++-- .../org/apache/coyote/http11/common/HttpMethodTest.java | 3 +-- .../http11/{request => common}/HttpVersionTest.java | 7 +++---- .../org/apache/coyote/http11/request/RequestLineTest.java | 4 ++-- 9 files changed, 12 insertions(+), 33 deletions(-) rename tomcat/src/main/java/org/apache/coyote/http11/{exception => common}/Http11Exception.java (76%) rename tomcat/src/main/java/org/apache/coyote/http11/{request => common}/HttpVersion.java (72%) delete mode 100644 tomcat/src/main/java/org/apache/coyote/http11/exception/InvalidHttpMethodException.java delete mode 100644 tomcat/src/main/java/org/apache/coyote/http11/exception/InvalidRequestLineException.java rename tomcat/src/test/java/org/apache/coyote/http11/{request => common}/HttpVersionTest.java (80%) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/exception/Http11Exception.java b/tomcat/src/main/java/org/apache/coyote/http11/common/Http11Exception.java similarity index 76% rename from tomcat/src/main/java/org/apache/coyote/http11/exception/Http11Exception.java rename to tomcat/src/main/java/org/apache/coyote/http11/common/Http11Exception.java index 1339c38abf..cda5599e41 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/exception/Http11Exception.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/Http11Exception.java @@ -1,4 +1,4 @@ -package org.apache.coyote.http11.exception; +package org.apache.coyote.http11.common; public class Http11Exception extends RuntimeException { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpMethod.java b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpMethod.java index e57a21e81c..4b50a84315 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpMethod.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpMethod.java @@ -1,7 +1,6 @@ package org.apache.coyote.http11.common; import java.util.Arrays; -import org.apache.coyote.http11.exception.InvalidHttpMethodException; public enum HttpMethod { GET, @@ -11,6 +10,6 @@ public static HttpMethod from(final String input) { return Arrays.stream(values()) .filter(value -> value.name().equals(input)) .findAny() - .orElseThrow(InvalidHttpMethodException::new); + .orElseThrow(() -> new Http11Exception("올바르지 않은 HttpMethod 형식입니다.")); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpVersion.java b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpVersion.java similarity index 72% rename from tomcat/src/main/java/org/apache/coyote/http11/request/HttpVersion.java rename to tomcat/src/main/java/org/apache/coyote/http11/common/HttpVersion.java index a4128a888e..499090b57d 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpVersion.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpVersion.java @@ -1,14 +1,12 @@ -package org.apache.coyote.http11.request; +package org.apache.coyote.http11.common; import java.util.Arrays; -import org.apache.coyote.http11.exception.Http11Exception; /** * HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT * . */ public enum HttpVersion { - HTTP_1_0("HTTP/1.0"), HTTP_1_1("HTTP/1.1"), ; @@ -23,7 +21,7 @@ public static HttpVersion from(final String field) { return Arrays.stream(values()) .filter(httpVersion -> httpVersion.field.equals(field)) .findAny() - .orElseThrow(() -> new Http11Exception("입력한 Http Version을 지원할 수 없습니다.")); + .orElseThrow(() -> new Http11Exception("해당 Http Version을 지원할 수 없습니다.")); } public String getField() { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/exception/InvalidHttpMethodException.java b/tomcat/src/main/java/org/apache/coyote/http11/exception/InvalidHttpMethodException.java deleted file mode 100644 index c251e74e2b..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/exception/InvalidHttpMethodException.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.apache.coyote.http11.exception; - -public class InvalidHttpMethodException extends Http11Exception { - - public InvalidHttpMethodException() { - super("올바르지 않은 HttpMethod 형식입니다."); - } -} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/exception/InvalidRequestLineException.java b/tomcat/src/main/java/org/apache/coyote/http11/exception/InvalidRequestLineException.java deleted file mode 100644 index 5b30a82b9f..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/exception/InvalidRequestLineException.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.apache.coyote.http11.exception; - -public class InvalidRequestLineException extends Http11Exception { - - public InvalidRequestLineException() { - super("올바르지 않은 RequestLine 형식입니다."); - } -} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestLine.java b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestLine.java index 0e0b152270..9d96e2b2fa 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestLine.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestLine.java @@ -1,7 +1,7 @@ package org.apache.coyote.http11.request; +import org.apache.coyote.http11.common.Http11Exception; import org.apache.coyote.http11.common.HttpMethod; -import org.apache.coyote.http11.exception.InvalidRequestLineException; public class RequestLine { @@ -35,7 +35,7 @@ public static RequestLine from(final String line) { private static void validate(final String[] requestLine) { if (requestLine.length != VALID_REQUEST_LINE_SIZE) { - throw new InvalidRequestLineException(); + throw new Http11Exception("올바르지 않은 RequestLine 형식입니다."); } } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/common/HttpMethodTest.java b/tomcat/src/test/java/org/apache/coyote/http11/common/HttpMethodTest.java index eeb80d192f..fd70394b98 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/common/HttpMethodTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/common/HttpMethodTest.java @@ -2,7 +2,6 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; -import org.apache.coyote.http11.exception.InvalidHttpMethodException; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -18,7 +17,7 @@ class HttpMethodTest { // expect assertThatThrownBy(() -> HttpMethod.from(http)) - .isInstanceOf(InvalidHttpMethodException.class) + .isInstanceOf(Http11Exception.class) .hasMessage("올바르지 않은 HttpMethod 형식입니다."); } } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/request/HttpVersionTest.java b/tomcat/src/test/java/org/apache/coyote/http11/common/HttpVersionTest.java similarity index 80% rename from tomcat/src/test/java/org/apache/coyote/http11/request/HttpVersionTest.java rename to tomcat/src/test/java/org/apache/coyote/http11/common/HttpVersionTest.java index d3a125e3d2..e34a97f147 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/request/HttpVersionTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/common/HttpVersionTest.java @@ -1,10 +1,9 @@ -package org.apache.coyote.http11.request; +package org.apache.coyote.http11.common; -import static org.apache.coyote.http11.request.HttpVersion.from; +import static org.apache.coyote.http11.common.HttpVersion.from; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import org.apache.coyote.http11.exception.Http11Exception; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -21,7 +20,7 @@ class HttpVersionTest { // expect assertThatThrownBy(() -> HttpVersion.from(version)) .isInstanceOf(Http11Exception.class) - .hasMessage("입력한 Http Version을 지원할 수 없습니다."); + .hasMessage("해당 Http Version을 지원할 수 없습니다."); } @Test diff --git a/tomcat/src/test/java/org/apache/coyote/http11/request/RequestLineTest.java b/tomcat/src/test/java/org/apache/coyote/http11/request/RequestLineTest.java index 0798199e60..95c7129f67 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/request/RequestLineTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/request/RequestLineTest.java @@ -5,8 +5,8 @@ import static org.assertj.core.api.Assertions.entry; import static org.junit.jupiter.api.Assertions.assertAll; +import org.apache.coyote.http11.common.Http11Exception; import org.apache.coyote.http11.common.HttpMethod; -import org.apache.coyote.http11.exception.InvalidRequestLineException; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -22,7 +22,7 @@ class RequestLineTest { // expect assertThatThrownBy(() -> RequestLine.from(line)) - .isInstanceOf(InvalidRequestLineException.class) + .isInstanceOf(Http11Exception.class) .hasMessage("올바르지 않은 RequestLine 형식입니다."); } From 2aa52b6e384dccc05a1b43a704d2b8066c36921d Mon Sep 17 00:00:00 2001 From: greeng00se Date: Tue, 5 Sep 2023 00:05:01 +0900 Subject: [PATCH 04/42] =?UTF-8?q?feat:=20RequestLine=EC=97=90=20HttpVersio?= =?UTF-8?q?n=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/apache/coyote/http11/request/RequestLine.java | 9 +++++---- .../apache/coyote/http11/request/RequestLineTest.java | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestLine.java b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestLine.java index 9d96e2b2fa..790f5d01e7 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestLine.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestLine.java @@ -2,6 +2,7 @@ import org.apache.coyote.http11.common.Http11Exception; import org.apache.coyote.http11.common.HttpMethod; +import org.apache.coyote.http11.common.HttpVersion; public class RequestLine { @@ -15,9 +16,9 @@ public class RequestLine { private final HttpMethod httpMethod; private final String uri; - private final String httpVersion; + private final HttpVersion httpVersion; - private RequestLine(final HttpMethod httpMethod, final String uri, final String httpVersion) { + private RequestLine(final HttpMethod httpMethod, final String uri, final HttpVersion httpVersion) { this.httpMethod = httpMethod; this.uri = uri; this.httpVersion = httpVersion; @@ -29,7 +30,7 @@ public static RequestLine from(final String line) { return new RequestLine( HttpMethod.from(requestLine[HTTP_METHOD_INDEX]), requestLine[URI_INDEX], - requestLine[HTTP_VERSION_INDEX] + HttpVersion.from(requestLine[HTTP_VERSION_INDEX]) ); } @@ -59,7 +60,7 @@ public String getUri() { return uri; } - public String getHttpVersion() { + public HttpVersion getHttpVersion() { return httpVersion; } } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/request/RequestLineTest.java b/tomcat/src/test/java/org/apache/coyote/http11/request/RequestLineTest.java index 95c7129f67..8d3c08abbb 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/request/RequestLineTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/request/RequestLineTest.java @@ -1,5 +1,6 @@ package org.apache.coyote.http11.request; +import static org.apache.coyote.http11.common.HttpVersion.HTTP_1_1; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.entry; @@ -38,7 +39,7 @@ class RequestLineTest { assertAll( () -> assertThat(requestLine.getHttpMethod()).isEqualTo(HttpMethod.GET), () -> assertThat(requestLine.getUri()).isEqualTo("/index.html"), - () -> assertThat(requestLine.getHttpVersion()).isEqualTo("HTTP/1.1") + () -> assertThat(requestLine.getHttpVersion()).isEqualTo(HTTP_1_1) ); } From 99c9efb28ebf9d20556aeacb278146d82a56a511 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Tue, 5 Sep 2023 00:14:52 +0900 Subject: [PATCH 05/42] =?UTF-8?q?feat:=20Path=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 8 ++-- .../apache/coyote/http11/request/Path.java | 29 +++++++++++++++ .../coyote/http11/request/QueryString.java | 9 ++--- .../coyote/http11/request/RequestLine.java | 24 +++++------- .../coyote/http11/request/PathTest.java | 37 +++++++++++++++++++ .../http11/request/RequestLineTest.java | 4 +- 6 files changed, 84 insertions(+), 27 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/request/Path.java create mode 100644 tomcat/src/test/java/org/apache/coyote/http11/request/PathTest.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 71a73715f1..bb178068e2 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -98,14 +98,14 @@ private ResponseEntity handleRequest( final RequestHeader requestHeader, final RequestBody requestBody ) { - final String path = requestLine.parseUriWithOutQueryString(); - if (path.equals("/login")) { + final String uri = requestLine.parseUri(); + if (uri.equals("/login")) { return login(requestLine, requestHeader, requestBody); } - if (path.equals("/register")) { + if (uri.equals("/register")) { return register(requestLine, requestBody); } - return new ResponseEntity(HttpStatus.OK, path); + return new ResponseEntity(HttpStatus.OK, uri); } private ResponseEntity login( diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/Path.java b/tomcat/src/main/java/org/apache/coyote/http11/request/Path.java new file mode 100644 index 0000000000..7f049931dd --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/Path.java @@ -0,0 +1,29 @@ +package org.apache.coyote.http11.request; + +public class Path { + + private static final String QUERY_STRING_BEGIN = "?"; + private static final int EMPTY_QUERY_STRING = -1; + + private final String value; + + private Path(final String value) { + this.value = value; + } + + public static Path from(final String path) { + return new Path(path); + } + + public String parseUri() { + final int queryStringIndex = value.indexOf(QUERY_STRING_BEGIN); + if (queryStringIndex == EMPTY_QUERY_STRING) { + return value; + } + return value.substring(0, queryStringIndex); + } + + public QueryString parseQueryString() { + return QueryString.from(value); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/QueryString.java b/tomcat/src/main/java/org/apache/coyote/http11/request/QueryString.java index ad50c1713a..c1f5240f50 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/QueryString.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/QueryString.java @@ -1,5 +1,6 @@ package org.apache.coyote.http11.request; +import static java.util.Collections.emptyMap; import static java.util.stream.Collectors.toMap; import java.util.Arrays; @@ -17,18 +18,14 @@ public class QueryString { private final Map items = new HashMap<>(); - private QueryString() { - this(Map.of()); - } - - public QueryString(final Map items) { + private QueryString(final Map items) { this.items.putAll(items); } public static QueryString from(final String uri) { int queryStringIndex = uri.indexOf(QUERY_STRING_BEGIN); if (queryStringIndex == EMPTY) { - return new QueryString(); + return new QueryString(emptyMap()); } return new QueryString(parseQueryString(uri, queryStringIndex)); } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestLine.java b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestLine.java index 790f5d01e7..7a95766266 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestLine.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestLine.java @@ -11,16 +11,14 @@ public class RequestLine { private static final int HTTP_VERSION_INDEX = 2; private static final String DELIMITER = " "; private static final int VALID_REQUEST_LINE_SIZE = 3; - private static final String QUERY_STRING_BEGIN = "?"; - private static final int EMPTY_QUERY_STRING = -1; private final HttpMethod httpMethod; - private final String uri; + private final Path path; private final HttpVersion httpVersion; - private RequestLine(final HttpMethod httpMethod, final String uri, final HttpVersion httpVersion) { + private RequestLine(final HttpMethod httpMethod, final Path path, final HttpVersion httpVersion) { this.httpMethod = httpMethod; - this.uri = uri; + this.path = path; this.httpVersion = httpVersion; } @@ -29,7 +27,7 @@ public static RequestLine from(final String line) { validate(requestLine); return new RequestLine( HttpMethod.from(requestLine[HTTP_METHOD_INDEX]), - requestLine[URI_INDEX], + Path.from(requestLine[URI_INDEX]), HttpVersion.from(requestLine[HTTP_VERSION_INDEX]) ); } @@ -40,24 +38,20 @@ private static void validate(final String[] requestLine) { } } - public String parseUriWithOutQueryString() { - final int queryStringIndex = uri.indexOf(QUERY_STRING_BEGIN); - if (queryStringIndex == EMPTY_QUERY_STRING) { - return uri; - } - return uri.substring(0, queryStringIndex); + public String parseUri() { + return path.parseUri(); } public QueryString parseQueryString() { - return QueryString.from(uri); + return path.parseQueryString(); } public HttpMethod getHttpMethod() { return httpMethod; } - public String getUri() { - return uri; + public Path getPath() { + return path; } public HttpVersion getHttpVersion() { diff --git a/tomcat/src/test/java/org/apache/coyote/http11/request/PathTest.java b/tomcat/src/test/java/org/apache/coyote/http11/request/PathTest.java new file mode 100644 index 0000000000..53baf5b89e --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/request/PathTest.java @@ -0,0 +1,37 @@ +package org.apache.coyote.http11.request; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class PathTest { + + @Test + void uri를_반환한다() { + // given + final Path path = Path.from("/login?account=gugu&password=password"); + + // expect + assertThat(path.parseUri()).isEqualTo("/login"); + } + + @Test + void queryString을_반환한다() { + // given + final Path path = Path.from("/login?account=gugu&password=password"); + + // when + final QueryString queryString = path.parseQueryString(); + + // then + assertThat(queryString.getItems()).contains( + entry("account", "gugu"), + entry("password", "password") + ); + } +} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/request/RequestLineTest.java b/tomcat/src/test/java/org/apache/coyote/http11/request/RequestLineTest.java index 8d3c08abbb..fc874d5db0 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/request/RequestLineTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/request/RequestLineTest.java @@ -38,7 +38,7 @@ class RequestLineTest { // then assertAll( () -> assertThat(requestLine.getHttpMethod()).isEqualTo(HttpMethod.GET), - () -> assertThat(requestLine.getUri()).isEqualTo("/index.html"), + () -> assertThat(requestLine.parseUri()).isEqualTo("/index.html"), () -> assertThat(requestLine.getHttpVersion()).isEqualTo(HTTP_1_1) ); } @@ -50,7 +50,7 @@ class RequestLineTest { final RequestLine requestLine = RequestLine.from(line); // when - final String result = requestLine.parseUriWithOutQueryString(); + final String result = requestLine.parseUri(); // then assertThat(result).isEqualTo("/login"); From c42531015b9779cc9eeda5e1961cc3178ae58f51 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Tue, 5 Sep 2023 00:53:13 +0900 Subject: [PATCH 06/42] =?UTF-8?q?feat:=20HttpRequest,=20HttpRequestParser?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 46 +++----------- .../coyote/http11/common/Constants.java | 1 + .../coyote/http11/request/HttpRequest.java | 30 +++++++++ .../http11/request/HttpRequestParser.java | 39 ++++++++++++ .../coyote/http11/request/RequestBody.java | 11 +--- .../coyote/http11/request/RequestHeader.java | 5 +- .../http11/request/HttpRequestParserTest.java | 62 +++++++++++++++++++ 7 files changed, 147 insertions(+), 47 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestParser.java create mode 100644 tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestParserTest.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 bb178068e2..30e529c1f8 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -1,7 +1,5 @@ package org.apache.coyote.http11; -import static org.apache.coyote.http11.common.Constants.CRLF; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -18,6 +16,8 @@ import org.apache.coyote.http11.common.HttpStatus; import org.apache.coyote.http11.common.Session; import org.apache.coyote.http11.common.SessionManager; +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.request.HttpRequestParser; import org.apache.coyote.http11.request.RequestBody; import org.apache.coyote.http11.request.RequestHeader; import org.apache.coyote.http11.request.RequestLine; @@ -38,6 +38,7 @@ public class Http11Processor implements Runnable, Processor { private final Socket connection; private final HttpResponseGenerator httpResponseGenerator = new HttpResponseGenerator(); + private final HttpRequestParser httpRequestParser = new HttpRequestParser(); private final SessionManager sessionManager = new SessionManager(); public Http11Processor(final Socket connection) { @@ -55,16 +56,8 @@ public void process(final Socket connection) { try (final InputStream inputStream = connection.getInputStream(); final OutputStream outputStream = connection.getOutputStream(); final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) { - final String firstLine = bufferedReader.readLine(); - if (firstLine == null) { - return; - } - final RequestLine requestLine = RequestLine.from(firstLine); - final RequestHeader requestHeader = readHeader(bufferedReader); - final RequestBody requestBody = readBody(bufferedReader, requestHeader); - - final ResponseEntity responseEntity = handleRequest(requestLine, requestHeader, requestBody); - + final HttpRequest httpRequest = httpRequestParser.parse(bufferedReader); + final ResponseEntity responseEntity = handleRequest(httpRequest); final String response = httpResponseGenerator.generate(responseEntity); outputStream.write(response.getBytes()); outputStream.flush(); @@ -73,31 +66,10 @@ public void process(final Socket connection) { } } - private RequestHeader readHeader(final BufferedReader bufferedReader) throws IOException { - final StringBuilder stringBuilder = new StringBuilder(); - for (String line = bufferedReader.readLine(); !"".equals(line); line = bufferedReader.readLine()) { - stringBuilder.append(line).append(CRLF); - } - return RequestHeader.from(stringBuilder.toString()); - } - - private RequestBody readBody(final BufferedReader bufferedReader, final RequestHeader requestHeader) - throws IOException { - final String contentLength = requestHeader.get("Content-Length"); - if (contentLength == null) { - return RequestBody.empty(); - } - final int length = Integer.parseInt(contentLength); - char[] buffer = new char[length]; - bufferedReader.read(buffer, 0, length); - return RequestBody.from(new String(buffer)); - } - - private ResponseEntity handleRequest( - final RequestLine requestLine, - final RequestHeader requestHeader, - final RequestBody requestBody - ) { + private ResponseEntity handleRequest(final HttpRequest httpRequest) { + final RequestLine requestLine = httpRequest.getRequestLine(); + final RequestHeader requestHeader = httpRequest.getRequestHeader(); + final RequestBody requestBody = httpRequest.getRequestBody(); final String uri = requestLine.parseUri(); if (uri.equals("/login")) { return login(requestLine, requestHeader, requestBody); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/Constants.java b/tomcat/src/main/java/org/apache/coyote/http11/common/Constants.java index be6350538c..ffabe48a83 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/common/Constants.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/Constants.java @@ -4,6 +4,7 @@ public class Constants { public static final String CRLF = "\r\n"; public static final String BLANK = " "; + public static final String EMPTY = ""; private Constants() { } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java new file mode 100644 index 0000000000..8fbddf4eec --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java @@ -0,0 +1,30 @@ +package org.apache.coyote.http11.request; + +public class HttpRequest { + + private final RequestLine requestLine; + private final RequestHeader requestHeader; + private final RequestBody requestBody; + + public HttpRequest( + final RequestLine requestLine, + final RequestHeader requestHeader, + final RequestBody requestBody + ) { + this.requestLine = requestLine; + this.requestHeader = requestHeader; + this.requestBody = requestBody; + } + + public RequestLine getRequestLine() { + return requestLine; + } + + public RequestHeader getRequestHeader() { + return requestHeader; + } + + public RequestBody getRequestBody() { + return requestBody; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestParser.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestParser.java new file mode 100644 index 0000000000..0da15c2d7f --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestParser.java @@ -0,0 +1,39 @@ +package org.apache.coyote.http11.request; + +import static org.apache.coyote.http11.common.Constants.CRLF; +import static org.apache.coyote.http11.common.Constants.EMPTY; + +import java.io.BufferedReader; +import java.io.IOException; + +public class HttpRequestParser { + + public HttpRequest parse(final BufferedReader bufferedReader) throws IOException { + final RequestLine requestLine = RequestLine.from(bufferedReader.readLine()); + final RequestHeader requestHeader = parseRequestHeader(bufferedReader); + final RequestBody requestBody = parseRequestBody(bufferedReader, requestHeader); + return new HttpRequest(requestLine, requestHeader, requestBody); + } + + private RequestHeader parseRequestHeader(final BufferedReader bufferedReader) throws IOException { + final StringBuilder stringBuilder = new StringBuilder(); + for (String line = bufferedReader.readLine(); !EMPTY.equals(line); line = bufferedReader.readLine()) { + stringBuilder.append(line).append(CRLF); + } + return RequestHeader.from(stringBuilder.toString()); + } + + private RequestBody parseRequestBody( + final BufferedReader bufferedReader, + final RequestHeader requestHeader + ) throws IOException { + final String contentLength = requestHeader.get("Content-Length"); + if (contentLength == null) { + return RequestBody.empty(); + } + final int length = Integer.parseInt(contentLength); + char[] buffer = new char[length]; + bufferedReader.read(buffer, 0, length); + return RequestBody.from(new String(buffer)); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java index ec779872aa..7ec10261d1 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java @@ -4,6 +4,7 @@ import static java.util.stream.Collectors.toMap; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -16,25 +17,19 @@ public class RequestBody { private final Map items = new HashMap<>(); - private RequestBody() { - } - private RequestBody(final Map items) { this.items.putAll(items); } public static RequestBody empty() { - return new RequestBody(); + return new RequestBody(Collections.emptyMap()); } public static RequestBody from(final String body) { - if (body.isEmpty()) { - return new RequestBody(); - } return Arrays.stream(body.split(SEPARATOR)) .map(field -> field.split(DELIMITER)) .collect(collectingAndThen( - toMap(field -> field[KEY_INDEX], field -> field[VALUE_INDEX]), + toMap(field -> field[KEY_INDEX].strip(), field -> field[VALUE_INDEX].strip()), RequestBody::new )); } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestHeader.java b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestHeader.java index 44f4c78c71..dbbefe1ce8 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestHeader.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestHeader.java @@ -3,6 +3,7 @@ import static java.util.stream.Collectors.collectingAndThen; import static java.util.stream.Collectors.toMap; import static org.apache.coyote.http11.common.Constants.CRLF; +import static org.apache.coyote.http11.common.Constants.EMPTY; import java.util.Arrays; import java.util.HashMap; @@ -26,13 +27,13 @@ public static RequestHeader from(final String headers) { return Arrays.stream(headers.split(CRLF)) .map(header -> header.split(DELIMITER)) .collect(collectingAndThen( - toMap(header -> header[KEY_INDEX], header -> header[VALUE_INDEX]), + toMap(header -> header[KEY_INDEX].strip(), header -> header[VALUE_INDEX].strip()), RequestHeader::new )); } public HttpCookie parseCookie() { - final String cookie = items.getOrDefault(COOKIE_HEADER, ""); + final String cookie = items.getOrDefault(COOKIE_HEADER, EMPTY); return HttpCookie.from(cookie); } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestParserTest.java b/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestParserTest.java new file mode 100644 index 0000000000..2b7b753c1f --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestParserTest.java @@ -0,0 +1,62 @@ +package org.apache.coyote.http11.request; + +import static org.apache.coyote.http11.common.Constants.CRLF; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; +import static org.junit.jupiter.api.Assertions.assertAll; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import org.apache.coyote.http11.common.HttpMethod; +import org.apache.coyote.http11.common.HttpVersion; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class HttpRequestParserTest { + + private final HttpRequestParser httpRequestParser = new HttpRequestParser(); + + @Test + void Http_요청을_입력받아_HttpRequest를_반환한다() throws IOException { + // given + final String content = "account=gugu&password=password&email=hkkang@woowahan.com"; + final String input = String.join( + CRLF, + "POST /register HTTP/1.1 ", + "Host: localhost:8080 ", + "Content-Length: " + content.getBytes().length, + "", + content + ); + final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(input.getBytes()); + final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(byteArrayInputStream)); + + // when + final HttpRequest httpRequest = httpRequestParser.parse(bufferedReader); + + // then + final RequestLine requestLine = httpRequest.getRequestLine(); + final RequestHeader requestHeader = httpRequest.getRequestHeader(); + final RequestBody requestBody = httpRequest.getRequestBody(); + assertAll( + () -> assertThat(requestLine.getHttpMethod()).isEqualTo(HttpMethod.POST), + () -> assertThat(requestLine.parseUri()).isEqualTo("/register"), + () -> assertThat(requestLine.getHttpVersion()).isEqualTo(HttpVersion.HTTP_1_1), + () -> assertThat(requestHeader.getItems()).contains( + entry("Host", "localhost:8080"), + entry("Content-Length", String.valueOf(content.getBytes().length)) + ), + () -> assertThat(requestBody.getItems()).contains( + entry("account", "gugu"), + entry("password", "password"), + entry("email", "hkkang@woowahan.com") + ) + ); + + } +} From ef65b9204aede112ff3a1c4d327aff1bf312db30 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Thu, 7 Sep 2023 22:29:58 +0900 Subject: [PATCH 07/42] =?UTF-8?q?feat:=20Thread=20=EC=8B=A4=EC=8A=B5=20?= =?UTF-8?q?=EB=82=B4=EC=9A=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 2 +- study/src/main/resources/application.yml | 7 +++-- .../thread/stage0/SynchronizationTest.java | 25 +++++++---------- .../java/thread/stage0/ThreadPoolsTest.java | 28 ++++++++----------- 4 files changed, 28 insertions(+), 34 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d672c0be0a..f5921d32ac 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,4 +34,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: ./gradlew build sonar --info -x :study:build + run: ./gradlew build codeCoverageReport sonar --info -x :study:build diff --git a/study/src/main/resources/application.yml b/study/src/main/resources/application.yml index 385c11d5f1..17ef63cf79 100644 --- a/study/src/main/resources/application.yml +++ b/study/src/main/resources/application.yml @@ -3,8 +3,11 @@ handlebars: server: tomcat: - accept-count: 1 - max-connections: 1 +# 모든 쓰레드가 사용 중 일 때 들어온 요청이 대기하는 최대 큐의 길이 + accept-count: 2 + # 서버가 유지할 수 있는 최대 Connection의 수 + max-connections: 2 + # 최대 실행 가능 Thread 수 threads: max: 2 compression: diff --git a/study/src/test/java/thread/stage0/SynchronizationTest.java b/study/src/test/java/thread/stage0/SynchronizationTest.java index 0333c18e3b..6297f3dfdf 100644 --- a/study/src/test/java/thread/stage0/SynchronizationTest.java +++ b/study/src/test/java/thread/stage0/SynchronizationTest.java @@ -1,29 +1,24 @@ package thread.stage0; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; /** - * 다중 스레드 환경에서 두 개 이상의 스레드가 변경 가능한(mutable) 공유 데이터를 동시에 업데이트하면 경쟁 조건(race condition)이 발생한다. - * 자바는 공유 데이터에 대한 스레드 접근을 동기화(synchronization)하여 경쟁 조건을 방지한다. - * 동기화된 블록은 하나의 스레드만 접근하여 실행할 수 있다. - * - * Synchronization - * https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html + * 다중 스레드 환경에서 두 개 이상의 스레드가 변경 가능한(mutable) 공유 데이터를 동시에 업데이트하면 경쟁 조건(race condition)이 발생한다. 자바는 공유 데이터에 대한 스레드 접근을 + * 동기화(synchronization)하여 경쟁 조건을 방지한다. 동기화된 블록은 하나의 스레드만 접근하여 실행할 수 있다. + *

+ * Synchronization https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html */ class SynchronizationTest { /** - * 테스트가 성공하도록 SynchronizedMethods 클래스에 동기화를 적용해보자. - * synchronized 키워드에 대하여 찾아보고 적용하면 된다. - * - * Guide to the Synchronized Keyword in Java - * https://www.baeldung.com/java-synchronized + * 테스트가 성공하도록 SynchronizedMethods 클래스에 동기화를 적용해보자. synchronized 키워드에 대하여 찾아보고 적용하면 된다. + *

+ * Guide to the Synchronized Keyword in Java https://www.baeldung.com/java-synchronized */ @Test void testSynchronized() throws InterruptedException { @@ -41,7 +36,7 @@ private static final class SynchronizedMethods { private int sum = 0; - public void calculate() { + public synchronized void calculate() { setSum(getSum() + 1); } diff --git a/study/src/test/java/thread/stage0/ThreadPoolsTest.java b/study/src/test/java/thread/stage0/ThreadPoolsTest.java index 238611ebfe..afe5ff6db2 100644 --- a/study/src/test/java/thread/stage0/ThreadPoolsTest.java +++ b/study/src/test/java/thread/stage0/ThreadPoolsTest.java @@ -1,23 +1,19 @@ package thread.stage0; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import static org.assertj.core.api.Assertions.assertThat; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * 스레드 풀은 무엇이고 어떻게 동작할까? - * 테스트를 통과시키고 왜 해당 결과가 나왔는지 생각해보자. - * - * Thread Pools - * https://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html - * - * Introduction to Thread Pools in Java - * https://www.baeldung.com/thread-pool-java-and-guava + * 스레드 풀은 무엇이고 어떻게 동작할까? 테스트를 통과시키고 왜 해당 결과가 나왔는지 생각해보자. + *

+ * Thread Pools https://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html + *

+ * Introduction to Thread Pools in Java https://www.baeldung.com/thread-pool-java-and-guava */ class ThreadPoolsTest { @@ -31,8 +27,8 @@ void testNewFixedThreadPool() { executor.submit(logWithSleep("hello fixed thread pools")); // 올바른 값으로 바꿔서 테스트를 통과시키자. - final int expectedPoolSize = 0; - final int expectedQueueSize = 0; + final int expectedPoolSize = 2; + final int expectedQueueSize = 1; assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize()); assertThat(expectedQueueSize).isEqualTo(executor.getQueue().size()); @@ -46,7 +42,7 @@ void testNewCachedThreadPool() { executor.submit(logWithSleep("hello cached thread pools")); // 올바른 값으로 바꿔서 테스트를 통과시키자. - final int expectedPoolSize = 0; + final int expectedPoolSize = 3; final int expectedQueueSize = 0; assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize()); From 680320e6869a0e84409c7e3ac103b560ac8304e0 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Fri, 8 Sep 2023 00:29:05 +0900 Subject: [PATCH 08/42] =?UTF-8?q?refactor:=20RequestHeader=20Headers?= =?UTF-8?q?=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 --- .../apache/coyote/http11/Http11Processor.java | 6 +- .../apache/coyote/http11/common/Headers.java | 50 +++++++++++++++ .../coyote/http11/common/HttpCookie.java | 3 +- .../coyote/http11/request/HttpRequest.java | 12 ++-- .../http11/request/HttpRequestParser.java | 18 +++--- .../coyote/http11/request/RequestHeader.java | 47 -------------- .../coyote/http11/common/HeadersTest.java | 61 +++++++++++++++++++ .../http11/request/HttpRequestParserTest.java | 5 +- .../http11/request/RequestHeaderTest.java | 46 -------------- 9 files changed, 135 insertions(+), 113 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/common/Headers.java delete mode 100644 tomcat/src/main/java/org/apache/coyote/http11/request/RequestHeader.java create mode 100644 tomcat/src/test/java/org/apache/coyote/http11/common/HeadersTest.java delete mode 100644 tomcat/src/test/java/org/apache/coyote/http11/request/RequestHeaderTest.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 30e529c1f8..8abf044455 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -11,6 +11,7 @@ import nextstep.jwp.exception.UncheckedServletException; import nextstep.jwp.model.User; import org.apache.coyote.Processor; +import org.apache.coyote.http11.common.Headers; import org.apache.coyote.http11.common.HttpCookie; import org.apache.coyote.http11.common.HttpMethod; import org.apache.coyote.http11.common.HttpStatus; @@ -19,7 +20,6 @@ import org.apache.coyote.http11.request.HttpRequest; import org.apache.coyote.http11.request.HttpRequestParser; import org.apache.coyote.http11.request.RequestBody; -import org.apache.coyote.http11.request.RequestHeader; import org.apache.coyote.http11.request.RequestLine; import org.apache.coyote.http11.response.HttpResponseGenerator; import org.apache.coyote.http11.response.ResponseEntity; @@ -68,7 +68,7 @@ public void process(final Socket connection) { private ResponseEntity handleRequest(final HttpRequest httpRequest) { final RequestLine requestLine = httpRequest.getRequestLine(); - final RequestHeader requestHeader = httpRequest.getRequestHeader(); + final Headers requestHeader = httpRequest.getHeaders(); final RequestBody requestBody = httpRequest.getRequestBody(); final String uri = requestLine.parseUri(); if (uri.equals("/login")) { @@ -82,7 +82,7 @@ private ResponseEntity handleRequest(final HttpRequest httpRequest) { private ResponseEntity login( final RequestLine requestLine, - final RequestHeader requestHeader, + final Headers requestHeader, final RequestBody requestBody ) { if (requestLine.getHttpMethod() == HttpMethod.GET) { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/Headers.java b/tomcat/src/main/java/org/apache/coyote/http11/common/Headers.java new file mode 100644 index 0000000000..6f85855510 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/Headers.java @@ -0,0 +1,50 @@ +package org.apache.coyote.http11.common; + +import static org.apache.coyote.http11.common.Constants.EMPTY; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class Headers { + + private static final String DELIMITER = ": "; + private static final int KEY_INDEX = 0; + private static final int VALUE_INDEX = 1; + private static final String COOKIE_HEADER = "Cookie"; + + private final Map items = new HashMap<>(); + + public Headers() { + this(Collections.emptyMap()); + } + + private Headers(final Map items) { + this.items.putAll(items); + } + + public void addHeader(String line) { + if (line == null || line.isEmpty()) { + return; + } + final String[] header = line.split(DELIMITER); + items.put(header[KEY_INDEX].strip(), header[VALUE_INDEX].strip()); + } + + public void addHeader(final String key, final String value) { + items.put(key, value); + } + + public HttpCookie parseCookie() { + final String cookie = items.getOrDefault(COOKIE_HEADER, EMPTY); + return HttpCookie.from(cookie); + } + + public String get(final String key) { + return items.getOrDefault(key, EMPTY); + } + + public Map getItems() { + return items; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java index 779c9270c5..1c96bb918e 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java @@ -2,6 +2,7 @@ import static java.util.stream.Collectors.collectingAndThen; import static java.util.stream.Collectors.toMap; +import static org.apache.coyote.http11.common.Constants.EMPTY; import java.util.Arrays; import java.util.HashMap; @@ -41,7 +42,7 @@ public void put(final String key, final String value) { } public String get(final String key) { - return items.get(key); + return items.getOrDefault(key, EMPTY); } public String getJSessionId() { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java index 8fbddf4eec..d7cc1bfd9e 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java @@ -1,18 +1,20 @@ package org.apache.coyote.http11.request; +import org.apache.coyote.http11.common.Headers; + public class HttpRequest { private final RequestLine requestLine; - private final RequestHeader requestHeader; + private final Headers headers; private final RequestBody requestBody; public HttpRequest( final RequestLine requestLine, - final RequestHeader requestHeader, + final Headers requestHeader, final RequestBody requestBody ) { this.requestLine = requestLine; - this.requestHeader = requestHeader; + this.headers = requestHeader; this.requestBody = requestBody; } @@ -20,8 +22,8 @@ public RequestLine getRequestLine() { return requestLine; } - public RequestHeader getRequestHeader() { - return requestHeader; + public Headers getHeaders() { + return headers; } public RequestBody getRequestBody() { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestParser.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestParser.java index 0da15c2d7f..f6487c69de 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestParser.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestParser.java @@ -1,34 +1,34 @@ package org.apache.coyote.http11.request; -import static org.apache.coyote.http11.common.Constants.CRLF; import static org.apache.coyote.http11.common.Constants.EMPTY; import java.io.BufferedReader; import java.io.IOException; +import org.apache.coyote.http11.common.Headers; public class HttpRequestParser { public HttpRequest parse(final BufferedReader bufferedReader) throws IOException { final RequestLine requestLine = RequestLine.from(bufferedReader.readLine()); - final RequestHeader requestHeader = parseRequestHeader(bufferedReader); + final Headers requestHeader = parseRequestHeader(bufferedReader); final RequestBody requestBody = parseRequestBody(bufferedReader, requestHeader); return new HttpRequest(requestLine, requestHeader, requestBody); } - private RequestHeader parseRequestHeader(final BufferedReader bufferedReader) throws IOException { - final StringBuilder stringBuilder = new StringBuilder(); + private Headers parseRequestHeader(final BufferedReader bufferedReader) throws IOException { + final Headers headers = new Headers(); for (String line = bufferedReader.readLine(); !EMPTY.equals(line); line = bufferedReader.readLine()) { - stringBuilder.append(line).append(CRLF); + headers.addHeader(line); } - return RequestHeader.from(stringBuilder.toString()); + return headers; } private RequestBody parseRequestBody( final BufferedReader bufferedReader, - final RequestHeader requestHeader + final Headers headers ) throws IOException { - final String contentLength = requestHeader.get("Content-Length"); - if (contentLength == null) { + final String contentLength = headers.get("Content-Length"); + if (contentLength.isEmpty()) { return RequestBody.empty(); } final int length = Integer.parseInt(contentLength); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestHeader.java b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestHeader.java deleted file mode 100644 index dbbefe1ce8..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestHeader.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.apache.coyote.http11.request; - -import static java.util.stream.Collectors.collectingAndThen; -import static java.util.stream.Collectors.toMap; -import static org.apache.coyote.http11.common.Constants.CRLF; -import static org.apache.coyote.http11.common.Constants.EMPTY; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import org.apache.coyote.http11.common.HttpCookie; - -public class RequestHeader { - - private static final String DELIMITER = ": "; - private static final int KEY_INDEX = 0; - private static final int VALUE_INDEX = 1; - private static final String COOKIE_HEADER = "Cookie"; - - private final Map items = new HashMap<>(); - - private RequestHeader(final Map items) { - this.items.putAll(items); - } - - public static RequestHeader from(final String headers) { - return Arrays.stream(headers.split(CRLF)) - .map(header -> header.split(DELIMITER)) - .collect(collectingAndThen( - toMap(header -> header[KEY_INDEX].strip(), header -> header[VALUE_INDEX].strip()), - RequestHeader::new - )); - } - - public HttpCookie parseCookie() { - final String cookie = items.getOrDefault(COOKIE_HEADER, EMPTY); - return HttpCookie.from(cookie); - } - - public String get(final String key) { - return items.get(key); - } - - public Map getItems() { - return items; - } -} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/common/HeadersTest.java b/tomcat/src/test/java/org/apache/coyote/http11/common/HeadersTest.java new file mode 100644 index 0000000000..dc4b1b01dc --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/common/HeadersTest.java @@ -0,0 +1,61 @@ +package org.apache.coyote.http11.common; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class HeadersTest { + + @Test + void 헤더값을_추가한다() { + // given + final Headers headers = new Headers(); + + // when + headers.addHeader("Connection: keep-alive"); + headers.addHeader("Sec-Fetch-Dest: image"); + + // then + assertThat(headers.getItems()).contains( + entry("Connection", "keep-alive"), + entry("Sec-Fetch-Dest", "image") + ); + } + + @Test + void 헤더값을_Key_Value_형식으로_추가한다() { + // given + final Headers headers = new Headers(); + + // when + headers.addHeader("Connection", "keep-alive"); + headers.addHeader("Sec-Fetch-Dest", "image"); + + // then + assertThat(headers.getItems()).contains( + entry("Connection", "keep-alive"), + entry("Sec-Fetch-Dest", "image") + ); + } + + @Test + void 쿠키값을_파싱하여_반환한다() { + // given + final Headers headers = new Headers(); + headers.addHeader("Cookie: yummy_cookie=choco; JSESSIONID=656cef62-e3c4-40bc-a8df-94732920ed46"); + + // when + final HttpCookie httpCookie = headers.parseCookie(); + + // then + assertThat(httpCookie.getItems()).contains( + entry("yummy_cookie", "choco"), + entry("JSESSIONID", "656cef62-e3c4-40bc-a8df-94732920ed46") + ); + } +} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestParserTest.java b/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestParserTest.java index 2b7b753c1f..08e7405ab7 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestParserTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestParserTest.java @@ -9,6 +9,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; +import org.apache.coyote.http11.common.Headers; import org.apache.coyote.http11.common.HttpMethod; import org.apache.coyote.http11.common.HttpVersion; import org.junit.jupiter.api.DisplayNameGeneration; @@ -41,13 +42,13 @@ class HttpRequestParserTest { // then final RequestLine requestLine = httpRequest.getRequestLine(); - final RequestHeader requestHeader = httpRequest.getRequestHeader(); + final Headers headers = httpRequest.getHeaders(); final RequestBody requestBody = httpRequest.getRequestBody(); assertAll( () -> assertThat(requestLine.getHttpMethod()).isEqualTo(HttpMethod.POST), () -> assertThat(requestLine.parseUri()).isEqualTo("/register"), () -> assertThat(requestLine.getHttpVersion()).isEqualTo(HttpVersion.HTTP_1_1), - () -> assertThat(requestHeader.getItems()).contains( + () -> assertThat(headers.getItems()).contains( entry("Host", "localhost:8080"), entry("Content-Length", String.valueOf(content.getBytes().length)) ), diff --git a/tomcat/src/test/java/org/apache/coyote/http11/request/RequestHeaderTest.java b/tomcat/src/test/java/org/apache/coyote/http11/request/RequestHeaderTest.java deleted file mode 100644 index 506d4bb724..0000000000 --- a/tomcat/src/test/java/org/apache/coyote/http11/request/RequestHeaderTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.apache.coyote.http11.request; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; - -import org.apache.coyote.http11.common.HttpCookie; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator; -import org.junit.jupiter.api.Test; - -@SuppressWarnings("NonAsciiCharacters") -@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) -class RequestHeaderTest { - - @Test - void header_문자열을_입력받아_RequestHeader를_반환한다() { - // given - final String header = "Connection: keep-alive\r\nSec-Fetch-Dest: image\r\n"; - - // when - final RequestHeader requestHeader = RequestHeader.from(header); - - // then - assertThat(requestHeader.getItems()).contains( - entry("Connection", "keep-alive"), - entry("Sec-Fetch-Dest", "image") - ); - } - - @Test - void 쿠키값을_파싱하여_반환한다() { - // given - final String header = "Cookie: yummy_cookie=choco; JSESSIONID=656cef62-e3c4-40bc-a8df-94732920ed46\r\n" - + "Sec-Fetch-Dest: image\r\n"; - final RequestHeader requestHeader = RequestHeader.from(header); - - // when - final HttpCookie httpCookie = requestHeader.parseCookie(); - - // then - assertThat(httpCookie.getItems()).contains( - entry("yummy_cookie", "choco"), - entry("JSESSIONID", "656cef62-e3c4-40bc-a8df-94732920ed46") - ); - } -} From 1feabc5368189a1996ce1fad2b066d635cbe5a90 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Fri, 8 Sep 2023 14:39:39 +0900 Subject: [PATCH 09/42] =?UTF-8?q?feat:=20Controller=EC=99=80=20AbstractCon?= =?UTF-8?q?troller=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AbstractController.java | 23 ++++++++ .../catalina/controller/Controller.java | 8 +++ .../coyote/http11/request/HttpRequest.java | 9 ++++ .../http11/request/HttpRequestParserTest.java | 1 - .../http11/request/HttpRequestTest.java | 52 +++++++++++++++++++ 5 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java create mode 100644 tomcat/src/main/java/org/apache/catalina/controller/Controller.java create mode 100644 tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestTest.java diff --git a/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java b/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java new file mode 100644 index 0000000000..b1a305aa6e --- /dev/null +++ b/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java @@ -0,0 +1,23 @@ +package org.apache.catalina.controller; + +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.response.HttpResponse; + +public abstract class AbstractController implements Controller { + + @Override + public void service(HttpRequest request, HttpResponse response) throws Exception { + if (request.isGet()) { + doGet(request, response); + } + if (request.isPost()) { + doPost(request, response); + } + } + + protected void doPost(HttpRequest request, HttpResponse response) throws Exception { + } + + protected void doGet(HttpRequest request, HttpResponse response) throws Exception { + } +} diff --git a/tomcat/src/main/java/org/apache/catalina/controller/Controller.java b/tomcat/src/main/java/org/apache/catalina/controller/Controller.java new file mode 100644 index 0000000000..112d7c0310 --- /dev/null +++ b/tomcat/src/main/java/org/apache/catalina/controller/Controller.java @@ -0,0 +1,8 @@ +package org.apache.catalina.controller; + +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.response.HttpResponse; + +public interface Controller { + void service(HttpRequest request, HttpResponse response) throws Exception; +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java index d7cc1bfd9e..c9b6b9e9e6 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java @@ -1,6 +1,7 @@ package org.apache.coyote.http11.request; import org.apache.coyote.http11.common.Headers; +import org.apache.coyote.http11.common.HttpMethod; public class HttpRequest { @@ -18,6 +19,14 @@ public HttpRequest( this.requestBody = requestBody; } + public boolean isGet() { + return requestLine.getHttpMethod() == HttpMethod.GET; + } + + public boolean isPost() { + return requestLine.getHttpMethod() == HttpMethod.POST; + } + public RequestLine getRequestLine() { return requestLine; } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestParserTest.java b/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestParserTest.java index 08e7405ab7..ac947a4da3 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestParserTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestParserTest.java @@ -58,6 +58,5 @@ class HttpRequestParserTest { entry("email", "hkkang@woowahan.com") ) ); - } } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestTest.java b/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestTest.java new file mode 100644 index 0000000000..3e630fc23e --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestTest.java @@ -0,0 +1,52 @@ +package org.apache.coyote.http11.request; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class HttpRequestTest { + + @Test + void isGet_메서드는_GET_요청인_경우_true를_반환한다() { + // given + final RequestLine requestLine = RequestLine.from("GET /index.html HTTP/1.1 "); + final HttpRequest httpRequest = new HttpRequest(requestLine, null, null); + + // expect + assertThat(httpRequest.isGet()).isTrue(); + } + + @Test + void isGet_메서드는_GET_요청이_아닌_경우_false를_반환한다() { + // given + final RequestLine requestLine = RequestLine.from("POST /register HTTP/1.1 "); + final HttpRequest httpRequest = new HttpRequest(requestLine, null, null); + + // expect + assertThat(httpRequest.isGet()).isFalse(); + } + + @Test + void isPost_메서드는_Post_요청인_경우_true를_반환한다() { + // given + final RequestLine requestLine = RequestLine.from("POST /register HTTP/1.1 "); + final HttpRequest httpRequest = new HttpRequest(requestLine, null, null); + + // expect + assertThat(httpRequest.isPost()).isTrue(); + } + + @Test + void isPost_메서드는_Post_요청이_아닌_경우_true를_반환한다() { + // given + final RequestLine requestLine = RequestLine.from("GET /index.html HTTP/1.1 "); + final HttpRequest httpRequest = new HttpRequest(requestLine, null, null); + + // expect + assertThat(httpRequest.isPost()).isFalse(); + } +} From 9d2d82b38662cb6b71dcf298072f5383d8da3c83 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Fri, 8 Sep 2023 15:44:27 +0900 Subject: [PATCH 10/42] =?UTF-8?q?refactor:=20Session=20catalina=20?= =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=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 --- .../java/org/apache/catalina/Manager.java | 52 ++----------------- .../http11/common => catalina}/Session.java | 2 +- .../common => catalina}/SessionManager.java | 7 ++- .../apache/coyote/http11/Http11Processor.java | 8 +-- .../SessionManagerTest.java | 2 +- .../common => catalina}/SessionTest.java | 2 +- 6 files changed, 15 insertions(+), 58 deletions(-) rename tomcat/src/main/java/org/apache/{coyote/http11/common => catalina}/Session.java (94%) rename tomcat/src/main/java/org/apache/{coyote/http11/common => catalina}/SessionManager.java (76%) rename tomcat/src/test/java/org/apache/{coyote/http11/common => catalina}/SessionManagerTest.java (97%) rename tomcat/src/test/java/org/apache/{coyote/http11/common => catalina}/SessionTest.java (97%) diff --git a/tomcat/src/main/java/org/apache/catalina/Manager.java b/tomcat/src/main/java/org/apache/catalina/Manager.java index e69410f6a9..ecf0f13f80 100644 --- a/tomcat/src/main/java/org/apache/catalina/Manager.java +++ b/tomcat/src/main/java/org/apache/catalina/Manager.java @@ -1,56 +1,10 @@ package org.apache.catalina; -import jakarta.servlet.http.HttpSession; - -import java.io.IOException; - -/** - * A Manager manages the pool of Sessions that are associated with a - * particular Container. Different Manager implementations may support - * value-added features such as the persistent storage of session data, - * as well as migrating sessions for distributable web applications. - *

- * In order for a Manager implementation to successfully operate - * with a Context implementation that implements reloading, it - * must obey the following constraints: - *

    - *
  • Must implement Lifecycle so that the Context can indicate - * that a restart is required. - *
  • Must allow a call to stop() to be followed by a call to - * start() on the same Manager instance. - *
- * - * @author Craig R. McClanahan - */ public interface Manager { - /** - * Add this Session to the set of active Sessions for this Manager. - * - * @param session Session to be added - */ - void add(HttpSession session); + void add(final Session session); - /** - * Return the active Session, associated with this Manager, with the - * specified session id (if any); otherwise return null. - * - * @param id The session id for the session to be returned - * - * @exception IllegalStateException if a new session cannot be - * instantiated for any reason - * @exception IOException if an input/output error occurs while - * processing this request - * - * @return the request session or {@code null} if a session with the - * requested ID could not be found - */ - HttpSession findSession(String id) throws IOException; + Session findSession(final String id); - /** - * Remove this Session from the active Sessions for this Manager. - * - * @param session Session to be removed - */ - void remove(HttpSession session); + void remove(final String id); } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/Session.java b/tomcat/src/main/java/org/apache/catalina/Session.java similarity index 94% rename from tomcat/src/main/java/org/apache/coyote/http11/common/Session.java rename to tomcat/src/main/java/org/apache/catalina/Session.java index 1cb2c12f7e..7b193b7bfa 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/common/Session.java +++ b/tomcat/src/main/java/org/apache/catalina/Session.java @@ -1,4 +1,4 @@ -package org.apache.coyote.http11.common; +package org.apache.catalina; import java.util.HashMap; import java.util.Map; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/SessionManager.java b/tomcat/src/main/java/org/apache/catalina/SessionManager.java similarity index 76% rename from tomcat/src/main/java/org/apache/coyote/http11/common/SessionManager.java rename to tomcat/src/main/java/org/apache/catalina/SessionManager.java index 08d276e37b..9ee7b845b2 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/common/SessionManager.java +++ b/tomcat/src/main/java/org/apache/catalina/SessionManager.java @@ -1,20 +1,23 @@ -package org.apache.coyote.http11.common; +package org.apache.catalina; import java.util.HashMap; import java.util.Map; -public class SessionManager { +public class SessionManager implements Manager { private static final Map SESSIONS = new HashMap<>(); + @Override public void add(final Session session) { SESSIONS.put(session.getId(), session); } + @Override public Session findSession(final String id) { return SESSIONS.get(id); } + @Override public void remove(final String id) { SESSIONS.remove(id); } 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 8abf044455..0a6cb882f3 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -10,13 +10,13 @@ import nextstep.jwp.db.InMemoryUserRepository; import nextstep.jwp.exception.UncheckedServletException; import nextstep.jwp.model.User; +import org.apache.catalina.Session; +import org.apache.catalina.SessionManager; import org.apache.coyote.Processor; import org.apache.coyote.http11.common.Headers; import org.apache.coyote.http11.common.HttpCookie; import org.apache.coyote.http11.common.HttpMethod; import org.apache.coyote.http11.common.HttpStatus; -import org.apache.coyote.http11.common.Session; -import org.apache.coyote.http11.common.SessionManager; import org.apache.coyote.http11.request.HttpRequest; import org.apache.coyote.http11.request.HttpRequestParser; import org.apache.coyote.http11.request.RequestBody; @@ -66,7 +66,7 @@ public void process(final Socket connection) { } } - private ResponseEntity handleRequest(final HttpRequest httpRequest) { + private ResponseEntity handleRequest(final HttpRequest httpRequest) throws IOException { final RequestLine requestLine = httpRequest.getRequestLine(); final Headers requestHeader = httpRequest.getHeaders(); final RequestBody requestBody = httpRequest.getRequestBody(); @@ -84,7 +84,7 @@ private ResponseEntity login( final RequestLine requestLine, final Headers requestHeader, final RequestBody requestBody - ) { + ) throws IOException { if (requestLine.getHttpMethod() == HttpMethod.GET) { final HttpCookie httpCookie = requestHeader.parseCookie(); final Session session = sessionManager.findSession(httpCookie.getJSessionId()); diff --git a/tomcat/src/test/java/org/apache/coyote/http11/common/SessionManagerTest.java b/tomcat/src/test/java/org/apache/catalina/SessionManagerTest.java similarity index 97% rename from tomcat/src/test/java/org/apache/coyote/http11/common/SessionManagerTest.java rename to tomcat/src/test/java/org/apache/catalina/SessionManagerTest.java index 23c59319eb..792dd4b05d 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/common/SessionManagerTest.java +++ b/tomcat/src/test/java/org/apache/catalina/SessionManagerTest.java @@ -1,4 +1,4 @@ -package org.apache.coyote.http11.common; +package org.apache.catalina; import static org.assertj.core.api.Assertions.assertThat; diff --git a/tomcat/src/test/java/org/apache/coyote/http11/common/SessionTest.java b/tomcat/src/test/java/org/apache/catalina/SessionTest.java similarity index 97% rename from tomcat/src/test/java/org/apache/coyote/http11/common/SessionTest.java rename to tomcat/src/test/java/org/apache/catalina/SessionTest.java index af13cbef4d..801a820d04 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/common/SessionTest.java +++ b/tomcat/src/test/java/org/apache/catalina/SessionTest.java @@ -1,4 +1,4 @@ -package org.apache.coyote.http11.common; +package org.apache.catalina; import static org.assertj.core.api.Assertions.assertThat; From 6048c94e900d9d582196df6ab5748d2cd0684fc1 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Fri, 8 Sep 2023 15:47:58 +0900 Subject: [PATCH 11/42] =?UTF-8?q?feat:=20HttpResponse=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coyote/http11/response/HttpResponse.java | 48 ++++++++++++++++++ .../http11/response/HttpResponseTest.java | 50 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java create mode 100644 tomcat/src/test/java/org/apache/coyote/http11/response/HttpResponseTest.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java new file mode 100644 index 0000000000..02006e8f58 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java @@ -0,0 +1,48 @@ +package org.apache.coyote.http11.response; + +import org.apache.coyote.http11.common.Headers; +import org.apache.coyote.http11.common.HttpStatus; +import org.apache.coyote.http11.common.HttpVersion; + +public class HttpResponse { + + private final HttpVersion httpVersion; + private final Headers headers = new Headers(); + private HttpStatus httpStatus; + private String redirect; + + public HttpResponse(HttpVersion httpVersion) { + this.httpVersion = httpVersion; + } + + public HttpResponse addHeader(final String key, final String value) { + headers.addHeader(key, value); + return this; + } + + public HttpResponse setHttpStatus(final HttpStatus httpStatus) { + this.httpStatus = httpStatus; + return this; + } + + public HttpResponse sendRedirect(final String url) { + this.redirect = url; + return this; + } + + public HttpVersion getHttpVersion() { + return httpVersion; + } + + public Headers getHeaders() { + return headers; + } + + public HttpStatus getHttpStatus() { + return httpStatus; + } + + public String getRedirect() { + return redirect; + } +} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/response/HttpResponseTest.java b/tomcat/src/test/java/org/apache/coyote/http11/response/HttpResponseTest.java new file mode 100644 index 0000000000..5097515ecf --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/response/HttpResponseTest.java @@ -0,0 +1,50 @@ +package org.apache.coyote.http11.response; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.coyote.http11.common.HttpStatus; +import org.apache.coyote.http11.common.HttpVersion; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class HttpResponseTest { + + @Test + void 헤더를_추가한다() { + // given + final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1); + + // when + httpResponse.addHeader("HEADER", "HI"); + + // then + assertThat(httpResponse.getHeaders().get("HEADER")).isEqualTo("HI"); + } + + @Test + void httpStatus를_설정한다() { + // given + final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1); + + // when + httpResponse.setHttpStatus(HttpStatus.OK); + + // then + assertThat(httpResponse.getHttpStatus()).isEqualTo(HttpStatus.OK); + } + + @Test + void 리다이렉트_url을_설정한다() { + // given + final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1); + + // when + httpResponse.sendRedirect("hello.html"); + + // then + assertThat(httpResponse.getRedirect()).isEqualTo("hello.html"); + } +} From 3ea8e9c5283aba2f2ff67710e2b35c49ec636782 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Fri, 8 Sep 2023 16:24:41 +0900 Subject: [PATCH 12/42] =?UTF-8?q?refactor:=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=20throws=20Exception=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/apache/catalina/controller/AbstractController.java | 6 +++--- .../java/org/apache/catalina/controller/Controller.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java b/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java index b1a305aa6e..b584aa0d66 100644 --- a/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java +++ b/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java @@ -6,7 +6,7 @@ public abstract class AbstractController implements Controller { @Override - public void service(HttpRequest request, HttpResponse response) throws Exception { + public void service(HttpRequest request, HttpResponse response) { if (request.isGet()) { doGet(request, response); } @@ -15,9 +15,9 @@ public void service(HttpRequest request, HttpResponse response) throws Exception } } - protected void doPost(HttpRequest request, HttpResponse response) throws Exception { + protected void doPost(HttpRequest request, HttpResponse response) { } - protected void doGet(HttpRequest request, HttpResponse response) throws Exception { + protected void doGet(HttpRequest request, HttpResponse response) { } } diff --git a/tomcat/src/main/java/org/apache/catalina/controller/Controller.java b/tomcat/src/main/java/org/apache/catalina/controller/Controller.java index 112d7c0310..bb949b32e3 100644 --- a/tomcat/src/main/java/org/apache/catalina/controller/Controller.java +++ b/tomcat/src/main/java/org/apache/catalina/controller/Controller.java @@ -4,5 +4,5 @@ import org.apache.coyote.http11.response.HttpResponse; public interface Controller { - void service(HttpRequest request, HttpResponse response) throws Exception; + void service(HttpRequest request, HttpResponse response); } From 7096104f9bb96336e4f9bd11f842b2fb977fd737 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Fri, 8 Sep 2023 16:25:01 +0900 Subject: [PATCH 13/42] =?UTF-8?q?feat:=20HttpRequest=20parseCookie=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coyote/http11/request/HttpRequest.java | 5 +++++ .../http11/request/HttpRequestParser.java | 2 +- .../http11/request/HttpRequestTest.java | 20 +++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java index c9b6b9e9e6..e60b93f68e 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java @@ -1,6 +1,7 @@ package org.apache.coyote.http11.request; import org.apache.coyote.http11.common.Headers; +import org.apache.coyote.http11.common.HttpCookie; import org.apache.coyote.http11.common.HttpMethod; public class HttpRequest { @@ -27,6 +28,10 @@ public boolean isPost() { return requestLine.getHttpMethod() == HttpMethod.POST; } + public HttpCookie parseCookie() { + return headers.parseCookie(); + } + public RequestLine getRequestLine() { return requestLine; } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestParser.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestParser.java index f6487c69de..988b675630 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestParser.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestParser.java @@ -29,7 +29,7 @@ private RequestBody parseRequestBody( ) throws IOException { final String contentLength = headers.get("Content-Length"); if (contentLength.isEmpty()) { - return RequestBody.empty(); + return new RequestBody(); } final int length = Integer.parseInt(contentLength); char[] buffer = new char[length]; diff --git a/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestTest.java b/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestTest.java index 3e630fc23e..079203a989 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestTest.java @@ -1,7 +1,10 @@ package org.apache.coyote.http11.request; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; +import org.apache.coyote.http11.common.Headers; +import org.apache.coyote.http11.common.HttpCookie; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -49,4 +52,21 @@ class HttpRequestTest { // expect assertThat(httpRequest.isPost()).isFalse(); } + + @Test + void Cookie를_반환한다() { + // given + final Headers headers = new Headers(); + headers.addHeader("Cookie: yummy_cookie=choco; JSESSIONID=656cef62-e3c4-40bc-a8df-94732920ed46"); + final HttpRequest httpRequest = new HttpRequest(null, headers, null); + + // when + final HttpCookie httpCookie = httpRequest.parseCookie(); + + // then + assertThat(httpCookie.getItems()).contains( + entry("yummy_cookie", "choco"), + entry("JSESSIONID", "656cef62-e3c4-40bc-a8df-94732920ed46") + ); + } } From cadc5bc58a1a690454b217e168201f18493c3cc6 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Fri, 8 Sep 2023 16:25:25 +0900 Subject: [PATCH 14/42] =?UTF-8?q?feat:=20RequestBody=20=EA=B8=B0=EB=B3=B8?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=EC=9E=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/apache/coyote/http11/request/RequestBody.java | 8 ++++---- .../org/apache/coyote/http11/request/RequestBodyTest.java | 7 ------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java index 7ec10261d1..97bbb09817 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java @@ -17,12 +17,12 @@ public class RequestBody { private final Map items = new HashMap<>(); - private RequestBody(final Map items) { - this.items.putAll(items); + public RequestBody() { + this(Collections.emptyMap()); } - public static RequestBody empty() { - return new RequestBody(Collections.emptyMap()); + private RequestBody(final Map items) { + this.items.putAll(items); } public static RequestBody from(final String body) { diff --git a/tomcat/src/test/java/org/apache/coyote/http11/request/RequestBodyTest.java b/tomcat/src/test/java/org/apache/coyote/http11/request/RequestBodyTest.java index bc6ae51778..d9d8fb2068 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/request/RequestBodyTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/request/RequestBodyTest.java @@ -27,13 +27,6 @@ class RequestBodyTest { ); } - @Test - void 빈_RequestBody를_반환한다() { - // expect - final RequestBody requestBody = RequestBody.empty(); - assertThat(requestBody.getItems()).isEmpty(); - } - @Test void key를_입력받아_값을_반환한다() { // given From e46a70efc4d1c3499178c0f853b75040ae27c7cc Mon Sep 17 00:00:00 2001 From: greeng00se Date: Fri, 8 Sep 2023 16:27:24 +0900 Subject: [PATCH 15/42] =?UTF-8?q?feat:=20=EC=84=B8=EC=85=98=20clear=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/apache/catalina/SessionManager.java | 4 ++++ .../apache/catalina/SessionManagerTest.java | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/tomcat/src/main/java/org/apache/catalina/SessionManager.java b/tomcat/src/main/java/org/apache/catalina/SessionManager.java index 9ee7b845b2..55a295ce2e 100644 --- a/tomcat/src/main/java/org/apache/catalina/SessionManager.java +++ b/tomcat/src/main/java/org/apache/catalina/SessionManager.java @@ -21,4 +21,8 @@ public Session findSession(final String id) { public void remove(final String id) { SESSIONS.remove(id); } + + public void clear() { + SESSIONS.clear(); + } } diff --git a/tomcat/src/test/java/org/apache/catalina/SessionManagerTest.java b/tomcat/src/test/java/org/apache/catalina/SessionManagerTest.java index 792dd4b05d..e98245e863 100644 --- a/tomcat/src/test/java/org/apache/catalina/SessionManagerTest.java +++ b/tomcat/src/test/java/org/apache/catalina/SessionManagerTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -12,6 +13,11 @@ class SessionManagerTest { private final SessionManager sessionManager = new SessionManager(); + @BeforeEach + void setUp() { + sessionManager.clear(); + } + @Test void 세션을_추가한다() { // given @@ -49,4 +55,17 @@ class SessionManagerTest { // then assertThat(sessionManager.findSession("helloworld")).isNull(); } + + @Test + void 세션을_초기화한다() { + // given + final Session session = new Session("helloworld"); + sessionManager.add(session); + + // when + sessionManager.clear(); + + // then + assertThat(session.getItems()).isEmpty(); + } } From 978aebf1937064f96e424c82948263f6023cc5b2 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Sat, 9 Sep 2023 00:24:22 +0900 Subject: [PATCH 16/42] =?UTF-8?q?feat:=20LoginController=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jwp/controller/LoginController.java | 55 +++++++++ .../jwp/db/InMemoryUserRepository.java | 10 +- .../coyote/http11/request/RequestBody.java | 3 +- .../coyote/http11/response/HttpResponse.java | 6 + .../jwp/controller/LoginControllerTest.java | 112 ++++++++++++++++++ 5 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 tomcat/src/main/java/nextstep/jwp/controller/LoginController.java create mode 100644 tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java diff --git a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java new file mode 100644 index 0000000000..665b76efd8 --- /dev/null +++ b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java @@ -0,0 +1,55 @@ +package nextstep.jwp.controller; + +import java.util.UUID; +import nextstep.jwp.db.InMemoryUserRepository; +import nextstep.jwp.model.User; +import org.apache.catalina.Session; +import org.apache.catalina.SessionManager; +import org.apache.catalina.controller.AbstractController; +import org.apache.coyote.http11.common.HttpCookie; +import org.apache.coyote.http11.common.HttpStatus; +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.request.RequestBody; +import org.apache.coyote.http11.response.HttpResponse; + +public class LoginController extends AbstractController { + + private static final String ACCOUNT = "account"; + private static final String PASSWORD = "password"; + private static final String INDEX_PAGE = "index.html"; + private static final String LOGIN_PAGE = "login.html"; + private static final String UNAUTHORIZED_PAGE = "401.html"; + + private final SessionManager sessionManager = new SessionManager(); + + @Override + protected void doPost(final HttpRequest request, final HttpResponse response) { + final RequestBody requestBody = request.getRequestBody(); + final String account = requestBody.get(ACCOUNT); + final String password = requestBody.get(PASSWORD); + + final User user = InMemoryUserRepository.findByAccount(account).orElseThrow(); + if (!user.checkPassword(password)) { + response.setHttpStatus(HttpStatus.UNAUTHORIZED).sendRedirect(UNAUTHORIZED_PAGE); + return; + } + + final String uuid = UUID.randomUUID().toString(); + response.setCookie("JSESSIONID", uuid); + final Session session = new Session(uuid); + session.setAttribute("user", user); + sessionManager.add(session); + response.setHttpStatus(HttpStatus.FOUND).sendRedirect(INDEX_PAGE); + } + + @Override + protected void doGet(final HttpRequest request, final HttpResponse response) { + final HttpCookie httpCookie = request.parseCookie(); + final Session session = sessionManager.findSession(httpCookie.getJSessionId()); + if (session != null) { + response.setHttpStatus(HttpStatus.FOUND).sendRedirect(INDEX_PAGE); + return; + } + response.setHttpStatus(HttpStatus.OK).sendRedirect(LOGIN_PAGE); + } +} diff --git a/tomcat/src/main/java/nextstep/jwp/db/InMemoryUserRepository.java b/tomcat/src/main/java/nextstep/jwp/db/InMemoryUserRepository.java index 1ca30e8383..10a7e11d22 100644 --- a/tomcat/src/main/java/nextstep/jwp/db/InMemoryUserRepository.java +++ b/tomcat/src/main/java/nextstep/jwp/db/InMemoryUserRepository.java @@ -1,10 +1,9 @@ package nextstep.jwp.db; -import nextstep.jwp.model.User; - import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import nextstep.jwp.model.User; public class InMemoryUserRepository { @@ -23,5 +22,10 @@ public static Optional findByAccount(String account) { return Optional.ofNullable(database.get(account)); } - private InMemoryUserRepository() {} + public static void clear() { + database.clear(); + } + + private InMemoryUserRepository() { + } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java index 97bbb09817..1dff04fa3a 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java @@ -2,6 +2,7 @@ import static java.util.stream.Collectors.collectingAndThen; import static java.util.stream.Collectors.toMap; +import static org.apache.coyote.http11.common.Constants.EMPTY; import java.util.Arrays; import java.util.Collections; @@ -35,7 +36,7 @@ public static RequestBody from(final String body) { } public String get(final String key) { - return items.get(key); + return items.getOrDefault(key, EMPTY); } public Map getItems() { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java index 02006e8f58..1f64d6ceac 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java @@ -1,6 +1,7 @@ package org.apache.coyote.http11.response; import org.apache.coyote.http11.common.Headers; +import org.apache.coyote.http11.common.HttpCookie; import org.apache.coyote.http11.common.HttpStatus; import org.apache.coyote.http11.common.HttpVersion; @@ -9,6 +10,7 @@ public class HttpResponse { private final HttpVersion httpVersion; private final Headers headers = new Headers(); private HttpStatus httpStatus; + private HttpCookie httpCookie = new HttpCookie(); private String redirect; public HttpResponse(HttpVersion httpVersion) { @@ -30,6 +32,10 @@ public HttpResponse sendRedirect(final String url) { return this; } + public void setCookie(final String key, final String value) { + httpCookie.put(key, value); + } + public HttpVersion getHttpVersion() { return httpVersion; } diff --git a/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java b/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java new file mode 100644 index 0000000000..98eb33d9e2 --- /dev/null +++ b/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java @@ -0,0 +1,112 @@ +package nextstep.jwp.controller; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import java.util.UUID; +import nextstep.jwp.db.InMemoryUserRepository; +import nextstep.jwp.model.User; +import org.apache.catalina.Session; +import org.apache.catalina.SessionManager; +import org.apache.catalina.controller.Controller; +import org.apache.coyote.http11.common.Headers; +import org.apache.coyote.http11.common.HttpStatus; +import org.apache.coyote.http11.common.HttpVersion; +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.request.RequestBody; +import org.apache.coyote.http11.request.RequestLine; +import org.apache.coyote.http11.response.HttpResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class LoginControllerTest { + + private final Controller controller = new LoginController(); + private final SessionManager sessionManager = new SessionManager(); + + @BeforeEach + void setUp() { + sessionManager.clear(); + InMemoryUserRepository.clear(); + } + + @Test + void GET_요청을_받았을_때_세션에_사용자_정보가_없는_경우_login_페이지를_반환하도록_설정한다() { + // given + final RequestLine requestLine = RequestLine.from("GET /login HTTP/1.1"); + final HttpRequest httpRequest = new HttpRequest(requestLine, new Headers(), new RequestBody()); + final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1); + + // when + controller.service(httpRequest, httpResponse); + + // then + assertAll( + () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(HttpStatus.OK), + () -> assertThat(httpResponse.getRedirect()).isEqualTo("login.html") + ); + } + + @Test + void GET_요청을_받았을_때_세션에_사용자_정보가_있는_경우_index_페이지를_반환하도록_설정한다() { + // given + final RequestLine requestLine = RequestLine.from("GET /login HTTP/1.1"); + final Headers header = new Headers(); + final String uuid = UUID.randomUUID().toString(); + header.addHeader("Cookie", "JSESSIONID=" + uuid); + final HttpRequest httpRequest = new HttpRequest(requestLine, header, new RequestBody()); + final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1); + sessionManager.add(new Session(uuid)); + + // when + controller.service(httpRequest, httpResponse); + + // then + assertAll( + () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(HttpStatus.FOUND), + () -> assertThat(httpResponse.getRedirect()).isEqualTo("index.html") + ); + } + + @Test + void POST_요청을_받았을_때_인증이_실패하는_경우_401_UNAUTHORIZED를_반환한다() { + // given + final RequestLine requestLine = RequestLine.from("POST /login HTTP/1.1"); + final RequestBody requestBody = RequestBody.from("account=hello&password=pw"); + final HttpRequest httpRequest = new HttpRequest(requestLine, new Headers(), requestBody); + final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1); + InMemoryUserRepository.save(new User("hello", "world", "email@email.com")); + + // when + controller.service(httpRequest, httpResponse); + + // then + assertAll( + () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(HttpStatus.UNAUTHORIZED), + () -> assertThat(httpResponse.getRedirect()).isEqualTo("401.html") + ); + } + + @Test + void POST_요청을_받았을_때_인증에_성공하는_경우_index_페이지를_반환하도록_설정한다() { + // given + final RequestLine requestLine = RequestLine.from("POST /login HTTP/1.1"); + final RequestBody requestBody = RequestBody.from("account=hello&password=world"); + final HttpRequest httpRequest = new HttpRequest(requestLine, new Headers(), requestBody); + final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1); + InMemoryUserRepository.save(new User("hello", "world", "email@email.com")); + + // when + controller.service(httpRequest, httpResponse); + + // then + assertAll( + () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(HttpStatus.FOUND), + () -> assertThat(httpResponse.getRedirect()).isEqualTo("index.html") + ); + } +} From 858e10e5fe6fae067486f9c1217e52da347f0d1c Mon Sep 17 00:00:00 2001 From: greeng00se Date: Sat, 9 Sep 2023 00:28:33 +0900 Subject: [PATCH 17/42] =?UTF-8?q?feat:=20HomeController=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jwp/controller/HomeController.java | 17 ++++++++ tomcat/src/main/resources/static/home.html | 1 + .../jwp/controller/HomeControllerTest.java | 40 +++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 tomcat/src/main/java/nextstep/jwp/controller/HomeController.java create mode 100644 tomcat/src/main/resources/static/home.html create mode 100644 tomcat/src/test/java/nextstep/jwp/controller/HomeControllerTest.java diff --git a/tomcat/src/main/java/nextstep/jwp/controller/HomeController.java b/tomcat/src/main/java/nextstep/jwp/controller/HomeController.java new file mode 100644 index 0000000000..e1fb71a4cf --- /dev/null +++ b/tomcat/src/main/java/nextstep/jwp/controller/HomeController.java @@ -0,0 +1,17 @@ +package nextstep.jwp.controller; + +import org.apache.catalina.controller.AbstractController; +import org.apache.coyote.http11.common.HttpStatus; +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.response.HttpResponse; + +public class HomeController extends AbstractController { + + private static final String HOME_PAGE = "home.html"; + + @Override + protected void doGet(final HttpRequest request, final HttpResponse response) { + response.setHttpStatus(HttpStatus.OK) + .sendRedirect(HOME_PAGE); + } +} diff --git a/tomcat/src/main/resources/static/home.html b/tomcat/src/main/resources/static/home.html new file mode 100644 index 0000000000..a042389697 --- /dev/null +++ b/tomcat/src/main/resources/static/home.html @@ -0,0 +1 @@ +hello world! diff --git a/tomcat/src/test/java/nextstep/jwp/controller/HomeControllerTest.java b/tomcat/src/test/java/nextstep/jwp/controller/HomeControllerTest.java new file mode 100644 index 0000000000..79517444e6 --- /dev/null +++ b/tomcat/src/test/java/nextstep/jwp/controller/HomeControllerTest.java @@ -0,0 +1,40 @@ +package nextstep.jwp.controller; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import org.apache.catalina.controller.Controller; +import org.apache.coyote.http11.common.Headers; +import org.apache.coyote.http11.common.HttpStatus; +import org.apache.coyote.http11.common.HttpVersion; +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.request.RequestBody; +import org.apache.coyote.http11.request.RequestLine; +import org.apache.coyote.http11.response.HttpResponse; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class HomeControllerTest { + + private final Controller controller = new HomeController(); + + @Test + void GET_요청을_하는_경우_OK_응답을_반환한다() { + // given + final RequestLine requestLine = RequestLine.from("GET / HTTP/1.1"); + final HttpRequest httpRequest = new HttpRequest(requestLine, new Headers(), new RequestBody()); + final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1); + + // when + controller.service(httpRequest, httpResponse); + + // then + assertAll( + () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(HttpStatus.OK), + () -> assertThat(httpResponse.getRedirect()).isEqualTo("home.html") + ); + } +} From 94504e846b430507e753ec6e2d0ded787d31059a Mon Sep 17 00:00:00 2001 From: greeng00se Date: Sat, 9 Sep 2023 01:05:35 +0900 Subject: [PATCH 18/42] =?UTF-8?q?feat:=20RegisterController=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jwp/controller/RegisterController.java | 43 ++++++++++ .../controller/RegisterControllerTest.java | 81 +++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java create mode 100644 tomcat/src/test/java/nextstep/jwp/controller/RegisterControllerTest.java diff --git a/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java b/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java new file mode 100644 index 0000000000..f6dd5bf01d --- /dev/null +++ b/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java @@ -0,0 +1,43 @@ +package nextstep.jwp.controller; + +import nextstep.jwp.db.InMemoryUserRepository; +import nextstep.jwp.model.User; +import org.apache.catalina.controller.AbstractController; +import org.apache.coyote.http11.common.HttpStatus; +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.request.RequestBody; +import org.apache.coyote.http11.response.HttpResponse; + +public class RegisterController extends AbstractController { + + private static final String ACCOUNT = "account"; + private static final String PASSWORD = "password"; + private static final String EMAIL = "email"; + private static final String INDEX_PAGE = "index.html"; + private static final String REGISTER_PAGE = "register.html"; + private static final String CONFLICT_PAGE = "409.html"; + + @Override + protected void doPost(final HttpRequest request, final HttpResponse response) { + final RequestBody requestBody = request.getRequestBody(); + final String account = requestBody.get(ACCOUNT); + + if (InMemoryUserRepository.findByAccount(account).isPresent()) { + response.setHttpStatus(HttpStatus.CONFLICT) + .sendRedirect(CONFLICT_PAGE); + return; + } + + final String password = requestBody.get(PASSWORD); + final String email = requestBody.get(EMAIL); + InMemoryUserRepository.save(new User(account, password, email)); + response.setHttpStatus(HttpStatus.FOUND) + .sendRedirect(INDEX_PAGE); + } + + @Override + protected void doGet(final HttpRequest request, final HttpResponse response) { + response.setHttpStatus(HttpStatus.OK) + .sendRedirect(REGISTER_PAGE); + } +} diff --git a/tomcat/src/test/java/nextstep/jwp/controller/RegisterControllerTest.java b/tomcat/src/test/java/nextstep/jwp/controller/RegisterControllerTest.java new file mode 100644 index 0000000000..f95f5d6648 --- /dev/null +++ b/tomcat/src/test/java/nextstep/jwp/controller/RegisterControllerTest.java @@ -0,0 +1,81 @@ +package nextstep.jwp.controller; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import nextstep.jwp.db.InMemoryUserRepository; +import org.apache.catalina.controller.Controller; +import org.apache.coyote.http11.common.Headers; +import org.apache.coyote.http11.common.HttpStatus; +import org.apache.coyote.http11.common.HttpVersion; +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.request.RequestBody; +import org.apache.coyote.http11.request.RequestLine; +import org.apache.coyote.http11.response.HttpResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class RegisterControllerTest { + + private final Controller controller = new RegisterController(); + + @BeforeEach + void setUp() { + InMemoryUserRepository.clear(); + } + + @Test + void GET_요청을_시_회원가입_페이지를_반환하도록_설정한다() { + // given + final RequestLine requestLine = RequestLine.from("GET /register HTTP/1.1"); + final HttpRequest httpRequest = new HttpRequest(requestLine, new Headers(), new RequestBody()); + final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1); + + // when + controller.service(httpRequest, httpResponse); + + // then + assertAll( + () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(HttpStatus.OK), + () -> assertThat(httpResponse.getRedirect()).isEqualTo("register.html") + ); + } + + @Test + void POST_요청_시_이미_존재하는_회원인_경우_회원가입_페이지를_반환하도록_설정한다() { + // given + final RequestLine requestLine = RequestLine.from("GET /register HTTP/1.1"); + final HttpRequest httpRequest = new HttpRequest(requestLine, new Headers(), new RequestBody()); + final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1); + + // when + controller.service(httpRequest, httpResponse); + + // then + assertAll( + () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(HttpStatus.OK), + () -> assertThat(httpResponse.getRedirect()).isEqualTo("register.html") + ); + } + + @Test + void POST_요청_시_회원가입에_성공하는_경우_index_페이지를_반환하도록_설정한다() { + // given + final RequestLine requestLine = RequestLine.from("POST /register HTTP/1.1"); + final HttpRequest httpRequest = new HttpRequest(requestLine, new Headers(), new RequestBody()); + final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1); + + // when + controller.service(httpRequest, httpResponse); + + // then + assertAll( + () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(HttpStatus.FOUND), + () -> assertThat(httpResponse.getRedirect()).isEqualTo("index.html") + ); + } +} From ac716487cba2055dbc277acdfe8acc162e737bda Mon Sep 17 00:00:00 2001 From: greeng00se Date: Sat, 9 Sep 2023 01:42:25 +0900 Subject: [PATCH 19/42] =?UTF-8?q?feat:=20Static=20Controller=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../catalina/controller/StaticController.java | 30 ++++++++++ .../coyote/http11/common/HttpStatus.java | 1 + .../controller/StaticControllerTest.java | 58 +++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 tomcat/src/main/java/org/apache/catalina/controller/StaticController.java create mode 100644 tomcat/src/test/java/org/apache/catalina/controller/StaticControllerTest.java diff --git a/tomcat/src/main/java/org/apache/catalina/controller/StaticController.java b/tomcat/src/main/java/org/apache/catalina/controller/StaticController.java new file mode 100644 index 0000000000..56485faaa7 --- /dev/null +++ b/tomcat/src/main/java/org/apache/catalina/controller/StaticController.java @@ -0,0 +1,30 @@ +package org.apache.catalina.controller; + +import static org.apache.coyote.http11.common.HttpStatus.NOT_FOUND; +import static org.apache.coyote.http11.common.HttpStatus.OK; + +import java.net.URL; +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.request.RequestLine; +import org.apache.coyote.http11.response.HttpResponse; + +public class StaticController extends AbstractController { + + private static final String STATIC_RESOURCE_PREFIX = "static"; + private static final String NOT_FOUND_PAGE = "/404.html"; + + private final ClassLoader classLoader = ClassLoader.getSystemClassLoader(); + + @Override + protected void doGet(final HttpRequest request, final HttpResponse response) { + final RequestLine requestLine = request.getRequestLine(); + final String uri = requestLine.parseUri(); + + final URL resource = classLoader.getResource(STATIC_RESOURCE_PREFIX + uri); + if (resource == null) { + response.setHttpStatus(NOT_FOUND).sendRedirect(NOT_FOUND_PAGE); + return; + } + response.setHttpStatus(OK).sendRedirect(uri); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpStatus.java b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpStatus.java index cabc6af05f..26eb23d017 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpStatus.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpStatus.java @@ -4,6 +4,7 @@ public enum HttpStatus { OK("200"), FOUND("302"), UNAUTHORIZED("401"), + NOT_FOUND("404"), CONFLICT("409"); private final String code; diff --git a/tomcat/src/test/java/org/apache/catalina/controller/StaticControllerTest.java b/tomcat/src/test/java/org/apache/catalina/controller/StaticControllerTest.java new file mode 100644 index 0000000000..260fa4b9b5 --- /dev/null +++ b/tomcat/src/test/java/org/apache/catalina/controller/StaticControllerTest.java @@ -0,0 +1,58 @@ +package org.apache.catalina.controller; + +import static org.apache.coyote.http11.common.HttpStatus.NOT_FOUND; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import org.apache.coyote.http11.common.Headers; +import org.apache.coyote.http11.common.HttpStatus; +import org.apache.coyote.http11.common.HttpVersion; +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.request.RequestBody; +import org.apache.coyote.http11.request.RequestLine; +import org.apache.coyote.http11.response.HttpResponse; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class +StaticControllerTest { + + private final Controller controller = new StaticController(); + + @Test + void Get_요청시_정적_파일을_반환하도록_설정한다() { + // given + final RequestLine requestLine = RequestLine.from("GET /index.html HTTP/1.1"); + final HttpRequest httpRequest = new HttpRequest(requestLine, new Headers(), new RequestBody()); + final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1); + + // when + controller.service(httpRequest, httpResponse); + + // then + assertAll( + () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(HttpStatus.OK), + () -> assertThat(httpResponse.getRedirect()).isEqualTo("/index.html") + ); + } + + @Test + void Get_요청시_uri에_해당하는_파일이_없는경우_404_NOT_FOUND_페이지를_반환하도록_설정한다() { + // given + final RequestLine requestLine = RequestLine.from("GET /helloworld.html HTTP/1.1"); + final HttpRequest httpRequest = new HttpRequest(requestLine, new Headers(), new RequestBody()); + final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1); + + // when + controller.service(httpRequest, httpResponse); + + // then + assertAll( + () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(NOT_FOUND), + () -> assertThat(httpResponse.getRedirect()).isEqualTo("/404.html") + ); + } +} From 4df9960234499cf756c4a9109f72f78167c28361 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Sat, 9 Sep 2023 01:46:30 +0900 Subject: [PATCH 20/42] =?UTF-8?q?feat:=20RequestMapper=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/apache/catalina/RequestMapper.java | 30 +++++++++++++++++++ .../java/org/apache/coyote/http11/Mapper.java | 8 +++++ 2 files changed, 38 insertions(+) create mode 100644 tomcat/src/main/java/org/apache/catalina/RequestMapper.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/Mapper.java diff --git a/tomcat/src/main/java/org/apache/catalina/RequestMapper.java b/tomcat/src/main/java/org/apache/catalina/RequestMapper.java new file mode 100644 index 0000000000..bc49b9f47b --- /dev/null +++ b/tomcat/src/main/java/org/apache/catalina/RequestMapper.java @@ -0,0 +1,30 @@ +package org.apache.catalina; + +import java.util.HashMap; +import java.util.Map; +import org.apache.catalina.controller.Controller; +import org.apache.coyote.http11.Mapper; +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.request.RequestLine; +import org.apache.coyote.http11.response.HttpResponse; + +public class RequestMapper implements Mapper { + + private final Controller defaultController; + private final Map controllers = new HashMap<>(); + + public RequestMapper(final Controller defaultController) { + this.defaultController = defaultController; + } + + public void addController(final String path, final Controller controller) { + controllers.put(path, controller); + } + + @Override + public void service(final HttpRequest httpRequest, final HttpResponse httpResponse) { + final RequestLine requestLine = httpRequest.getRequestLine(); + final Controller controller = controllers.getOrDefault(requestLine.parseUri(), defaultController); + controller.service(httpRequest, httpResponse); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Mapper.java b/tomcat/src/main/java/org/apache/coyote/http11/Mapper.java new file mode 100644 index 0000000000..b3d0a68056 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/Mapper.java @@ -0,0 +1,8 @@ +package org.apache.coyote.http11; + +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.response.HttpResponse; + +public interface Mapper { + void service(final HttpRequest httpRequest, final HttpResponse httpResponse); +} From a159ff00a26639b60b790448d3297fc2e2bc843a Mon Sep 17 00:00:00 2001 From: greeng00se Date: Sat, 9 Sep 2023 02:59:55 +0900 Subject: [PATCH 21/42] =?UTF-8?q?feat:=20Tomcat=EC=97=90=20Mapper=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tomcat/src/main/java/nextstep/Application.java | 6 ++++++ .../jwp/controller/HomeController.java | 2 +- .../jwp/controller/LoginController.java | 6 +++--- .../org/apache/catalina/RequestMapper.java | 1 + .../apache/catalina/connector/Connector.java | 9 ++++++--- .../controller/AbstractController.java | 6 ++++++ .../org/apache/catalina/startup/Tomcat.java | 18 ++++++++++++++---- .../java/org/apache/coyote/http11/Mapper.java | 3 +++ tomcat/src/main/resources/static/home.html | 2 +- 9 files changed, 41 insertions(+), 12 deletions(-) diff --git a/tomcat/src/main/java/nextstep/Application.java b/tomcat/src/main/java/nextstep/Application.java index 3dd7593507..dbb8601fad 100644 --- a/tomcat/src/main/java/nextstep/Application.java +++ b/tomcat/src/main/java/nextstep/Application.java @@ -1,11 +1,17 @@ package nextstep; +import nextstep.jwp.controller.HomeController; +import nextstep.jwp.controller.LoginController; +import nextstep.jwp.controller.RegisterController; import org.apache.catalina.startup.Tomcat; public class Application { public static void main(String[] args) { final var tomcat = new Tomcat(); + tomcat.addController("/", new HomeController()); + tomcat.addController("/login", new LoginController()); + tomcat.addController("/register", new RegisterController()); tomcat.start(); } } diff --git a/tomcat/src/main/java/nextstep/jwp/controller/HomeController.java b/tomcat/src/main/java/nextstep/jwp/controller/HomeController.java index e1fb71a4cf..def4797c22 100644 --- a/tomcat/src/main/java/nextstep/jwp/controller/HomeController.java +++ b/tomcat/src/main/java/nextstep/jwp/controller/HomeController.java @@ -7,7 +7,7 @@ public class HomeController extends AbstractController { - private static final String HOME_PAGE = "home.html"; + private static final String HOME_PAGE = "/home.html"; @Override protected void doGet(final HttpRequest request, final HttpResponse response) { diff --git a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java index 665b76efd8..f08ff8a9e4 100644 --- a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java +++ b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java @@ -16,9 +16,9 @@ public class LoginController extends AbstractController { private static final String ACCOUNT = "account"; private static final String PASSWORD = "password"; - private static final String INDEX_PAGE = "index.html"; - private static final String LOGIN_PAGE = "login.html"; - private static final String UNAUTHORIZED_PAGE = "401.html"; + private static final String INDEX_PAGE = "/index.html"; + private static final String LOGIN_PAGE = "/login.html"; + private static final String UNAUTHORIZED_PAGE = "/401.html"; private final SessionManager sessionManager = new SessionManager(); diff --git a/tomcat/src/main/java/org/apache/catalina/RequestMapper.java b/tomcat/src/main/java/org/apache/catalina/RequestMapper.java index bc49b9f47b..825b830cc6 100644 --- a/tomcat/src/main/java/org/apache/catalina/RequestMapper.java +++ b/tomcat/src/main/java/org/apache/catalina/RequestMapper.java @@ -17,6 +17,7 @@ public RequestMapper(final Controller defaultController) { this.defaultController = defaultController; } + @Override public void addController(final String path, final Controller controller) { controllers.put(path, controller); } 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 d171bb84a8..3a7bbf1a28 100644 --- a/tomcat/src/main/java/org/apache/catalina/connector/Connector.java +++ b/tomcat/src/main/java/org/apache/catalina/connector/Connector.java @@ -5,6 +5,7 @@ import java.net.ServerSocket; import java.net.Socket; import org.apache.coyote.http11.Http11Processor; +import org.apache.coyote.http11.Mapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,14 +18,16 @@ public class Connector implements Runnable { private final ServerSocket serverSocket; private boolean stopped; + private final Mapper mapper; - public Connector() { - this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT); + public Connector(final Mapper mapper) { + this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, mapper); } - public Connector(final int port, final int acceptCount) { + public Connector(final int port, final int acceptCount, final Mapper mapper) { this.serverSocket = createServerSocket(port, acceptCount); this.stopped = false; + this.mapper = mapper; } private ServerSocket createServerSocket(final int port, final int acceptCount) { diff --git a/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java b/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java index b584aa0d66..e4980b41b6 100644 --- a/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java +++ b/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java @@ -1,18 +1,24 @@ package org.apache.catalina.controller; +import org.apache.coyote.http11.common.HttpStatus; import org.apache.coyote.http11.request.HttpRequest; import org.apache.coyote.http11.response.HttpResponse; public abstract class AbstractController implements Controller { + private static final String NOT_FOUND_PAGE = "/404.html"; + @Override public void service(HttpRequest request, HttpResponse response) { if (request.isGet()) { doGet(request, response); + return; } if (request.isPost()) { doPost(request, response); + return; } + response.setHttpStatus(HttpStatus.NOT_FOUND).sendRedirect(NOT_FOUND_PAGE); } protected void doPost(HttpRequest request, HttpResponse response) { 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..134aecd5ac 100644 --- a/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java +++ b/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java @@ -1,17 +1,27 @@ package org.apache.catalina.startup; +import java.io.IOException; +import org.apache.catalina.RequestMapper; import org.apache.catalina.connector.Connector; +import org.apache.catalina.controller.Controller; +import org.apache.catalina.controller.StaticController; +import org.apache.coyote.http11.Mapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; - public class Tomcat { private static final Logger log = LoggerFactory.getLogger(Tomcat.class); + private final Mapper mapper = new RequestMapper(new StaticController()); + + public void addController(final String path, final Controller controller) { + mapper.addController(path, controller); + } + public void start() { - var connector = new Connector(); + var connector = new Connector(mapper); + connector.start(); try { @@ -19,7 +29,7 @@ public void start() { System.in.read(); } catch (IOException e) { log.error(e.getMessage(), e); - } finally { + } finally{ log.info("web server stop."); connector.stop(); } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Mapper.java b/tomcat/src/main/java/org/apache/coyote/http11/Mapper.java index b3d0a68056..4c1d7b5b5a 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Mapper.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Mapper.java @@ -1,8 +1,11 @@ package org.apache.coyote.http11; +import org.apache.catalina.controller.Controller; import org.apache.coyote.http11.request.HttpRequest; import org.apache.coyote.http11.response.HttpResponse; public interface Mapper { + void addController(String path, Controller controller); + void service(final HttpRequest httpRequest, final HttpResponse httpResponse); } diff --git a/tomcat/src/main/resources/static/home.html b/tomcat/src/main/resources/static/home.html index a042389697..cd0875583a 100644 --- a/tomcat/src/main/resources/static/home.html +++ b/tomcat/src/main/resources/static/home.html @@ -1 +1 @@ -hello world! +Hello world! From 96098be8c1e7954409605ca79ef58d99aa7d0d75 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Sat, 9 Sep 2023 13:33:03 +0900 Subject: [PATCH 22/42] =?UTF-8?q?feat:=20HttpResponseGenerator=20HttpRespo?= =?UTF-8?q?nse=20=EB=B0=9B=EC=95=84=EC=84=9C=20=EB=B0=98=ED=99=98=EA=B0=92?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jwp/controller/LoginController.java | 12 +- .../jwp/controller/RegisterController.java | 7 +- .../apache/catalina/connector/Connector.java | 2 +- .../apache/coyote/http11/Http11Processor.java | 94 +-------- .../apache/coyote/http11/common/Headers.java | 4 + .../coyote/http11/common/HttpCookie.java | 4 + .../http11/common/HttpExtensionType.java | 9 +- .../coyote/http11/response/HttpResponse.java | 4 + .../response/HttpResponseGenerator.java | 94 +++++---- .../coyote/http11/Http11ProcessorTest.java | 190 +----------------- .../coyote/http11/common/HttpCookieTest.java | 9 + .../response/HttpResponseGeneratorTest.java | 48 +---- 12 files changed, 110 insertions(+), 367 deletions(-) diff --git a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java index f08ff8a9e4..382c6d73c1 100644 --- a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java +++ b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java @@ -39,17 +39,21 @@ protected void doPost(final HttpRequest request, final HttpResponse response) { final Session session = new Session(uuid); session.setAttribute("user", user); sessionManager.add(session); - response.setHttpStatus(HttpStatus.FOUND).sendRedirect(INDEX_PAGE); + response.setHttpStatus(HttpStatus.FOUND) + .addHeader("Location", INDEX_PAGE) + .sendRedirect(INDEX_PAGE); } @Override protected void doGet(final HttpRequest request, final HttpResponse response) { final HttpCookie httpCookie = request.parseCookie(); final Session session = sessionManager.findSession(httpCookie.getJSessionId()); - if (session != null) { - response.setHttpStatus(HttpStatus.FOUND).sendRedirect(INDEX_PAGE); + if (session == null) { + response.setHttpStatus(HttpStatus.OK).sendRedirect(LOGIN_PAGE); return; } - response.setHttpStatus(HttpStatus.OK).sendRedirect(LOGIN_PAGE); + response.setHttpStatus(HttpStatus.FOUND) + .addHeader("Location", INDEX_PAGE) + .sendRedirect(INDEX_PAGE); } } diff --git a/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java b/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java index f6dd5bf01d..c11b8e8cd5 100644 --- a/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java +++ b/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java @@ -13,9 +13,9 @@ public class RegisterController extends AbstractController { private static final String ACCOUNT = "account"; private static final String PASSWORD = "password"; private static final String EMAIL = "email"; - private static final String INDEX_PAGE = "index.html"; - private static final String REGISTER_PAGE = "register.html"; - private static final String CONFLICT_PAGE = "409.html"; + private static final String INDEX_PAGE = "/index.html"; + private static final String REGISTER_PAGE = "/register.html"; + private static final String CONFLICT_PAGE = "/409.html"; @Override protected void doPost(final HttpRequest request, final HttpResponse response) { @@ -32,6 +32,7 @@ protected void doPost(final HttpRequest request, final HttpResponse response) { final String email = requestBody.get(EMAIL); InMemoryUserRepository.save(new User(account, password, email)); response.setHttpStatus(HttpStatus.FOUND) + .addHeader("Location", INDEX_PAGE) .sendRedirect(INDEX_PAGE); } 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 3a7bbf1a28..b36c9512ee 100644 --- a/tomcat/src/main/java/org/apache/catalina/connector/Connector.java +++ b/tomcat/src/main/java/org/apache/catalina/connector/Connector.java @@ -68,7 +68,7 @@ private void process(final Socket connection) { if (connection == null) { return; } - var processor = new Http11Processor(connection); + var processor = new Http11Processor(connection, mapper); new Thread(processor).start(); } 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 0a6cb882f3..0d90abe528 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -6,43 +6,27 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; -import java.util.UUID; -import nextstep.jwp.db.InMemoryUserRepository; import nextstep.jwp.exception.UncheckedServletException; -import nextstep.jwp.model.User; -import org.apache.catalina.Session; -import org.apache.catalina.SessionManager; import org.apache.coyote.Processor; -import org.apache.coyote.http11.common.Headers; -import org.apache.coyote.http11.common.HttpCookie; -import org.apache.coyote.http11.common.HttpMethod; -import org.apache.coyote.http11.common.HttpStatus; import org.apache.coyote.http11.request.HttpRequest; import org.apache.coyote.http11.request.HttpRequestParser; -import org.apache.coyote.http11.request.RequestBody; -import org.apache.coyote.http11.request.RequestLine; +import org.apache.coyote.http11.response.HttpResponse; import org.apache.coyote.http11.response.HttpResponseGenerator; -import org.apache.coyote.http11.response.ResponseEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Http11Processor implements Runnable, Processor { private static final Logger LOG = LoggerFactory.getLogger(Http11Processor.class); - private static final String INDEX_PAGE = "/index.html"; - private static final String REGISTER_PAGE = "/register.html"; - private static final String LOGIN_PAGE = "/login.html"; - private static final String ACCOUNT = "account"; - private static final String PASSWORD = "password"; - private static final String EMAIL = "email"; private final Socket connection; - private final HttpResponseGenerator httpResponseGenerator = new HttpResponseGenerator(); + private final Mapper mapper; private final HttpRequestParser httpRequestParser = new HttpRequestParser(); - private final SessionManager sessionManager = new SessionManager(); + private final HttpResponseGenerator httpResponseGenerator = new HttpResponseGenerator(); - public Http11Processor(final Socket connection) { + public Http11Processor(final Socket connection, final Mapper mapper) { this.connection = connection; + this.mapper = mapper; } @Override @@ -57,73 +41,13 @@ public void process(final Socket connection) { final OutputStream outputStream = connection.getOutputStream(); final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) { final HttpRequest httpRequest = httpRequestParser.parse(bufferedReader); - final ResponseEntity responseEntity = handleRequest(httpRequest); - final String response = httpResponseGenerator.generate(responseEntity); - outputStream.write(response.getBytes()); + final HttpResponse httpResponse = new HttpResponse(httpRequest.getRequestLine().getHttpVersion()); + mapper.service(httpRequest, httpResponse); + final String generate = httpResponseGenerator.generate(httpResponse); + outputStream.write(generate.getBytes()); outputStream.flush(); } catch (IOException | UncheckedServletException e) { LOG.error(e.getMessage(), e); } } - - private ResponseEntity handleRequest(final HttpRequest httpRequest) throws IOException { - final RequestLine requestLine = httpRequest.getRequestLine(); - final Headers requestHeader = httpRequest.getHeaders(); - final RequestBody requestBody = httpRequest.getRequestBody(); - final String uri = requestLine.parseUri(); - if (uri.equals("/login")) { - return login(requestLine, requestHeader, requestBody); - } - if (uri.equals("/register")) { - return register(requestLine, requestBody); - } - return new ResponseEntity(HttpStatus.OK, uri); - } - - private ResponseEntity login( - final RequestLine requestLine, - final Headers requestHeader, - final RequestBody requestBody - ) throws IOException { - if (requestLine.getHttpMethod() == HttpMethod.GET) { - final HttpCookie httpCookie = requestHeader.parseCookie(); - final Session session = sessionManager.findSession(httpCookie.getJSessionId()); - if (session != null) { - return new ResponseEntity(HttpStatus.FOUND, INDEX_PAGE); - } - return new ResponseEntity(HttpStatus.OK, LOGIN_PAGE); - } - final String account = requestBody.get(ACCOUNT); - final String password = requestBody.get(PASSWORD); - return InMemoryUserRepository.findByAccount(account) - .filter(user -> user.checkPassword(password)) - .map(this::loginSuccess) - .orElseGet(() -> new ResponseEntity(HttpStatus.UNAUTHORIZED, "/401.html")); - } - - private ResponseEntity loginSuccess(final User user) { - final String uuid = UUID.randomUUID().toString(); - final ResponseEntity responseEntity = new ResponseEntity(HttpStatus.FOUND, INDEX_PAGE); - responseEntity.setJSessionId(uuid); - final Session session = new Session(uuid); - session.setAttribute("user", user); - sessionManager.add(session); - return responseEntity; - } - - private ResponseEntity register(final RequestLine requestLine, final RequestBody requestBody) { - if (requestLine.getHttpMethod() == HttpMethod.GET) { - return new ResponseEntity(HttpStatus.OK, REGISTER_PAGE); - } - final String account = requestBody.get(ACCOUNT); - - if (InMemoryUserRepository.findByAccount(account).isPresent()) { - return new ResponseEntity(HttpStatus.CONFLICT, "/409.html"); - } - - final String password = requestBody.get(PASSWORD); - final String email = requestBody.get(EMAIL); - InMemoryUserRepository.save(new User(account, password, email)); - return new ResponseEntity(HttpStatus.FOUND, INDEX_PAGE); - } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/Headers.java b/tomcat/src/main/java/org/apache/coyote/http11/common/Headers.java index 6f85855510..f2017f7ec7 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/common/Headers.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/Headers.java @@ -44,6 +44,10 @@ public String get(final String key) { return items.getOrDefault(key, EMPTY); } + public boolean isEmpty() { + return items.isEmpty(); + } + public Map getItems() { return items; } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java index 1c96bb918e..6af89c21dc 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java @@ -49,6 +49,10 @@ public String getJSessionId() { return items.get(JSESSION_ID); } + public boolean isEmpty() { + return items.isEmpty(); + } + public Map getItems() { return items; } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpExtensionType.java b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpExtensionType.java index b815f235e9..bfbfeab904 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpExtensionType.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpExtensionType.java @@ -3,10 +3,11 @@ import java.util.Arrays; public enum HttpExtensionType { - HTML(".html", "text/html"), - CSS(".css", "text/css"), - JS(".js", "text/javascript"), - ICO(".ico", "image/svg+xml"), + HTML(".html", "text/html;charset=utf-8 "), + CSS(".css", "text/css; "), + JS(".js", "text/javascript; "), + ICO(".ico", "image/svg+xml; "), + SVG(".svg", "text/html; "), ; private final String extension; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java index 1f64d6ceac..1880dfa83e 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java @@ -51,4 +51,8 @@ public HttpStatus getHttpStatus() { public String getRedirect() { return redirect; } + + public HttpCookie getHttpCookie() { + return httpCookie; + } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseGenerator.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseGenerator.java index 8423da2bbe..1fad3a7982 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseGenerator.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseGenerator.java @@ -2,75 +2,87 @@ import static org.apache.coyote.http11.common.Constants.BLANK; import static org.apache.coyote.http11.common.Constants.CRLF; +import static org.apache.coyote.http11.common.Constants.EMPTY; import java.io.File; import java.io.IOException; import java.net.URL; import java.nio.file.Files; +import java.util.stream.Collectors; +import org.apache.coyote.http11.common.Headers; +import org.apache.coyote.http11.common.HttpCookie; import org.apache.coyote.http11.common.HttpExtensionType; import org.apache.coyote.http11.common.HttpStatus; public class HttpResponseGenerator { + private static final String STATIC_FOLDER_PATH = "static"; private final ClassLoader classLoader = ClassLoader.getSystemClassLoader(); - public String generate(final ResponseEntity responseEntity) throws IOException { - final String uri = responseEntity.getUri(); - if (uri.equals("/")) { - return generateResponse(responseEntity, "Hello world!"); - } - if (responseEntity.getHttpStatus() == HttpStatus.FOUND) { - return generateRedirectResponse(responseEntity); - } - final String responseBody = readStaticFile(uri); - return generateResponse(responseEntity, responseBody); - } - - private String generateResponse(final ResponseEntity responseEntity, final String responseBody) { + public String generate(final HttpResponse httpResponse) throws IOException { + final String responseBody = readStaticFile(httpResponse.getRedirect()); return String.join( CRLF, - generateHttpStatusLine(responseEntity.getHttpStatus()), - generateContentTypeLine(responseEntity.getHttpExtensionType()), - generateContentLengthLine(responseBody), - "", + generateHttpStatusLine(httpResponse), + generateHttpHeadersLine(httpResponse, responseBody), + EMPTY, responseBody ); } - private String generateHttpStatusLine(final HttpStatus httpStatus) { - return String.join(BLANK, "HTTP/1.1", httpStatus.getCode(), httpStatus.name(), ""); + private String readStaticFile(final String uri) throws IOException { + final URL resource = classLoader.getResource(STATIC_FOLDER_PATH + uri); + final File file = new File(resource.getFile()); + return new String(Files.readAllBytes(file.toPath())); + } + + private String generateHttpStatusLine(final HttpResponse httpResponse) { + final HttpStatus httpStatus = httpResponse.getHttpStatus(); + return String.join( + BLANK, + httpResponse.getHttpVersion().getField(), + httpStatus.getCode(), + httpStatus.name(), + EMPTY + ); + } + + private String generateHttpHeadersLine(final HttpResponse httpResponse, final String responseBody) { + final StringBuilder stringBuilder = new StringBuilder() + .append(generateContentTypeLine(HttpExtensionType.from(httpResponse.getRedirect()))) + .append(CRLF) + .append(generateContentLengthLine(responseBody)); + + final Headers headers = httpResponse.getHeaders(); + if (!headers.isEmpty()) { + stringBuilder.append(CRLF).append(generateHeaderLine(headers)); + } + + final HttpCookie httpCookie = httpResponse.getHttpCookie(); + if (!httpCookie.isEmpty()) { + stringBuilder.append(CRLF).append(generateCookieLine(httpCookie)); + } + + return stringBuilder.toString(); } private String generateContentTypeLine(final HttpExtensionType httpExtensionType) { - return "Content-Type: " + httpExtensionType.getContentType() + ";charset=utf-8 "; + return "Content-Type: " + httpExtensionType.getContentType(); } private CharSequence generateContentLengthLine(final String responseBody) { return "Content-Length: " + responseBody.getBytes().length + BLANK; } - private String readStaticFile(final String uri) throws IOException { - final URL resource = classLoader.getResource("static" + uri); - final File file = new File(resource.getFile()); - return new String(Files.readAllBytes(file.toPath())); + private String generateHeaderLine(final Headers headers) { + return headers.getItems().entrySet().stream() + .map(header -> header.getKey() + ": " + header.getValue()) + .collect(Collectors.joining(CRLF)); } - private String generateRedirectResponse(final ResponseEntity responseEntity) { - final HttpStatus httpStatus = responseEntity.getHttpStatus(); - final String firstLine = String.join(BLANK, "HTTP/1.1", httpStatus.getCode(), httpStatus.name(), ""); - return String.join( - CRLF, - firstLine, - "Location: " + responseEntity.getUri(), - generateSetCookieLine(responseEntity) - ); - } - - private String generateSetCookieLine(final ResponseEntity responseEntity) { - final String jsessionid = responseEntity.getHttpCookie().get("JSESSIONID"); - if (jsessionid == null) { - return ""; - } - return "Set-Cookie: JSESSIONID=" + jsessionid + " "; + private String generateCookieLine(final HttpCookie httpCookie) { + return "Set-Cookie: " + httpCookie.getItems().entrySet().stream() + .map(cookie -> cookie.getKey() + "=" + cookie.getValue() + ";") + .collect(Collectors.joining(BLANK)); } } 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 d221405d2d..20eb10ef99 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java @@ -6,9 +6,10 @@ import java.io.IOException; import java.net.URL; import java.nio.file.Files; +import org.apache.catalina.RequestMapper; +import org.apache.catalina.controller.StaticController; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; -import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import support.StubSocket; @@ -16,26 +17,6 @@ @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) class Http11ProcessorTest { - @Test - void process() { - // given - final var socket = new StubSocket(); - final var processor = new Http11Processor(socket); - - // when - processor.process(socket); - - // then - final String expected = String.join("\r\n", - "HTTP/1.1 200 OK ", - "Content-Type: text/html;charset=utf-8 ", - "Content-Length: 12 ", - "", - "Hello world!"); - - assertThat(socket.output()).isEqualTo(expected); - } - @Test void index() throws IOException { // given @@ -47,7 +28,8 @@ void index() throws IOException { ""); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final RequestMapper requestMapper = new RequestMapper(new StaticController()); + final Http11Processor processor = new Http11Processor(socket, requestMapper); // when processor.process(socket); @@ -62,168 +44,4 @@ void index() throws IOException { assertThat(socket.output()).isEqualTo(expected); } - - @Nested - class 로그인 { - - @Test - void 페이지_접속시_200_OK_를_반환한다() throws IOException { - // given - final String httpRequest = String.join("\r\n", - "GET /login HTTP/1.1 ", - "Host: localhost:8080 ", - "Connection: keep-alive ", - "", - ""); - - final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); - - // when - processor.process(socket); - - // then - final URL resource = getClass().getClassLoader().getResource("static/login.html"); - final String responseBody = new String(Files.readAllBytes(new File(resource.getFile()).toPath())); - final String expected = "HTTP/1.1 200 OK \r\n" + - "Content-Type: text/html;charset=utf-8 \r\n" + - "Content-Length: " + responseBody.getBytes().length + " \r\n" + - "\r\n" + - responseBody; - - assertThat(socket.output()).isEqualTo(expected); - } - - @Test - void 성공_시_302_FOUND를_반환한다() { - // given - final String content = "account=gugu&password=password"; - final String httpRequest = String.join("\r\n", - "POST /login HTTP/1.1 ", - "Host: localhost:8080 ", - "Content-Length: " + content.getBytes().length, - "", - content); - - final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); - - // when - processor.process(socket); - - // then - final String expected = "HTTP/1.1 302 FOUND \r\nLocation: /index.html"; - assertThat(socket.output()).contains(expected); - } - - @Test - void 실패_시_401_UNAUTHORIZE를_반환한다() throws IOException { - // given - final String content = "account=gugu&password=password2"; - final String httpRequest = String.join("\r\n", - "POST /login HTTP/1.1 ", - "Host: localhost:8080 ", - "Content-Length: " + content.getBytes().length, - "", - content); - - final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); - - // when - processor.process(socket); - - // then - final URL resource = getClass().getClassLoader().getResource("static/401.html"); - final String responseBody = new String(Files.readAllBytes(new File(resource.getFile()).toPath())); - final String expected = "HTTP/1.1 401 UNAUTHORIZED \r\n" + - "Content-Type: text/html;charset=utf-8 \r\n" + - "Content-Length: " + responseBody.getBytes().length + " \r\n" + - "\r\n" + - responseBody; - assertThat(socket.output()).isEqualTo(expected); - } - } - - @Nested - class 회원가입 { - - @Test - void 페이지_접속시_200_OK_를_반환한다() throws IOException { - // given - final String httpRequest = String.join("\r\n", - "GET /register HTTP/1.1 ", - "Host: localhost:8080 ", - "Connection: keep-alive ", - "", - ""); - - final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); - - // when - processor.process(socket); - - // then - final URL resource = getClass().getClassLoader().getResource("static/register.html"); - final String responseBody = new String(Files.readAllBytes(new File(resource.getFile()).toPath())); - final String expected = "HTTP/1.1 200 OK \r\n" + - "Content-Type: text/html;charset=utf-8 \r\n" + - "Content-Length: " + responseBody.getBytes().length + " \r\n" + - "\r\n" + - responseBody; - - assertThat(socket.output()).isEqualTo(expected); - } - - @Test - void 성공_시_302_FOUND를_반환한다() { - // given - final String content = "account=gugu2&password=password&email=hkkang@woowahan.com"; - final String httpRequest = String.join("\r\n", - "POST /register HTTP/1.1 ", - "Host: localhost:8080 ", - "Content-Length: " + content.getBytes().length, - "", - content); - - final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); - - // when - processor.process(socket); - - // then - final String expected = "HTTP/1.1 302 FOUND \r\nLocation: /index.html"; - assertThat(socket.output()).contains(expected); - } - - @Test - void 닉네임_중복시_409_CONFLICT를_반환한다() throws IOException { - // given - final String content = "account=gugu&password=password&email=hkkang@woowahan.com"; - final String httpRequest = String.join("\r\n", - "POST /register HTTP/1.1 ", - "Host: localhost:8080 ", - "Content-Length: " + content.getBytes().length, - "", - content); - - final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); - - // when - processor.process(socket); - - // then - final URL resource = getClass().getClassLoader().getResource("static/409.html"); - final String responseBody = new String(Files.readAllBytes(new File(resource.getFile()).toPath())); - final String expected = "HTTP/1.1 409 CONFLICT \r\n" + - "Content-Type: text/html;charset=utf-8 \r\n" + - "Content-Length: " + responseBody.getBytes().length + " \r\n" + - "\r\n" + - responseBody; - assertThat(socket.output()).isEqualTo(expected); - } - } } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/common/HttpCookieTest.java b/tomcat/src/test/java/org/apache/coyote/http11/common/HttpCookieTest.java index e0b24f04b6..4ebd6ca92e 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/common/HttpCookieTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/common/HttpCookieTest.java @@ -60,4 +60,13 @@ class HttpCookieTest { // expect assertThat(cookie.getJSessionId()).isEqualTo(uuid); } + + @Test + void cookie가_비어있는지_확인한다() { + // given + final HttpCookie cookie = new HttpCookie(); + + // expect + assertThat(cookie.isEmpty()).isTrue(); + } } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/response/HttpResponseGeneratorTest.java b/tomcat/src/test/java/org/apache/coyote/http11/response/HttpResponseGeneratorTest.java index 409e67bea8..8aabf4c28e 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/response/HttpResponseGeneratorTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/response/HttpResponseGeneratorTest.java @@ -7,6 +7,7 @@ import java.net.URL; import java.nio.file.Files; import org.apache.coyote.http11.common.HttpStatus; +import org.apache.coyote.http11.common.HttpVersion; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -17,53 +18,15 @@ class HttpResponseGeneratorTest { private final HttpResponseGenerator httpResponseGenerator = new HttpResponseGenerator(); - @Test - void 입력받은_ResponseEntity의_uri가_루트인_경우_Hello_world가_담긴_HttpResponse를_반환한다() throws IOException { - // given - final ResponseEntity responseEntity = new ResponseEntity(HttpStatus.OK, "/"); - - // when - final String actual = httpResponseGenerator.generate(responseEntity); - - // then - final String expected = String.join( - "\r\n", - "HTTP/1.1 200 OK ", - "Content-Type: text/html;charset=utf-8 ", - "Content-Length: 12 ", - "", - "Hello world!" - ); - assertThat(actual).isEqualTo(expected); - } - - @Test - void 입력받은_HttpStatus에_따라_HttpResponse의_상태가_달라진다() throws IOException { - // given - final ResponseEntity responseEntity = new ResponseEntity(HttpStatus.UNAUTHORIZED, "/"); - - // when - final String actual = httpResponseGenerator.generate(responseEntity); - - // then - final String expected = String.join( - "\r\n", - "HTTP/1.1 401 UNAUTHORIZED ", - "Content-Type: text/html;charset=utf-8 ", - "Content-Length: 12 ", - "", - "Hello world!" - ); - assertThat(actual).isEqualTo(expected); - } - @Test void 입력받은_uri에_해당하는_파일을_읽어와_HttpResponse를_반환한다() throws IOException { // given - final ResponseEntity responseEntity = new ResponseEntity(HttpStatus.OK, "/index.html"); + final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1) + .setHttpStatus(HttpStatus.OK) + .sendRedirect("/index.html"); // when - final String actual = httpResponseGenerator.generate(responseEntity); + final String actual = httpResponseGenerator.generate(httpResponse); // then final URL resource = getClass().getClassLoader().getResource("static/index.html"); @@ -72,7 +35,6 @@ class HttpResponseGeneratorTest { "Content-Length: 5564 \r\n" + "\r\n" + new String(Files.readAllBytes(new File(resource.getFile()).toPath())); - assertThat(actual).isEqualTo(expected); } } From e6e0a9ebd8b8054dfcc7ac8b6f42bb6c1f3efca9 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Sat, 9 Sep 2023 13:45:26 +0900 Subject: [PATCH 23/42] =?UTF-8?q?feat:=20=EC=95=8C=20=EC=88=98=20=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EC=98=88=EC=99=B8=EA=B0=80=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20Internal=20Server=20E?= =?UTF-8?q?rror=20=EB=B0=98=ED=99=98=ED=95=98=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 24 ++++++++++++++++++- .../coyote/http11/common/HttpStatus.java | 3 ++- 2 files changed, 25 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 0d90abe528..011d0675cd 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,8 @@ import java.net.Socket; import nextstep.jwp.exception.UncheckedServletException; import org.apache.coyote.Processor; +import org.apache.coyote.http11.common.HttpStatus; +import org.apache.coyote.http11.common.HttpVersion; import org.apache.coyote.http11.request.HttpRequest; import org.apache.coyote.http11.request.HttpRequestParser; import org.apache.coyote.http11.response.HttpResponse; @@ -40,14 +42,34 @@ public void process(final Socket connection) { try (final InputStream inputStream = connection.getInputStream(); final OutputStream outputStream = connection.getOutputStream(); final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) { + execute(bufferedReader, outputStream); + } catch (final IOException | UncheckedServletException e) { + LOG.error(e.getMessage(), e); + } + } + + private void execute(final BufferedReader bufferedReader, final OutputStream outputStream) throws IOException { + try { final HttpRequest httpRequest = httpRequestParser.parse(bufferedReader); final HttpResponse httpResponse = new HttpResponse(httpRequest.getRequestLine().getHttpVersion()); + mapper.service(httpRequest, httpResponse); + final String generate = httpResponseGenerator.generate(httpResponse); outputStream.write(generate.getBytes()); outputStream.flush(); - } catch (IOException | UncheckedServletException e) { + } catch (final Exception e) { LOG.error(e.getMessage(), e); + internalServerError(outputStream); } } + + private void internalServerError(final OutputStream outputStream) throws IOException { + final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1) + .setHttpStatus(HttpStatus.INTERNAL_SERVER_ERROR) + .sendRedirect("/500.html"); + final String response = httpResponseGenerator.generate(httpResponse); + outputStream.write(response.getBytes()); + outputStream.flush(); + } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpStatus.java b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpStatus.java index 26eb23d017..4a9ee5facb 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpStatus.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpStatus.java @@ -5,7 +5,8 @@ public enum HttpStatus { FOUND("302"), UNAUTHORIZED("401"), NOT_FOUND("404"), - CONFLICT("409"); + CONFLICT("409"), + INTERNAL_SERVER_ERROR("500"); private final String code; From ff4a5c3c1829c62e87519fa1a338c90d519e8e23 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Sat, 9 Sep 2023 13:48:18 +0900 Subject: [PATCH 24/42] =?UTF-8?q?refactor:=20=ED=86=B0=EC=BA=A3=20?= =?UTF-8?q?=EC=8B=A4=ED=96=89=20=EB=B6=80=EB=B6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tomcat/src/main/java/nextstep/Application.java | 10 +++++----- .../main/java/org/apache/catalina/startup/Tomcat.java | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/tomcat/src/main/java/nextstep/Application.java b/tomcat/src/main/java/nextstep/Application.java index dbb8601fad..5dad6e00d2 100644 --- a/tomcat/src/main/java/nextstep/Application.java +++ b/tomcat/src/main/java/nextstep/Application.java @@ -8,10 +8,10 @@ public class Application { public static void main(String[] args) { - final var tomcat = new Tomcat(); - tomcat.addController("/", new HomeController()); - tomcat.addController("/login", new LoginController()); - tomcat.addController("/register", new RegisterController()); - tomcat.start(); + new Tomcat() + .addController("/", new HomeController()) + .addController("/login", new LoginController()) + .addController("/register", new RegisterController()) + .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 134aecd5ac..e8dd710707 100644 --- a/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java +++ b/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java @@ -15,8 +15,9 @@ public class Tomcat { private final Mapper mapper = new RequestMapper(new StaticController()); - public void addController(final String path, final Controller controller) { + public Tomcat addController(final String path, final Controller controller) { mapper.addController(path, controller); + return this; } public void start() { From 44580862f8cb4e4bf0d2f347ed0b10bc12252e36 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Sat, 9 Sep 2023 13:53:12 +0900 Subject: [PATCH 25/42] =?UTF-8?q?test:=20redirect=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20=EA=B2=80=EC=A6=9D=EB=B6=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/jwp/controller/HomeControllerTest.java | 2 +- .../java/nextstep/jwp/controller/LoginControllerTest.java | 8 ++++---- .../nextstep/jwp/controller/RegisterControllerTest.java | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tomcat/src/test/java/nextstep/jwp/controller/HomeControllerTest.java b/tomcat/src/test/java/nextstep/jwp/controller/HomeControllerTest.java index 79517444e6..bdec91e5bf 100644 --- a/tomcat/src/test/java/nextstep/jwp/controller/HomeControllerTest.java +++ b/tomcat/src/test/java/nextstep/jwp/controller/HomeControllerTest.java @@ -34,7 +34,7 @@ class HomeControllerTest { // then assertAll( () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(HttpStatus.OK), - () -> assertThat(httpResponse.getRedirect()).isEqualTo("home.html") + () -> assertThat(httpResponse.getRedirect()).isEqualTo("/home.html") ); } } diff --git a/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java b/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java index 98eb33d9e2..d39eb1971a 100644 --- a/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java +++ b/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java @@ -47,7 +47,7 @@ void setUp() { // then assertAll( () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(HttpStatus.OK), - () -> assertThat(httpResponse.getRedirect()).isEqualTo("login.html") + () -> assertThat(httpResponse.getRedirect()).isEqualTo("/login.html") ); } @@ -68,7 +68,7 @@ void setUp() { // then assertAll( () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(HttpStatus.FOUND), - () -> assertThat(httpResponse.getRedirect()).isEqualTo("index.html") + () -> assertThat(httpResponse.getRedirect()).isEqualTo("/index.html") ); } @@ -87,7 +87,7 @@ void setUp() { // then assertAll( () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(HttpStatus.UNAUTHORIZED), - () -> assertThat(httpResponse.getRedirect()).isEqualTo("401.html") + () -> assertThat(httpResponse.getRedirect()).isEqualTo("/401.html") ); } @@ -106,7 +106,7 @@ void setUp() { // then assertAll( () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(HttpStatus.FOUND), - () -> assertThat(httpResponse.getRedirect()).isEqualTo("index.html") + () -> assertThat(httpResponse.getRedirect()).isEqualTo("/index.html") ); } } diff --git a/tomcat/src/test/java/nextstep/jwp/controller/RegisterControllerTest.java b/tomcat/src/test/java/nextstep/jwp/controller/RegisterControllerTest.java index f95f5d6648..fb40213504 100644 --- a/tomcat/src/test/java/nextstep/jwp/controller/RegisterControllerTest.java +++ b/tomcat/src/test/java/nextstep/jwp/controller/RegisterControllerTest.java @@ -41,7 +41,7 @@ void setUp() { // then assertAll( () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(HttpStatus.OK), - () -> assertThat(httpResponse.getRedirect()).isEqualTo("register.html") + () -> assertThat(httpResponse.getRedirect()).isEqualTo("/register.html") ); } @@ -58,7 +58,7 @@ void setUp() { // then assertAll( () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(HttpStatus.OK), - () -> assertThat(httpResponse.getRedirect()).isEqualTo("register.html") + () -> assertThat(httpResponse.getRedirect()).isEqualTo("/register.html") ); } @@ -75,7 +75,7 @@ void setUp() { // then assertAll( () -> assertThat(httpResponse.getHttpStatus()).isEqualTo(HttpStatus.FOUND), - () -> assertThat(httpResponse.getRedirect()).isEqualTo("index.html") + () -> assertThat(httpResponse.getRedirect()).isEqualTo("/index.html") ); } } From d7bd9535be86ddffd8fbffb958e6c26a2e7d9c0e Mon Sep 17 00:00:00 2001 From: greeng00se Date: Sat, 9 Sep 2023 13:53:34 +0900 Subject: [PATCH 26/42] =?UTF-8?q?docs:=20README=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 70be385f28..d9bf3fc734 100644 --- a/README.md +++ b/README.md @@ -14,4 +14,11 @@ - [x] 회원가입 페이지의 경우 GET을 사용하여 보여준다. - [x] 회원가입의 경우 POST를 사용한다. - [x] 회원가입을 완료하는 경우 index.html로 리다이렉트한다. - +- [x] 리팩터링을 진행한다. + - [x] HttpRequest 클래스를 구현한다. + - [x] HttpResponse 클래스를 구현한다. + - [x] 알 수 없는 예외 발생시 Internal Server Error 페이지 반환한다. + - [x] Controller 인터페이스를 추가한다. + - [x] Controller를 매핑해주는 클래스를 구현한다. +- [ ] ThreadPoolExecutor를 사용해서 스레드 풀(thread pool) 기능을 추가한다. +- [ ] 세션의 경우 동시성 컬렉션을 사용한다. From fd7a306714a68e1c7b92a697d72068fa320b3e6e Mon Sep 17 00:00:00 2001 From: greeng00se Date: Sat, 9 Sep 2023 16:55:23 +0900 Subject: [PATCH 27/42] =?UTF-8?q?feat:=20=EC=B5=9C=EB=8C=80=20ThreadPool?= =?UTF-8?q?=20=ED=81=AC=EA=B8=B0=20250=EC=9C=BC=EB=A1=9C=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/catalina/connector/Connector.java | 20 +++++++++++-------- .../org/apache/catalina/startup/Tomcat.java | 3 ++- 2 files changed, 14 insertions(+), 9 deletions(-) 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 b36c9512ee..28bd347e61 100644 --- a/tomcat/src/main/java/org/apache/catalina/connector/Connector.java +++ b/tomcat/src/main/java/org/apache/catalina/connector/Connector.java @@ -4,6 +4,8 @@ import java.io.UncheckedIOException; import java.net.ServerSocket; import java.net.Socket; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import org.apache.coyote.http11.Http11Processor; import org.apache.coyote.http11.Mapper; import org.slf4j.Logger; @@ -19,15 +21,17 @@ public class Connector implements Runnable { private final ServerSocket serverSocket; private boolean stopped; private final Mapper mapper; + private final ExecutorService executorService; - public Connector(final Mapper mapper) { - this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, mapper); + public Connector(final Mapper mapper, final int threadCount) { + this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, mapper, threadCount); } - public Connector(final int port, final int acceptCount, final Mapper mapper) { + public Connector(final int port, final int acceptCount, final Mapper mapper, final int threadCount) { this.serverSocket = createServerSocket(port, acceptCount); this.stopped = false; this.mapper = mapper; + this.executorService = Executors.newFixedThreadPool(threadCount); } private ServerSocket createServerSocket(final int port, final int acceptCount) { @@ -41,7 +45,7 @@ private ServerSocket createServerSocket(final int port, final int acceptCount) { } public void start() { - var thread = new Thread(this); + final Thread thread = new Thread(this); thread.setDaemon(true); thread.start(); stopped = false; @@ -68,8 +72,8 @@ private void process(final Socket connection) { if (connection == null) { return; } - var processor = new Http11Processor(connection, mapper); - new Thread(processor).start(); + final Http11Processor processor = new Http11Processor(connection, mapper); + executorService.execute(processor); } public void stop() { @@ -82,8 +86,8 @@ public void stop() { } private int checkPort(final int port) { - final var MIN_PORT = 1; - final var MAX_PORT = 65535; + final int MIN_PORT = 1; + final int MAX_PORT = 65535; if (port < MIN_PORT || MAX_PORT < port) { return DEFAULT_PORT; 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 e8dd710707..598a7358bd 100644 --- a/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java +++ b/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java @@ -12,6 +12,7 @@ public class Tomcat { private static final Logger log = LoggerFactory.getLogger(Tomcat.class); + private static final int DEFAULT_THREAD_COUNT = 250; private final Mapper mapper = new RequestMapper(new StaticController()); @@ -21,7 +22,7 @@ public Tomcat addController(final String path, final Controller controller) { } public void start() { - var connector = new Connector(mapper); + final Connector connector = new Connector(mapper, DEFAULT_THREAD_COUNT); connector.start(); From c0c6e36c93b30654eaca9ce1469b7b3040fb4686 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Sat, 9 Sep 2023 17:10:49 +0900 Subject: [PATCH 28/42] =?UTF-8?q?feat:=20ConcurrentHashMap=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- .../jwp/db/InMemoryUserRepository.java | 8 ++++++-- .../src/main/java/nextstep/jwp/model/User.java | 18 +++++++++++++++--- .../org/apache/catalina/SessionManager.java | 4 ++-- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index d9bf3fc734..83658875c0 100644 --- a/README.md +++ b/README.md @@ -20,5 +20,5 @@ - [x] 알 수 없는 예외 발생시 Internal Server Error 페이지 반환한다. - [x] Controller 인터페이스를 추가한다. - [x] Controller를 매핑해주는 클래스를 구현한다. -- [ ] ThreadPoolExecutor를 사용해서 스레드 풀(thread pool) 기능을 추가한다. -- [ ] 세션의 경우 동시성 컬렉션을 사용한다. +- [x] ThreadPoolExecutor를 사용해서 스레드 풀(thread pool) 기능을 추가한다. +- [x] 세션의 경우 동시성 컬렉션을 사용한다. diff --git a/tomcat/src/main/java/nextstep/jwp/db/InMemoryUserRepository.java b/tomcat/src/main/java/nextstep/jwp/db/InMemoryUserRepository.java index 10a7e11d22..65e59b42e5 100644 --- a/tomcat/src/main/java/nextstep/jwp/db/InMemoryUserRepository.java +++ b/tomcat/src/main/java/nextstep/jwp/db/InMemoryUserRepository.java @@ -3,19 +3,23 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; import nextstep.jwp.model.User; public class InMemoryUserRepository { private static final Map database = new ConcurrentHashMap<>(); + private static final long INITIAL_KEY_VALUE = 1L; + private static final AtomicLong id = new AtomicLong(INITIAL_KEY_VALUE); static { - final User user = new User(1L, "gugu", "password", "hkkang@woowahan.com"); + final User user = new User(id.getAndIncrement(), "gugu", "password", "hkkang@woowahan.com"); database.put(user.getAccount(), user); } public static void save(User user) { - database.put(user.getAccount(), user); + final User savedUser = new User(id.getAndIncrement(), user.getAccount(), user.getPassword(), user.getEmail()); + database.put(savedUser.getAccount(), savedUser); } public static Optional findByAccount(String account) { diff --git a/tomcat/src/main/java/nextstep/jwp/model/User.java b/tomcat/src/main/java/nextstep/jwp/model/User.java index 4c2a2cd184..5032a64f2d 100644 --- a/tomcat/src/main/java/nextstep/jwp/model/User.java +++ b/tomcat/src/main/java/nextstep/jwp/model/User.java @@ -7,25 +7,37 @@ public class User { private final String password; private final String email; - public User(Long id, String account, String password, String email) { + public User(final Long id, final String account, final String password, final String email) { this.id = id; this.account = account; this.password = password; this.email = email; } - public User(String account, String password, String email) { + public User(final String account, final String password, final String email) { this(null, account, password, email); } - public boolean checkPassword(String password) { + public boolean checkPassword(final String password) { return this.password.equals(password); } + public Long getId() { + return id; + } + public String getAccount() { return account; } + public String getPassword() { + return password; + } + + public String getEmail() { + return email; + } + @Override public String toString() { return "User{" + diff --git a/tomcat/src/main/java/org/apache/catalina/SessionManager.java b/tomcat/src/main/java/org/apache/catalina/SessionManager.java index 55a295ce2e..a2de9ee9ed 100644 --- a/tomcat/src/main/java/org/apache/catalina/SessionManager.java +++ b/tomcat/src/main/java/org/apache/catalina/SessionManager.java @@ -1,11 +1,11 @@ package org.apache.catalina; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public class SessionManager implements Manager { - private static final Map SESSIONS = new HashMap<>(); + private static final Map SESSIONS = new ConcurrentHashMap<>(); @Override public void add(final Session session) { From 990d1830c3f1780419c37415f40616fd46c63587 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Sat, 9 Sep 2023 17:13:04 +0900 Subject: [PATCH 29/42] =?UTF-8?q?fix:=20JSESSION=5FID=20=EA=B0=80=EC=A0=B8?= =?UTF-8?q?=EC=98=AC=20=EB=95=8C=20getOrDefault=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/org/apache/coyote/http11/common/HttpCookie.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java index 6af89c21dc..20bd3046bc 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java @@ -46,7 +46,7 @@ public String get(final String key) { } public String getJSessionId() { - return items.get(JSESSION_ID); + return items.getOrDefault(JSESSION_ID, EMPTY); } public boolean isEmpty() { From 4d6dea6415779fcc6b5d04e76a1e3097da40caa9 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Sat, 9 Sep 2023 20:13:06 +0900 Subject: [PATCH 30/42] =?UTF-8?q?feat:=20SessionManager=EB=A5=BC=20Request?= =?UTF-8?q?Mapper=EA=B0=80=20=EC=9D=98=EC=A1=B4=ED=95=98=EB=8F=84=EB=A1=9D?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jwp/controller/LoginController.java | 20 +++++++------------ .../jwp/controller/RegisterController.java | 11 ++++------ .../org/apache/catalina/RequestMapper.java | 20 +++++++++++++++++++ .../java/org/apache/catalina/Session.java | 6 ++++++ .../coyote/http11/common/HttpCookie.java | 2 +- .../coyote/http11/request/HttpRequest.java | 15 ++++++++++++++ .../coyote/http11/response/HttpResponse.java | 14 ++++++++++++- .../jwp/controller/LoginControllerTest.java | 5 ++--- 8 files changed, 68 insertions(+), 25 deletions(-) diff --git a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java index 382c6d73c1..5e29bdbbd4 100644 --- a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java +++ b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java @@ -1,21 +1,19 @@ package nextstep.jwp.controller; +import java.util.Map; import java.util.UUID; import nextstep.jwp.db.InMemoryUserRepository; import nextstep.jwp.model.User; import org.apache.catalina.Session; import org.apache.catalina.SessionManager; import org.apache.catalina.controller.AbstractController; -import org.apache.coyote.http11.common.HttpCookie; import org.apache.coyote.http11.common.HttpStatus; import org.apache.coyote.http11.request.HttpRequest; import org.apache.coyote.http11.request.RequestBody; import org.apache.coyote.http11.response.HttpResponse; public class LoginController extends AbstractController { - - private static final String ACCOUNT = "account"; - private static final String PASSWORD = "password"; + private static final String INDEX_PAGE = "/index.html"; private static final String LOGIN_PAGE = "/login.html"; private static final String UNAUTHORIZED_PAGE = "/401.html"; @@ -25,8 +23,8 @@ public class LoginController extends AbstractController { @Override protected void doPost(final HttpRequest request, final HttpResponse response) { final RequestBody requestBody = request.getRequestBody(); - final String account = requestBody.get(ACCOUNT); - final String password = requestBody.get(PASSWORD); + final String account = requestBody.get("account"); + final String password = requestBody.get("password"); final User user = InMemoryUserRepository.findByAccount(account).orElseThrow(); if (!user.checkPassword(password)) { @@ -35,20 +33,16 @@ protected void doPost(final HttpRequest request, final HttpResponse response) { } final String uuid = UUID.randomUUID().toString(); - response.setCookie("JSESSIONID", uuid); - final Session session = new Session(uuid); - session.setAttribute("user", user); - sessionManager.add(session); response.setHttpStatus(HttpStatus.FOUND) + .setCookie("JSESSIONID", uuid) + .setSession(new Session(uuid, Map.of("user", user))) .addHeader("Location", INDEX_PAGE) .sendRedirect(INDEX_PAGE); } @Override protected void doGet(final HttpRequest request, final HttpResponse response) { - final HttpCookie httpCookie = request.parseCookie(); - final Session session = sessionManager.findSession(httpCookie.getJSessionId()); - if (session == null) { + if (request.getSession() == null) { response.setHttpStatus(HttpStatus.OK).sendRedirect(LOGIN_PAGE); return; } diff --git a/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java b/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java index c11b8e8cd5..81b7678a59 100644 --- a/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java +++ b/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java @@ -9,10 +9,7 @@ import org.apache.coyote.http11.response.HttpResponse; public class RegisterController extends AbstractController { - - private static final String ACCOUNT = "account"; - private static final String PASSWORD = "password"; - private static final String EMAIL = "email"; + private static final String INDEX_PAGE = "/index.html"; private static final String REGISTER_PAGE = "/register.html"; private static final String CONFLICT_PAGE = "/409.html"; @@ -20,7 +17,7 @@ public class RegisterController extends AbstractController { @Override protected void doPost(final HttpRequest request, final HttpResponse response) { final RequestBody requestBody = request.getRequestBody(); - final String account = requestBody.get(ACCOUNT); + final String account = requestBody.get("account"); if (InMemoryUserRepository.findByAccount(account).isPresent()) { response.setHttpStatus(HttpStatus.CONFLICT) @@ -28,8 +25,8 @@ protected void doPost(final HttpRequest request, final HttpResponse response) { return; } - final String password = requestBody.get(PASSWORD); - final String email = requestBody.get(EMAIL); + final String password = requestBody.get("password"); + final String email = requestBody.get("email"); InMemoryUserRepository.save(new User(account, password, email)); response.setHttpStatus(HttpStatus.FOUND) .addHeader("Location", INDEX_PAGE) diff --git a/tomcat/src/main/java/org/apache/catalina/RequestMapper.java b/tomcat/src/main/java/org/apache/catalina/RequestMapper.java index 825b830cc6..158d024a95 100644 --- a/tomcat/src/main/java/org/apache/catalina/RequestMapper.java +++ b/tomcat/src/main/java/org/apache/catalina/RequestMapper.java @@ -12,6 +12,7 @@ public class RequestMapper implements Mapper { private final Controller defaultController; private final Map controllers = new HashMap<>(); + private final SessionManager sessionManager = new SessionManager(); public RequestMapper(final Controller defaultController) { this.defaultController = defaultController; @@ -25,7 +26,26 @@ public void addController(final String path, final Controller controller) { @Override public void service(final HttpRequest httpRequest, final HttpResponse httpResponse) { final RequestLine requestLine = httpRequest.getRequestLine(); + setSessionToHttpRequest(httpRequest); final Controller controller = controllers.getOrDefault(requestLine.parseUri(), defaultController); controller.service(httpRequest, httpResponse); + addSession(httpResponse); + } + + private void setSessionToHttpRequest(final HttpRequest httpRequest) { + final String sessionId = httpRequest.parseSessionId(); + if (sessionId == null) { + return; + } + final Session session = sessionManager.findSession(sessionId); + httpRequest.setSession(session); + } + + private void addSession(final HttpResponse httpResponse) { + final Session session = httpResponse.getSession(); + if (session == null) { + return; + } + sessionManager.add(session); } } diff --git a/tomcat/src/main/java/org/apache/catalina/Session.java b/tomcat/src/main/java/org/apache/catalina/Session.java index 7b193b7bfa..04f3742cdf 100644 --- a/tomcat/src/main/java/org/apache/catalina/Session.java +++ b/tomcat/src/main/java/org/apache/catalina/Session.java @@ -1,5 +1,6 @@ package org.apache.catalina; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -9,7 +10,12 @@ public class Session { private final Map items = new HashMap<>(); public Session(final String id) { + this(id, Collections.emptyMap()); + } + + public Session(final String id, Map attributes) { this.id = id; + this.items.putAll(attributes); } public Object getAttribute(final String key) { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java index 20bd3046bc..6af89c21dc 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java @@ -46,7 +46,7 @@ public String get(final String key) { } public String getJSessionId() { - return items.getOrDefault(JSESSION_ID, EMPTY); + return items.get(JSESSION_ID); } public boolean isEmpty() { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java index e60b93f68e..9eb9998032 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java @@ -1,5 +1,6 @@ package org.apache.coyote.http11.request; +import org.apache.catalina.Session; import org.apache.coyote.http11.common.Headers; import org.apache.coyote.http11.common.HttpCookie; import org.apache.coyote.http11.common.HttpMethod; @@ -9,6 +10,7 @@ public class HttpRequest { private final RequestLine requestLine; private final Headers headers; private final RequestBody requestBody; + private Session session; public HttpRequest( final RequestLine requestLine, @@ -32,6 +34,15 @@ public HttpCookie parseCookie() { return headers.parseCookie(); } + public void setSession(final Session session) { + this.session = session; + } + + public String parseSessionId() { + final HttpCookie httpCookie = headers.parseCookie(); + return httpCookie.getJSessionId(); + } + public RequestLine getRequestLine() { return requestLine; } @@ -43,4 +54,8 @@ public Headers getHeaders() { public RequestBody getRequestBody() { return requestBody; } + + public Session getSession() { + return session; + } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java index 1880dfa83e..6b51d0826d 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java @@ -1,5 +1,6 @@ package org.apache.coyote.http11.response; +import org.apache.catalina.Session; import org.apache.coyote.http11.common.Headers; import org.apache.coyote.http11.common.HttpCookie; import org.apache.coyote.http11.common.HttpStatus; @@ -11,6 +12,7 @@ public class HttpResponse { private final Headers headers = new Headers(); private HttpStatus httpStatus; private HttpCookie httpCookie = new HttpCookie(); + private Session session; private String redirect; public HttpResponse(HttpVersion httpVersion) { @@ -32,8 +34,14 @@ public HttpResponse sendRedirect(final String url) { return this; } - public void setCookie(final String key, final String value) { + public HttpResponse setSession(final Session session) { + this.session = session; + return this; + } + + public HttpResponse setCookie(final String key, final String value) { httpCookie.put(key, value); + return this; } public HttpVersion getHttpVersion() { @@ -55,4 +63,8 @@ public String getRedirect() { public HttpCookie getHttpCookie() { return httpCookie; } + + public Session getSession() { + return session; + } } diff --git a/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java b/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java index d39eb1971a..96d849d0b4 100644 --- a/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java +++ b/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java @@ -55,10 +55,9 @@ void setUp() { void GET_요청을_받았을_때_세션에_사용자_정보가_있는_경우_index_페이지를_반환하도록_설정한다() { // given final RequestLine requestLine = RequestLine.from("GET /login HTTP/1.1"); - final Headers header = new Headers(); final String uuid = UUID.randomUUID().toString(); - header.addHeader("Cookie", "JSESSIONID=" + uuid); - final HttpRequest httpRequest = new HttpRequest(requestLine, header, new RequestBody()); + final HttpRequest httpRequest = new HttpRequest(requestLine, new Headers(), new RequestBody()); + httpRequest.setSession(new Session(uuid)); final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1); sessionManager.add(new Session(uuid)); From 69b0f73d1f6d9a02674b024a9d97020cc3135b82 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Sat, 9 Sep 2023 20:17:07 +0900 Subject: [PATCH 31/42] =?UTF-8?q?chore:=20=EB=B9=8C=EB=93=9C=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f5921d32ac..d672c0be0a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,4 +34,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: ./gradlew build codeCoverageReport sonar --info -x :study:build + run: ./gradlew build sonar --info -x :study:build From 801ddc8bfe2f84fa1d889f4b68b0441171cab525 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Sat, 9 Sep 2023 20:19:33 +0900 Subject: [PATCH 32/42] =?UTF-8?q?remove:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=ED=95=84=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/nextstep/jwp/controller/LoginController.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java index 5e29bdbbd4..062fb84778 100644 --- a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java +++ b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java @@ -5,7 +5,6 @@ import nextstep.jwp.db.InMemoryUserRepository; import nextstep.jwp.model.User; import org.apache.catalina.Session; -import org.apache.catalina.SessionManager; import org.apache.catalina.controller.AbstractController; import org.apache.coyote.http11.common.HttpStatus; import org.apache.coyote.http11.request.HttpRequest; @@ -13,13 +12,11 @@ import org.apache.coyote.http11.response.HttpResponse; public class LoginController extends AbstractController { - + private static final String INDEX_PAGE = "/index.html"; private static final String LOGIN_PAGE = "/login.html"; private static final String UNAUTHORIZED_PAGE = "/401.html"; - private final SessionManager sessionManager = new SessionManager(); - @Override protected void doPost(final HttpRequest request, final HttpResponse response) { final RequestBody requestBody = request.getRequestBody(); From 233020549a8daa69072b0fdb2f40b51a8d27166f Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 11 Sep 2023 14:35:06 +0900 Subject: [PATCH 33/42] =?UTF-8?q?fix:=20finally=20=ED=9B=84=20space=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 598a7358bd..0367ff26fc 100644 --- a/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java +++ b/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java @@ -31,7 +31,7 @@ public void start() { System.in.read(); } catch (IOException e) { log.error(e.getMessage(), e); - } finally{ + } finally { log.info("web server stop."); connector.stop(); } From 67f7dd24cb3d5ad7a129bf3da10b03cc762acc9c Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 11 Sep 2023 14:38:40 +0900 Subject: [PATCH 34/42] =?UTF-8?q?refactor:=20=EC=83=81=EC=88=98=EB=AA=85?= =?UTF-8?q?=20=EC=9D=BC=EA=B4=80=EC=84=B1=20=EC=9E=88=EA=B2=8C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/org/apache/catalina/SessionManager.java | 10 +++++----- .../java/org/apache/catalina/connector/Connector.java | 1 - .../java/org/apache/coyote/http11/Http11Processor.java | 8 ++++---- .../apache/coyote/http11/response/HttpResponse.java | 2 +- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/tomcat/src/main/java/org/apache/catalina/SessionManager.java b/tomcat/src/main/java/org/apache/catalina/SessionManager.java index a2de9ee9ed..ad2e76ecf6 100644 --- a/tomcat/src/main/java/org/apache/catalina/SessionManager.java +++ b/tomcat/src/main/java/org/apache/catalina/SessionManager.java @@ -5,24 +5,24 @@ public class SessionManager implements Manager { - private static final Map SESSIONS = new ConcurrentHashMap<>(); + private static final Map sessions = new ConcurrentHashMap<>(); @Override public void add(final Session session) { - SESSIONS.put(session.getId(), session); + sessions.put(session.getId(), session); } @Override public Session findSession(final String id) { - return SESSIONS.get(id); + return sessions.get(id); } @Override public void remove(final String id) { - SESSIONS.remove(id); + sessions.remove(id); } public void clear() { - SESSIONS.clear(); + sessions.clear(); } } 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 28bd347e61..3450f54cc5 100644 --- a/tomcat/src/main/java/org/apache/catalina/connector/Connector.java +++ b/tomcat/src/main/java/org/apache/catalina/connector/Connector.java @@ -14,7 +14,6 @@ public class Connector implements Runnable { private static final Logger log = LoggerFactory.getLogger(Connector.class); - private static final int DEFAULT_PORT = 8080; private static final int DEFAULT_ACCEPT_COUNT = 100; 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 011d0675cd..096d0aa308 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -19,7 +19,7 @@ public class Http11Processor implements Runnable, Processor { - private static final Logger LOG = LoggerFactory.getLogger(Http11Processor.class); + private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); private final Socket connection; private final Mapper mapper; @@ -33,7 +33,7 @@ public Http11Processor(final Socket connection, final Mapper mapper) { @Override public void run() { - LOG.info("connect host: {}, port: {}", connection.getInetAddress(), connection.getPort()); + log.info("connect host: {}, port: {}", connection.getInetAddress(), connection.getPort()); process(connection); } @@ -44,7 +44,7 @@ public void process(final Socket connection) { final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) { execute(bufferedReader, outputStream); } catch (final IOException | UncheckedServletException e) { - LOG.error(e.getMessage(), e); + log.error(e.getMessage(), e); } } @@ -59,7 +59,7 @@ private void execute(final BufferedReader bufferedReader, final OutputStream out outputStream.write(generate.getBytes()); outputStream.flush(); } catch (final Exception e) { - LOG.error(e.getMessage(), e); + log.error(e.getMessage(), e); internalServerError(outputStream); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java index 6b51d0826d..6dd0283703 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java @@ -10,8 +10,8 @@ public class HttpResponse { private final HttpVersion httpVersion; private final Headers headers = new Headers(); + private final HttpCookie httpCookie = new HttpCookie(); private HttpStatus httpStatus; - private HttpCookie httpCookie = new HttpCookie(); private Session session; private String redirect; From 139bef62b0744ec533890ab1f8010b1ce79bcf00 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 11 Sep 2023 16:00:19 +0900 Subject: [PATCH 35/42] =?UTF-8?q?refactor:=20Http11Processor=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 21 ++++++++----------- .../coyote/http11/request/HttpRequest.java | 5 +++++ .../http11/request/HttpRequestTest.java | 11 ++++++++++ 3 files changed, 25 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 096d0aa308..1baf3dd1fd 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -20,6 +20,9 @@ public class Http11Processor implements Runnable, Processor { private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); + private static final HttpResponse INTERNAL_SERVER_ERROR_RESPONSE = new HttpResponse(HttpVersion.HTTP_1_1) + .setHttpStatus(HttpStatus.INTERNAL_SERVER_ERROR) + .sendRedirect("/500.html"); private final Socket connection; private final Mapper mapper; @@ -51,25 +54,19 @@ public void process(final Socket connection) { private void execute(final BufferedReader bufferedReader, final OutputStream outputStream) throws IOException { try { final HttpRequest httpRequest = httpRequestParser.parse(bufferedReader); - final HttpResponse httpResponse = new HttpResponse(httpRequest.getRequestLine().getHttpVersion()); + final HttpResponse httpResponse = new HttpResponse(httpRequest.getHttpVersion()); mapper.service(httpRequest, httpResponse); - - final String generate = httpResponseGenerator.generate(httpResponse); - outputStream.write(generate.getBytes()); - outputStream.flush(); + write(outputStream, httpResponse); } catch (final Exception e) { log.error(e.getMessage(), e); - internalServerError(outputStream); + write(outputStream, INTERNAL_SERVER_ERROR_RESPONSE); } } - private void internalServerError(final OutputStream outputStream) throws IOException { - final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1) - .setHttpStatus(HttpStatus.INTERNAL_SERVER_ERROR) - .sendRedirect("/500.html"); - final String response = httpResponseGenerator.generate(httpResponse); - outputStream.write(response.getBytes()); + private void write(final OutputStream outputStream, final HttpResponse httpResponse) throws IOException { + final String generate = httpResponseGenerator.generate(httpResponse); + outputStream.write(generate.getBytes()); outputStream.flush(); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java index 9eb9998032..145d44ca33 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java @@ -4,6 +4,7 @@ import org.apache.coyote.http11.common.Headers; import org.apache.coyote.http11.common.HttpCookie; import org.apache.coyote.http11.common.HttpMethod; +import org.apache.coyote.http11.common.HttpVersion; public class HttpRequest { @@ -43,6 +44,10 @@ public String parseSessionId() { return httpCookie.getJSessionId(); } + public HttpVersion getHttpVersion() { + return requestLine.getHttpVersion(); + } + public RequestLine getRequestLine() { return requestLine; } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestTest.java b/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestTest.java index 079203a989..010bc4da64 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestTest.java @@ -5,6 +5,7 @@ import org.apache.coyote.http11.common.Headers; import org.apache.coyote.http11.common.HttpCookie; +import org.apache.coyote.http11.common.HttpVersion; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -69,4 +70,14 @@ class HttpRequestTest { entry("JSESSIONID", "656cef62-e3c4-40bc-a8df-94732920ed46") ); } + + @Test + void httpVersion을_반환한다() { + // given + final RequestLine requestLine = RequestLine.from("GET /index.html HTTP/1.1 "); + final HttpRequest httpRequest = new HttpRequest(requestLine, null, null); + + // expect + assertThat(httpRequest.getHttpVersion()).isEqualTo(HttpVersion.HTTP_1_1); + } } From 812e5f8ec11d8aaac70d2f7ad873a4b1e700d87f Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 11 Sep 2023 16:12:39 +0900 Subject: [PATCH 36/42] =?UTF-8?q?refactor:=20isEmpty=20->=20isBlank?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/org/apache/coyote/http11/common/Headers.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/Headers.java b/tomcat/src/main/java/org/apache/coyote/http11/common/Headers.java index f2017f7ec7..d96ec8946a 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/common/Headers.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/Headers.java @@ -24,7 +24,7 @@ private Headers(final Map items) { } public void addHeader(String line) { - if (line == null || line.isEmpty()) { + if (line == null || line.isBlank()) { return; } final String[] header = line.split(DELIMITER); From 9254604b5baadcc76885be595c6d093d656f5c0a Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 11 Sep 2023 16:33:58 +0900 Subject: [PATCH 37/42] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=8B=9C=20=EA=B2=80=EC=A6=9D=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=B6=80=EB=B6=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jwp/controller/RegisterController.java | 6 +-- .../main/java/nextstep/jwp/model/User.java | 11 +++- .../controller/AbstractController.java | 11 ++++ .../coyote/http11/common/HttpStatus.java | 1 + .../coyote/http11/request/RequestBody.java | 3 +- tomcat/src/main/resources/static/400.html | 52 +++++++++++++++++++ .../java/nextstep/jwp/model/UserTest.java | 22 ++++++++ .../http11/request/RequestBodyTest.java | 16 ++++++ 8 files changed, 114 insertions(+), 8 deletions(-) create mode 100644 tomcat/src/main/resources/static/400.html create mode 100644 tomcat/src/test/java/nextstep/jwp/model/UserTest.java diff --git a/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java b/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java index 81b7678a59..675550dfc8 100644 --- a/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java +++ b/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java @@ -18,16 +18,12 @@ public class RegisterController extends AbstractController { protected void doPost(final HttpRequest request, final HttpResponse response) { final RequestBody requestBody = request.getRequestBody(); final String account = requestBody.get("account"); - if (InMemoryUserRepository.findByAccount(account).isPresent()) { response.setHttpStatus(HttpStatus.CONFLICT) .sendRedirect(CONFLICT_PAGE); return; } - - final String password = requestBody.get("password"); - final String email = requestBody.get("email"); - InMemoryUserRepository.save(new User(account, password, email)); + InMemoryUserRepository.save(new User(account, requestBody.get("password"), requestBody.get("email"))); response.setHttpStatus(HttpStatus.FOUND) .addHeader("Location", INDEX_PAGE) .sendRedirect(INDEX_PAGE); diff --git a/tomcat/src/main/java/nextstep/jwp/model/User.java b/tomcat/src/main/java/nextstep/jwp/model/User.java index 5032a64f2d..06be6a6454 100644 --- a/tomcat/src/main/java/nextstep/jwp/model/User.java +++ b/tomcat/src/main/java/nextstep/jwp/model/User.java @@ -7,15 +7,22 @@ public class User { private final String password; private final String email; + public User(final String account, final String password, final String email) { + this(null, account, password, email); + } + public User(final Long id, final String account, final String password, final String email) { + validate(account, password, email); this.id = id; this.account = account; this.password = password; this.email = email; } - public User(final String account, final String password, final String email) { - this(null, account, password, email); + private void validate(final String account, final String password, final String email) { + if (account.isEmpty() || password.isEmpty() || email.isEmpty()) { + throw new IllegalArgumentException("올바른 사용자 정보를 입력해주세요."); + } } public boolean checkPassword(final String password) { diff --git a/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java b/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java index e4980b41b6..2556f19834 100644 --- a/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java +++ b/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java @@ -6,10 +6,19 @@ public abstract class AbstractController implements Controller { + private static final String BAD_REQUEST_PAGE = "/400.html"; private static final String NOT_FOUND_PAGE = "/404.html"; @Override public void service(HttpRequest request, HttpResponse response) { + try { + routing(request, response); + } catch (final IllegalArgumentException e) { + response.setHttpStatus(HttpStatus.BAD_REQUEST).sendRedirect(BAD_REQUEST_PAGE); + } + } + + private void routing(final HttpRequest request, final HttpResponse response) { if (request.isGet()) { doGet(request, response); return; @@ -22,8 +31,10 @@ public void service(HttpRequest request, HttpResponse response) { } protected void doPost(HttpRequest request, HttpResponse response) { + response.setHttpStatus(HttpStatus.NOT_FOUND).sendRedirect(NOT_FOUND_PAGE); } protected void doGet(HttpRequest request, HttpResponse response) { + response.setHttpStatus(HttpStatus.NOT_FOUND).sendRedirect(NOT_FOUND_PAGE); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpStatus.java b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpStatus.java index 4a9ee5facb..2f2c28bf94 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpStatus.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpStatus.java @@ -3,6 +3,7 @@ public enum HttpStatus { OK("200"), FOUND("302"), + BAD_REQUEST("400"), UNAUTHORIZED("401"), NOT_FOUND("404"), CONFLICT("409"), diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java index 1dff04fa3a..050cabf734 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java @@ -15,6 +15,7 @@ public class RequestBody { private static final String DELIMITER = "="; private static final int KEY_INDEX = 0; private static final int VALUE_INDEX = 1; + private static final int FIELD_COUNT = 2; private final Map items = new HashMap<>(); @@ -28,7 +29,7 @@ private RequestBody(final Map items) { public static RequestBody from(final String body) { return Arrays.stream(body.split(SEPARATOR)) - .map(field -> field.split(DELIMITER)) + .map(field -> field.split(DELIMITER, FIELD_COUNT)) .collect(collectingAndThen( toMap(field -> field[KEY_INDEX].strip(), field -> field[VALUE_INDEX].strip()), RequestBody::new diff --git a/tomcat/src/main/resources/static/400.html b/tomcat/src/main/resources/static/400.html new file mode 100644 index 0000000000..2828ac208a --- /dev/null +++ b/tomcat/src/main/resources/static/400.html @@ -0,0 +1,52 @@ + + + + + + + + + 404 Error - SB Admin + + + + +
+
+
+
+
+
+
+

400

+

Bad Request

+

Access to this resource is denied.

+ + + Return to Dashboard + +
+
+
+
+
+
+ +
+ + + + diff --git a/tomcat/src/test/java/nextstep/jwp/model/UserTest.java b/tomcat/src/test/java/nextstep/jwp/model/UserTest.java new file mode 100644 index 0000000000..76ce2960ea --- /dev/null +++ b/tomcat/src/test/java/nextstep/jwp/model/UserTest.java @@ -0,0 +1,22 @@ +package nextstep.jwp.model; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class UserTest { + + @Test + void 사용자를_생성할_때_빈_정보를_입력하는_경우_예외를_던진다() { + // expect + assertThatThrownBy(() -> new User("", "password", "email")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("올바른 사용자 정보를 입력해주세요."); + } +} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/request/RequestBodyTest.java b/tomcat/src/test/java/org/apache/coyote/http11/request/RequestBodyTest.java index d9d8fb2068..f7fb3583d0 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/request/RequestBodyTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/request/RequestBodyTest.java @@ -27,6 +27,22 @@ class RequestBodyTest { ); } + @Test + void body_문자열을_입력받을_때_빈_값이_들어오는_경우_빈_문자열이_들어간다() { + // given + final String body = "account=gugu&password=&email=hkkang@woowahan.com"; + + // when + final RequestBody requestBody = RequestBody.from(body); + + // then + assertThat(requestBody.getItems()).contains( + entry("account", "gugu"), + entry("password", ""), + entry("email", "hkkang@woowahan.com") + ); + } + @Test void key를_입력받아_값을_반환한다() { // given From 643c045e1f5faed9879f679178547caaea7d7228 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 11 Sep 2023 16:56:45 +0900 Subject: [PATCH 38/42] =?UTF-8?q?refactor:=20session=20null=20check=20?= =?UTF-8?q?=EB=B6=80=EB=B6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/jwp/controller/LoginController.java | 14 +++++++------- .../src/main/java/org/apache/catalina/Manager.java | 2 ++ .../java/org/apache/catalina/RequestMapper.java | 7 +------ .../java/org/apache/catalina/SessionManager.java | 6 +++++- .../apache/coyote/http11/common/HttpCookie.java | 2 +- .../http11/common}/Session.java | 7 ++++++- .../apache/coyote/http11/request/HttpRequest.java | 2 +- .../coyote/http11/response/HttpResponse.java | 2 +- .../jwp/controller/LoginControllerTest.java | 9 ++++++--- .../jwp/controller/RegisterControllerTest.java | 3 ++- .../org/apache/catalina/SessionManagerTest.java | 4 +++- .../test/java/org/apache/catalina/SessionTest.java | 1 + 12 files changed, 36 insertions(+), 23 deletions(-) rename tomcat/src/main/java/org/apache/{catalina => coyote/http11/common}/Session.java (84%) diff --git a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java index 062fb84778..e0d1f50109 100644 --- a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java +++ b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java @@ -1,12 +1,10 @@ package nextstep.jwp.controller; -import java.util.Map; -import java.util.UUID; import nextstep.jwp.db.InMemoryUserRepository; import nextstep.jwp.model.User; -import org.apache.catalina.Session; import org.apache.catalina.controller.AbstractController; import org.apache.coyote.http11.common.HttpStatus; +import org.apache.coyote.http11.common.Session; import org.apache.coyote.http11.request.HttpRequest; import org.apache.coyote.http11.request.RequestBody; import org.apache.coyote.http11.response.HttpResponse; @@ -29,17 +27,19 @@ protected void doPost(final HttpRequest request, final HttpResponse response) { return; } - final String uuid = UUID.randomUUID().toString(); + final Session session = request.getSession(); + session.setAttribute("user", user); response.setHttpStatus(HttpStatus.FOUND) - .setCookie("JSESSIONID", uuid) - .setSession(new Session(uuid, Map.of("user", user))) + .setCookie("JSESSIONID", session.getId()) + .setSession(session) .addHeader("Location", INDEX_PAGE) .sendRedirect(INDEX_PAGE); } @Override protected void doGet(final HttpRequest request, final HttpResponse response) { - if (request.getSession() == null) { + final Session session = request.getSession(); + if (session.getAttribute("user") == null) { response.setHttpStatus(HttpStatus.OK).sendRedirect(LOGIN_PAGE); return; } diff --git a/tomcat/src/main/java/org/apache/catalina/Manager.java b/tomcat/src/main/java/org/apache/catalina/Manager.java index ecf0f13f80..884657d510 100644 --- a/tomcat/src/main/java/org/apache/catalina/Manager.java +++ b/tomcat/src/main/java/org/apache/catalina/Manager.java @@ -1,5 +1,7 @@ package org.apache.catalina; +import org.apache.coyote.http11.common.Session; + public interface Manager { void add(final Session session); diff --git a/tomcat/src/main/java/org/apache/catalina/RequestMapper.java b/tomcat/src/main/java/org/apache/catalina/RequestMapper.java index 158d024a95..7513d3c273 100644 --- a/tomcat/src/main/java/org/apache/catalina/RequestMapper.java +++ b/tomcat/src/main/java/org/apache/catalina/RequestMapper.java @@ -4,6 +4,7 @@ import java.util.Map; import org.apache.catalina.controller.Controller; import org.apache.coyote.http11.Mapper; +import org.apache.coyote.http11.common.Session; import org.apache.coyote.http11.request.HttpRequest; import org.apache.coyote.http11.request.RequestLine; import org.apache.coyote.http11.response.HttpResponse; @@ -34,18 +35,12 @@ public void service(final HttpRequest httpRequest, final HttpResponse httpRespon private void setSessionToHttpRequest(final HttpRequest httpRequest) { final String sessionId = httpRequest.parseSessionId(); - if (sessionId == null) { - return; - } final Session session = sessionManager.findSession(sessionId); httpRequest.setSession(session); } private void addSession(final HttpResponse httpResponse) { final Session session = httpResponse.getSession(); - if (session == null) { - return; - } sessionManager.add(session); } } diff --git a/tomcat/src/main/java/org/apache/catalina/SessionManager.java b/tomcat/src/main/java/org/apache/catalina/SessionManager.java index ad2e76ecf6..cb27ea0030 100644 --- a/tomcat/src/main/java/org/apache/catalina/SessionManager.java +++ b/tomcat/src/main/java/org/apache/catalina/SessionManager.java @@ -2,6 +2,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.apache.coyote.http11.common.Session; public class SessionManager implements Manager { @@ -9,12 +10,15 @@ public class SessionManager implements Manager { @Override public void add(final Session session) { + if (session == null) { + return; + } sessions.put(session.getId(), session); } @Override public Session findSession(final String id) { - return sessions.get(id); + return sessions.getOrDefault(id, new Session()); } @Override diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java index 6af89c21dc..20bd3046bc 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpCookie.java @@ -46,7 +46,7 @@ public String get(final String key) { } public String getJSessionId() { - return items.get(JSESSION_ID); + return items.getOrDefault(JSESSION_ID, EMPTY); } public boolean isEmpty() { diff --git a/tomcat/src/main/java/org/apache/catalina/Session.java b/tomcat/src/main/java/org/apache/coyote/http11/common/Session.java similarity index 84% rename from tomcat/src/main/java/org/apache/catalina/Session.java rename to tomcat/src/main/java/org/apache/coyote/http11/common/Session.java index 04f3742cdf..c67ef18408 100644 --- a/tomcat/src/main/java/org/apache/catalina/Session.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/Session.java @@ -1,14 +1,19 @@ -package org.apache.catalina; +package org.apache.coyote.http11.common; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class Session { private final String id; private final Map items = new HashMap<>(); + public Session() { + this(UUID.randomUUID().toString(), Collections.emptyMap()); + } + public Session(final String id) { this(id, Collections.emptyMap()); } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java index 145d44ca33..b29e3b7437 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java @@ -1,10 +1,10 @@ package org.apache.coyote.http11.request; -import org.apache.catalina.Session; import org.apache.coyote.http11.common.Headers; import org.apache.coyote.http11.common.HttpCookie; import org.apache.coyote.http11.common.HttpMethod; import org.apache.coyote.http11.common.HttpVersion; +import org.apache.coyote.http11.common.Session; public class HttpRequest { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java index 6dd0283703..310ff58d6c 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java @@ -1,6 +1,6 @@ package org.apache.coyote.http11.response; -import org.apache.catalina.Session; +import org.apache.coyote.http11.common.Session; import org.apache.coyote.http11.common.Headers; import org.apache.coyote.http11.common.HttpCookie; import org.apache.coyote.http11.common.HttpStatus; diff --git a/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java b/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java index 96d849d0b4..b55f9ee1d5 100644 --- a/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java +++ b/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java @@ -6,12 +6,12 @@ import java.util.UUID; import nextstep.jwp.db.InMemoryUserRepository; import nextstep.jwp.model.User; -import org.apache.catalina.Session; import org.apache.catalina.SessionManager; import org.apache.catalina.controller.Controller; import org.apache.coyote.http11.common.Headers; import org.apache.coyote.http11.common.HttpStatus; import org.apache.coyote.http11.common.HttpVersion; +import org.apache.coyote.http11.common.Session; import org.apache.coyote.http11.request.HttpRequest; import org.apache.coyote.http11.request.RequestBody; import org.apache.coyote.http11.request.RequestLine; @@ -39,6 +39,7 @@ void setUp() { // given final RequestLine requestLine = RequestLine.from("GET /login HTTP/1.1"); final HttpRequest httpRequest = new HttpRequest(requestLine, new Headers(), new RequestBody()); + httpRequest.setSession(new Session()); final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1); // when @@ -57,9 +58,11 @@ void setUp() { final RequestLine requestLine = RequestLine.from("GET /login HTTP/1.1"); final String uuid = UUID.randomUUID().toString(); final HttpRequest httpRequest = new HttpRequest(requestLine, new Headers(), new RequestBody()); - httpRequest.setSession(new Session(uuid)); + final Session session = new Session(uuid); + session.setAttribute("user", new User("gugu", "password", "gugu@naver.com")); + httpRequest.setSession(session); final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1); - sessionManager.add(new Session(uuid)); + sessionManager.add(session); // when controller.service(httpRequest, httpResponse); diff --git a/tomcat/src/test/java/nextstep/jwp/controller/RegisterControllerTest.java b/tomcat/src/test/java/nextstep/jwp/controller/RegisterControllerTest.java index fb40213504..7953e62505 100644 --- a/tomcat/src/test/java/nextstep/jwp/controller/RegisterControllerTest.java +++ b/tomcat/src/test/java/nextstep/jwp/controller/RegisterControllerTest.java @@ -66,7 +66,8 @@ void setUp() { void POST_요청_시_회원가입에_성공하는_경우_index_페이지를_반환하도록_설정한다() { // given final RequestLine requestLine = RequestLine.from("POST /register HTTP/1.1"); - final HttpRequest httpRequest = new HttpRequest(requestLine, new Headers(), new RequestBody()); + final RequestBody requestBody = RequestBody.from("account=gugu&password=password&email=hkkang@woowahan.com"); + final HttpRequest httpRequest = new HttpRequest(requestLine, new Headers(), requestBody); final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1); // when diff --git a/tomcat/src/test/java/org/apache/catalina/SessionManagerTest.java b/tomcat/src/test/java/org/apache/catalina/SessionManagerTest.java index e98245e863..0843cb5f27 100644 --- a/tomcat/src/test/java/org/apache/catalina/SessionManagerTest.java +++ b/tomcat/src/test/java/org/apache/catalina/SessionManagerTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import org.apache.coyote.http11.common.Session; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; @@ -53,7 +54,8 @@ void setUp() { sessionManager.remove("helloworld"); // then - assertThat(sessionManager.findSession("helloworld")).isNull(); + final Session findSession = sessionManager.findSession("helloworld"); + assertThat(findSession).isNotEqualTo(session); } @Test diff --git a/tomcat/src/test/java/org/apache/catalina/SessionTest.java b/tomcat/src/test/java/org/apache/catalina/SessionTest.java index 801a820d04..943829a02c 100644 --- a/tomcat/src/test/java/org/apache/catalina/SessionTest.java +++ b/tomcat/src/test/java/org/apache/catalina/SessionTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import org.apache.coyote.http11.common.Session; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; From 1c49621d2b97d430a7d701a58ee7c6faa3ec3c98 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 11 Sep 2023 17:12:08 +0900 Subject: [PATCH 39/42] =?UTF-8?q?refactor:=20final=20=EB=B9=A0=EC=A7=84=20?= =?UTF-8?q?=EB=B6=80=EB=B6=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/apache/catalina/controller/AbstractController.java | 2 +- .../main/java/org/apache/catalina/controller/Controller.java | 2 +- .../src/main/java/org/apache/coyote/http11/common/Headers.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java b/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java index 2556f19834..530538d515 100644 --- a/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java +++ b/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java @@ -10,7 +10,7 @@ public abstract class AbstractController implements Controller { private static final String NOT_FOUND_PAGE = "/404.html"; @Override - public void service(HttpRequest request, HttpResponse response) { + public void service(final HttpRequest request, final HttpResponse response) { try { routing(request, response); } catch (final IllegalArgumentException e) { diff --git a/tomcat/src/main/java/org/apache/catalina/controller/Controller.java b/tomcat/src/main/java/org/apache/catalina/controller/Controller.java index bb949b32e3..4285447029 100644 --- a/tomcat/src/main/java/org/apache/catalina/controller/Controller.java +++ b/tomcat/src/main/java/org/apache/catalina/controller/Controller.java @@ -4,5 +4,5 @@ import org.apache.coyote.http11.response.HttpResponse; public interface Controller { - void service(HttpRequest request, HttpResponse response); + void service(final HttpRequest request, final HttpResponse response); } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/Headers.java b/tomcat/src/main/java/org/apache/coyote/http11/common/Headers.java index d96ec8946a..7b0515ff0c 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/common/Headers.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/Headers.java @@ -23,7 +23,7 @@ private Headers(final Map items) { this.items.putAll(items); } - public void addHeader(String line) { + public void addHeader(final String line) { if (line == null || line.isBlank()) { return; } From 351882acf6c3b4ddb359256b7a0e4c39b3f6f07f Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 11 Sep 2023 17:31:45 +0900 Subject: [PATCH 40/42] =?UTF-8?q?feat:=20Adapter=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/nextstep/Application.java | 12 +++--- .../org/apache/catalina/RequestAdapter.java | 38 ++++++++++++++++++ .../org/apache/catalina/RequestMapper.java | 40 ++++--------------- .../apache/catalina/connector/Connector.java | 14 +++---- .../org/apache/catalina/startup/Tomcat.java | 15 +++---- .../http11/{Mapper.java => Adapter.java} | 4 +- .../apache/coyote/http11/Http11Processor.java | 8 ++-- .../coyote/http11/Http11ProcessorTest.java | 6 +-- .../http11/common}/SessionTest.java | 3 +- 9 files changed, 76 insertions(+), 64 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/catalina/RequestAdapter.java rename tomcat/src/main/java/org/apache/coyote/http11/{Mapper.java => Adapter.java} (62%) rename tomcat/src/test/java/org/apache/{catalina => coyote/http11/common}/SessionTest.java (95%) diff --git a/tomcat/src/main/java/nextstep/Application.java b/tomcat/src/main/java/nextstep/Application.java index 5dad6e00d2..28bbffe0c6 100644 --- a/tomcat/src/main/java/nextstep/Application.java +++ b/tomcat/src/main/java/nextstep/Application.java @@ -1,5 +1,6 @@ package nextstep; +import java.util.Map; import nextstep.jwp.controller.HomeController; import nextstep.jwp.controller.LoginController; import nextstep.jwp.controller.RegisterController; @@ -8,10 +9,11 @@ public class Application { public static void main(String[] args) { - new Tomcat() - .addController("/", new HomeController()) - .addController("/login", new LoginController()) - .addController("/register", new RegisterController()) - .start(); + final Tomcat tomcat = new Tomcat(Map.of( + "/", new HomeController(), + "/login", new LoginController(), + "/register", new RegisterController() + )); + tomcat.start(); } } diff --git a/tomcat/src/main/java/org/apache/catalina/RequestAdapter.java b/tomcat/src/main/java/org/apache/catalina/RequestAdapter.java new file mode 100644 index 0000000000..6aed8fdc30 --- /dev/null +++ b/tomcat/src/main/java/org/apache/catalina/RequestAdapter.java @@ -0,0 +1,38 @@ +package org.apache.catalina; + +import org.apache.catalina.controller.Controller; +import org.apache.coyote.http11.Adapter; +import org.apache.coyote.http11.common.Session; +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.request.RequestLine; +import org.apache.coyote.http11.response.HttpResponse; + +public class RequestAdapter implements Adapter { + + private final RequestMapper requestMapper; + private final SessionManager sessionManager = new SessionManager(); + + public RequestAdapter(final RequestMapper requestMapper) { + this.requestMapper = requestMapper; + } + + @Override + public void service(final HttpRequest httpRequest, final HttpResponse httpResponse) { + final RequestLine requestLine = httpRequest.getRequestLine(); + final Controller controller = requestMapper.getController(requestLine.parseUri()); + setSessionToHttpRequest(httpRequest); + controller.service(httpRequest, httpResponse); + addSession(httpResponse); + } + + private void setSessionToHttpRequest(final HttpRequest httpRequest) { + final String sessionId = httpRequest.parseSessionId(); + final Session session = sessionManager.findSession(sessionId); + httpRequest.setSession(session); + } + + private void addSession(final HttpResponse httpResponse) { + final Session session = httpResponse.getSession(); + sessionManager.add(session); + } +} diff --git a/tomcat/src/main/java/org/apache/catalina/RequestMapper.java b/tomcat/src/main/java/org/apache/catalina/RequestMapper.java index 7513d3c273..6ba3b15230 100644 --- a/tomcat/src/main/java/org/apache/catalina/RequestMapper.java +++ b/tomcat/src/main/java/org/apache/catalina/RequestMapper.java @@ -3,44 +3,18 @@ import java.util.HashMap; import java.util.Map; import org.apache.catalina.controller.Controller; -import org.apache.coyote.http11.Mapper; -import org.apache.coyote.http11.common.Session; -import org.apache.coyote.http11.request.HttpRequest; -import org.apache.coyote.http11.request.RequestLine; -import org.apache.coyote.http11.response.HttpResponse; +import org.apache.catalina.controller.StaticController; -public class RequestMapper implements Mapper { +public class RequestMapper { - private final Controller defaultController; + private final Controller defaultController = new StaticController(); private final Map controllers = new HashMap<>(); - private final SessionManager sessionManager = new SessionManager(); - public RequestMapper(final Controller defaultController) { - this.defaultController = defaultController; + public RequestMapper(final Map controllers) { + this.controllers.putAll(controllers); } - @Override - public void addController(final String path, final Controller controller) { - controllers.put(path, controller); - } - - @Override - public void service(final HttpRequest httpRequest, final HttpResponse httpResponse) { - final RequestLine requestLine = httpRequest.getRequestLine(); - setSessionToHttpRequest(httpRequest); - final Controller controller = controllers.getOrDefault(requestLine.parseUri(), defaultController); - controller.service(httpRequest, httpResponse); - addSession(httpResponse); - } - - private void setSessionToHttpRequest(final HttpRequest httpRequest) { - final String sessionId = httpRequest.parseSessionId(); - final Session session = sessionManager.findSession(sessionId); - httpRequest.setSession(session); - } - - private void addSession(final HttpResponse httpResponse) { - final Session session = httpResponse.getSession(); - sessionManager.add(session); + public Controller getController(final String uri) { + return controllers.getOrDefault(uri, defaultController); } } 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 3450f54cc5..38b11084ad 100644 --- a/tomcat/src/main/java/org/apache/catalina/connector/Connector.java +++ b/tomcat/src/main/java/org/apache/catalina/connector/Connector.java @@ -6,8 +6,8 @@ import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import org.apache.coyote.http11.Adapter; import org.apache.coyote.http11.Http11Processor; -import org.apache.coyote.http11.Mapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,17 +19,17 @@ public class Connector implements Runnable { private final ServerSocket serverSocket; private boolean stopped; - private final Mapper mapper; + private final Adapter adapter; private final ExecutorService executorService; - public Connector(final Mapper mapper, final int threadCount) { - this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, mapper, threadCount); + public Connector(final Adapter adapter, final int threadCount) { + this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, adapter, threadCount); } - public Connector(final int port, final int acceptCount, final Mapper mapper, final int threadCount) { + public Connector(final int port, final int acceptCount, final Adapter adapter, final int threadCount) { this.serverSocket = createServerSocket(port, acceptCount); this.stopped = false; - this.mapper = mapper; + this.adapter = adapter; this.executorService = Executors.newFixedThreadPool(threadCount); } @@ -71,7 +71,7 @@ private void process(final Socket connection) { if (connection == null) { return; } - final Http11Processor processor = new Http11Processor(connection, mapper); + final Http11Processor processor = new Http11Processor(connection, adapter); executorService.execute(processor); } 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 0367ff26fc..7d52dd56e4 100644 --- a/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java +++ b/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java @@ -1,11 +1,12 @@ package org.apache.catalina.startup; import java.io.IOException; +import java.util.Map; +import org.apache.catalina.RequestAdapter; import org.apache.catalina.RequestMapper; import org.apache.catalina.connector.Connector; import org.apache.catalina.controller.Controller; -import org.apache.catalina.controller.StaticController; -import org.apache.coyote.http11.Mapper; +import org.apache.coyote.http11.Adapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -14,15 +15,15 @@ public class Tomcat { private static final Logger log = LoggerFactory.getLogger(Tomcat.class); private static final int DEFAULT_THREAD_COUNT = 250; - private final Mapper mapper = new RequestMapper(new StaticController()); + private final Adapter adapter; - public Tomcat addController(final String path, final Controller controller) { - mapper.addController(path, controller); - return this; + public Tomcat(final Map controllers) { + final RequestMapper requestMapper = new RequestMapper(controllers); + this.adapter = new RequestAdapter(requestMapper); } public void start() { - final Connector connector = new Connector(mapper, DEFAULT_THREAD_COUNT); + final Connector connector = new Connector(adapter, DEFAULT_THREAD_COUNT); connector.start(); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Mapper.java b/tomcat/src/main/java/org/apache/coyote/http11/Adapter.java similarity index 62% rename from tomcat/src/main/java/org/apache/coyote/http11/Mapper.java rename to tomcat/src/main/java/org/apache/coyote/http11/Adapter.java index 4c1d7b5b5a..3fe8c770ff 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Mapper.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Adapter.java @@ -1,11 +1,9 @@ package org.apache.coyote.http11; -import org.apache.catalina.controller.Controller; import org.apache.coyote.http11.request.HttpRequest; import org.apache.coyote.http11.response.HttpResponse; -public interface Mapper { - void addController(String path, Controller controller); +public interface Adapter { void service(final HttpRequest httpRequest, final HttpResponse httpResponse); } 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 1baf3dd1fd..ef5be04ed3 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -25,13 +25,13 @@ public class Http11Processor implements Runnable, Processor { .sendRedirect("/500.html"); private final Socket connection; - private final Mapper mapper; + private final Adapter adapter; private final HttpRequestParser httpRequestParser = new HttpRequestParser(); private final HttpResponseGenerator httpResponseGenerator = new HttpResponseGenerator(); - public Http11Processor(final Socket connection, final Mapper mapper) { + public Http11Processor(final Socket connection, final Adapter adapter) { this.connection = connection; - this.mapper = mapper; + this.adapter = adapter; } @Override @@ -56,7 +56,7 @@ private void execute(final BufferedReader bufferedReader, final OutputStream out final HttpRequest httpRequest = httpRequestParser.parse(bufferedReader); final HttpResponse httpResponse = new HttpResponse(httpRequest.getHttpVersion()); - mapper.service(httpRequest, httpResponse); + adapter.service(httpRequest, httpResponse); write(outputStream, httpResponse); } catch (final Exception e) { log.error(e.getMessage(), e); 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 20eb10ef99..7fd4f34bf9 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java @@ -6,7 +6,7 @@ import java.io.IOException; import java.net.URL; import java.nio.file.Files; -import org.apache.catalina.RequestMapper; +import org.apache.catalina.RequestAdapter; import org.apache.catalina.controller.StaticController; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; @@ -28,8 +28,8 @@ void index() throws IOException { ""); final var socket = new StubSocket(httpRequest); - final RequestMapper requestMapper = new RequestMapper(new StaticController()); - final Http11Processor processor = new Http11Processor(socket, requestMapper); + final RequestAdapter requestAdapter = new RequestAdapter(new StaticController()); + final Http11Processor processor = new Http11Processor(socket, requestAdapter); // when processor.process(socket); diff --git a/tomcat/src/test/java/org/apache/catalina/SessionTest.java b/tomcat/src/test/java/org/apache/coyote/http11/common/SessionTest.java similarity index 95% rename from tomcat/src/test/java/org/apache/catalina/SessionTest.java rename to tomcat/src/test/java/org/apache/coyote/http11/common/SessionTest.java index 943829a02c..af13cbef4d 100644 --- a/tomcat/src/test/java/org/apache/catalina/SessionTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/common/SessionTest.java @@ -1,8 +1,7 @@ -package org.apache.catalina; +package org.apache.coyote.http11.common; import static org.assertj.core.api.Assertions.assertThat; -import org.apache.coyote.http11.common.Session; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; From 5825bae546db8e2724d22e9b9c74b72c71c542cd Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 11 Sep 2023 17:38:02 +0900 Subject: [PATCH 41/42] =?UTF-8?q?test:=20=EC=84=B8=EC=85=98=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/jwp/controller/LoginControllerTest.java | 5 ++++- .../java/org/apache/coyote/http11/Http11ProcessorTest.java | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java b/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java index b55f9ee1d5..1220d75b83 100644 --- a/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java +++ b/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java @@ -57,8 +57,8 @@ void setUp() { // given final RequestLine requestLine = RequestLine.from("GET /login HTTP/1.1"); final String uuid = UUID.randomUUID().toString(); - final HttpRequest httpRequest = new HttpRequest(requestLine, new Headers(), new RequestBody()); final Session session = new Session(uuid); + final HttpRequest httpRequest = new HttpRequest(requestLine, new Headers(), new RequestBody()); session.setAttribute("user", new User("gugu", "password", "gugu@naver.com")); httpRequest.setSession(session); final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1); @@ -99,6 +99,9 @@ void setUp() { final RequestLine requestLine = RequestLine.from("POST /login HTTP/1.1"); final RequestBody requestBody = RequestBody.from("account=hello&password=world"); final HttpRequest httpRequest = new HttpRequest(requestLine, new Headers(), requestBody); + final String uuid = UUID.randomUUID().toString(); + final Session session = new Session(uuid); + httpRequest.setSession(session); final HttpResponse httpResponse = new HttpResponse(HttpVersion.HTTP_1_1); InMemoryUserRepository.save(new User("hello", "world", "email@email.com")); 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 7fd4f34bf9..ab848a092d 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java @@ -6,7 +6,9 @@ import java.io.IOException; import java.net.URL; import java.nio.file.Files; +import java.util.Collections; import org.apache.catalina.RequestAdapter; +import org.apache.catalina.RequestMapper; import org.apache.catalina.controller.StaticController; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; @@ -28,7 +30,7 @@ void index() throws IOException { ""); final var socket = new StubSocket(httpRequest); - final RequestAdapter requestAdapter = new RequestAdapter(new StaticController()); + final RequestAdapter requestAdapter = new RequestAdapter(new RequestMapper(Collections.emptyMap())); final Http11Processor processor = new Http11Processor(socket, requestAdapter); // when From da4e1f648fd4dcd83cff6db8b31584cb8b1fcc72 Mon Sep 17 00:00:00 2001 From: greeng00se Date: Mon, 11 Sep 2023 17:43:28 +0900 Subject: [PATCH 42/42] =?UTF-8?q?fix:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EA=B5=AC=EB=AC=B8=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/controller/AbstractController.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java b/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java index 530538d515..bcad07bf39 100644 --- a/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java +++ b/tomcat/src/main/java/org/apache/catalina/controller/AbstractController.java @@ -31,10 +31,8 @@ private void routing(final HttpRequest request, final HttpResponse response) { } protected void doPost(HttpRequest request, HttpResponse response) { - response.setHttpStatus(HttpStatus.NOT_FOUND).sendRedirect(NOT_FOUND_PAGE); } protected void doGet(HttpRequest request, HttpResponse response) { - response.setHttpStatus(HttpStatus.NOT_FOUND).sendRedirect(NOT_FOUND_PAGE); } }