From 201e52f3baab02cb78d00ff62facc8e01d956aae Mon Sep 17 00:00:00 2001 From: ReO Date: Tue, 5 Sep 2023 16:28:45 +0900 Subject: [PATCH 01/24] =?UTF-8?q?feat:=20=EC=BA=90=EC=8B=9C=20=ED=95=99?= =?UTF-8?q?=EC=8A=B5=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/cachecontrol/CacheWebConfig.java | 5 +++++ .../com/example/etag/EtagFilterConfiguration.java | 14 ++++++++++---- .../com/example/version/CacheBustingWebConfig.java | 5 +++++ study/src/main/resources/application.yml | 4 ++++ 4 files changed, 24 insertions(+), 4 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..cfd047f69c 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,18 @@ 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) { + WebContentInterceptor webContentInterceptor = new WebContentInterceptor(); + webContentInterceptor.addCacheMapping(CacheControl.noCache().cachePrivate(), "/**"); + 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..fce9249d04 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() { + ShallowEtagHeaderFilter shallowEtagHeaderFilter = new ShallowEtagHeaderFilter(); + FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(shallowEtagHeaderFilter); + filterRegistrationBean.addUrlPatterns("/etag", "/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..d42a257f3d 100644 --- a/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java +++ b/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java @@ -2,9 +2,12 @@ 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; +import java.util.concurrent.TimeUnit; + @Configuration public class CacheBustingWebConfig implements WebMvcConfigurer { @@ -20,6 +23,8 @@ public CacheBustingWebConfig(ResourceVersion version) { @Override public void addResourceHandlers(final ResourceHandlerRegistry registry) { registry.addResourceHandler(PREFIX_STATIC_RESOURCES + "/" + version.getVersion() + "/**") + .setCacheControl(CacheControl.maxAge(365L, TimeUnit.DAYS).cachePublic()) + .setUseLastModified(true) .addResourceLocations("classpath:/static/"); } } diff --git a/study/src/main/resources/application.yml b/study/src/main/resources/application.yml index 4e8655a962..8699cc7ebd 100644 --- a/study/src/main/resources/application.yml +++ b/study/src/main/resources/application.yml @@ -7,3 +7,7 @@ server: max-connections: 1 threads: max: 2 + + compression: + min-response-size: 10 + enabled: true From a3a854323ead9ccf34593e6ca185a7d82cf34eac Mon Sep 17 00:00:00 2001 From: ReO Date: Thu, 7 Sep 2023 11:38:06 +0900 Subject: [PATCH 02/24] =?UTF-8?q?feat:=20=EC=93=B0=EB=A0=88=EB=93=9C=20?= =?UTF-8?q?=ED=95=99=EC=8A=B5=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- study/src/main/resources/application.yml | 2 +- study/src/test/java/thread/stage0/SynchronizationTest.java | 2 +- study/src/test/java/thread/stage0/ThreadPoolsTest.java | 6 +++--- study/src/test/java/thread/stage1/UserServlet.java | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/study/src/main/resources/application.yml b/study/src/main/resources/application.yml index 8699cc7ebd..dbd1a8fca7 100644 --- a/study/src/main/resources/application.yml +++ b/study/src/main/resources/application.yml @@ -4,7 +4,7 @@ handlebars: server: tomcat: accept-count: 1 - max-connections: 1 + max-connections: 2 threads: max: 2 diff --git a/study/src/test/java/thread/stage0/SynchronizationTest.java b/study/src/test/java/thread/stage0/SynchronizationTest.java index 0333c18e3b..b463c2b984 100644 --- a/study/src/test/java/thread/stage0/SynchronizationTest.java +++ b/study/src/test/java/thread/stage0/SynchronizationTest.java @@ -41,7 +41,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..03efdabc8d 100644 --- a/study/src/test/java/thread/stage0/ThreadPoolsTest.java +++ b/study/src/test/java/thread/stage0/ThreadPoolsTest.java @@ -31,8 +31,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 +46,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()); diff --git a/study/src/test/java/thread/stage1/UserServlet.java b/study/src/test/java/thread/stage1/UserServlet.java index b180a84c32..a78e7b4ad4 100644 --- a/study/src/test/java/thread/stage1/UserServlet.java +++ b/study/src/test/java/thread/stage1/UserServlet.java @@ -11,7 +11,7 @@ public void service(final User user) { join(user); } - private void join(final User user) { + private synchronized void join(final User user) { if (!users.contains(user)) { users.add(user); } From b089e99d76ccae39c09dcf4eae917a2e4ed16067 Mon Sep 17 00:00:00 2001 From: ReO Date: Mon, 11 Sep 2023 17:51:12 +0900 Subject: [PATCH 03/24] =?UTF-8?q?feat:=203,4=20=EB=8B=A8=EA=B3=84=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 11 +- tomcat/src/main/java/common/Constants.java | 9 + .../java/common/http/AbstractController.java | 39 +++++ .../main/java/common/http/ContentType.java | 42 +++++ .../src/main/java/common/http/Controller.java | 5 + .../java/common/http/ControllerManager.java | 8 + tomcat/src/main/java/common/http/Cookie.java | 58 +++++++ tomcat/src/main/java/common/http/Cookies.java | 19 ++ .../http11 => common/http}/HttpMethod.java | 2 +- .../http11 => common/http}/HttpStatus.java | 12 +- tomcat/src/main/java/common/http/Request.java | 24 +++ .../src/main/java/common/http/Response.java | 22 +++ .../session => common/http}/Session.java | 2 +- .../src/main/java/nextstep/Application.java | 10 +- .../jwp/controller/HomeController.java | 19 ++ .../jwp/controller/LoginController.java | 54 ++++++ .../jwp/controller/RegisterController.java | 37 ++++ .../http11/session => catalina}/Manager.java | 4 +- .../apache/catalina/connector/Connector.java | 25 ++- .../startup/DynamicControllerManager.java | 29 ++++ .../catalina/startup/SessionManager.java | 50 ++++++ .../org/apache/catalina/startup/Sessions.java | 30 ++++ .../org/apache/catalina/startup/Tomcat.java | 14 +- .../org/apache/coyote/http11/ContentType.java | 18 -- .../apache/coyote/http11/Http11Processor.java | 28 ++- .../coyote/http11/HttpRequestParser.java | 101 ----------- .../coyote/http11/HttpResponseMaker.java | 163 ------------------ .../apache/coyote/http11/RequestMapper.java | 60 ------- .../controller/StaticControllerManager.java | 21 +++ .../controller/StaticResourceController.java | 60 +++++++ .../coyote/http11/request/HttpRequest.java | 76 ++++++++ .../http11/request/HttpRequestBody.java | 24 +++ .../http11/request/HttpRequestHeaders.java | 20 +++ .../HttpRequestLine.java} | 36 ++-- .../http11/request/HttpRequestParser.java | 78 +++++++++ .../coyote/http11/response/HttpResponse.java | 71 ++++++++ .../http11/response/HttpResponseBody.java | 32 ++++ .../http11/response/HttpResponseHeaders.java | 60 +++++++ .../http11/response/HttpStatusLine.java | 34 ++++ .../coyote/http11/session/HttpCookie.java | 50 ------ .../coyote/http11/session/SessionManager.java | 24 --- .../java/common/http/ContentTypeTest.java | 70 ++++++++ .../http/CookieTest.java} | 8 +- .../test/java/common/http/CookiesTest.java | 24 +++ .../test/java/common/http/HttpMethodTest.java | 32 ++++ .../test/java/common/http/SessionTest.java | 41 +++++ .../jwp/controller/HomeControllerTest.java | 37 ++++ .../jwp/controller/LoginControllerTest.java | 61 +++++++ .../startup/DynamicControllerManagerTest.java | 32 ++++ .../coyote/http11/Http11ProcessorTest.java | 30 ++-- .../http11/HttpRequestFirstLineInfoTest.java | 51 ------ .../coyote/http11/HttpResponseMakerTest.java | 98 ----------- .../coyote/http11/RequestMapperTest.java | 37 ---- .../{ => request}/HttpRequestParserTest.java | 41 ++--- 54 files changed, 1352 insertions(+), 691 deletions(-) create mode 100644 tomcat/src/main/java/common/Constants.java create mode 100644 tomcat/src/main/java/common/http/AbstractController.java create mode 100644 tomcat/src/main/java/common/http/ContentType.java create mode 100644 tomcat/src/main/java/common/http/Controller.java create mode 100644 tomcat/src/main/java/common/http/ControllerManager.java create mode 100644 tomcat/src/main/java/common/http/Cookie.java create mode 100644 tomcat/src/main/java/common/http/Cookies.java rename tomcat/src/main/java/{org/apache/coyote/http11 => common/http}/HttpMethod.java (91%) rename tomcat/src/main/java/{org/apache/coyote/http11 => common/http}/HttpStatus.java (62%) create mode 100644 tomcat/src/main/java/common/http/Request.java create mode 100644 tomcat/src/main/java/common/http/Response.java rename tomcat/src/main/java/{org/apache/coyote/http11/session => common/http}/Session.java (93%) create mode 100644 tomcat/src/main/java/nextstep/jwp/controller/HomeController.java create mode 100644 tomcat/src/main/java/nextstep/jwp/controller/LoginController.java create mode 100644 tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java rename tomcat/src/main/java/org/apache/{coyote/http11/session => catalina}/Manager.java (96%) create mode 100644 tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java create mode 100644 tomcat/src/main/java/org/apache/catalina/startup/SessionManager.java create mode 100644 tomcat/src/main/java/org/apache/catalina/startup/Sessions.java delete mode 100644 tomcat/src/main/java/org/apache/coyote/http11/ContentType.java delete mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpRequestParser.java delete mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpResponseMaker.java delete mode 100644 tomcat/src/main/java/org/apache/coyote/http11/RequestMapper.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/controller/StaticControllerManager.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java 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/HttpRequestBody.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestHeaders.java rename tomcat/src/main/java/org/apache/coyote/http11/{HttpRequestFirstLineInfo.java => request/HttpRequestLine.java} (59%) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestParser.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseBody.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatusLine.java delete mode 100644 tomcat/src/main/java/org/apache/coyote/http11/session/HttpCookie.java delete mode 100644 tomcat/src/main/java/org/apache/coyote/http11/session/SessionManager.java create mode 100644 tomcat/src/test/java/common/http/ContentTypeTest.java rename tomcat/src/test/java/{org/apache/coyote/http11/session/HttpCookieTest.java => common/http/CookieTest.java} (70%) create mode 100644 tomcat/src/test/java/common/http/CookiesTest.java create mode 100644 tomcat/src/test/java/common/http/HttpMethodTest.java create mode 100644 tomcat/src/test/java/common/http/SessionTest.java create mode 100644 tomcat/src/test/java/nextstep/jwp/controller/HomeControllerTest.java create mode 100644 tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java create mode 100644 tomcat/src/test/java/org/apache/catalina/startup/DynamicControllerManagerTest.java delete mode 100644 tomcat/src/test/java/org/apache/coyote/http11/HttpRequestFirstLineInfoTest.java delete mode 100644 tomcat/src/test/java/org/apache/coyote/http11/HttpResponseMakerTest.java delete mode 100644 tomcat/src/test/java/org/apache/coyote/http11/RequestMapperTest.java rename tomcat/src/test/java/org/apache/coyote/http11/{ => request}/HttpRequestParserTest.java (62%) diff --git a/README.md b/README.md index f1ace3f84d..9ec86e959a 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,19 @@ -[x] Cookie에 JSESSIONID 값 저장하기 -[x] Session 구현하기 +## 3단계 - 리팩터링 +-[x] HttpRequest 클래스 구현 +-[x] HttpResponse 클래스 구현 +-[x] Controller 인터페이스 구현 +-[x] AbstractController 추상 클래스 구현 +-[x] RequestMapping 클래스 구현 + ### TODO -[x] 예외 처리 -[x] 매직 넘버 정리 -[x] 리팩터링(클래스 분리, 패키지 정리) --[ ] 상태코드, http 메서드 종류 추가 +-[x] 상태코드, http 메서드 종류 추가 -[ ] 테스트 - 회원가입, 정적리소스 -[ ] 로깅 +-[ ] 예외 처리 +-[ ] 캐싱 구현해보기 diff --git a/tomcat/src/main/java/common/Constants.java b/tomcat/src/main/java/common/Constants.java new file mode 100644 index 0000000000..2b95f7c465 --- /dev/null +++ b/tomcat/src/main/java/common/Constants.java @@ -0,0 +1,9 @@ +package common; + +public class Constants { + + public static final String SPACE = " "; + public static final String CRLF = "\r\n"; + + private Constants() {} +} diff --git a/tomcat/src/main/java/common/http/AbstractController.java b/tomcat/src/main/java/common/http/AbstractController.java new file mode 100644 index 0000000000..5401d8f5b1 --- /dev/null +++ b/tomcat/src/main/java/common/http/AbstractController.java @@ -0,0 +1,39 @@ +package common.http; + +import java.util.EnumMap; +import java.util.Map; +import java.util.function.BiConsumer; + +import static common.http.HttpMethod.DELETE; +import static common.http.HttpMethod.GET; +import static common.http.HttpMethod.PATCH; +import static common.http.HttpMethod.POST; +import static common.http.HttpMethod.PUT; + +public abstract class AbstractController implements Controller { + + private final Map> methodMapping = new EnumMap<>(HttpMethod.class); + + protected AbstractController() { + methodMapping.put(GET, this::doGet); + methodMapping.put(POST, this::doPost); + methodMapping.put(PUT, this::doPut); + methodMapping.put(PATCH, this::doPatch); + methodMapping.put(DELETE, this::doDelete); + } + + @Override + public void service(Request request, Response response) { + methodMapping.get(request.getHttpMethod()).accept(request, response); + } + + protected void doGet(Request request, Response response) { /* NOOP */ } + + protected void doPost(Request request, Response response) { /* NOOP */ } + + protected void doPut(Request request, Response response) { /* NOOP */ } + + protected void doPatch(Request request, Response response) { /* NOOP */ } + + protected void doDelete(Request request, Response response) { /* NOOP */ } +} diff --git a/tomcat/src/main/java/common/http/ContentType.java b/tomcat/src/main/java/common/http/ContentType.java new file mode 100644 index 0000000000..caf7787d66 --- /dev/null +++ b/tomcat/src/main/java/common/http/ContentType.java @@ -0,0 +1,42 @@ +package common.http; + +import java.util.Arrays; + +public enum ContentType { + HTML("text/html", "html"), + CSS("text/css", "css"), + JS("text/javascript", "js"), + ICO("image/ico", "ico"), + ; + + public static final String DELIMITER_FOR_EXTENSION = "."; + + private final String type; + private final String extension; + + ContentType(String type, String extension) { + this.type = type; + this.extension = extension; + } + + public static ContentType findByExtension(String extension) { + return Arrays.stream(ContentType.values()) + .filter(contentType -> contentType.extension.equals(extension)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("유효하지 않은 확장자명 입니다.")); + } + + public static ContentType findByPath(String path) { + int indexBeforeExtension = path.lastIndexOf(DELIMITER_FOR_EXTENSION); + if (indexBeforeExtension == -1) { + throw new IllegalArgumentException("파일의 확장자명이 없습니다."); + } + + String extension = path.substring(indexBeforeExtension + 1); + return findByExtension(extension); + } + + public String getType() { + return type; + } +} diff --git a/tomcat/src/main/java/common/http/Controller.java b/tomcat/src/main/java/common/http/Controller.java new file mode 100644 index 0000000000..30459ae8ae --- /dev/null +++ b/tomcat/src/main/java/common/http/Controller.java @@ -0,0 +1,5 @@ +package common.http; + +public interface Controller { + void service(Request request, Response response); +} diff --git a/tomcat/src/main/java/common/http/ControllerManager.java b/tomcat/src/main/java/common/http/ControllerManager.java new file mode 100644 index 0000000000..ed2e7ff96c --- /dev/null +++ b/tomcat/src/main/java/common/http/ControllerManager.java @@ -0,0 +1,8 @@ +package common.http; + +public interface ControllerManager { + + void add(String path, Controller controller); + + void service(Request request, Response response); +} diff --git a/tomcat/src/main/java/common/http/Cookie.java b/tomcat/src/main/java/common/http/Cookie.java new file mode 100644 index 0000000000..5a2c436be2 --- /dev/null +++ b/tomcat/src/main/java/common/http/Cookie.java @@ -0,0 +1,58 @@ +package common.http; + +import java.util.HashMap; +import java.util.Map; + +public class Cookie { + + private static final String SEPARATOR = "; "; + private static final String DELIMITER = "="; + + private static final int ATTRIBUTE_INDEX = 0; + private static final int VALUE_INDEX = 1; + + private final Map items; + + private Cookie(Map items) { + this.items = items; + } + + public static Cookie from(String cookieHeader) { + if (cookieHeader == null || cookieHeader.isEmpty()) { + return new Cookie(new HashMap<>()); + } + return new Cookie(parse(cookieHeader)); + } + + private static Map parse(String values) { + Map items = new HashMap<>(); + String[] attributesAndValues = values.split(SEPARATOR); + for (String cookie : attributesAndValues) { + String[] attributeAndValue = cookie.split(DELIMITER); + items.put(attributeAndValue[ATTRIBUTE_INDEX], attributeAndValue[VALUE_INDEX].trim()); + } + return items; + } + + public void addAttribute(String attribute, String value) { + items.put(attribute, value); + } + + boolean hasAttribute(String attribute) { + return items.containsKey(attribute); + } + + String getAttribute(String attribute) { + return items.get(attribute); + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder(); + for (Map.Entry item : items.entrySet()) { + stringBuilder.append(item.getKey()).append(DELIMITER).append(item.getValue()).append(SEPARATOR); + } + String cookie = stringBuilder.toString(); + return cookie.substring(0, cookie.length()-2); + } +} diff --git a/tomcat/src/main/java/common/http/Cookies.java b/tomcat/src/main/java/common/http/Cookies.java new file mode 100644 index 0000000000..a5b2e5476e --- /dev/null +++ b/tomcat/src/main/java/common/http/Cookies.java @@ -0,0 +1,19 @@ +package common.http; + +public class Cookies { + + private static final String JSESSIONID = "JSESSIONID"; + + private Cookies() {} + + public static Cookie ofJSessionId(String id) { + return Cookie.from(JSESSIONID + "=" + id); + } + + public static String getJsessionid(Cookie cookie) { + if (cookie.hasAttribute(JSESSIONID)) { + return cookie.getAttribute(JSESSIONID); + } + throw new IllegalArgumentException("쿠키에 세션 아이디가 없습니다."); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java b/tomcat/src/main/java/common/http/HttpMethod.java similarity index 91% rename from tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java rename to tomcat/src/main/java/common/http/HttpMethod.java index 1b48458635..a364a3cf69 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java +++ b/tomcat/src/main/java/common/http/HttpMethod.java @@ -1,4 +1,4 @@ -package org.apache.coyote.http11; +package common.http; public enum HttpMethod { GET, diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpStatus.java b/tomcat/src/main/java/common/http/HttpStatus.java similarity index 62% rename from tomcat/src/main/java/org/apache/coyote/http11/HttpStatus.java rename to tomcat/src/main/java/common/http/HttpStatus.java index 8be9ed417b..381610c94b 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpStatus.java +++ b/tomcat/src/main/java/common/http/HttpStatus.java @@ -1,4 +1,4 @@ -package org.apache.coyote.http11; +package common.http; public enum HttpStatus { OK("OK", 200), @@ -8,16 +8,16 @@ public enum HttpStatus { NOT_FOUND("Not Found", 404), INTERNAL_SERVER_ERROR("Internal Server Error", 500); - private final String status; + private final String statusMessage; private final int statusCode; - HttpStatus(String status, int statusCode) { - this.status = status; + HttpStatus(String statusMessage, int statusCode) { + this.statusMessage = statusMessage; this.statusCode = statusCode; } - public String getStatus() { - return status; + public String getStatusMessage() { + return statusMessage; } public int getStatusCode() { diff --git a/tomcat/src/main/java/common/http/Request.java b/tomcat/src/main/java/common/http/Request.java new file mode 100644 index 0000000000..576ec8270e --- /dev/null +++ b/tomcat/src/main/java/common/http/Request.java @@ -0,0 +1,24 @@ +package common.http; + +public interface Request { + + HttpMethod getHttpMethod(); + + String getVersionOfTheProtocol(); + + String getAccount(); + + String getPassword(); + + Session getSession(boolean create); + + Session getSession(); + + String getCookie(); + + String getPath(); + + boolean hasValidSession(); + + String getEmail(); +} diff --git a/tomcat/src/main/java/common/http/Response.java b/tomcat/src/main/java/common/http/Response.java new file mode 100644 index 0000000000..cca6cbd5a6 --- /dev/null +++ b/tomcat/src/main/java/common/http/Response.java @@ -0,0 +1,22 @@ +package common.http; + +public interface Response { + + void addVersionOfTheProtocol(String versionOfTheProtocol); + + void addHttpStatus(HttpStatus httpStatus); + + void addContentType(ContentType contentType); + + void sendRedirect(String redirectURL); + + void addStaticResourcePath(String name); + + void addCookie(Cookie cookie); + + boolean hasStaticResourcePath(); + + String getStaticResourcePath(); + + void addBody(String body); +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/session/Session.java b/tomcat/src/main/java/common/http/Session.java similarity index 93% rename from tomcat/src/main/java/org/apache/coyote/http11/session/Session.java rename to tomcat/src/main/java/common/http/Session.java index 25197dba4b..a5deecd9c1 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/session/Session.java +++ b/tomcat/src/main/java/common/http/Session.java @@ -1,4 +1,4 @@ -package org.apache.coyote.http11.session; +package common.http; import java.util.HashMap; import java.util.Map; diff --git a/tomcat/src/main/java/nextstep/Application.java b/tomcat/src/main/java/nextstep/Application.java index 3dd7593507..6b2ab04917 100644 --- a/tomcat/src/main/java/nextstep/Application.java +++ b/tomcat/src/main/java/nextstep/Application.java @@ -1,11 +1,19 @@ 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(); + final Tomcat 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 new file mode 100644 index 0000000000..85929c9e21 --- /dev/null +++ b/tomcat/src/main/java/nextstep/jwp/controller/HomeController.java @@ -0,0 +1,19 @@ +package nextstep.jwp.controller; + +import common.http.AbstractController; +import common.http.Request; +import common.http.Response; + +import static common.http.ContentType.HTML; +import static common.http.HttpStatus.OK; + +public class HomeController extends AbstractController { + + @Override + protected void doGet(Request request, Response response) { + response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); + response.addHttpStatus(OK); + response.addContentType(HTML); + response.addBody("Hello world!"); + } +} 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..6f70b84920 --- /dev/null +++ b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java @@ -0,0 +1,54 @@ +package nextstep.jwp.controller; + +import common.http.AbstractController; +import common.http.Cookies; +import common.http.Request; +import common.http.Response; +import common.http.Session; +import nextstep.jwp.db.InMemoryUserRepository; +import nextstep.jwp.model.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static common.http.HttpStatus.FOUND; +import static common.http.HttpStatus.UNAUTHORIZED; + +public class LoginController extends AbstractController { + + private static final Logger log = LoggerFactory.getLogger(LoginController.class); + + @Override + protected void doGet(Request request, Response response) { + if (request.hasValidSession()) { + Session session = request.getSession(); + response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); + response.addHttpStatus(FOUND); + response.addCookie(Cookies.ofJSessionId(session.getId())); + response.sendRedirect("/index.html"); + return; + } + + response.addStaticResourcePath("/login.html"); + } + + @Override + protected void doPost(Request request, Response response) { + User user = InMemoryUserRepository.findByAccount(request.getAccount()) + .orElseThrow(() -> new IllegalArgumentException("회원 정보가 존재하지 않습니다.")); + + if (!user.checkPassword(request.getPassword())) { + response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); + response.addHttpStatus(UNAUTHORIZED); + response.sendRedirect("/401.html"); + return; + } + + log.info("user: {}", user); + final Session session = request.getSession(true); + session.setAttribute("user", user); + response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); + response.addHttpStatus(FOUND); + response.addCookie(Cookies.ofJSessionId(session.getId())); + response.sendRedirect("/index.html"); + } +} 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..33c75f8109 --- /dev/null +++ b/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java @@ -0,0 +1,37 @@ +package nextstep.jwp.controller; + +import common.http.AbstractController; +import common.http.Cookies; +import common.http.Request; +import common.http.Response; +import common.http.Session; +import nextstep.jwp.db.InMemoryUserRepository; +import nextstep.jwp.model.User; + +import static common.http.HttpStatus.FOUND; + +public class RegisterController extends AbstractController { + + @Override + protected void doGet(Request request, Response response) { + // 로그아웃이 없으므로 모든 요청에 대해 진행합니다. + response.addStaticResourcePath("/register.html"); + } + + @Override + protected void doPost(Request request, Response response) { + if (InMemoryUserRepository.findByAccount(request.getAccount()).isPresent()) { + throw new IllegalArgumentException("이미 가입된 회원입니다."); + } + + User user = new User(request.getAccount(), request.getPassword(), request.getEmail()); + InMemoryUserRepository.save(user); + + final Session session = request.getSession(true); + session.setAttribute("user", user); + response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); + response.addHttpStatus(FOUND); + response.addCookie(Cookies.ofJSessionId(session.getId())); + response.sendRedirect("/index.html"); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/session/Manager.java b/tomcat/src/main/java/org/apache/catalina/Manager.java similarity index 96% rename from tomcat/src/main/java/org/apache/coyote/http11/session/Manager.java rename to tomcat/src/main/java/org/apache/catalina/Manager.java index 0929ea39c9..be621e9da5 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/session/Manager.java +++ b/tomcat/src/main/java/org/apache/catalina/Manager.java @@ -1,4 +1,6 @@ -package org.apache.coyote.http11.session; +package org.apache.catalina; + +import common.http.Session; import java.io.IOException; diff --git a/tomcat/src/main/java/org/apache/catalina/connector/Connector.java b/tomcat/src/main/java/org/apache/catalina/connector/Connector.java index 3b2c4dda7c..5f9ad66f12 100644 --- a/tomcat/src/main/java/org/apache/catalina/connector/Connector.java +++ b/tomcat/src/main/java/org/apache/catalina/connector/Connector.java @@ -1,5 +1,6 @@ package org.apache.catalina.connector; +import common.http.ControllerManager; import org.apache.coyote.http11.Http11Processor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -8,6 +9,8 @@ import java.io.UncheckedIOException; import java.net.ServerSocket; import java.net.Socket; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; public class Connector implements Runnable { @@ -15,17 +18,23 @@ public class Connector implements Runnable { private static final int DEFAULT_PORT = 8080; private static final int DEFAULT_ACCEPT_COUNT = 100; + private static final int DEFAULT_THREAD_COUNT = 250; private final ServerSocket serverSocket; + private final ControllerManager controllerManager; + private final ExecutorService executorService; + private boolean stopped; - public Connector() { - this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT); + public Connector(ControllerManager controllerManager) { + this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, controllerManager, DEFAULT_THREAD_COUNT); } - public Connector(final int port, final int acceptCount) { + public Connector(final int port, final int acceptCount, ControllerManager controllerManager, final int maxThreads) { this.serverSocket = createServerSocket(port, acceptCount); this.stopped = false; + this.controllerManager = controllerManager; + this.executorService = Executors.newFixedThreadPool(maxThreads); } private ServerSocket createServerSocket(final int port, final int acceptCount) { @@ -39,7 +48,7 @@ private ServerSocket createServerSocket(final int port, final int acceptCount) { } public void start() { - var thread = new Thread(this); + Thread thread = new Thread(this); thread.setDaemon(true); thread.start(); stopped = false; @@ -66,8 +75,8 @@ private void process(final Socket connection) { if (connection == null) { return; } - var processor = new Http11Processor(connection); - new Thread(processor).start(); + Runnable processor = new Http11Processor(connection, controllerManager); + executorService.execute(processor); } public void stop() { @@ -80,8 +89,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/DynamicControllerManager.java b/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java new file mode 100644 index 0000000000..a8d4f17c18 --- /dev/null +++ b/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java @@ -0,0 +1,29 @@ +package org.apache.catalina.startup; + +import common.http.Controller; +import common.http.ControllerManager; +import common.http.Request; +import common.http.Response; + +import java.util.HashMap; +import java.util.Map; + +public class DynamicControllerManager implements ControllerManager { + + private static final Map mapper = new HashMap<>(); + + @Override + public void add(String path, Controller controller) { + mapper.put(path, controller); + } + + @Override + public void service(Request request, Response response) { + Controller controller = mapper.get(request.getPath()); + if (controller == null) { + return; + } + + controller.service(request, response); + } +} diff --git a/tomcat/src/main/java/org/apache/catalina/startup/SessionManager.java b/tomcat/src/main/java/org/apache/catalina/startup/SessionManager.java new file mode 100644 index 0000000000..8e07946345 --- /dev/null +++ b/tomcat/src/main/java/org/apache/catalina/startup/SessionManager.java @@ -0,0 +1,50 @@ +package org.apache.catalina.startup; + +import ch.qos.logback.core.spi.LifeCycle; +import common.http.Session; +import org.apache.catalina.Manager; + +public class SessionManager implements Manager, LifeCycle { + + private static final Sessions sessions = new Sessions(); + private boolean isStarted = false; + + public SessionManager() { + start(); + } + + @Override + public void add(Session session) { + sessions.add(session.getId(), session); + } + + @Override + public Session findSession(String id) { + return sessions.find(id); + } + + @Override + public void remove(Session session) { + sessions.remove(session.getId()); + } + + @Override + public void start() { + if (!isStarted) { + isStarted = true; + } + } + + @Override + public void stop() { + if (isStarted) { + sessions.clear(); + isStarted = false; + } + } + + @Override + public boolean isStarted() { + return isStarted; + } +} diff --git a/tomcat/src/main/java/org/apache/catalina/startup/Sessions.java b/tomcat/src/main/java/org/apache/catalina/startup/Sessions.java new file mode 100644 index 0000000000..88a3cca3ca --- /dev/null +++ b/tomcat/src/main/java/org/apache/catalina/startup/Sessions.java @@ -0,0 +1,30 @@ +package org.apache.catalina.startup; + +import common.http.Session; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class Sessions { + + private static final Map idAndSessions = new ConcurrentHashMap<>(); + + void add(String id, Session session) { + idAndSessions.put(id, session); + } + + Session find(String id) { + if (!idAndSessions.containsKey(id)) { + return null; + } + return idAndSessions.get(id); + } + + void remove(String id) { + idAndSessions.remove(id); + } + + public void clear() { + idAndSessions.clear(); + } +} 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..88a9356d67 100644 --- a/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java +++ b/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java @@ -1,5 +1,7 @@ package org.apache.catalina.startup; +import common.http.Controller; +import common.http.ControllerManager; import org.apache.catalina.connector.Connector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,8 +12,18 @@ public class Tomcat { private static final Logger log = LoggerFactory.getLogger(Tomcat.class); + private final ControllerManager controllerManager; + + public Tomcat() { + this.controllerManager = new DynamicControllerManager(); + } + + public void addController(String path, Controller controller) { + controllerManager.add(path, controller); + } + public void start() { - var connector = new Connector(); + Connector connector = new Connector(controllerManager); connector.start(); try { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java b/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java deleted file mode 100644 index 4e97afeed3..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.apache.coyote.http11; - -public enum ContentType { - HTML("text/html"), - CSS("text/css"), - JS("text/javascript"), - ICO("image/ico"); - - private final String type; - - ContentType(String type) { - this.type = type; - } - - public String getType() { - return type; - } -} 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 1d2f83835b..e7dc98390c 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -1,25 +1,29 @@ package org.apache.coyote.http11; -import nextstep.jwp.exception.UncheckedServletException; +import common.http.ControllerManager; import org.apache.coyote.Processor; -import org.apache.coyote.http11.session.SessionManager; +import org.apache.coyote.http11.controller.StaticControllerManager; +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.request.HttpRequestParser; +import org.apache.coyote.http11.response.HttpResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.BufferedReader; -import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; public class Http11Processor implements Runnable, Processor { private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); - private static final SessionManager sessionManager = new SessionManager(); private final Socket connection; - public Http11Processor(Socket connection) { + private ControllerManager controllerManager; + + public Http11Processor(Socket connection, ControllerManager controllerManager) { this.connection = connection; + this.controllerManager = controllerManager; } @Override @@ -34,13 +38,19 @@ public void process(final Socket connection) { final var outputStream = connection.getOutputStream(); final var reader = new BufferedReader(new InputStreamReader(inputStream)) ) { - HttpRequestParser requestParser = HttpRequestParser.from(reader); + HttpRequest httpRequest = HttpRequestParser.parse(reader); + HttpResponse httpResponse = new HttpResponse(); + + controllerManager.service(httpRequest, httpResponse); - String response = HttpResponseMaker.makeFrom(requestParser, sessionManager); + if (httpRequest.hasStaticResourcePath() || httpResponse.hasStaticResourcePath()) { + controllerManager = new StaticControllerManager(); + controllerManager.service(httpRequest, httpResponse); + } - outputStream.write(response.getBytes()); + outputStream.write(httpResponse.toString().getBytes()); outputStream.flush(); - } catch (IOException | UncheckedServletException | IllegalArgumentException e) { + } catch (Exception e) { log.error(e.getMessage(), e); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestParser.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestParser.java deleted file mode 100644 index f5923595ca..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestParser.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.apache.coyote.http11; - -import java.io.BufferedReader; -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Collectors; - -public class HttpRequestParser { - - private static final String DELIMITER = ":"; - private static final String PARAMS_DELIMITER = "&"; - private static final String PARAM_VALUE_DELIMITER = "="; - - private static final int PROPERTY_INDEX = 0; - private static final int VALUE_INDEX = 1; - private static final int START_AFTER_SPACE = 2; - - private final HttpRequestFirstLineInfo httpRequestFirstLineInfo; - private final Map headers; - private final Map body; - - - public HttpRequestParser( - HttpRequestFirstLineInfo httpRequestFirstLineInfo, - Map headers, - Map body - ) { - this.httpRequestFirstLineInfo = httpRequestFirstLineInfo; - this.headers = headers; - this.body = body; - } - - public static HttpRequestParser from(BufferedReader reader) throws IOException { - HttpRequestFirstLineInfo firstLineInfo = parseFirstLine(reader); - Map headers = parseHeaders(reader); - Map body = parseBody(reader, headers); - - return new HttpRequestParser(firstLineInfo, headers, body); - } - - private static HttpRequestFirstLineInfo parseFirstLine(BufferedReader reader) throws IOException { - String requestLine = reader.readLine(); - if (requestLine == null) { - throw new IOException("요청에 Reqeust-line이 없습니다."); - } - return HttpRequestFirstLineInfo.from(requestLine); - } - - private static Map parseHeaders(BufferedReader reader) throws IOException { - Map headers = new HashMap<>(); - - String headerLine; - while ((headerLine = reader.readLine()) != null && !headerLine.isEmpty()) { - int indexOfDelimiter = headerLine.indexOf(DELIMITER); - if (indexOfDelimiter != -1) { - String property = headerLine.substring(0, indexOfDelimiter); - String value = headerLine.substring(indexOfDelimiter + START_AFTER_SPACE); - headers.put(property, value); - } - } - - return headers; - } - - private static Map parseBody(BufferedReader reader, Map headers) throws IOException { - int contentLength = Integer.parseInt(headers.getOrDefault("Content-Length", "0")); - - if (contentLength > 0) { - char[] buffer = new char[contentLength]; - reader.read(buffer, 0, contentLength); - String requestBody = new String(buffer); - - Map body = Arrays.stream(requestBody.split(PARAMS_DELIMITER)) - .map(property -> property.split(PARAM_VALUE_DELIMITER)) - .filter(propertyAndValue -> propertyAndValue.length == 2) - .collect(Collectors.toMap(data -> data[PROPERTY_INDEX], data -> data[VALUE_INDEX])); - - if (body.size() != requestBody.split(PARAMS_DELIMITER).length) { - throw new IllegalArgumentException("요청의 바디가 잘못되었습니다."); - } - - return body; - } - - return new HashMap<>(); - } - - public HttpRequestFirstLineInfo getHttpRequestFirstLineInfo() { - return httpRequestFirstLineInfo; - } - - public Map getHeaders() { - return headers; - } - - public Map getBody() { - return body; - } -} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponseMaker.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponseMaker.java deleted file mode 100644 index e499dbd997..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponseMaker.java +++ /dev/null @@ -1,163 +0,0 @@ -package org.apache.coyote.http11; - -import nextstep.jwp.db.InMemoryUserRepository; -import nextstep.jwp.model.User; -import org.apache.coyote.http11.session.HttpCookie; -import org.apache.coyote.http11.session.Session; -import org.apache.coyote.http11.session.SessionManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Map; - -import static org.apache.coyote.http11.RequestMapper.*; - -public class HttpResponseMaker { - - public static final String COOKIE = "Cookie"; - private static final String CRLF = "\r\n"; - private static final String SPACE = " "; - private static final String ACCOUNT = "account"; - private static final String PASSWORD = "password"; - private static final String STATIC_RESOURCE_DIR = "static"; - private static final String EMAIL = "email"; - private static final Logger log = LoggerFactory.getLogger(HttpResponseMaker.class); - - public static String makeFrom(HttpRequestParser httpRequestParser, SessionManager sessionManager) throws IOException { - HttpRequestFirstLineInfo firstLineInfo = httpRequestParser.getHttpRequestFirstLineInfo(); - RequestMapper mapper = RequestMapper.findMapper(firstLineInfo); - Map headers = httpRequestParser.getHeaders(); - Map body = httpRequestParser.getBody(); - - if (mapper == LOG_IN) { - HttpCookie cookie = HttpCookie.from(headers.get(COOKIE)); - - if (hasValidCookie(sessionManager, cookie)) { - return buildRedirectResponse(firstLineInfo, RedirectLocation.LOG_IN_SUCCESS, mapper); - } - - return buildResponse(firstLineInfo, mapper, readStaticResource(mapper)); - } - - if (mapper == LOG_IN_WITH_INFOS) { - return handleLoginRequest(firstLineInfo, headers, body, mapper, sessionManager); - } - - if (mapper == REGISTER_WITH_INFOS) { - return handleRegisterRequest(firstLineInfo, body, mapper); - } - - String responseBody = "Hello world!"; - - if (!mapper.getPath().equals("/")) { - responseBody = readStaticResource(mapper); - } - - return buildResponse(firstLineInfo, mapper, responseBody); - } - - private static boolean hasValidCookie(SessionManager sessionManager, HttpCookie cookie) { - return cookie.hasJSessionId() && sessionManager.findSession(cookie.getJSessionId()) != null; - } - - private static String handleLoginRequest( - HttpRequestFirstLineInfo firstLineInfo, - Map headers, - Map body, - RequestMapper mapper, - SessionManager sessionManager - ) { - User user = InMemoryUserRepository.findByAccount(body.get(ACCOUNT)) - .orElseThrow(() -> new IllegalArgumentException("회원 정보가 존재하지 않습니다.")); - - if (!user.checkPassword(body.get(PASSWORD))) { - return buildRedirectResponse(firstLineInfo, RedirectLocation.LOG_IN_FAIL, mapper); - } - log.info("user: {}", user); - - String cookieHeader = headers.get(COOKIE); - HttpCookie cookie = HttpCookie.from(cookieHeader); - cookie.bake(); - Session session = new Session(cookie.getJSessionId()); - session.setAttribute("user", user); - sessionManager.add(session); - - return String.join(CRLF, - buildFirstLine(firstLineInfo, mapper), - "Location:" + SPACE + RedirectLocation.LOG_IN_SUCCESS.getRedirectUrl(), - "Set-Cookie:" + SPACE + "JSESSIONID=" + session.getId()); - } - - private static String handleRegisterRequest( - HttpRequestFirstLineInfo firstLineInfo, - Map body, - RequestMapper mapper - ) { - if (InMemoryUserRepository.findByAccount(body.get(ACCOUNT)).isPresent()) { - throw new IllegalArgumentException("이미 가입된 회원입니다."); - } - - User user = new User(body.get(ACCOUNT), body.get(PASSWORD), body.get(EMAIL)); - InMemoryUserRepository.save(user); - - return buildRedirectResponse(firstLineInfo, RedirectLocation.REGISTER, mapper); - } - - private static String readStaticResource(RequestMapper mapper) throws IOException { - ClassLoader classLoader = HttpResponseMaker.class.getClassLoader(); - URL resource = classLoader.getResource(STATIC_RESOURCE_DIR + mapper.getPath()); - - if (resource == null) { - throw new FileNotFoundException("해당하는 파일을 찾을 수 없습니다."); - } - - Path path = new File(resource.getFile()).toPath(); - - return new String(Files.readAllBytes(path)); - } - - private static String buildRedirectResponse( - HttpRequestFirstLineInfo firstLineInfo, - RedirectLocation redirectLocation, - RequestMapper mapper - ) { - return String.join(CRLF, - buildFirstLine(firstLineInfo, mapper), - "Location:" + SPACE + redirectLocation.getRedirectUrl()); - } - - private static String buildResponse(HttpRequestFirstLineInfo firstLineInfo, RequestMapper mapper, String responseBody) { - return String.join(CRLF, - buildFirstLine(firstLineInfo, mapper), - "Content-Type:" + SPACE + mapper.getContentType().getType() + ";charset=utf-8" + SPACE, - "Content-Length:" + SPACE + responseBody.getBytes().length + SPACE, - "", - responseBody); - } - - private static String buildFirstLine(HttpRequestFirstLineInfo firstLineInfo, RequestMapper mapper) { - return String.join(CRLF, firstLineInfo.getVersionOfTheProtocol() + SPACE + mapper.getHttpStatus().getStatusCode() + SPACE + mapper.getHttpStatus().getStatus() + SPACE); - } - - private enum RedirectLocation { - LOG_IN_SUCCESS("/index.html"), - LOG_IN_FAIL("/401.html"), - REGISTER("/index.html"); - - private final String redirectUrl; - - RedirectLocation(String redirectUrl) { - this.redirectUrl = redirectUrl; - } - - public String getRedirectUrl() { - return redirectUrl; - } - } -} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/RequestMapper.java b/tomcat/src/main/java/org/apache/coyote/http11/RequestMapper.java deleted file mode 100644 index 30579bfa26..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/RequestMapper.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.apache.coyote.http11; - -import java.util.Arrays; - -import static org.apache.coyote.http11.ContentType.*; -import static org.apache.coyote.http11.HttpMethod.GET; -import static org.apache.coyote.http11.HttpMethod.POST; -import static org.apache.coyote.http11.HttpStatus.FOUND; -import static org.apache.coyote.http11.HttpStatus.OK; - -public enum RequestMapper { - HOME("/", "/", GET, OK, HTML), - INDEX("/index.html", "/index.html", GET, OK, HTML), - SCRIPTS_JS("/js/scripts.js", "/js/scripts.js", GET, OK, JS), - CHART_AREA_JS("/assets/chart-area.js", "/assets/chart-area.js", GET, OK, JS), - CHART_BAR_JS("/assets/chart-bar.js", "/assets/chart-bar.js", GET, OK, JS), - CHART_PIE_JS("/assets/chart-pie.js", "/assets/chart-pie.js", GET, OK, JS), - FAVICON("/favicon.ico", "/favicon.ico", GET, OK, ICO), - CSS("/css/styles.css", "/css/styles.css", GET, OK, ContentType.CSS), - LOG_IN("/login", "/login.html", GET, FOUND, HTML), - LOG_IN_WITH_INFOS("/login", null, POST, FOUND, HTML), - UNAUTHORIZED("/401.html", "/401.html", GET, OK, HTML), - REGISTER("/register", "/register.html", GET, OK, HTML), - REGISTER_WITH_INFOS("/register", null, POST, FOUND, HTML), - ; - - private final String uri; - private final String path; - private final HttpMethod httpMethod; - private final HttpStatus httpStatus; - private final ContentType contentType; - - RequestMapper(String uri, String path, HttpMethod httpMethod, HttpStatus httpStatus, ContentType contentType) { - this.uri = uri; - this.path = path; - this.httpMethod = httpMethod; - this.httpStatus = httpStatus; - this.contentType = contentType; - } - - public static RequestMapper findMapper(HttpRequestFirstLineInfo httpRequestFirstLineInfo) { - return Arrays.stream(RequestMapper.values()) - .filter(requestMapper -> httpRequestFirstLineInfo.getHttpMethod().equals(requestMapper.httpMethod)) - .filter(requestMapper -> httpRequestFirstLineInfo.getUri().equals(requestMapper.uri)) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException("잘못된 요청입니다.")); - } - - public String getPath() { - return path; - } - - public HttpStatus getHttpStatus() { - return httpStatus; - } - - public ContentType getContentType() { - return contentType; - } -} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticControllerManager.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticControllerManager.java new file mode 100644 index 0000000000..7cb1d1d5cd --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticControllerManager.java @@ -0,0 +1,21 @@ +package org.apache.coyote.http11.controller; + +import common.http.Controller; +import common.http.ControllerManager; +import common.http.Request; +import common.http.Response; + +public class StaticControllerManager implements ControllerManager { + + private static final Controller controller = new StaticResourceController(); + + @Override + public void add(String path, Controller controller) { + throw new IllegalStateException("컨트롤러를 추가할 수 없습니다."); + } + + @Override + public void service(Request request, Response response) { + controller.service(request, response); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java new file mode 100644 index 0000000000..4498f0e31f --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java @@ -0,0 +1,60 @@ +package org.apache.coyote.http11.controller; + +import common.http.ContentType; +import common.http.Controller; +import common.http.Request; +import common.http.Response; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; + +import static common.http.HttpStatus.OK; + +public class StaticResourceController implements Controller { + + private static final String STATIC_RESOURCE_DIR = "static"; + + @Override + public void service(Request request, Response response) { + if (response.hasStaticResourcePath()) { + buildResponse(response.getStaticResourcePath(), request, response); + return; + } + + buildResponse(request.getPath(), request, response); + } + + private void buildResponse(String path, Request request, Response response) throws IOException { + response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); + response.addHttpStatus(OK); + + URL resource = getResource(path); + + String fileContent = readFileContent(resource); + ContentType contentType = ContentType.findByPath(path); + + response.addContentType(contentType); + + if (fileContent.length() != 0) { + response.addBody(fileContent); + } + } + + private URL getResource(String path) throws FileNotFoundException { + ClassLoader classLoader = StaticResourceController.class.getClassLoader(); + URL resource = classLoader.getResource(STATIC_RESOURCE_DIR + path); + + if (resource == null) { + throw new FileNotFoundException("해당하는 파일을 찾을 수 없습니다."); + } + + return resource; + } + + private String readFileContent(URL resource) throws IOException { + return new String(Files.readAllBytes(Paths.get(resource.getFile()))); + } +} 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..6062b140ed --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java @@ -0,0 +1,76 @@ +package org.apache.coyote.http11.request; + +import common.http.Cookie; +import common.http.Cookies; +import common.http.HttpMethod; +import common.http.Request; +import common.http.Session; +import org.apache.catalina.startup.SessionManager; + +import java.util.UUID; +import java.util.regex.Pattern; + +public class HttpRequest implements Request { + + private static final Pattern STATIC_RESOURCE_PATH_PATTERN = Pattern.compile("\\.[a-zA-Z0-9]+$"); + + private final HttpRequestLine httpRequestLine; + private final HttpRequestHeaders httpRequestHeaders; + private final HttpRequestBody httpRequestBody; + private Session session; + + public HttpRequest(HttpRequestLine httpRequestLine, HttpRequestHeaders httpRequestHeaders, HttpRequestBody httpRequestBody) { + this.httpRequestLine = httpRequestLine; + this.httpRequestHeaders = httpRequestHeaders; + this.httpRequestBody = httpRequestBody; + } + + public String getVersionOfTheProtocol() { + return httpRequestLine.getVersionOfTheProtocol(); + } + + public String getPath() { + return httpRequestLine.getPath(); + } + + public boolean hasValidSession() { + Cookie cookie = Cookie.from(getCookie()); + session = new SessionManager().findSession(Cookies.getJsessionid(cookie)); + return session != null; + } + + public HttpMethod getHttpMethod() { + return httpRequestLine.getHttpMethod(); + } + + public String getCookie() { + return httpRequestHeaders.getCookie(); + } + + public String getAccount() { + return httpRequestBody.getAccount(); + } + + public String getPassword() { + return httpRequestBody.getPassword(); + } + + public String getEmail() { + return httpRequestBody.getEmail(); + } + + public Session getSession(boolean create) { + UUID uuid = UUID.randomUUID(); + this.session = new Session(uuid.toString()); + new SessionManager().add(session); + return session; + } + + public Session getSession() { + return session; + } + + public boolean hasStaticResourcePath() { + return STATIC_RESOURCE_PATH_PATTERN.matcher(getPath()).find(); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestBody.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestBody.java new file mode 100644 index 0000000000..ff028698bf --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestBody.java @@ -0,0 +1,24 @@ +package org.apache.coyote.http11.request; + +import java.util.Map; + +public class HttpRequestBody { + + private final Map body; + + HttpRequestBody(Map body) { + this.body = body; + } + + String getAccount() { + return body.get("account"); + } + + String getPassword() { + return body.get("password"); + } + + String getEmail() { + return body.get("email"); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestHeaders.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestHeaders.java new file mode 100644 index 0000000000..38a1b4ec60 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestHeaders.java @@ -0,0 +1,20 @@ +package org.apache.coyote.http11.request; + +import java.util.Map; + +public class HttpRequestHeaders { + + private final Map headers; + + HttpRequestHeaders(Map headers) { + this.headers = headers; + } + + int getContentLength() { + return Integer.parseInt(headers.getOrDefault("Content-Length", "0")); + } + + String getCookie() { + return headers.get("Cookie"); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestFirstLineInfo.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestLine.java similarity index 59% rename from tomcat/src/main/java/org/apache/coyote/http11/HttpRequestFirstLineInfo.java rename to tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestLine.java index ac4f1fbac2..55a32bead1 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestFirstLineInfo.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestLine.java @@ -1,53 +1,55 @@ -package org.apache.coyote.http11; +package org.apache.coyote.http11.request; -public class HttpRequestFirstLineInfo { +import common.http.HttpMethod; + +public class HttpRequestLine { private static final String SPACE = " "; private static final String QUERY_STRING_DELIMITER = "?"; private static final int HTTP_METHOD_INDEX = 0; - private static final int URI_INDEX = 1; + private static final int PATH_INDEX = 1; private static final int PROTOCOL_VERSION_INDEX = 2; private static final int NUMBER_OF_FIRST_LINE_INFOS = 3; private final HttpMethod httpMethod; - private final String uri; + private final String path; private final String versionOfTheProtocol; - private HttpRequestFirstLineInfo(HttpMethod httpMethod, String uri, String versionOfTheProtocol) { + private HttpRequestLine(HttpMethod httpMethod, String path, String versionOfTheProtocol) { this.httpMethod = httpMethod; - this.uri = uri; + this.path = path; this.versionOfTheProtocol = versionOfTheProtocol; } - public static HttpRequestFirstLineInfo from(String firstLine) { - String[] infos = firstLine.split(SPACE); + static HttpRequestLine from(String requestLine) { + String[] infos = requestLine.split(SPACE); if (infos.length != NUMBER_OF_FIRST_LINE_INFOS) { throw new IllegalArgumentException("유효하지 않은 Request-line 입니다."); } HttpMethod httpMethod = HttpMethod.parseHttpMethod(infos[HTTP_METHOD_INDEX]); - String uriWithQueryString = infos[URI_INDEX]; + String uriWithQueryString = infos[PATH_INDEX]; String versionOfTheProtocol = infos[PROTOCOL_VERSION_INDEX]; int indexOfQueryStringDelimiter = uriWithQueryString.indexOf(QUERY_STRING_DELIMITER); if (indexOfQueryStringDelimiter != -1) { String uri = uriWithQueryString.substring(0, indexOfQueryStringDelimiter); - return new HttpRequestFirstLineInfo(httpMethod, uri, versionOfTheProtocol); + return new HttpRequestLine(httpMethod, uri, versionOfTheProtocol); } - return new HttpRequestFirstLineInfo(httpMethod, uriWithQueryString, versionOfTheProtocol); + return new HttpRequestLine(httpMethod, uriWithQueryString, versionOfTheProtocol); } - public HttpMethod getHttpMethod() { - return httpMethod; + String getVersionOfTheProtocol() { + return versionOfTheProtocol; } - public String getUri() { - return uri; + HttpMethod getHttpMethod() { + return httpMethod; } - public String getVersionOfTheProtocol() { - return versionOfTheProtocol; + String getPath() { + return path; } } 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..879f07b6ea --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestParser.java @@ -0,0 +1,78 @@ +package org.apache.coyote.http11.request; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +public class HttpRequestParser { + + private static final String DELIMITER = ":"; + private static final String PARAMS_DELIMITER = "&"; + private static final String PARAM_VALUE_DELIMITER = "="; + + private static final int PROPERTY_INDEX = 0; + private static final int VALUE_INDEX = 1; + private static final int START_AFTER_SPACE = 2; + + public static HttpRequest parse(BufferedReader reader) throws IOException { + HttpRequestLine requestLine = parseRequestLine(reader); + HttpRequestHeaders headers = parseHeaders(reader); + HttpRequestBody body = parseBody(reader, headers.getContentLength()); + + return new HttpRequest(requestLine, headers, body); + } + + private static HttpRequestLine parseRequestLine(BufferedReader reader) throws IOException { + String requestLine = reader.readLine(); + if (requestLine == null) { + throw new IOException("요청에 Reqeust-line이 없습니다."); + } + return HttpRequestLine.from(requestLine); + } + + private static HttpRequestHeaders parseHeaders(BufferedReader reader) throws IOException { + Map headers = new HashMap<>(); + + String headerLine; + while ((headerLine = reader.readLine()) != null && !headerLine.isEmpty()) { + int indexOfDelimiter = headerLine.indexOf(DELIMITER); + + if (indexOfDelimiter == -1) { + throw new IllegalArgumentException("요청의 헤더가 잘못되었습니다."); + } + + String property = headerLine.substring(0, indexOfDelimiter); + String value = headerLine.substring(indexOfDelimiter + START_AFTER_SPACE); + headers.put(property, value); + } + + return new HttpRequestHeaders(headers); + } + + private static HttpRequestBody parseBody(BufferedReader reader, int contentLength) throws IOException { + if (contentLength == 0) { + return new HttpRequestBody(new HashMap<>()); + } + + char[] buffer = new char[contentLength]; + reader.read(buffer, 0, contentLength); + String requestBody = new String(buffer); + + Map body = Arrays.stream(requestBody.split(PARAMS_DELIMITER)) + .map(property -> property.split(PARAM_VALUE_DELIMITER)) + .filter(propertyAndValue -> propertyAndValue.length == 2) + .collect(Collectors.toMap(data -> data[PROPERTY_INDEX], data -> data[VALUE_INDEX])); + + if (body.size() != requestBody.split(PARAMS_DELIMITER).length) { + throw new IllegalArgumentException("요청의 바디가 잘못되었습니다."); + } + + return new HttpRequestBody(body); + } + + private HttpRequestParser() { + } +} 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..b29c67f884 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java @@ -0,0 +1,71 @@ +package org.apache.coyote.http11.response; + +import common.http.ContentType; +import common.http.Cookie; +import common.http.HttpStatus; +import common.http.Response; + +public class HttpResponse implements Response { + + private static final String CRLF = "\r\n"; + + private final HttpStatusLine httpStatusLine; + private final HttpResponseHeaders httpResponseHeaders; + private final HttpResponseBody httpResponseBody; + + public HttpResponse() { + this.httpStatusLine = new HttpStatusLine(); + this.httpResponseHeaders = new HttpResponseHeaders(); + this.httpResponseBody = new HttpResponseBody(); + } + + public void addVersionOfTheProtocol(String versionOfTheProtocol) { + httpStatusLine.addVersionOfTheProtocol(versionOfTheProtocol); + } + + public void addHttpStatus(HttpStatus httpStatus) { + httpStatusLine.addHttpStatus(httpStatus); + } + + public void addContentType(ContentType contentType) { + httpResponseHeaders.addHeaderFieldAndValue("Content-Type", contentType.getType() + ";charset=utf-8"); + } + + public void addCookie(Cookie cookie) { + httpResponseHeaders.addHeaderFieldAndValue("Set-Cookie", cookie.toString()); + } + + public void sendRedirect(String redirectURL) { + httpResponseHeaders.addHeaderFieldAndValue("Location", redirectURL); + } + + public void addBody(String body) { + httpResponseHeaders.addHeaderFieldAndValue("Content-Length", String.valueOf(body.getBytes().length)); + httpResponseBody.addBody(body); + } + + public void addStaticResourcePath(String name) { + httpResponseHeaders.addHeaderFieldAndValue("Resource-Path", name); + } + + public boolean hasStaticResourcePath() { + return httpResponseHeaders.hasStaticResourcePath(); + } + + public String getStaticResourcePath() { + return httpResponseHeaders.getStaticResourcePath(); + } + + @Override + public String toString() { + if (httpResponseBody.hasBody()) { + return String.join(CRLF, + httpStatusLine.toString(), + httpResponseHeaders.toString(), + httpResponseBody.toString()); + } + return String.join(CRLF, + httpStatusLine.toString(), + httpResponseHeaders.toString()); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseBody.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseBody.java new file mode 100644 index 0000000000..c20276cd2e --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseBody.java @@ -0,0 +1,32 @@ +package org.apache.coyote.http11.response; + +class HttpResponseBody { + + private String body; + + HttpResponseBody() { + } + + void addBody(String body) { + this.body = body; + } + + void validateLength(long length) { + if (length != body.length()) { + throw new IllegalArgumentException("Response Body의 길이가 유효하지 않습니다."); + } + } + + boolean exist() { + return body.length() != 0; + } + + @Override + public String toString() { + return body; + } + + public boolean hasBody() { + return body != null; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java new file mode 100644 index 0000000000..a15859a10c --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java @@ -0,0 +1,60 @@ +package org.apache.coyote.http11.response; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static common.Constants.CRLF; +import static common.Constants.SPACE; + +class HttpResponseHeaders { + + private final Map> headers; + + HttpResponseHeaders() { + this.headers = new HashMap<>(); + } + + void addHeaderFieldAndValue(String field, String value) { + Set uniqueHeaderFields = Set.of("Content-Type", "Content-Length", "Location"); + + if (headers.containsKey(field) && uniqueHeaderFields.contains(field)) { + throw new IllegalStateException(String.format("헤더에 이미 %s에 대한 값이 존재합니다.", field)); + } + + if (headers.containsKey(field) && !uniqueHeaderFields.contains(field)) { + headers.get(field).add(value); + return; + } + + headers.put(field, List.of(value)); + } + + boolean hasStaticResourcePath() { + return headers.containsKey("Resource-Path"); + } + + String getStaticResourcePath() { + return headers.get("Resource-Path").get(0); + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder(); + + for (Map.Entry> fieldAndValue : headers.entrySet()) { + for (String value : fieldAndValue.getValue()) { + stringBuilder + .append(fieldAndValue.getKey()) + .append(":") + .append(SPACE) + .append(value) + .append(SPACE) + .append(CRLF); + } + } + + return stringBuilder.toString(); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatusLine.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatusLine.java new file mode 100644 index 0000000000..6fff0e7e17 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatusLine.java @@ -0,0 +1,34 @@ +package org.apache.coyote.http11.response; + +import common.http.HttpStatus; + +import static common.Constants.SPACE; + +class HttpStatusLine { + + private String versionOfTheProtocol; + private HttpStatus httpStatus; + + HttpStatusLine() {} + + void addVersionOfTheProtocol(String versionOfTheProtocol) { + if (this.versionOfTheProtocol != null) { + throw new IllegalStateException("프로토콜의 버전이 이미 존재합니다."); + } + + this.versionOfTheProtocol = versionOfTheProtocol; + } + + void addHttpStatus(HttpStatus httpStatus) { + if (this.httpStatus != null) { + throw new IllegalStateException("Http Status가 이미 존재합니다."); + } + + this.httpStatus = httpStatus; + } + + @Override + public String toString() { + return versionOfTheProtocol + SPACE + httpStatus.getStatusCode() + SPACE + httpStatus.getStatusMessage() + SPACE; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/session/HttpCookie.java b/tomcat/src/main/java/org/apache/coyote/http11/session/HttpCookie.java deleted file mode 100644 index 1bf7101cf8..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/session/HttpCookie.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.apache.coyote.http11.session; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -public class HttpCookie { - - private static final String JSESSIONID = "JSESSIONID"; - private static final String SEPARATOR = "; "; - private static final String DELIMITER = "="; - - private static final int NAME_INDEX = 0; - private static final int VALUE_INDEX = 1; - - private final Map items; - - private HttpCookie(Map items) { - this.items = items; - } - - public static HttpCookie from(String cookieHeader) { - if (cookieHeader == null || cookieHeader.isEmpty()) { - return new HttpCookie(new HashMap<>()); - } - return new HttpCookie(parse(cookieHeader)); - } - - private static Map parse(String values) { - Map items = new HashMap<>(); - String[] nameAndValues = values.split(SEPARATOR); - for (String cookie : nameAndValues) { - String[] nameAndValue = cookie.split(DELIMITER); - items.put(nameAndValue[NAME_INDEX], nameAndValue[VALUE_INDEX]); - } - return items; - } - - public boolean hasJSessionId() { - return items.containsKey(JSESSIONID); - } - - public void bake() { - items.put(JSESSIONID, UUID.randomUUID().toString()); - } - - public String getJSessionId() { - return items.get(JSESSIONID); - } -} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/session/SessionManager.java b/tomcat/src/main/java/org/apache/coyote/http11/session/SessionManager.java deleted file mode 100644 index e7765a6fbf..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/session/SessionManager.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.apache.coyote.http11.session; - -import java.util.HashMap; -import java.util.Map; - -public class SessionManager implements Manager { - - private static final Map SESSIONS = new HashMap<>(); - - @Override - public void add(Session session) { - SESSIONS.put(session.getId(), session); - } - - @Override - public Session findSession(String id) { - return SESSIONS.get(id); - } - - @Override - public void remove(Session session) { - SESSIONS.remove(session.getId()); - } -} diff --git a/tomcat/src/test/java/common/http/ContentTypeTest.java b/tomcat/src/test/java/common/http/ContentTypeTest.java new file mode 100644 index 0000000000..77f8ebe36f --- /dev/null +++ b/tomcat/src/test/java/common/http/ContentTypeTest.java @@ -0,0 +1,70 @@ +package common.http; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class ContentTypeTest { + + @Test + void 확장자명으로_Content_Type을_찾는다() { + // given + String html = "html"; + String css = "css"; + String js = "js"; + String ico = "ico"; + + // expect + assertThat(ContentType.findByExtension(html)).isEqualTo(ContentType.HTML); + assertThat(ContentType.findByExtension(css)).isEqualTo(ContentType.CSS); + assertThat(ContentType.findByExtension(js)).isEqualTo(ContentType.JS); + assertThat(ContentType.findByExtension(ico)).isEqualTo(ContentType.ICO); + } + + @Test + void 유효한_확장자명이_아니면_예외를_반환한다() { + // given + String avi = "avi"; + + // expect + assertThatThrownBy(() -> ContentType.findByExtension(avi)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("유효하지 않은 확장자명 입니다."); + } + + @Test + void 경로로_Content_Type을_찾는다 () { + // given + String path = "/index.html"; + + // expect + assertThat(ContentType.findByPath(path)).isEqualTo(ContentType.HTML); + } + + @Test + void 경로에_확장자가_없으면_예외를_반환한다() { + // given + String path = "/로이스의은밀한사생활"; + + // expect + assertThatThrownBy(() -> ContentType.findByPath(path)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("파일의 확장자명이 없습니다."); + } + + @Test + void 경로의_확장자가_유효하지_않으면_예외를_반환한다() { + // given + String path = "/로이스의은밀한사생활.avi"; + + // expect + assertThatThrownBy(() -> ContentType.findByPath(path)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("유효하지 않은 확장자명 입니다."); + } +} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/session/HttpCookieTest.java b/tomcat/src/test/java/common/http/CookieTest.java similarity index 70% rename from tomcat/src/test/java/org/apache/coyote/http11/session/HttpCookieTest.java rename to tomcat/src/test/java/common/http/CookieTest.java index 27902d25ec..130f6663dd 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/session/HttpCookieTest.java +++ b/tomcat/src/test/java/common/http/CookieTest.java @@ -1,4 +1,4 @@ -package org.apache.coyote.http11.session; +package common.http; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayNameGeneration; @@ -7,7 +7,7 @@ @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) -class HttpCookieTest { +class CookieTest { @Test void Cookie_헤더로부터_Cookie를_생성한다() { @@ -15,9 +15,9 @@ class HttpCookieTest { String cookieHeader = "yummy_cookie=choco; tasty_cookie=strawberry; JSESSIONID=656cef62-e3c4-40bc-a8df-94732920ed46"; // when - HttpCookie cookie = HttpCookie.from(cookieHeader); + Cookie cookie = Cookie.from(cookieHeader); // then - Assertions.assertThat(cookie.getJSessionId()).isEqualTo("656cef62-e3c4-40bc-a8df-94732920ed46"); + Assertions.assertThat(cookie.getAttribute("JSESSIONID")).isEqualTo("656cef62-e3c4-40bc-a8df-94732920ed46"); } } diff --git a/tomcat/src/test/java/common/http/CookiesTest.java b/tomcat/src/test/java/common/http/CookiesTest.java new file mode 100644 index 0000000000..3afe71a1a9 --- /dev/null +++ b/tomcat/src/test/java/common/http/CookiesTest.java @@ -0,0 +1,24 @@ +package common.http; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class CookiesTest { + + @Test + void 세션_아이디로_쿠키를_생성한다() { + // given + String id = "sessionid"; + + // when + Cookie cookie = Cookies.ofJSessionId(id); + + // then + assertThat(cookie.getAttribute("JSESSIONID")).isEqualTo(id); + } +} diff --git a/tomcat/src/test/java/common/http/HttpMethodTest.java b/tomcat/src/test/java/common/http/HttpMethodTest.java new file mode 100644 index 0000000000..642cb7dab9 --- /dev/null +++ b/tomcat/src/test/java/common/http/HttpMethodTest.java @@ -0,0 +1,32 @@ +package common.http; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class HttpMethodTest { + @Test + void 메서드_이름으로_Http_Method를_가져온다() { + // given + String get = "GET"; + + // expect + assertThat(HttpMethod.parseHttpMethod(get)).isEqualTo(HttpMethod.GET); + } + + @Test + void 유효하지_않은_메서드_이름이면_예외를_반환한다() { + // given + String 가져온나 = "가져온나"; + + // expect + Assertions.assertThatThrownBy(() -> HttpMethod.parseHttpMethod(가져온나)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("유효하지 않은 HTTP 메서드입니다."); + } +} diff --git a/tomcat/src/test/java/common/http/SessionTest.java b/tomcat/src/test/java/common/http/SessionTest.java new file mode 100644 index 0000000000..93abfaae80 --- /dev/null +++ b/tomcat/src/test/java/common/http/SessionTest.java @@ -0,0 +1,41 @@ +package common.http; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class SessionTest { + + @Test + void 세션의_속성을_가져온다() { + // given + Session session = new Session("id"); + session.setAttribute("user", "로이스"); + + // when + Object value = session.getAttribute("user"); + + // then + assertThat(value).isEqualTo("로이스"); + } + + @Test + void 세션에서_속성을_삭제한다() { + // given + Session session = new Session("id"); + session.setAttribute("user", "로이스"); + session.setAttribute("reviewee", "리오"); + + // when + session.removeAttribute("user"); + + // then + assertThat(session.getAttribute("user")).isNull(); + assertThat(session.getAttribute("reviewee")).isEqualTo("리오"); + } + +} 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..60c1903078 --- /dev/null +++ b/tomcat/src/test/java/nextstep/jwp/controller/HomeControllerTest.java @@ -0,0 +1,37 @@ +package nextstep.jwp.controller; + +import common.http.Request; +import common.http.Response; +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.response.HttpResponse; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +import static common.Constants.CRLF; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class HomeControllerTest { + + @Test + void 응답에_속성을_추가한다() { + // given + HomeController homeController = new HomeController(); + Request request = mock(HttpRequest.class); + Response httpResponse = new HttpResponse(); + when(request.getVersionOfTheProtocol()).thenReturn("HTTP/1.1"); + // when + homeController.doGet(request, httpResponse); + + // then + Assertions.assertThat(httpResponse.toString()).hasToString( + "HTTP/1.1 200 OK " + CRLF + + "Content-Length: 12 " + CRLF + + "Content-Type: text/html;charset=utf-8 " + CRLF + CRLF + + "Hello world!"); + } +} 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..05b0e9e189 --- /dev/null +++ b/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java @@ -0,0 +1,61 @@ +package nextstep.jwp.controller; + +import common.http.Request; +import common.http.Response; +import common.http.Session; +import org.apache.catalina.startup.SessionManager; +import org.apache.coyote.http11.request.HttpRequest; +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; + +import static common.Constants.CRLF; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class LoginControllerTest { + + @Test + void 세션이_존재하는_경우_indexhtml로_리다이렉션을_한다 () { + // given + Request request = mock(HttpRequest.class); + Response response = new HttpResponse(); + Session session = new Session("id"); + SessionManager sessionManager = mock(SessionManager.class); + LoginController loginController = new LoginController(); + + when(request.getSession()).thenReturn(session); + when(request.getVersionOfTheProtocol()).thenReturn("HTTP/1.1"); + when(sessionManager.findSession(any())).thenReturn(session); + when(request.hasValidSession()).thenReturn(true); + + // when + loginController.doGet(request, response); + + // then + assertThat(response.toString()).hasToString( + "HTTP/1.1 302 Found " + CRLF + + "Set-Cookie: JSESSIONID=id " + CRLF + + "Location: /index.html " + CRLF + ); + } + + @Test + void 세션이_존재하지_않는_경우_loginhtml을_응답에_실어준다() { + // given + Request request = mock(HttpRequest.class); + Response response = new HttpResponse(); + LoginController loginController = new LoginController(); + + // when + loginController.doGet(request, response); + + // then + assertThat(response.getStaticResourcePath()).isEqualTo("/login.html"); + } +} diff --git a/tomcat/src/test/java/org/apache/catalina/startup/DynamicControllerManagerTest.java b/tomcat/src/test/java/org/apache/catalina/startup/DynamicControllerManagerTest.java new file mode 100644 index 0000000000..699257a25f --- /dev/null +++ b/tomcat/src/test/java/org/apache/catalina/startup/DynamicControllerManagerTest.java @@ -0,0 +1,32 @@ +package org.apache.catalina.startup; + +import common.http.HttpMethod; +import common.http.Request; +import nextstep.jwp.controller.LoginController; +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.response.HttpResponse; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class DynamicControllerManagerTest { + + @Test + void 요청의_첫_줄_정보로_controller를_찾아서_실행시킨다() throws Exception { + // given + DynamicControllerManager dynamicControllerManager = new DynamicControllerManager(); + dynamicControllerManager.add("/login", new LoginController()); + Request request = mock(HttpRequest.class); + HttpResponse response = new HttpResponse(); + when(request.getPath()).thenReturn("/login"); + when(request.getHttpMethod()).thenReturn(HttpMethod.GET); + + // when + dynamicControllerManager.service(request, response); + + // then + assertThat(response.getStaticResourcePath()).isEqualTo("/login.html"); + } +} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java b/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java index 2aba8c56e0..b1f778cfa9 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java @@ -1,5 +1,9 @@ package org.apache.coyote.http11; +import common.http.ControllerManager; +import nextstep.jwp.controller.HomeController; +import org.apache.catalina.startup.DynamicControllerManager; +import org.apache.coyote.Processor; import org.junit.jupiter.api.Test; import support.StubSocket; @@ -7,6 +11,7 @@ import java.io.IOException; import java.net.URL; import java.nio.file.Files; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -15,21 +20,23 @@ class Http11ProcessorTest { @Test void process() { // given - final var socket = new StubSocket(); - final var processor = new Http11Processor(socket); + final StubSocket socket = new StubSocket(); + final ControllerManager controllerManager = new DynamicControllerManager(); + controllerManager.add("/", new HomeController()); + final Processor processor = new Http11Processor(socket, controllerManager); // when processor.process(socket); // then - var expected = String.join("\r\n", + var expected = List.of("\r\n", "HTTP/1.1 200 OK ", "Content-Type: text/html;charset=utf-8 ", "Content-Length: 12 ", "", "Hello world!"); - assertThat(socket.output()).isEqualTo(expected); + assertThat(socket.output()).contains(expected); } @Test @@ -43,19 +50,20 @@ void index() throws IOException { ""); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var controllerManager = new DynamicControllerManager(); + final Http11Processor processor = new Http11Processor(socket, controllerManager); // when processor.process(socket); // then final URL resource = getClass().getClassLoader().getResource("static/index.html"); - var expected = "HTTP/1.1 200 OK \r\n" + - "Content-Type: text/html;charset=utf-8 \r\n" + - "Content-Length: 5564 \r\n" + - "\r\n"+ - new String(Files.readAllBytes(new File(resource.getFile()).toPath())); + var expected = List.of("HTTP/1.1 200 OK \r\n", + "Content-Type: text/html;charset=utf-8 \r\n", + "Content-Length: 5564 \r\n", + "\r\n", + new String(Files.readAllBytes(new File(resource.getFile()).toPath()))); - assertThat(socket.output()).isEqualTo(expected); + assertThat(socket.output()).contains(expected); } } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/HttpRequestFirstLineInfoTest.java b/tomcat/src/test/java/org/apache/coyote/http11/HttpRequestFirstLineInfoTest.java deleted file mode 100644 index 1f13c919de..0000000000 --- a/tomcat/src/test/java/org/apache/coyote/http11/HttpRequestFirstLineInfoTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.apache.coyote.http11; - -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - -@SuppressWarnings("NonAsciiCharacters") -@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) -class HttpRequestFirstLineInfoTest { - - @Test - void HTTP_요청의_첫_줄의_정보를_읽는다() { - // given - final String firstLine = "GET /index.html HTTP/1.1 "; - - // when - HttpRequestFirstLineInfo infos = HttpRequestFirstLineInfo.from(firstLine); - - // then - assertSoftly(softly -> { - softly.assertThat(infos.getHttpMethod()).isEqualTo(HttpMethod.GET); - softly.assertThat(infos.getUri()).isEqualTo("/index.html"); - softly.assertThat(infos.getVersionOfTheProtocol()).isEqualTo("HTTP/1.1"); - }); - } - - @Test - void 첫_줄이_유효하지_않으면_예외를_발생시킨다() { - // given - final String firstLine = "GET HTTP/1.1 "; - - // expect - assertThatThrownBy(() -> HttpRequestFirstLineInfo.from(firstLine)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("유효하지 않은 Request-line 입니다."); - } - - @Test - void HTTP_메서드가_유효하지_않으면_예외를_발생시킨다() { - // given - final String firstLine = "내놔라 /index.html HTTP/1.1 "; - - // expect - assertThatThrownBy(() -> HttpRequestFirstLineInfo.from(firstLine)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("유효하지 않은 HTTP 메서드입니다."); - } -} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/HttpResponseMakerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/HttpResponseMakerTest.java deleted file mode 100644 index 5bd75f51ba..0000000000 --- a/tomcat/src/test/java/org/apache/coyote/http11/HttpResponseMakerTest.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.apache.coyote.http11; - -import nextstep.jwp.db.InMemoryUserRepository; -import nextstep.jwp.model.User; -import org.apache.coyote.http11.session.SessionManager; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.IOException; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.when; - -@SuppressWarnings("NonAsciiCharacters") -@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) -class HttpResponseMakerTest { - - @Mock - private HttpRequestParser mockParser; - - @BeforeEach - public void setUp() { - MockitoAnnotations.openMocks(this); - } - - @Test - void 로그인_성공_시에_요청에_해당하는_응답을_반환한다() throws IOException { - // given - when(mockParser.getHttpRequestFirstLineInfo()).thenReturn( - HttpRequestFirstLineInfo.from("POST /login HTTP/1.1")); - when(mockParser.getBody()) - .thenReturn( - Map.of( - "account", "testuser", - "password", "password123", - "email", "email") - ); - - InMemoryUserRepository.save(new User("testuser", "password123", "email")); - - // when - String response = HttpResponseMaker.makeFrom(mockParser, new SessionManager()); - - // then - assertTrue(response.contains("Location: /index.html")); - } - - @Test - void 로그인_실패_시에_요청에_해당하는_응답을_반환한다() throws IOException { - // given - when(mockParser.getHttpRequestFirstLineInfo()).thenReturn( - HttpRequestFirstLineInfo.from("POST /login HTTP/1.1")); - when(mockParser.getBody()) - .thenReturn( - Map.of( - "account", "testuser", - "password", "password456", - "email", "email") - ); - - InMemoryUserRepository.save(new User("testuser", "password123", "email")); - - // when - String response = HttpResponseMaker.makeFrom(mockParser, new SessionManager()); - - // then - assertTrue(response.contains("Location: /401.html")); - } - - @Test - void 회원_정보가_없을_시에_예외를_발생시킨다() { - // given - when(mockParser.getHttpRequestFirstLineInfo()).thenReturn( - HttpRequestFirstLineInfo.from("POST /login HTTP/1.1")); - when(mockParser.getBody()) - .thenReturn( - Map.of( - "account", "test", - "password", "password123", - "email", "email") - ); - - InMemoryUserRepository.save(new User("testuser", "password123", "email")); - - // expect - SessionManager sessionManager = new SessionManager(); - Assertions.assertThatThrownBy(() -> HttpResponseMaker.makeFrom(mockParser, sessionManager)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("회원 정보가 존재하지 않습니다."); - } -} - diff --git a/tomcat/src/test/java/org/apache/coyote/http11/RequestMapperTest.java b/tomcat/src/test/java/org/apache/coyote/http11/RequestMapperTest.java deleted file mode 100644 index 877a3b4697..0000000000 --- a/tomcat/src/test/java/org/apache/coyote/http11/RequestMapperTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.apache.coyote.http11; - -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -@SuppressWarnings("NonAsciiCharacters") -@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) -class RequestMapperTest { - - @Test - void 요청의_첫_줄_정보로_request_mapper를_찾는다() { - // given - HttpRequestFirstLineInfo info = HttpRequestFirstLineInfo.from("GET /index.html HTTP/1.1"); - - // when - RequestMapper mapper = RequestMapper.findMapper(info); - - // then - assertThat(mapper).isSameAs(RequestMapper.INDEX); - - } - - @Test - void 요청의_첫_줄_정보로_request_mapper를_찾을_수_없으면_예외를_발생시킨다() { - // given - HttpRequestFirstLineInfo info = HttpRequestFirstLineInfo.from("POST /index.html HTTP/1.1"); - - // expect - assertThatThrownBy(() -> RequestMapper.findMapper(info)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("잘못된 요청입니다."); - } -} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/HttpRequestParserTest.java b/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestParserTest.java similarity index 62% rename from tomcat/src/test/java/org/apache/coyote/http11/HttpRequestParserTest.java rename to tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestParserTest.java index 6b80ce00ce..b2097517fb 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/HttpRequestParserTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestParserTest.java @@ -1,5 +1,6 @@ -package org.apache.coyote.http11; +package org.apache.coyote.http11.request; +import common.http.HttpMethod; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -18,8 +19,8 @@ class HttpRequestParserTest { @Test void HTTP_요청을_파싱한다() throws IOException { // given - final String httpRequest = String.join("\r\n", - "GET /index.html HTTP/1.1 ", + final String request = String.join("\r\n", + "POST /index.html HTTP/1.1 ", "Host: localhost:8080 ", "Connection: keep-alive ", "Content-Length: 80", @@ -27,34 +28,18 @@ class HttpRequestParserTest { "", "account=gugu&password=password&email=hkkang%40woowahan.com"); - BufferedReader reader = new BufferedReader(new StringReader(httpRequest)); + BufferedReader reader = new BufferedReader(new StringReader(request)); // when - HttpRequestParser requestParser = HttpRequestParser.from(reader); + HttpRequest parsed = HttpRequestParser.parse(reader); // then assertSoftly(softly -> { - softly.assertThat(requestParser) - .isNotNull(); - - softly.assertThat(requestParser.getHttpRequestFirstLineInfo()) - .isNotNull() - .extracting("httpMethod", "uri", "versionOfTheProtocol") - .containsExactly(HttpMethod.GET, "/index.html", "HTTP/1.1"); - - softly.assertThat(requestParser.getHeaders()) - .isNotNull() - .hasSize(4) - .containsEntry("Host", "localhost:8080 ") - .containsEntry("Connection", "keep-alive ") - .containsEntry("Content-Length", "80") - .containsEntry("Content-Type", "application/x-www-form-urlencoded"); - - softly.assertThat(requestParser.getBody()) - .isNotNull() - .hasSize(3) - .containsEntry("account", "gugu") - .containsEntry("password", "password"); + softly.assertThat(parsed).isNotNull(); + softly.assertThat(parsed.getHttpMethod()).isEqualTo(HttpMethod.POST); + softly.assertThat(parsed.getVersionOfTheProtocol()).isEqualTo("HTTP/1.1"); + softly.assertThat(parsed.getAccount()).isEqualTo("gugu"); + softly.assertThat(parsed.getPassword()).isEqualTo("password"); }); reader.close(); @@ -68,7 +53,7 @@ class HttpRequestParserTest { BufferedReader reader = new BufferedReader(new StringReader(httpRequest)); // expect - assertThatThrownBy(() -> HttpRequestParser.from(reader)) + assertThatThrownBy(() -> HttpRequestParser.parse(reader)) .isInstanceOf(IOException.class) .hasMessage("요청에 Reqeust-line이 없습니다."); @@ -90,7 +75,7 @@ class HttpRequestParserTest { BufferedReader reader = new BufferedReader(new StringReader(httpRequest)); // expect - assertThatThrownBy(() -> HttpRequestParser.from(reader)) + assertThatThrownBy(() -> HttpRequestParser.parse(reader)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("요청의 바디가 잘못되었습니다."); From e71df35bf327d56c7b1a70416fa76bfed89e3c21 Mon Sep 17 00:00:00 2001 From: ReO Date: Mon, 11 Sep 2023 17:53:43 +0900 Subject: [PATCH 04/24] =?UTF-8?q?fix:=20=EC=97=90=EB=9F=AC=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 --- .../controller/StaticResourceController.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java index 4498f0e31f..09c2d19f47 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java @@ -27,19 +27,22 @@ public void service(Request request, Response response) { buildResponse(request.getPath(), request, response); } - private void buildResponse(String path, Request request, Response response) throws IOException { + private void buildResponse(String path, Request request, Response response) { response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); response.addHttpStatus(OK); - URL resource = getResource(path); + try { + URL resource = getResource(path); + String fileContent = readFileContent(resource); + ContentType contentType = ContentType.findByPath(path); - String fileContent = readFileContent(resource); - ContentType contentType = ContentType.findByPath(path); + response.addContentType(contentType); - response.addContentType(contentType); - - if (fileContent.length() != 0) { - response.addBody(fileContent); + if (fileContent.length() != 0) { + response.addBody(fileContent); + } + } catch (IOException e) { + throw new IllegalArgumentException("파일을 찾을 수 없습니다."); } } From c8dfb8443445fc0cd9b696ed2864a4e72e5881a6 Mon Sep 17 00:00:00 2001 From: ReO Date: Mon, 11 Sep 2023 19:39:21 +0900 Subject: [PATCH 05/24] =?UTF-8?q?refactor:=20=ED=8C=A8=ED=82=A4=EC=A7=80?= =?UTF-8?q?=20=EA=B0=84=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tomcat/src/main/java/common/http/Request.java | 2 ++ .../startup/DynamicControllerManager.java | 16 ++++++++++++++++ .../apache/catalina/startup/SessionManager.java | 6 +++++- .../org/apache/catalina/startup/Sessions.java | 6 +++++- .../coyote/http11/request/HttpRequest.java | 10 ++++------ .../startup/DynamicControllerManagerTest.java | 6 +++++- .../coyote/http11/Http11ProcessorTest.java | 3 ++- 7 files changed, 39 insertions(+), 10 deletions(-) diff --git a/tomcat/src/main/java/common/http/Request.java b/tomcat/src/main/java/common/http/Request.java index 576ec8270e..0cd2733988 100644 --- a/tomcat/src/main/java/common/http/Request.java +++ b/tomcat/src/main/java/common/http/Request.java @@ -21,4 +21,6 @@ public interface Request { boolean hasValidSession(); String getEmail(); + + void addSession(Session session); } diff --git a/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java b/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java index a8d4f17c18..7ea4a73e4d 100644 --- a/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java +++ b/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java @@ -2,8 +2,11 @@ import common.http.Controller; import common.http.ControllerManager; +import common.http.Cookie; +import common.http.Cookies; import common.http.Request; import common.http.Response; +import common.http.Session; import java.util.HashMap; import java.util.Map; @@ -12,6 +15,8 @@ public class DynamicControllerManager implements ControllerManager { private static final Map mapper = new HashMap<>(); + private final SessionManager sessionManager = new SessionManager(); + @Override public void add(String path, Controller controller) { mapper.put(path, controller); @@ -19,11 +24,22 @@ public void add(String path, Controller controller) { @Override public void service(Request request, Response response) { + String cookieHeader = request.getCookie(); + if (cookieHeader != null) { + Session session = sessionManager.findSession(Cookies.getJsessionid(Cookie.from(cookieHeader))); + request.addSession(session); + } + Controller controller = mapper.get(request.getPath()); if (controller == null) { return; } controller.service(request, response); + + Session session = request.getSession(); + if (session != null && !sessionManager.hasSession(session)) { + sessionManager.add(session); + } } } diff --git a/tomcat/src/main/java/org/apache/catalina/startup/SessionManager.java b/tomcat/src/main/java/org/apache/catalina/startup/SessionManager.java index 8e07946345..5d6a19daa8 100644 --- a/tomcat/src/main/java/org/apache/catalina/startup/SessionManager.java +++ b/tomcat/src/main/java/org/apache/catalina/startup/SessionManager.java @@ -20,7 +20,7 @@ public void add(Session session) { @Override public Session findSession(String id) { - return sessions.find(id); + return sessions.has(id); } @Override @@ -47,4 +47,8 @@ public void stop() { public boolean isStarted() { return isStarted; } + + public boolean hasSession(Session session) { + return sessions.has(session); + } } diff --git a/tomcat/src/main/java/org/apache/catalina/startup/Sessions.java b/tomcat/src/main/java/org/apache/catalina/startup/Sessions.java index 88a3cca3ca..7b93b6f066 100644 --- a/tomcat/src/main/java/org/apache/catalina/startup/Sessions.java +++ b/tomcat/src/main/java/org/apache/catalina/startup/Sessions.java @@ -13,7 +13,7 @@ void add(String id, Session session) { idAndSessions.put(id, session); } - Session find(String id) { + Session has(String id) { if (!idAndSessions.containsKey(id)) { return null; } @@ -27,4 +27,8 @@ void remove(String id) { public void clear() { idAndSessions.clear(); } + + public boolean has(Session session) { + return idAndSessions.containsValue(session); + } } 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 6062b140ed..e86504b1fd 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,11 +1,8 @@ package org.apache.coyote.http11.request; -import common.http.Cookie; -import common.http.Cookies; import common.http.HttpMethod; import common.http.Request; import common.http.Session; -import org.apache.catalina.startup.SessionManager; import java.util.UUID; import java.util.regex.Pattern; @@ -34,8 +31,6 @@ public String getPath() { } public boolean hasValidSession() { - Cookie cookie = Cookie.from(getCookie()); - session = new SessionManager().findSession(Cookies.getJsessionid(cookie)); return session != null; } @@ -62,10 +57,13 @@ public String getEmail() { public Session getSession(boolean create) { UUID uuid = UUID.randomUUID(); this.session = new Session(uuid.toString()); - new SessionManager().add(session); return session; } + public void addSession(Session session) { + this.session = session; + } + public Session getSession() { return session; } diff --git a/tomcat/src/test/java/org/apache/catalina/startup/DynamicControllerManagerTest.java b/tomcat/src/test/java/org/apache/catalina/startup/DynamicControllerManagerTest.java index 699257a25f..aef2a236b0 100644 --- a/tomcat/src/test/java/org/apache/catalina/startup/DynamicControllerManagerTest.java +++ b/tomcat/src/test/java/org/apache/catalina/startup/DynamicControllerManagerTest.java @@ -2,6 +2,7 @@ import common.http.HttpMethod; import common.http.Request; +import common.http.Session; import nextstep.jwp.controller.LoginController; import org.apache.coyote.http11.request.HttpRequest; import org.apache.coyote.http11.response.HttpResponse; @@ -14,13 +15,16 @@ class DynamicControllerManagerTest { @Test - void 요청의_첫_줄_정보로_controller를_찾아서_실행시킨다() throws Exception { + void 요청의_첫_줄_정보로_controller를_찾아서_실행시킨다() { // given DynamicControllerManager dynamicControllerManager = new DynamicControllerManager(); dynamicControllerManager.add("/login", new LoginController()); Request request = mock(HttpRequest.class); HttpResponse response = new HttpResponse(); + Session session = new Session("id"); when(request.getPath()).thenReturn("/login"); + when(request.getSession(true)).thenReturn(session); + when(request.getSession()).thenReturn(session); when(request.getHttpMethod()).thenReturn(HttpMethod.GET); // when 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 b1f778cfa9..cada36b45a 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java @@ -34,7 +34,8 @@ void process() { "Content-Type: text/html;charset=utf-8 ", "Content-Length: 12 ", "", - "Hello world!"); + "Hello world!" + ); assertThat(socket.output()).contains(expected); } From 9f091bb036302c7765bed9201b1294b4f02bf5d9 Mon Sep 17 00:00:00 2001 From: ReO Date: Mon, 11 Sep 2023 21:42:56 +0900 Subject: [PATCH 06/24] =?UTF-8?q?refactor:=20=EB=A7=A4=EC=A7=81=EB=84=98?= =?UTF-8?q?=EB=B2=84=20=EC=A0=95=EB=A6=AC=20=EB=B0=8F=20Http=20Status=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/common/http/Cookie.java | 2 +- tomcat/src/main/java/common/http/Cookies.java | 2 +- tomcat/src/main/java/common/http/HttpStatus.java | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/tomcat/src/main/java/common/http/Cookie.java b/tomcat/src/main/java/common/http/Cookie.java index 5a2c436be2..45b492ad6c 100644 --- a/tomcat/src/main/java/common/http/Cookie.java +++ b/tomcat/src/main/java/common/http/Cookie.java @@ -6,7 +6,7 @@ public class Cookie { private static final String SEPARATOR = "; "; - private static final String DELIMITER = "="; + public static final String DELIMITER = "="; private static final int ATTRIBUTE_INDEX = 0; private static final int VALUE_INDEX = 1; diff --git a/tomcat/src/main/java/common/http/Cookies.java b/tomcat/src/main/java/common/http/Cookies.java index a5b2e5476e..cc0d0ac7ed 100644 --- a/tomcat/src/main/java/common/http/Cookies.java +++ b/tomcat/src/main/java/common/http/Cookies.java @@ -7,7 +7,7 @@ public class Cookies { private Cookies() {} public static Cookie ofJSessionId(String id) { - return Cookie.from(JSESSIONID + "=" + id); + return Cookie.from(JSESSIONID + Cookie.DELIMITER + id); } public static String getJsessionid(Cookie cookie) { diff --git a/tomcat/src/main/java/common/http/HttpStatus.java b/tomcat/src/main/java/common/http/HttpStatus.java index 381610c94b..a3cbbcba97 100644 --- a/tomcat/src/main/java/common/http/HttpStatus.java +++ b/tomcat/src/main/java/common/http/HttpStatus.java @@ -3,9 +3,16 @@ public enum HttpStatus { OK("OK", 200), CREATED("Created", 201), + ACCEPTED("Accepted", 202), + NO_CONTENT("No Content", 204), + MOVED_PERMANENTLY("Moved Permanently", 301), + PERMANENT_REDIRECT("Permanent Redirect", 308), FOUND("Found", 302), + BAD_REQUEST("Bad Request", 400), UNAUTHORIZED("Unauthorized", 401), + FORBIDDEN("Forbidden", 403), NOT_FOUND("Not Found", 404), + CONFLICT("Conflict", 409), INTERNAL_SERVER_ERROR("Internal Server Error", 500); private final String statusMessage; From e10d7fa002632c4e7ac3d131a1ebc19cb7201253 Mon Sep 17 00:00:00 2001 From: ReO Date: Mon, 11 Sep 2023 22:03:44 +0900 Subject: [PATCH 07/24] =?UTF-8?q?refactor:=20=EC=9D=91=EB=8B=B5=20?= =?UTF-8?q?=ED=97=A4=EB=8D=94=EC=97=90=20=EA=B0=92=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jwp/controller/LoginController.java | 19 +++++++++++-------- .../jwp/controller/RegisterController.java | 3 +++ .../controller/StaticResourceController.java | 9 ++++----- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java index 6f70b84920..186b95e2a1 100644 --- a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java +++ b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java @@ -11,6 +11,7 @@ import org.slf4j.LoggerFactory; import static common.http.HttpStatus.FOUND; +import static common.http.HttpStatus.OK; import static common.http.HttpStatus.UNAUTHORIZED; public class LoginController extends AbstractController { @@ -19,16 +20,18 @@ public class LoginController extends AbstractController { @Override protected void doGet(Request request, Response response) { - if (request.hasValidSession()) { - Session session = request.getSession(); + if (!request.hasValidSession()) { response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); - response.addHttpStatus(FOUND); - response.addCookie(Cookies.ofJSessionId(session.getId())); - response.sendRedirect("/index.html"); + response.addHttpStatus(OK); + response.addStaticResourcePath("/login.html"); return; } - response.addStaticResourcePath("/login.html"); + Session session = request.getSession(); + response.addCookie(Cookies.ofJSessionId(session.getId())); + response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); + response.addHttpStatus(FOUND); + response.sendRedirect("/index.html"); } @Override @@ -39,16 +42,16 @@ protected void doPost(Request request, Response response) { if (!user.checkPassword(request.getPassword())) { response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); response.addHttpStatus(UNAUTHORIZED); - response.sendRedirect("/401.html"); + response.addStaticResourcePath("/401.html"); return; } log.info("user: {}", user); final Session session = request.getSession(true); session.setAttribute("user", user); + response.addCookie(Cookies.ofJSessionId(session.getId())); response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); response.addHttpStatus(FOUND); - response.addCookie(Cookies.ofJSessionId(session.getId())); response.sendRedirect("/index.html"); } } diff --git a/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java b/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java index 33c75f8109..710931a70e 100644 --- a/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java +++ b/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java @@ -9,12 +9,15 @@ import nextstep.jwp.model.User; import static common.http.HttpStatus.FOUND; +import static common.http.HttpStatus.OK; public class RegisterController extends AbstractController { @Override protected void doGet(Request request, Response response) { // 로그아웃이 없으므로 모든 요청에 대해 진행합니다. + response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); + response.addHttpStatus(OK); response.addStaticResourcePath("/register.html"); } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java index 09c2d19f47..55344fcbe6 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java @@ -20,17 +20,16 @@ public class StaticResourceController implements Controller { @Override public void service(Request request, Response response) { if (response.hasStaticResourcePath()) { - buildResponse(response.getStaticResourcePath(), request, response); + buildResponse(response.getStaticResourcePath(), response); return; } - buildResponse(request.getPath(), request, response); - } - - private void buildResponse(String path, Request request, Response response) { response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); response.addHttpStatus(OK); + buildResponse(request.getPath(), response); + } + private void buildResponse(String path, Response response) { try { URL resource = getResource(path); String fileContent = readFileContent(resource); From da9fff9b2b59a0b520c85d1810bb71e9cac28f08 Mon Sep 17 00:00:00 2001 From: ReO Date: Mon, 11 Sep 2023 22:15:03 +0900 Subject: [PATCH 08/24] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EC=A4=91=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 --- .../jwp/controller/LoginController.java | 26 +++++++++++-------- .../jwp/controller/RegisterController.java | 2 +- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java index 186b95e2a1..f9284c99e6 100644 --- a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java +++ b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java @@ -2,6 +2,7 @@ import common.http.AbstractController; import common.http.Cookies; +import common.http.HttpStatus; import common.http.Request; import common.http.Response; import common.http.Session; @@ -21,17 +22,12 @@ public class LoginController extends AbstractController { @Override protected void doGet(Request request, Response response) { if (!request.hasValidSession()) { - response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); - response.addHttpStatus(OK); - response.addStaticResourcePath("/login.html"); + buildStaticResourceResponse(request, response, OK, "/login.html"); return; } Session session = request.getSession(); - response.addCookie(Cookies.ofJSessionId(session.getId())); - response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); - response.addHttpStatus(FOUND); - response.sendRedirect("/index.html"); + buildRedirectResponse(request, response, session); } @Override @@ -40,15 +36,23 @@ protected void doPost(Request request, Response response) { .orElseThrow(() -> new IllegalArgumentException("회원 정보가 존재하지 않습니다.")); if (!user.checkPassword(request.getPassword())) { - response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); - response.addHttpStatus(UNAUTHORIZED); - response.addStaticResourcePath("/401.html"); + buildStaticResourceResponse(request, response, UNAUTHORIZED, "/401.html"); return; } log.info("user: {}", user); - final Session session = request.getSession(true); + Session session = request.getSession(true); session.setAttribute("user", user); + buildRedirectResponse(request, response, session); + } + + private void buildStaticResourceResponse(Request request, Response response, HttpStatus httpStatus, String path) { + response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); + response.addHttpStatus(httpStatus); + response.addStaticResourcePath(path); + } + + private void buildRedirectResponse(Request request, Response response, Session session) { response.addCookie(Cookies.ofJSessionId(session.getId())); response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); response.addHttpStatus(FOUND); diff --git a/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java b/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java index 710931a70e..30b34d4329 100644 --- a/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java +++ b/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java @@ -30,7 +30,7 @@ protected void doPost(Request request, Response response) { User user = new User(request.getAccount(), request.getPassword(), request.getEmail()); InMemoryUserRepository.save(user); - final Session session = request.getSession(true); + Session session = request.getSession(true); session.setAttribute("user", user); response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); response.addHttpStatus(FOUND); From bed381f0f64da007cfcd342613317bafad363b72 Mon Sep 17 00:00:00 2001 From: ReO Date: Mon, 11 Sep 2023 22:15:17 +0900 Subject: [PATCH 09/24] =?UTF-8?q?feat:=20409=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=8B=9C=20=EB=B3=B4=EC=97=AC=EC=A4=84=20html=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tomcat/src/main/resources/static/409.html | 51 +++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tomcat/src/main/resources/static/409.html diff --git a/tomcat/src/main/resources/static/409.html b/tomcat/src/main/resources/static/409.html new file mode 100644 index 0000000000..23b1c24465 --- /dev/null +++ b/tomcat/src/main/resources/static/409.html @@ -0,0 +1,51 @@ + + + + + + + + + 409 Error - SB Admin + + + + +
+
+
+
+
+
+
+

409

+

Conflict

+ + + Return to Dashboard + +
+
+
+
+
+
+ +
+ + + + From aa96ef7b6dab122d9582d6173afc93f9e5d67738 Mon Sep 17 00:00:00 2001 From: ReO Date: Mon, 11 Sep 2023 22:19:31 +0900 Subject: [PATCH 10/24] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../startup/DynamicControllerManager.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java b/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java index 7ea4a73e4d..fec4fa1d88 100644 --- a/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java +++ b/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java @@ -24,11 +24,7 @@ public void add(String path, Controller controller) { @Override public void service(Request request, Response response) { - String cookieHeader = request.getCookie(); - if (cookieHeader != null) { - Session session = sessionManager.findSession(Cookies.getJsessionid(Cookie.from(cookieHeader))); - request.addSession(session); - } + findSessionByCookie(request); Controller controller = mapper.get(request.getPath()); if (controller == null) { @@ -37,6 +33,20 @@ public void service(Request request, Response response) { controller.service(request, response); + saveNewSession(request); + } + + private void findSessionByCookie(Request request) { + String cookieHeader = request.getCookie(); + if (cookieHeader != null) { + Cookie cookie = Cookie.from(cookieHeader); + String jsessionid = Cookies.getJsessionid(cookie); + Session session = sessionManager.findSession(jsessionid); + request.addSession(session); + } + } + + private void saveNewSession(Request request) { Session session = request.getSession(); if (session != null && !sessionManager.hasSession(session)) { sessionManager.add(session); From 695e7d809a38dd40b86aec570cab372ff433bb07 Mon Sep 17 00:00:00 2001 From: ReO Date: Mon, 11 Sep 2023 22:19:31 +0900 Subject: [PATCH 11/24] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../startup/DynamicControllerManager.java | 20 +++++++++--- .../apache/coyote/http11/Http11Processor.java | 12 ++++--- .../controller/StaticResourceController.java | 10 +++--- .../coyote/http11/request/HttpRequest.java | 28 ++++++++--------- .../http11/request/HttpRequestLine.java | 31 ++++++++++++------- .../http11/request/HttpRequestParser.java | 27 ++++++++++------ .../coyote/http11/response/HttpResponse.java | 16 +++++----- .../http11/response/HttpResponseBody.java | 8 ++--- .../http11/response/HttpResponseHeaders.java | 15 +++++++-- .../http11/response/HttpStatusLine.java | 7 ++++- 10 files changed, 109 insertions(+), 65 deletions(-) diff --git a/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java b/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java index 7ea4a73e4d..fec4fa1d88 100644 --- a/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java +++ b/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java @@ -24,11 +24,7 @@ public void add(String path, Controller controller) { @Override public void service(Request request, Response response) { - String cookieHeader = request.getCookie(); - if (cookieHeader != null) { - Session session = sessionManager.findSession(Cookies.getJsessionid(Cookie.from(cookieHeader))); - request.addSession(session); - } + findSessionByCookie(request); Controller controller = mapper.get(request.getPath()); if (controller == null) { @@ -37,6 +33,20 @@ public void service(Request request, Response response) { controller.service(request, response); + saveNewSession(request); + } + + private void findSessionByCookie(Request request) { + String cookieHeader = request.getCookie(); + if (cookieHeader != null) { + Cookie cookie = Cookie.from(cookieHeader); + String jsessionid = Cookies.getJsessionid(cookie); + Session session = sessionManager.findSession(jsessionid); + request.addSession(session); + } + } + + private void saveNewSession(Request request) { Session session = request.getSession(); if (session != null && !sessionManager.hasSession(session)) { sessionManager.add(session); 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 e7dc98390c..e29f5cd165 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -43,10 +43,7 @@ public void process(final Socket connection) { controllerManager.service(httpRequest, httpResponse); - if (httpRequest.hasStaticResourcePath() || httpResponse.hasStaticResourcePath()) { - controllerManager = new StaticControllerManager(); - controllerManager.service(httpRequest, httpResponse); - } + serviceIfHasStaticResourcePath(httpRequest, httpResponse); outputStream.write(httpResponse.toString().getBytes()); outputStream.flush(); @@ -54,4 +51,11 @@ public void process(final Socket connection) { log.error(e.getMessage(), e); } } + + private void serviceIfHasStaticResourcePath(HttpRequest httpRequest, HttpResponse httpResponse) { + if (httpRequest.hasStaticResourcePath() || httpResponse.hasStaticResourcePath()) { + controllerManager = new StaticControllerManager(); + controllerManager.service(httpRequest, httpResponse); + } + } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java index 55344fcbe6..318da9a7e1 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java @@ -20,16 +20,16 @@ public class StaticResourceController implements Controller { @Override public void service(Request request, Response response) { if (response.hasStaticResourcePath()) { - buildResponse(response.getStaticResourcePath(), response); + buildResponse(response, response.getStaticResourcePath()); return; } response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); response.addHttpStatus(OK); - buildResponse(request.getPath(), response); + buildResponse(response, request.getPath()); } - private void buildResponse(String path, Response response) { + private void buildResponse(Response response, String path) { try { URL resource = getResource(path); String fileContent = readFileContent(resource); @@ -40,8 +40,10 @@ private void buildResponse(String path, Response response) { if (fileContent.length() != 0) { response.addBody(fileContent); } + } catch (FileNotFoundException e) { + throw new IllegalArgumentException("해당하는 파일을 찾을 수 없습니다."); } catch (IOException e) { - throw new IllegalArgumentException("파일을 찾을 수 없습니다."); + throw new IllegalArgumentException("파일을 읽을 수 없습니다."); } } 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 e86504b1fd..4bca527c1b 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 @@ -22,20 +22,16 @@ public HttpRequest(HttpRequestLine httpRequestLine, HttpRequestHeaders httpReque this.httpRequestBody = httpRequestBody; } - public String getVersionOfTheProtocol() { - return httpRequestLine.getVersionOfTheProtocol(); + public HttpMethod getHttpMethod() { + return httpRequestLine.getHttpMethod(); } public String getPath() { return httpRequestLine.getPath(); } - public boolean hasValidSession() { - return session != null; - } - - public HttpMethod getHttpMethod() { - return httpRequestLine.getHttpMethod(); + public String getVersionOfTheProtocol() { + return httpRequestLine.getVersionOfTheProtocol(); } public String getCookie() { @@ -54,21 +50,25 @@ public String getEmail() { return httpRequestBody.getEmail(); } + public Session getSession() { + return session; + } + public Session getSession(boolean create) { UUID uuid = UUID.randomUUID(); this.session = new Session(uuid.toString()); return session; } - public void addSession(Session session) { - this.session = session; - } - - public Session getSession() { - return session; + public boolean hasValidSession() { + return session != null; } public boolean hasStaticResourcePath() { return STATIC_RESOURCE_PATH_PATTERN.matcher(getPath()).find(); } + + public void addSession(Session session) { + this.session = session; + } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestLine.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestLine.java index 55a32bead1..18bbdeacfc 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestLine.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequestLine.java @@ -23,26 +23,29 @@ private HttpRequestLine(HttpMethod httpMethod, String path, String versionOfTheP } static HttpRequestLine from(String requestLine) { - String[] infos = requestLine.split(SPACE); - if (infos.length != NUMBER_OF_FIRST_LINE_INFOS) { - throw new IllegalArgumentException("유효하지 않은 Request-line 입니다."); - } + String[] infos = parseInfos(requestLine); HttpMethod httpMethod = HttpMethod.parseHttpMethod(infos[HTTP_METHOD_INDEX]); - String uriWithQueryString = infos[PATH_INDEX]; + String uri = removeQueryString(infos[PATH_INDEX]); String versionOfTheProtocol = infos[PROTOCOL_VERSION_INDEX]; - int indexOfQueryStringDelimiter = uriWithQueryString.indexOf(QUERY_STRING_DELIMITER); + return new HttpRequestLine(httpMethod, uri, versionOfTheProtocol); + } + + private static String removeQueryString(String uri) { + int indexOfQueryStringDelimiter = uri.indexOf(QUERY_STRING_DELIMITER); if (indexOfQueryStringDelimiter != -1) { - String uri = uriWithQueryString.substring(0, indexOfQueryStringDelimiter); - return new HttpRequestLine(httpMethod, uri, versionOfTheProtocol); + return uri.substring(0, indexOfQueryStringDelimiter); } - - return new HttpRequestLine(httpMethod, uriWithQueryString, versionOfTheProtocol); + return uri; } - String getVersionOfTheProtocol() { - return versionOfTheProtocol; + private static String[] parseInfos(String requestLine) { + String[] infos = requestLine.split(SPACE); + if (infos.length != NUMBER_OF_FIRST_LINE_INFOS) { + throw new IllegalArgumentException("유효하지 않은 Request-line 입니다."); + } + return infos; } HttpMethod getHttpMethod() { @@ -52,4 +55,8 @@ HttpMethod getHttpMethod() { String getPath() { return path; } + + String getVersionOfTheProtocol() { + return versionOfTheProtocol; + } } 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 879f07b6ea..d9ad0008bf 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 @@ -38,11 +38,7 @@ private static HttpRequestHeaders parseHeaders(BufferedReader reader) throws IOE String headerLine; while ((headerLine = reader.readLine()) != null && !headerLine.isEmpty()) { - int indexOfDelimiter = headerLine.indexOf(DELIMITER); - - if (indexOfDelimiter == -1) { - throw new IllegalArgumentException("요청의 헤더가 잘못되었습니다."); - } + int indexOfDelimiter = getIndexOfDelimiter(headerLine); String property = headerLine.substring(0, indexOfDelimiter); String value = headerLine.substring(indexOfDelimiter + START_AFTER_SPACE); @@ -52,6 +48,15 @@ private static HttpRequestHeaders parseHeaders(BufferedReader reader) throws IOE return new HttpRequestHeaders(headers); } + private static int getIndexOfDelimiter(String headerLine) { + int indexOfDelimiter = headerLine.indexOf(DELIMITER); + + if (indexOfDelimiter == -1) { + throw new IllegalArgumentException("요청의 헤더가 잘못되었습니다."); + } + return indexOfDelimiter; + } + private static HttpRequestBody parseBody(BufferedReader reader, int contentLength) throws IOException { if (contentLength == 0) { return new HttpRequestBody(new HashMap<>()); @@ -61,10 +66,7 @@ private static HttpRequestBody parseBody(BufferedReader reader, int contentLengt reader.read(buffer, 0, contentLength); String requestBody = new String(buffer); - Map body = Arrays.stream(requestBody.split(PARAMS_DELIMITER)) - .map(property -> property.split(PARAM_VALUE_DELIMITER)) - .filter(propertyAndValue -> propertyAndValue.length == 2) - .collect(Collectors.toMap(data -> data[PROPERTY_INDEX], data -> data[VALUE_INDEX])); + Map body = parse(requestBody); if (body.size() != requestBody.split(PARAMS_DELIMITER).length) { throw new IllegalArgumentException("요청의 바디가 잘못되었습니다."); @@ -73,6 +75,13 @@ private static HttpRequestBody parseBody(BufferedReader reader, int contentLengt return new HttpRequestBody(body); } + private static Map parse(String requestBody) { + return Arrays.stream(requestBody.split(PARAMS_DELIMITER)) + .map(property -> property.split(PARAM_VALUE_DELIMITER)) + .filter(propertyAndValue -> propertyAndValue.length == 2) + .collect(Collectors.toMap(data -> data[PROPERTY_INDEX], data -> data[VALUE_INDEX])); + } + private HttpRequestParser() { } } 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 b29c67f884..0235e9cd39 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 @@ -5,9 +5,9 @@ import common.http.HttpStatus; import common.http.Response; -public class HttpResponse implements Response { +import static common.Constants.CRLF; - private static final String CRLF = "\r\n"; +public class HttpResponse implements Response { private final HttpStatusLine httpStatusLine; private final HttpResponseHeaders httpResponseHeaders; @@ -39,15 +39,15 @@ public void sendRedirect(String redirectURL) { httpResponseHeaders.addHeaderFieldAndValue("Location", redirectURL); } + public void addStaticResourcePath(String name) { + httpResponseHeaders.addHeaderFieldAndValue("Resource-Path", name); + } + public void addBody(String body) { httpResponseHeaders.addHeaderFieldAndValue("Content-Length", String.valueOf(body.getBytes().length)); httpResponseBody.addBody(body); } - public void addStaticResourcePath(String name) { - httpResponseHeaders.addHeaderFieldAndValue("Resource-Path", name); - } - public boolean hasStaticResourcePath() { return httpResponseHeaders.hasStaticResourcePath(); } @@ -58,12 +58,14 @@ public String getStaticResourcePath() { @Override public String toString() { - if (httpResponseBody.hasBody()) { + if (httpResponseBody.exist()) { + httpResponseBody.validateLength(httpResponseHeaders.getContentLength()); return String.join(CRLF, httpStatusLine.toString(), httpResponseHeaders.toString(), httpResponseBody.toString()); } + return String.join(CRLF, httpStatusLine.toString(), httpResponseHeaders.toString()); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseBody.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseBody.java index c20276cd2e..610a8abf2d 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseBody.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseBody.java @@ -12,21 +12,17 @@ void addBody(String body) { } void validateLength(long length) { - if (length != body.length()) { + if (length != body.getBytes().length) { throw new IllegalArgumentException("Response Body의 길이가 유효하지 않습니다."); } } boolean exist() { - return body.length() != 0; + return body != null; } @Override public String toString() { return body; } - - public boolean hasBody() { - return body != null; - } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java index a15859a10c..3276ff392b 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java @@ -10,6 +10,11 @@ class HttpResponseHeaders { + public static final String CONTENT_TYPE = "Content-Type"; + public static final String CONTENT_LENGTH = "Content-Length"; + public static final String LOCATION = "Location"; + public static final String RESOURCE_PATH = "Resource-Path"; + private final Map> headers; HttpResponseHeaders() { @@ -17,7 +22,7 @@ class HttpResponseHeaders { } void addHeaderFieldAndValue(String field, String value) { - Set uniqueHeaderFields = Set.of("Content-Type", "Content-Length", "Location"); + Set uniqueHeaderFields = Set.of(CONTENT_TYPE, CONTENT_LENGTH, LOCATION); if (headers.containsKey(field) && uniqueHeaderFields.contains(field)) { throw new IllegalStateException(String.format("헤더에 이미 %s에 대한 값이 존재합니다.", field)); @@ -32,11 +37,15 @@ void addHeaderFieldAndValue(String field, String value) { } boolean hasStaticResourcePath() { - return headers.containsKey("Resource-Path"); + return headers.containsKey(RESOURCE_PATH); } String getStaticResourcePath() { - return headers.get("Resource-Path").get(0); + return headers.get(RESOURCE_PATH).get(0); + } + + public long getContentLength() { + return Long.parseLong(headers.get(CONTENT_LENGTH).get(0)); } @Override diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatusLine.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatusLine.java index 6fff0e7e17..74fb9617c7 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatusLine.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatusLine.java @@ -29,6 +29,11 @@ void addHttpStatus(HttpStatus httpStatus) { @Override public String toString() { - return versionOfTheProtocol + SPACE + httpStatus.getStatusCode() + SPACE + httpStatus.getStatusMessage() + SPACE; + return versionOfTheProtocol + + SPACE + + httpStatus.getStatusCode() + + SPACE + + httpStatus.getStatusMessage() + + SPACE; } } From 04cc27008fc6e6f79d616294550f481dcb7cf817 Mon Sep 17 00:00:00 2001 From: ReO Date: Tue, 12 Sep 2023 01:21:25 +0900 Subject: [PATCH 12/24] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=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/startup/Sessions.java | 4 +- .../jwp/controller/LoginControllerTest.java | 64 +++++++++++++++- .../controller/RegisterControllerTest.java | 76 +++++++++++++++++++ .../startup/DynamicControllerManagerTest.java | 4 + .../catalina/startup/SessionManagerTest.java | 40 ++++++++++ .../StaticControllerManagerTest.java | 49 ++++++++++++ .../StaticResourceControllerTest.java | 55 ++++++++++++++ .../http11/response/HttpResponseTest.java | 32 ++++++++ 8 files changed, 320 insertions(+), 4 deletions(-) create mode 100644 tomcat/src/test/java/nextstep/jwp/controller/RegisterControllerTest.java create mode 100644 tomcat/src/test/java/org/apache/catalina/startup/SessionManagerTest.java create mode 100644 tomcat/src/test/java/org/apache/coyote/http11/controller/StaticControllerManagerTest.java create mode 100644 tomcat/src/test/java/org/apache/coyote/http11/controller/StaticResourceControllerTest.java create mode 100644 tomcat/src/test/java/org/apache/coyote/http11/response/HttpResponseTest.java diff --git a/tomcat/src/main/java/org/apache/catalina/startup/Sessions.java b/tomcat/src/main/java/org/apache/catalina/startup/Sessions.java index 7b93b6f066..f8e112a2f9 100644 --- a/tomcat/src/main/java/org/apache/catalina/startup/Sessions.java +++ b/tomcat/src/main/java/org/apache/catalina/startup/Sessions.java @@ -24,11 +24,11 @@ void remove(String id) { idAndSessions.remove(id); } - public void clear() { + void clear() { idAndSessions.clear(); } - public boolean has(Session session) { + boolean has(Session session) { return idAndSessions.containsValue(session); } } diff --git a/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java b/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java index 05b0e9e189..71b031765b 100644 --- a/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java +++ b/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java @@ -3,6 +3,8 @@ import common.http.Request; import common.http.Response; import common.http.Session; +import nextstep.jwp.db.InMemoryUserRepository; +import nextstep.jwp.model.User; import org.apache.catalina.startup.SessionManager; import org.apache.coyote.http11.request.HttpRequest; import org.apache.coyote.http11.response.HttpResponse; @@ -12,6 +14,7 @@ import static common.Constants.CRLF; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -21,7 +24,7 @@ class LoginControllerTest { @Test - void 세션이_존재하는_경우_indexhtml로_리다이렉션을_한다 () { + void GET요청시_세션이_존재하는_경우_indexhtml로_리다이렉션을_한다 () { // given Request request = mock(HttpRequest.class); Response response = new HttpResponse(); @@ -46,7 +49,7 @@ class LoginControllerTest { } @Test - void 세션이_존재하지_않는_경우_loginhtml을_응답에_실어준다() { + void GET요청시_세션이_존재하지_않는_경우_loginhtml을_응답에_실어준다() { // given Request request = mock(HttpRequest.class); Response response = new HttpResponse(); @@ -58,4 +61,61 @@ class LoginControllerTest { // then assertThat(response.getStaticResourcePath()).isEqualTo("/login.html"); } + + @Test + void POST요청시_회원_정보가_없으면_예외를_반환한다() { + // given + Request request = mock(HttpRequest.class); + Response response = new HttpResponse(); + LoginController loginController = new LoginController(); + + when(request.getAccount()).thenReturn("롤스로이스"); + when(request.getVersionOfTheProtocol()).thenReturn("HTTP/1.1"); + + // expect + assertThatThrownBy(() -> loginController.doPost(request, response)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("회원 정보가 존재하지 않습니다."); + } + + @Test + void POST요청시_비밀번호가_틀리면_401html로_리다이렉트한다() { + // given + Request request = mock(HttpRequest.class); + Response response = new HttpResponse(); + LoginController loginController = new LoginController(); + InMemoryUserRepository.save(new User("로이스", "잘생김", "내마음속")); + when(request.getAccount()).thenReturn("로이스"); + when(request.getPassword()).thenReturn("못생김"); + when(request.getVersionOfTheProtocol()).thenReturn("HTTP/1.1"); + + // when + loginController.doPost(request, response); + + // then + assertThat(response.getStaticResourcePath()).isEqualTo("/401.html"); + } + + @Test + void POST요청시_로그인에_성공하면_indexhtml로_리다이렉트한다() { + // given + Request request = mock(HttpRequest.class); + Response response = new HttpResponse(); + LoginController loginController = new LoginController(); + InMemoryUserRepository.save(new User("로이스", "잘생김", "내마음속")); + when(request.getAccount()).thenReturn("로이스"); + when(request.getPassword()).thenReturn("잘생김"); + when(request.getSession(true)).thenReturn(new Session("로이스")); + when(request.getVersionOfTheProtocol()).thenReturn("HTTP/1.1"); + + // when + loginController.doPost(request, response); + + // then + assertThat(response.toString()).hasToString( + "HTTP/1.1 302 Found " + CRLF + + "Set-Cookie: JSESSIONID=로이스 " + CRLF + + "Location: /index.html " + CRLF + ); + } } 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..3f46352b37 --- /dev/null +++ b/tomcat/src/test/java/nextstep/jwp/controller/RegisterControllerTest.java @@ -0,0 +1,76 @@ +package nextstep.jwp.controller; + +import common.http.Request; +import common.http.Response; +import common.http.Session; +import nextstep.jwp.db.InMemoryUserRepository; +import nextstep.jwp.model.User; +import org.apache.coyote.http11.request.HttpRequest; +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; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class RegisterControllerTest { + + @Test + void GET요청시_registerhtml의_경로를_응답에_실는다() { + // given + Request request = mock(HttpRequest.class); + Response response = new HttpResponse(); + RegisterController registerController = new RegisterController(); + + when(request.getVersionOfTheProtocol()).thenReturn("HTTP/1.1"); + + // when + registerController.doGet(request, response); + + // then + assertThat(response.getStaticResourcePath()).isEqualTo("/register.html"); + } + + @Test + void POST요청시_이미_가입된_회원이면_예외를_반환한다() { + // given + InMemoryUserRepository.save(new User("로이스", "잘생김", "내마음속")); + Request request = mock(HttpRequest.class); + Response response = new HttpResponse(); + RegisterController registerController = new RegisterController(); + + when(request.getVersionOfTheProtocol()).thenReturn("HTTP/1.1"); + when(request.getAccount()).thenReturn("로이스"); + + // expect + assertThatThrownBy(() -> registerController.doPost(request, response)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이미 가입된 회원입니다."); + } + + @Test + void POST요청시_회원가입을_진행한다() { + // given + Request request = mock(HttpRequest.class); + Response response = new HttpResponse(); + RegisterController registerController = new RegisterController(); + + when(request.getVersionOfTheProtocol()).thenReturn("HTTP/1.1"); + when(request.getAccount()).thenReturn("롤스로이스"); + when(request.getPassword()).thenReturn("비쌈"); + when(request.getEmail()).thenReturn("근데내꺼ㅎ"); + when(request.getSession(true)).thenReturn(new Session("id")); + // when + registerController.doPost(request, response); + + // then + User user = InMemoryUserRepository.findByAccount("롤스로이스").get(); + assertThat(user.checkPassword("비쌈")).isTrue(); + assertThat(response.toString()).contains("Location: /index.html"); + } +} diff --git a/tomcat/src/test/java/org/apache/catalina/startup/DynamicControllerManagerTest.java b/tomcat/src/test/java/org/apache/catalina/startup/DynamicControllerManagerTest.java index aef2a236b0..3ec575e43c 100644 --- a/tomcat/src/test/java/org/apache/catalina/startup/DynamicControllerManagerTest.java +++ b/tomcat/src/test/java/org/apache/catalina/startup/DynamicControllerManagerTest.java @@ -6,12 +6,16 @@ import nextstep.jwp.controller.LoginController; import org.apache.coyote.http11.request.HttpRequest; 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; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) class DynamicControllerManagerTest { @Test diff --git a/tomcat/src/test/java/org/apache/catalina/startup/SessionManagerTest.java b/tomcat/src/test/java/org/apache/catalina/startup/SessionManagerTest.java new file mode 100644 index 0000000000..01362be76c --- /dev/null +++ b/tomcat/src/test/java/org/apache/catalina/startup/SessionManagerTest.java @@ -0,0 +1,40 @@ +package org.apache.catalina.startup; + +import common.http.Session; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class SessionManagerTest { + + @Test + void Session을_추가한다() { + // given + Session session = new Session("로이스"); + SessionManager sessionManager = new SessionManager(); + + // when + sessionManager.add(session); + + // then + assertThat(sessionManager.findSession("로이스")).isEqualTo(session); + } + + @Test + void Sessoin을_삭제한다() { + // given + Session session = new Session("로이스"); + SessionManager sessionManager = new SessionManager(); + sessionManager.add(session); + + // when + sessionManager.remove(session); + + // then + assertThat(sessionManager.hasSession(session)).isFalse(); + } +} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/controller/StaticControllerManagerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/controller/StaticControllerManagerTest.java new file mode 100644 index 0000000000..b14beb5b36 --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/controller/StaticControllerManagerTest.java @@ -0,0 +1,49 @@ +package org.apache.coyote.http11.controller; + +import common.http.HttpMethod; +import common.http.Request; +import nextstep.jwp.controller.LoginController; +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.response.HttpResponse; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class StaticControllerManagerTest { + + @Test + void 컨트롤러를_추가할_시_예외를_반환한다() { + // given + StaticControllerManager staticControllerManager = new StaticControllerManager(); + LoginController loginController = new LoginController(); + + // expect + Assertions.assertThatThrownBy(() -> staticControllerManager.add("/login", loginController)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("컨트롤러를 추가할 수 없습니다."); + } + + @Test + void 정적파일을_제공하는_컨트롤러를_찾아서_service를_실행한다() { + // given + StaticControllerManager staticControllerManager = new StaticControllerManager(); + Request request = mock(HttpRequest.class); + HttpResponse response = new HttpResponse(); + when(request.getVersionOfTheProtocol()).thenReturn("HTTP/1.1"); + when(request.getPath()).thenReturn("/login.html"); + when(request.getHttpMethod()).thenReturn(HttpMethod.GET); + + // when + staticControllerManager.service(request, response); + + // then + assertThat(response.toString()).contains("HTTP/1.1 200 OK"); + } +} diff --git a/tomcat/src/test/java/org/apache/coyote/http11/controller/StaticResourceControllerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/controller/StaticResourceControllerTest.java new file mode 100644 index 0000000000..9f538c46c0 --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/controller/StaticResourceControllerTest.java @@ -0,0 +1,55 @@ +package org.apache.coyote.http11.controller; + +import common.http.HttpMethod; +import common.http.HttpStatus; +import common.http.Request; +import org.apache.coyote.http11.request.HttpRequest; +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; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class StaticResourceControllerTest { + + @Test + void 요청에_정적리소스_경로가_있을_경우_정적리소스를_제공한다() { + // given + Request request = mock(HttpRequest.class); + HttpResponse response = new HttpResponse(); + when(request.getVersionOfTheProtocol()).thenReturn("HTTP/1.1"); + when(request.getPath()).thenReturn("/login.html"); + when(request.getHttpMethod()).thenReturn(HttpMethod.GET); + + StaticResourceController staticResourceController = new StaticResourceController(); + + // when + staticResourceController.service(request, response); + + // then + assertThat(response.toString()).contains("HTTP/1.1 200 OK"); + } + + @Test + void 응답에_정적리소스_경로가_있을_경우_정적리소스를_제공한다() { + // given + Request request = mock(HttpRequest.class); + HttpResponse response = new HttpResponse(); + response.addVersionOfTheProtocol("HTTP/1.1"); + response.addStaticResourcePath("/login.html"); + response.addHttpStatus(HttpStatus.OK); + + StaticResourceController staticResourceController = new StaticResourceController(); + + // when + staticResourceController.service(request, response); + + // then + assertThat(response.toString()).contains("HTTP/1.1 200 OK"); + } +} 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..809b47d373 --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/response/HttpResponseTest.java @@ -0,0 +1,32 @@ +package org.apache.coyote.http11.response; + +import common.http.ContentType; +import common.http.Cookie; +import common.http.HttpStatus; +import org.assertj.core.api.Assertions; +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 HttpResponse를_String으로_변환한다() { + // given + HttpResponse httpResponse = new HttpResponse(); + httpResponse.addVersionOfTheProtocol("HTTP/1.1"); + httpResponse.addHttpStatus(HttpStatus.OK); + httpResponse.addCookie(Cookie.from("JSESSIONID=1234")); + httpResponse.addContentType(ContentType.HTML); + httpResponse.addBody("로이스 잘생겼다"); + + // when + String string = httpResponse.toString(); + + // then + Assertions.assertThat(string).contains("HTTP/1.1 200 OK", "Set-Cookie: JSESSIONID=1234", "Content-Type: text/html", "로이스 잘생겼다"); + } + +} From bc072b1f89dae40d06933466abd1b46c12252da4 Mon Sep 17 00:00:00 2001 From: ReO Date: Wed, 13 Sep 2023 17:31:36 +0900 Subject: [PATCH 13/24] =?UTF-8?q?docs:=20README=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9ec86e959a..158c86d65d 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ -[x] 매직 넘버 정리 -[x] 리팩터링(클래스 분리, 패키지 정리) -[x] 상태코드, http 메서드 종류 추가 --[ ] 테스트 - 회원가입, 정적리소스 +-[x] 테스트 - 회원가입, 정적리소스 -[ ] 로깅 -[ ] 예외 처리 -[ ] 캐싱 구현해보기 From 5e484326ccf26656e0268be4ff193fdb42b75b3d Mon Sep 17 00:00:00 2001 From: ReO Date: Wed, 13 Sep 2023 17:31:58 +0900 Subject: [PATCH 14/24] =?UTF-8?q?refactor:=20Constants=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tomcat/src/main/java/{common => org/apache}/Constants.java | 2 +- .../java/org/apache/coyote/http11/response/HttpResponse.java | 2 +- .../apache/coyote/http11/response/HttpResponseHeaders.java | 4 ++-- .../org/apache/coyote/http11/response/HttpStatusLine.java | 2 +- .../test/java/nextstep/jwp/controller/HomeControllerTest.java | 2 +- .../java/nextstep/jwp/controller/LoginControllerTest.java | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) rename tomcat/src/main/java/{common => org/apache}/Constants.java (88%) diff --git a/tomcat/src/main/java/common/Constants.java b/tomcat/src/main/java/org/apache/Constants.java similarity index 88% rename from tomcat/src/main/java/common/Constants.java rename to tomcat/src/main/java/org/apache/Constants.java index 2b95f7c465..bb9187bde2 100644 --- a/tomcat/src/main/java/common/Constants.java +++ b/tomcat/src/main/java/org/apache/Constants.java @@ -1,4 +1,4 @@ -package common; +package org.apache; public class Constants { 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 0235e9cd39..086ee0bfb4 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 @@ -5,7 +5,7 @@ import common.http.HttpStatus; import common.http.Response; -import static common.Constants.CRLF; +import static org.apache.Constants.CRLF; public class HttpResponse implements Response { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java index 3276ff392b..7a810690db 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java @@ -5,8 +5,8 @@ import java.util.Map; import java.util.Set; -import static common.Constants.CRLF; -import static common.Constants.SPACE; +import static org.apache.Constants.CRLF; +import static org.apache.Constants.SPACE; class HttpResponseHeaders { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatusLine.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatusLine.java index 74fb9617c7..66eace2821 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatusLine.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatusLine.java @@ -2,7 +2,7 @@ import common.http.HttpStatus; -import static common.Constants.SPACE; +import static org.apache.Constants.SPACE; class HttpStatusLine { diff --git a/tomcat/src/test/java/nextstep/jwp/controller/HomeControllerTest.java b/tomcat/src/test/java/nextstep/jwp/controller/HomeControllerTest.java index 60c1903078..6d1893f00b 100644 --- a/tomcat/src/test/java/nextstep/jwp/controller/HomeControllerTest.java +++ b/tomcat/src/test/java/nextstep/jwp/controller/HomeControllerTest.java @@ -9,7 +9,7 @@ import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; -import static common.Constants.CRLF; +import static org.apache.Constants.CRLF; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java b/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java index 71b031765b..ae338fe64c 100644 --- a/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java +++ b/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java @@ -12,7 +12,7 @@ import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; -import static common.Constants.CRLF; +import static org.apache.Constants.CRLF; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; From cf1e1e6b9c4301daa4345cbcf2f8824083b6242a Mon Sep 17 00:00:00 2001 From: ReO Date: Wed, 13 Sep 2023 17:38:11 +0900 Subject: [PATCH 15/24] =?UTF-8?q?fix:=20=EC=9A=94=EC=B2=AD=EC=97=90=20?= =?UTF-8?q?=ED=95=B4=EB=8B=B9=ED=95=98=EB=8A=94=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EB=A5=BC=20=ED=98=B8=EC=B6=9C=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=EC=98=88=EC=99=B8=EB=A5=BC=20=EB=B0=9C?= =?UTF-8?q?=EC=83=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/common/http/AbstractController.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/tomcat/src/main/java/common/http/AbstractController.java b/tomcat/src/main/java/common/http/AbstractController.java index 5401d8f5b1..64045583b0 100644 --- a/tomcat/src/main/java/common/http/AbstractController.java +++ b/tomcat/src/main/java/common/http/AbstractController.java @@ -12,6 +12,7 @@ public abstract class AbstractController implements Controller { + public static final String ERROR_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD = "요청에 해당하는 메서드가 없습니다."; private final Map> methodMapping = new EnumMap<>(HttpMethod.class); protected AbstractController() { @@ -27,13 +28,23 @@ public void service(Request request, Response response) { methodMapping.get(request.getHttpMethod()).accept(request, response); } - protected void doGet(Request request, Response response) { /* NOOP */ } + protected void doGet(Request request, Response response) { + throw new IllegalArgumentException(ERROR_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); + } - protected void doPost(Request request, Response response) { /* NOOP */ } + protected void doPost(Request request, Response response) { + throw new IllegalArgumentException(ERROR_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); + } - protected void doPut(Request request, Response response) { /* NOOP */ } + protected void doPut(Request request, Response response) { + throw new IllegalArgumentException(ERROR_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); + } - protected void doPatch(Request request, Response response) { /* NOOP */ } + protected void doPatch(Request request, Response response) { + throw new IllegalArgumentException(ERROR_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); + } - protected void doDelete(Request request, Response response) { /* NOOP */ } + protected void doDelete(Request request, Response response) { + throw new IllegalArgumentException(ERROR_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); + } } From 163f07aa144430f6cb91785a9fad2fb9feecc2e5 Mon Sep 17 00:00:00 2001 From: ReO Date: Wed, 13 Sep 2023 19:23:51 +0900 Subject: [PATCH 16/24] =?UTF-8?q?refactor:=20toString=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tomcat/src/main/java/common/http/Cookie.java | 3 +-- tomcat/src/main/java/common/http/HttpStatus.java | 2 +- tomcat/src/main/java/common/http/Response.java | 2 ++ .../org/apache/coyote/http11/Http11Processor.java | 2 +- .../coyote/http11/response/HttpResponse.java | 15 +++++++-------- .../coyote/http11/response/HttpResponseBody.java | 3 +-- .../http11/response/HttpResponseHeaders.java | 3 +-- .../coyote/http11/response/HttpStatusLine.java | 3 +-- .../jwp/controller/HomeControllerTest.java | 2 +- .../jwp/controller/LoginControllerTest.java | 4 ++-- .../jwp/controller/RegisterControllerTest.java | 2 +- .../controller/StaticControllerManagerTest.java | 2 +- .../controller/StaticResourceControllerTest.java | 4 ++-- .../coyote/http11/response/HttpResponseTest.java | 2 +- 14 files changed, 23 insertions(+), 26 deletions(-) diff --git a/tomcat/src/main/java/common/http/Cookie.java b/tomcat/src/main/java/common/http/Cookie.java index 45b492ad6c..c77922c2c0 100644 --- a/tomcat/src/main/java/common/http/Cookie.java +++ b/tomcat/src/main/java/common/http/Cookie.java @@ -46,8 +46,7 @@ String getAttribute(String attribute) { return items.get(attribute); } - @Override - public String toString() { + public String getValue() { StringBuilder stringBuilder = new StringBuilder(); for (Map.Entry item : items.entrySet()) { stringBuilder.append(item.getKey()).append(DELIMITER).append(item.getValue()).append(SEPARATOR); diff --git a/tomcat/src/main/java/common/http/HttpStatus.java b/tomcat/src/main/java/common/http/HttpStatus.java index a3cbbcba97..cfea18cd5d 100644 --- a/tomcat/src/main/java/common/http/HttpStatus.java +++ b/tomcat/src/main/java/common/http/HttpStatus.java @@ -6,8 +6,8 @@ public enum HttpStatus { ACCEPTED("Accepted", 202), NO_CONTENT("No Content", 204), MOVED_PERMANENTLY("Moved Permanently", 301), - PERMANENT_REDIRECT("Permanent Redirect", 308), FOUND("Found", 302), + PERMANENT_REDIRECT("Permanent Redirect", 308), BAD_REQUEST("Bad Request", 400), UNAUTHORIZED("Unauthorized", 401), FORBIDDEN("Forbidden", 403), diff --git a/tomcat/src/main/java/common/http/Response.java b/tomcat/src/main/java/common/http/Response.java index cca6cbd5a6..570f1a4da1 100644 --- a/tomcat/src/main/java/common/http/Response.java +++ b/tomcat/src/main/java/common/http/Response.java @@ -19,4 +19,6 @@ public interface Response { String getStaticResourcePath(); void addBody(String body); + + String getMessage(); } 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 e29f5cd165..7c969668d6 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -45,7 +45,7 @@ public void process(final Socket connection) { serviceIfHasStaticResourcePath(httpRequest, httpResponse); - outputStream.write(httpResponse.toString().getBytes()); + outputStream.write(httpResponse.getMessage().getBytes()); outputStream.flush(); } catch (Exception e) { log.error(e.getMessage(), e); 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 086ee0bfb4..be226e76c6 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 @@ -32,7 +32,7 @@ public void addContentType(ContentType contentType) { } public void addCookie(Cookie cookie) { - httpResponseHeaders.addHeaderFieldAndValue("Set-Cookie", cookie.toString()); + httpResponseHeaders.addHeaderFieldAndValue("Set-Cookie", cookie.getValue()); } public void sendRedirect(String redirectURL) { @@ -56,18 +56,17 @@ public String getStaticResourcePath() { return httpResponseHeaders.getStaticResourcePath(); } - @Override - public String toString() { + public String getMessage() { if (httpResponseBody.exist()) { httpResponseBody.validateLength(httpResponseHeaders.getContentLength()); return String.join(CRLF, - httpStatusLine.toString(), - httpResponseHeaders.toString(), - httpResponseBody.toString()); + httpStatusLine.getMessage(), + httpResponseHeaders.getMessage(), + httpResponseBody.getMessage()); } return String.join(CRLF, - httpStatusLine.toString(), - httpResponseHeaders.toString()); + httpStatusLine.getMessage(), + httpResponseHeaders.getMessage()); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseBody.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseBody.java index 610a8abf2d..f36d536b10 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseBody.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseBody.java @@ -21,8 +21,7 @@ boolean exist() { return body != null; } - @Override - public String toString() { + public String getMessage() { return body; } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java index 7a810690db..6a189c767c 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java @@ -48,8 +48,7 @@ public long getContentLength() { return Long.parseLong(headers.get(CONTENT_LENGTH).get(0)); } - @Override - public String toString() { + public String getMessage() { StringBuilder stringBuilder = new StringBuilder(); for (Map.Entry> fieldAndValue : headers.entrySet()) { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatusLine.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatusLine.java index 66eace2821..9b40b16e36 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatusLine.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatusLine.java @@ -27,8 +27,7 @@ void addHttpStatus(HttpStatus httpStatus) { this.httpStatus = httpStatus; } - @Override - public String toString() { + public String getMessage() { return versionOfTheProtocol + SPACE + httpStatus.getStatusCode() + diff --git a/tomcat/src/test/java/nextstep/jwp/controller/HomeControllerTest.java b/tomcat/src/test/java/nextstep/jwp/controller/HomeControllerTest.java index 6d1893f00b..75d1838ff9 100644 --- a/tomcat/src/test/java/nextstep/jwp/controller/HomeControllerTest.java +++ b/tomcat/src/test/java/nextstep/jwp/controller/HomeControllerTest.java @@ -28,7 +28,7 @@ class HomeControllerTest { homeController.doGet(request, httpResponse); // then - Assertions.assertThat(httpResponse.toString()).hasToString( + Assertions.assertThat(httpResponse.getMessage()).hasToString( "HTTP/1.1 200 OK " + CRLF + "Content-Length: 12 " + CRLF + "Content-Type: text/html;charset=utf-8 " + CRLF + CRLF + diff --git a/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java b/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java index ae338fe64c..95e639f6cd 100644 --- a/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java +++ b/tomcat/src/test/java/nextstep/jwp/controller/LoginControllerTest.java @@ -41,7 +41,7 @@ class LoginControllerTest { loginController.doGet(request, response); // then - assertThat(response.toString()).hasToString( + assertThat(response.getMessage()).hasToString( "HTTP/1.1 302 Found " + CRLF + "Set-Cookie: JSESSIONID=id " + CRLF + "Location: /index.html " + CRLF @@ -112,7 +112,7 @@ class LoginControllerTest { loginController.doPost(request, response); // then - assertThat(response.toString()).hasToString( + assertThat(response.getMessage()).hasToString( "HTTP/1.1 302 Found " + CRLF + "Set-Cookie: JSESSIONID=로이스 " + CRLF + "Location: /index.html " + CRLF diff --git a/tomcat/src/test/java/nextstep/jwp/controller/RegisterControllerTest.java b/tomcat/src/test/java/nextstep/jwp/controller/RegisterControllerTest.java index 3f46352b37..7e7b336b84 100644 --- a/tomcat/src/test/java/nextstep/jwp/controller/RegisterControllerTest.java +++ b/tomcat/src/test/java/nextstep/jwp/controller/RegisterControllerTest.java @@ -71,6 +71,6 @@ class RegisterControllerTest { // then User user = InMemoryUserRepository.findByAccount("롤스로이스").get(); assertThat(user.checkPassword("비쌈")).isTrue(); - assertThat(response.toString()).contains("Location: /index.html"); + assertThat(response.getMessage()).contains("Location: /index.html"); } } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/controller/StaticControllerManagerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/controller/StaticControllerManagerTest.java index b14beb5b36..0eaea46a27 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/controller/StaticControllerManagerTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/controller/StaticControllerManagerTest.java @@ -44,6 +44,6 @@ class StaticControllerManagerTest { staticControllerManager.service(request, response); // then - assertThat(response.toString()).contains("HTTP/1.1 200 OK"); + assertThat(response.getMessage()).contains("HTTP/1.1 200 OK"); } } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/controller/StaticResourceControllerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/controller/StaticResourceControllerTest.java index 9f538c46c0..f76e8cadf2 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/controller/StaticResourceControllerTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/controller/StaticResourceControllerTest.java @@ -32,7 +32,7 @@ class StaticResourceControllerTest { staticResourceController.service(request, response); // then - assertThat(response.toString()).contains("HTTP/1.1 200 OK"); + assertThat(response.getMessage()).contains("HTTP/1.1 200 OK"); } @Test @@ -50,6 +50,6 @@ class StaticResourceControllerTest { staticResourceController.service(request, response); // then - assertThat(response.toString()).contains("HTTP/1.1 200 OK"); + assertThat(response.getMessage()).contains("HTTP/1.1 200 OK"); } } 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 index 809b47d373..8f26eb18d4 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/response/HttpResponseTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/response/HttpResponseTest.java @@ -23,7 +23,7 @@ class HttpResponseTest { httpResponse.addBody("로이스 잘생겼다"); // when - String string = httpResponse.toString(); + String string = httpResponse.getMessage(); // then Assertions.assertThat(string).contains("HTTP/1.1 200 OK", "Set-Cookie: JSESSIONID=1234", "Content-Type: text/html", "로이스 잘생겼다"); From f5f027d631e8400b9055e8f7c24a74c8fbbc084d Mon Sep 17 00:00:00 2001 From: ReO Date: Wed, 13 Sep 2023 19:45:00 +0900 Subject: [PATCH 17/24] =?UTF-8?q?refactor:=20=EB=88=84=EB=9D=BD=EB=90=9C?= =?UTF-8?q?=20=EC=84=A0=EC=96=B8=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/startup/DynamicControllerManager.java | 3 +-- .../main/java/org/apache/catalina/startup/SessionManager.java | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java b/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java index fec4fa1d88..767db07de9 100644 --- a/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java +++ b/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java @@ -14,8 +14,7 @@ public class DynamicControllerManager implements ControllerManager { private static final Map mapper = new HashMap<>(); - - private final SessionManager sessionManager = new SessionManager(); + private static final SessionManager sessionManager = new SessionManager(); @Override public void add(String path, Controller controller) { diff --git a/tomcat/src/main/java/org/apache/catalina/startup/SessionManager.java b/tomcat/src/main/java/org/apache/catalina/startup/SessionManager.java index 5d6a19daa8..c30a27d3d5 100644 --- a/tomcat/src/main/java/org/apache/catalina/startup/SessionManager.java +++ b/tomcat/src/main/java/org/apache/catalina/startup/SessionManager.java @@ -7,6 +7,7 @@ public class SessionManager implements Manager, LifeCycle { private static final Sessions sessions = new Sessions(); + private boolean isStarted = false; public SessionManager() { From 4333099c56b641845a9a5872d2c94f1ba845bc2b Mon Sep 17 00:00:00 2001 From: ReO Date: Wed, 13 Sep 2023 19:51:37 +0900 Subject: [PATCH 18/24] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/org/apache/catalina/startup/SessionManager.java | 4 ++-- .../src/main/java/org/apache/catalina/startup/Sessions.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tomcat/src/main/java/org/apache/catalina/startup/SessionManager.java b/tomcat/src/main/java/org/apache/catalina/startup/SessionManager.java index c30a27d3d5..66ff5282d9 100644 --- a/tomcat/src/main/java/org/apache/catalina/startup/SessionManager.java +++ b/tomcat/src/main/java/org/apache/catalina/startup/SessionManager.java @@ -21,7 +21,7 @@ public void add(Session session) { @Override public Session findSession(String id) { - return sessions.has(id); + return sessions.get(id); } @Override @@ -50,6 +50,6 @@ public boolean isStarted() { } public boolean hasSession(Session session) { - return sessions.has(session); + return sessions.get(session); } } diff --git a/tomcat/src/main/java/org/apache/catalina/startup/Sessions.java b/tomcat/src/main/java/org/apache/catalina/startup/Sessions.java index f8e112a2f9..45a611e38e 100644 --- a/tomcat/src/main/java/org/apache/catalina/startup/Sessions.java +++ b/tomcat/src/main/java/org/apache/catalina/startup/Sessions.java @@ -13,7 +13,7 @@ void add(String id, Session session) { idAndSessions.put(id, session); } - Session has(String id) { + Session get(String id) { if (!idAndSessions.containsKey(id)) { return null; } @@ -28,7 +28,7 @@ void clear() { idAndSessions.clear(); } - boolean has(Session session) { + boolean get(Session session) { return idAndSessions.containsValue(session); } } From 816870eb05517ec73d18625550b7f8cceeaeb902 Mon Sep 17 00:00:00 2001 From: ReO Date: Wed, 13 Sep 2023 19:53:35 +0900 Subject: [PATCH 19/24] =?UTF-8?q?refactor:=20=EC=B6=94=EC=83=81=ED=99=94?= =?UTF-8?q?=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tomcat/src/main/java/common/http/Request.java | 2 ++ .../java/org/apache/coyote/http11/Http11Processor.java | 9 +++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/tomcat/src/main/java/common/http/Request.java b/tomcat/src/main/java/common/http/Request.java index 0cd2733988..e4d7dca5a0 100644 --- a/tomcat/src/main/java/common/http/Request.java +++ b/tomcat/src/main/java/common/http/Request.java @@ -23,4 +23,6 @@ public interface Request { String getEmail(); void addSession(Session session); + + boolean hasStaticResourcePath(); } 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 7c969668d6..efd027d074 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -1,9 +1,10 @@ package org.apache.coyote.http11; import common.http.ControllerManager; +import common.http.Request; +import common.http.Response; import org.apache.coyote.Processor; import org.apache.coyote.http11.controller.StaticControllerManager; -import org.apache.coyote.http11.request.HttpRequest; import org.apache.coyote.http11.request.HttpRequestParser; import org.apache.coyote.http11.response.HttpResponse; import org.slf4j.Logger; @@ -38,8 +39,8 @@ public void process(final Socket connection) { final var outputStream = connection.getOutputStream(); final var reader = new BufferedReader(new InputStreamReader(inputStream)) ) { - HttpRequest httpRequest = HttpRequestParser.parse(reader); - HttpResponse httpResponse = new HttpResponse(); + Request httpRequest = HttpRequestParser.parse(reader); + Response httpResponse = new HttpResponse(); controllerManager.service(httpRequest, httpResponse); @@ -52,7 +53,7 @@ public void process(final Socket connection) { } } - private void serviceIfHasStaticResourcePath(HttpRequest httpRequest, HttpResponse httpResponse) { + private void serviceIfHasStaticResourcePath(Request httpRequest, Response httpResponse) { if (httpRequest.hasStaticResourcePath() || httpResponse.hasStaticResourcePath()) { controllerManager = new StaticControllerManager(); controllerManager.service(httpRequest, httpResponse); From 9d674790e7f801f33f5223ac284341d9d9b4126c Mon Sep 17 00:00:00 2001 From: ReO Date: Wed, 13 Sep 2023 19:59:54 +0900 Subject: [PATCH 20/24] =?UTF-8?q?refactor:=20=EC=8B=B1=EA=B8=80=ED=84=B4?= =?UTF-8?q?=20=ED=8C=A8=ED=84=B4=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/apache/coyote/http11/Http11Processor.java | 2 +- .../coyote/http11/controller/StaticControllerManager.java | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index efd027d074..dd52be714a 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -55,7 +55,7 @@ public void process(final Socket connection) { private void serviceIfHasStaticResourcePath(Request httpRequest, Response httpResponse) { if (httpRequest.hasStaticResourcePath() || httpResponse.hasStaticResourcePath()) { - controllerManager = new StaticControllerManager(); + controllerManager = StaticControllerManager.getInstance(); controllerManager.service(httpRequest, httpResponse); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticControllerManager.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticControllerManager.java index 7cb1d1d5cd..32b5f1716c 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticControllerManager.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticControllerManager.java @@ -7,8 +7,15 @@ public class StaticControllerManager implements ControllerManager { + private static final StaticControllerManager instance = new StaticControllerManager(); private static final Controller controller = new StaticResourceController(); + private StaticControllerManager() {} + + public static StaticControllerManager getInstance() { + return instance; + } + @Override public void add(String path, Controller controller) { throw new IllegalStateException("컨트롤러를 추가할 수 없습니다."); From 29da1dc8450e889af4749022d5094404317b7259 Mon Sep 17 00:00:00 2001 From: ReO Date: Wed, 13 Sep 2023 20:03:11 +0900 Subject: [PATCH 21/24] =?UTF-8?q?refactor:=20override=20=EC=96=B4=EB=85=B8?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/request/HttpRequest.java | 12 ++++++++++++ .../apache/coyote/http11/response/HttpResponse.java | 9 +++++++++ 2 files changed, 21 insertions(+) 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 4bca527c1b..313fdbf315 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 @@ -22,52 +22,64 @@ public HttpRequest(HttpRequestLine httpRequestLine, HttpRequestHeaders httpReque this.httpRequestBody = httpRequestBody; } + @Override public HttpMethod getHttpMethod() { return httpRequestLine.getHttpMethod(); } + @Override public String getPath() { return httpRequestLine.getPath(); } + @Override public String getVersionOfTheProtocol() { return httpRequestLine.getVersionOfTheProtocol(); } + @Override public String getCookie() { return httpRequestHeaders.getCookie(); } + @Override public String getAccount() { return httpRequestBody.getAccount(); } + @Override public String getPassword() { return httpRequestBody.getPassword(); } + @Override public String getEmail() { return httpRequestBody.getEmail(); } + @Override public Session getSession() { return session; } + @Override public Session getSession(boolean create) { UUID uuid = UUID.randomUUID(); this.session = new Session(uuid.toString()); return session; } + @Override public boolean hasValidSession() { return session != null; } + @Override public boolean hasStaticResourcePath() { return STATIC_RESOURCE_PATH_PATTERN.matcher(getPath()).find(); } + @Override public void addSession(Session session) { this.session = 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 be226e76c6..5ab074f3ad 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 @@ -19,39 +19,48 @@ public HttpResponse() { this.httpResponseBody = new HttpResponseBody(); } + @Override public void addVersionOfTheProtocol(String versionOfTheProtocol) { httpStatusLine.addVersionOfTheProtocol(versionOfTheProtocol); } + @Override public void addHttpStatus(HttpStatus httpStatus) { httpStatusLine.addHttpStatus(httpStatus); } + @Override public void addContentType(ContentType contentType) { httpResponseHeaders.addHeaderFieldAndValue("Content-Type", contentType.getType() + ";charset=utf-8"); } + @Override public void addCookie(Cookie cookie) { httpResponseHeaders.addHeaderFieldAndValue("Set-Cookie", cookie.getValue()); } + @Override public void sendRedirect(String redirectURL) { httpResponseHeaders.addHeaderFieldAndValue("Location", redirectURL); } + @Override public void addStaticResourcePath(String name) { httpResponseHeaders.addHeaderFieldAndValue("Resource-Path", name); } + @Override public void addBody(String body) { httpResponseHeaders.addHeaderFieldAndValue("Content-Length", String.valueOf(body.getBytes().length)); httpResponseBody.addBody(body); } + @Override public boolean hasStaticResourcePath() { return httpResponseHeaders.hasStaticResourcePath(); } + @Override public String getStaticResourcePath() { return httpResponseHeaders.getStaticResourcePath(); } From 1833e9f2ecc62cb4fc81ee8d66a25a7fb1b03d46 Mon Sep 17 00:00:00 2001 From: ReO Date: Wed, 13 Sep 2023 20:36:29 +0900 Subject: [PATCH 22/24] =?UTF-8?q?chore:=20=EB=A1=9C=EA=B9=85=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/apache/catalina/startup/DynamicControllerManager.java | 4 ++++ .../main/java/org/apache/coyote/http11/Http11Processor.java | 4 ++++ .../coyote/http11/controller/StaticControllerManager.java | 4 ++++ .../coyote/http11/controller/StaticControllerManagerTest.java | 4 ++-- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java b/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java index 767db07de9..a50a4a23da 100644 --- a/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java +++ b/tomcat/src/main/java/org/apache/catalina/startup/DynamicControllerManager.java @@ -7,6 +7,8 @@ import common.http.Request; import common.http.Response; import common.http.Session; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; @@ -15,6 +17,7 @@ public class DynamicControllerManager implements ControllerManager { private static final Map mapper = new HashMap<>(); private static final SessionManager sessionManager = new SessionManager(); + private static final Logger log = LoggerFactory.getLogger(DynamicControllerManager.class); @Override public void add(String path, Controller controller) { @@ -30,6 +33,7 @@ public void service(Request request, Response response) { return; } + log.info("Controller: {}", controller.getClass().getName()); controller.service(request, response); saveNewSession(request); 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 dd52be714a..e5c0c039da 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -42,10 +42,14 @@ public void process(final Socket connection) { Request httpRequest = HttpRequestParser.parse(reader); Response httpResponse = new HttpResponse(); + log.info("Path: {}, Method: {}", httpRequest.getPath(), httpRequest.getHttpMethod()); + controllerManager.service(httpRequest, httpResponse); serviceIfHasStaticResourcePath(httpRequest, httpResponse); + log.info(httpResponse.getMessage()); + outputStream.write(httpResponse.getMessage().getBytes()); outputStream.flush(); } catch (Exception e) { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticControllerManager.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticControllerManager.java index 32b5f1716c..cf2a0679e9 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticControllerManager.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticControllerManager.java @@ -4,11 +4,14 @@ import common.http.ControllerManager; import common.http.Request; import common.http.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class StaticControllerManager implements ControllerManager { private static final StaticControllerManager instance = new StaticControllerManager(); private static final Controller controller = new StaticResourceController(); + private static final Logger log = LoggerFactory.getLogger(StaticControllerManager.class); private StaticControllerManager() {} @@ -23,6 +26,7 @@ public void add(String path, Controller controller) { @Override public void service(Request request, Response response) { + log.info("Controller: {}", this.getClass().getName()); controller.service(request, response); } } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/controller/StaticControllerManagerTest.java b/tomcat/src/test/java/org/apache/coyote/http11/controller/StaticControllerManagerTest.java index 0eaea46a27..ad05c65da8 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/controller/StaticControllerManagerTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/controller/StaticControllerManagerTest.java @@ -21,7 +21,7 @@ class StaticControllerManagerTest { @Test void 컨트롤러를_추가할_시_예외를_반환한다() { // given - StaticControllerManager staticControllerManager = new StaticControllerManager(); + StaticControllerManager staticControllerManager = StaticControllerManager.getInstance(); LoginController loginController = new LoginController(); // expect @@ -33,7 +33,7 @@ class StaticControllerManagerTest { @Test void 정적파일을_제공하는_컨트롤러를_찾아서_service를_실행한다() { // given - StaticControllerManager staticControllerManager = new StaticControllerManager(); + StaticControllerManager staticControllerManager = StaticControllerManager.getInstance(); Request request = mock(HttpRequest.class); HttpResponse response = new HttpResponse(); when(request.getVersionOfTheProtocol()).thenReturn("HTTP/1.1"); From 2f7ee654bd81a73a0b12a2a6038e22c06fa80d95 Mon Sep 17 00:00:00 2001 From: ReO Date: Thu, 14 Sep 2023 12:38:27 +0900 Subject: [PATCH 23/24] =?UTF-8?q?feat:=20=EC=98=88=EC=99=B8=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../java/common/http/AbstractController.java | 12 +-- .../src/main/java/common/http/Response.java | 6 ++ .../jwp/db/InMemoryUserRepository.java | 2 +- .../apache/coyote/http11/Http11Processor.java | 19 +++- .../controller/ExceptionController.java | 56 ++++++++++++ .../controller/StaticResourceController.java | 6 ++ .../http11/request/HttpRequestParser.java | 2 +- .../coyote/http11/response/HttpResponse.java | 15 ++++ .../http11/response/HttpResponseHeaders.java | 12 +++ tomcat/src/main/resources/static/401.html | 90 ++++++++++--------- tomcat/src/main/resources/static/404.html | 88 +++++++++--------- tomcat/src/main/resources/static/409.html | 82 ++++++++--------- tomcat/src/main/resources/static/500.html | 88 +++++++++--------- .../http11/request/HttpRequestParserTest.java | 2 +- 15 files changed, 298 insertions(+), 184 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/controller/ExceptionController.java diff --git a/README.md b/README.md index 158c86d65d..251bec4d88 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,6 @@ -[x] 리팩터링(클래스 분리, 패키지 정리) -[x] 상태코드, http 메서드 종류 추가 -[x] 테스트 - 회원가입, 정적리소스 --[ ] 로깅 +-[x] 로깅 -[ ] 예외 처리 -[ ] 캐싱 구현해보기 diff --git a/tomcat/src/main/java/common/http/AbstractController.java b/tomcat/src/main/java/common/http/AbstractController.java index 64045583b0..bb14e4a0a7 100644 --- a/tomcat/src/main/java/common/http/AbstractController.java +++ b/tomcat/src/main/java/common/http/AbstractController.java @@ -12,7 +12,7 @@ public abstract class AbstractController implements Controller { - public static final String ERROR_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD = "요청에 해당하는 메서드가 없습니다."; + public static final String EXCEPTION_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD = "요청에 해당하는 메서드가 없습니다."; private final Map> methodMapping = new EnumMap<>(HttpMethod.class); protected AbstractController() { @@ -29,22 +29,22 @@ public void service(Request request, Response response) { } protected void doGet(Request request, Response response) { - throw new IllegalArgumentException(ERROR_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); + throw new IllegalArgumentException(EXCEPTION_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); } protected void doPost(Request request, Response response) { - throw new IllegalArgumentException(ERROR_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); + throw new IllegalArgumentException(EXCEPTION_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); } protected void doPut(Request request, Response response) { - throw new IllegalArgumentException(ERROR_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); + throw new IllegalArgumentException(EXCEPTION_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); } protected void doPatch(Request request, Response response) { - throw new IllegalArgumentException(ERROR_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); + throw new IllegalArgumentException(EXCEPTION_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); } protected void doDelete(Request request, Response response) { - throw new IllegalArgumentException(ERROR_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); + throw new IllegalArgumentException(EXCEPTION_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); } } diff --git a/tomcat/src/main/java/common/http/Response.java b/tomcat/src/main/java/common/http/Response.java index 570f1a4da1..65b31dcaf0 100644 --- a/tomcat/src/main/java/common/http/Response.java +++ b/tomcat/src/main/java/common/http/Response.java @@ -21,4 +21,10 @@ public interface Response { void addBody(String body); String getMessage(); + + void addException(Exception e); + + String getException(); + + boolean hasException(); } diff --git a/tomcat/src/main/java/nextstep/jwp/db/InMemoryUserRepository.java b/tomcat/src/main/java/nextstep/jwp/db/InMemoryUserRepository.java index 9e5d38a1d9..ebfb8f0d8c 100644 --- a/tomcat/src/main/java/nextstep/jwp/db/InMemoryUserRepository.java +++ b/tomcat/src/main/java/nextstep/jwp/db/InMemoryUserRepository.java @@ -25,7 +25,7 @@ public static void save(User user) { if (user.equals(database.get(user.getAccount()))) { throw new IllegalArgumentException("이미 가입된 유저입니다."); } - throw new IllegalArgumentException("잘못된 유저 정보입니다."); + throw new SecurityException("잘못된 유저 정보입니다."); } user.setId(id.getAndIncrement()); 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 e5c0c039da..ec755a1a85 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -4,6 +4,7 @@ import common.http.Request; import common.http.Response; import org.apache.coyote.Processor; +import org.apache.coyote.http11.controller.ExceptionController; import org.apache.coyote.http11.controller.StaticControllerManager; import org.apache.coyote.http11.request.HttpRequestParser; import org.apache.coyote.http11.response.HttpResponse; @@ -11,6 +12,7 @@ import org.slf4j.LoggerFactory; import java.io.BufferedReader; +import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; @@ -44,21 +46,30 @@ public void process(final Socket connection) { log.info("Path: {}, Method: {}", httpRequest.getPath(), httpRequest.getHttpMethod()); - controllerManager.service(httpRequest, httpResponse); - + processService(httpRequest, httpResponse); serviceIfHasStaticResourcePath(httpRequest, httpResponse); log.info(httpResponse.getMessage()); - outputStream.write(httpResponse.getMessage().getBytes()); outputStream.flush(); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + + private void processService(Request httpRequest, Response httpResponse) { + try { + controllerManager.service(httpRequest, httpResponse); } catch (Exception e) { log.error(e.getMessage(), e); + httpResponse.addException(e); + ExceptionController exceptionController = new ExceptionController(); + exceptionController.service(httpRequest, httpResponse); } } private void serviceIfHasStaticResourcePath(Request httpRequest, Response httpResponse) { - if (httpRequest.hasStaticResourcePath() || httpResponse.hasStaticResourcePath()) { + if (httpRequest.hasStaticResourcePath() || httpResponse.hasException() || httpResponse.hasStaticResourcePath()) { controllerManager = StaticControllerManager.getInstance(); controllerManager.service(httpRequest, httpResponse); } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/ExceptionController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/ExceptionController.java new file mode 100644 index 0000000000..0c27720de1 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/ExceptionController.java @@ -0,0 +1,56 @@ +package org.apache.coyote.http11.controller; + +import common.http.Controller; +import common.http.HttpStatus; +import common.http.Request; +import common.http.Response; + +import java.util.Arrays; + +public class ExceptionController implements Controller { + + @Override + public void service(Request request, Response response) { + String exception = response.getException(); + HttpStatus status = ExceptionType.getStatus(exception); + String pageName = ExceptionType.getExceptionPage(exception); + response.addHttpStatus(status); + response.addStaticResourcePath(pageName); + } + + private enum ExceptionType { + UNAUTHORIZED(SecurityException.class, HttpStatus.UNAUTHORIZED, "/401.html"), + BAD_REQUEST(IllegalArgumentException.class, HttpStatus.BAD_REQUEST, "/404.html"), + CONFLICT(IllegalStateException.class, HttpStatus.CONFLICT, "/409.html"), + INTERNAL_SERVER_ERROR(Exception.class, HttpStatus.INTERNAL_SERVER_ERROR, "/500.html") + ; + + private final Class exceptionClass; + private final HttpStatus httpStatus; + private final String filePath; + + ExceptionType(Class exceptionClass, HttpStatus httpStatus, String filePath) { + this.exceptionClass = exceptionClass; + this.httpStatus = httpStatus; + this.filePath = filePath; + } + + public static HttpStatus getStatus(String exception) { + ExceptionType type = Arrays.stream(ExceptionType.values()) + .filter(exceptionType -> exceptionType.exceptionClass.getName().equals(exception)) + .findFirst() + .orElse(INTERNAL_SERVER_ERROR); + + return type.httpStatus; + } + + public static String getExceptionPage(String exception) { + ExceptionType type = Arrays.stream(ExceptionType.values()) + .filter(exceptionType -> exceptionType.exceptionClass.getName().equals(exception)) + .findFirst() + .orElse(INTERNAL_SERVER_ERROR); + + return type.filePath; + } + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java index 318da9a7e1..c7162bdc8d 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java @@ -19,6 +19,12 @@ public class StaticResourceController implements Controller { @Override public void service(Request request, Response response) { + if (response.hasException()) { + response.addVersionOfTheProtocol(request.getVersionOfTheProtocol()); + buildResponse(response, response.getStaticResourcePath()); + return; + } + if (response.hasStaticResourcePath()) { buildResponse(response, response.getStaticResourcePath()); return; 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 d9ad0008bf..5b25132e22 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 @@ -28,7 +28,7 @@ public static HttpRequest parse(BufferedReader reader) throws IOException { private static HttpRequestLine parseRequestLine(BufferedReader reader) throws IOException { String requestLine = reader.readLine(); if (requestLine == null) { - throw new IOException("요청에 Reqeust-line이 없습니다."); + throw new IllegalArgumentException("요청에 Reqeust-line이 없습니다."); } return HttpRequestLine.from(requestLine); } 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 5ab074f3ad..6a73c86359 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 @@ -65,6 +65,21 @@ public String getStaticResourcePath() { return httpResponseHeaders.getStaticResourcePath(); } + @Override + public void addException(Exception e) { + httpResponseHeaders.addException(e); + } + + @Override + public String getException() { + return httpResponseHeaders.getException(); + } + + @Override + public boolean hasException() { + return httpResponseHeaders.hasException(); + } + public String getMessage() { if (httpResponseBody.exist()) { httpResponseBody.validateLength(httpResponseHeaders.getContentLength()); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java index 6a189c767c..3fdb9308e2 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java @@ -36,10 +36,18 @@ void addHeaderFieldAndValue(String field, String value) { headers.put(field, List.of(value)); } + public void addException(Exception e) { + headers.put("Exception-Type", List.of(e.getClass().getName())); + } + boolean hasStaticResourcePath() { return headers.containsKey(RESOURCE_PATH); } + public boolean hasException() { + return headers.containsKey("Exception-Type"); + } + String getStaticResourcePath() { return headers.get(RESOURCE_PATH).get(0); } @@ -48,6 +56,10 @@ public long getContentLength() { return Long.parseLong(headers.get(CONTENT_LENGTH).get(0)); } + public String getException() { + return headers.get("Exception-Type").get(0); + } + public String getMessage() { StringBuilder stringBuilder = new StringBuilder(); diff --git a/tomcat/src/main/resources/static/401.html b/tomcat/src/main/resources/static/401.html index 444019ac4e..902b7707df 100644 --- a/tomcat/src/main/resources/static/401.html +++ b/tomcat/src/main/resources/static/401.html @@ -1,52 +1,54 @@ - - - - - - - 404 Error - SB Admin - - - - -
-
-
-
-
-
-
-

401

-

Unauthorized

-

Access to this resource is denied.

- - - Return to Dashboard - -
-
+ + + + + + + 401 Error - SB Admin + + + + +
+
+
+
+
+
+
+

401

+

Unauthorized

+

Access to this resource is denied.

+ + + Return to Dashboard +
-
+
-
+
+
- - - - + + + + + + diff --git a/tomcat/src/main/resources/static/404.html b/tomcat/src/main/resources/static/404.html index 980e9ec7f0..e16472ebf7 100644 --- a/tomcat/src/main/resources/static/404.html +++ b/tomcat/src/main/resources/static/404.html @@ -1,51 +1,53 @@ - - - - - - - 404 Error - SB Admin - - - - -
-
-
-
-
-
-
- -

This requested URL was not found on this server.

- - - Return to Dashboard - -
-
+ + + + + + + 404 Error - SB Admin + + + + +
+
+
+
+
+
+
+ +

This requested URL was not found on this server.

+ + + Return to Dashboard +
-
+
-
+
+
- - - - + + + + + + diff --git a/tomcat/src/main/resources/static/409.html b/tomcat/src/main/resources/static/409.html index 23b1c24465..40b074c1fd 100644 --- a/tomcat/src/main/resources/static/409.html +++ b/tomcat/src/main/resources/static/409.html @@ -1,51 +1,53 @@ - - - - - - 409 Error - SB Admin - - + + + + + + 409 Error - SB Admin + +
-
-
-
-
-
-
-

409

-

Conflict

- - - Return to Dashboard - +
+
+
+
+
+
+

409

+

Conflict

+ + + Return to Dashboard + +
+
+
-
-
-
-
-
- + +
+ - + diff --git a/tomcat/src/main/resources/static/500.html b/tomcat/src/main/resources/static/500.html index f786af3509..854122eb7b 100644 --- a/tomcat/src/main/resources/static/500.html +++ b/tomcat/src/main/resources/static/500.html @@ -1,51 +1,53 @@ - - - - - - - 404 Error - SB Admin - - - - -
-
-
-
-
-
-
-

500

-

Internal Server Error

- - - Return to Dashboard - -
-
+ + + + + + + 404 Error - SB Admin + + + + +
+
+
+
+
+
+
+

500

+

Internal Server Error

+ + + Return to Dashboard +
-
+
-
+
+
- - - - + + + + + + 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 b2097517fb..4c6dced219 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 @@ -54,7 +54,7 @@ class HttpRequestParserTest { // expect assertThatThrownBy(() -> HttpRequestParser.parse(reader)) - .isInstanceOf(IOException.class) + .isInstanceOf(IllegalArgumentException.class) .hasMessage("요청에 Reqeust-line이 없습니다."); reader.close(); From 5e82471bdec27e024664b0b914d64736ce7f893b Mon Sep 17 00:00:00 2001 From: ReO Date: Thu, 14 Sep 2023 12:38:59 +0900 Subject: [PATCH 24/24] =?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 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 251bec4d88..e1b385e8b6 100644 --- a/README.md +++ b/README.md @@ -26,5 +26,4 @@ -[x] 상태코드, http 메서드 종류 추가 -[x] 테스트 - 회원가입, 정적리소스 -[x] 로깅 --[ ] 예외 처리 --[ ] 캐싱 구현해보기 +-[X] 예외 처리