From 5248e445987353087d90301eeb8710ad2a7a73ad Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Thu, 7 Sep 2023 21:52:49 +0900 Subject: [PATCH 01/24] =?UTF-8?q?refactor:=20httpRequest=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jwp/controller/LoginController.java | 6 +- .../http11/{HttpCookie.java => Cookies.java} | 8 +- .../apache/coyote/http11/Http11Processor.java | 96 ++++++------------- .../org/apache/coyote/http11/HttpRequest.java | 81 ++++++++++++++++ .../org/apache/coyote/http11/StartLine.java | 14 +-- .../coyote/http11/Http11ProcessorTest.java | 14 +-- 6 files changed, 128 insertions(+), 91 deletions(-) rename tomcat/src/main/java/nextstep/org/apache/coyote/http11/{HttpCookie.java => Cookies.java} (84%) create mode 100644 tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpRequest.java diff --git a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java index 04c979af46..88cbc92f32 100644 --- a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java +++ b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java @@ -4,17 +4,17 @@ import nextstep.jwp.dto.LoginResponseDto; import nextstep.jwp.exception.InvalidLoginInfoException; import nextstep.jwp.service.LoginService; -import nextstep.org.apache.coyote.http11.HttpCookie; +import nextstep.org.apache.coyote.http11.Cookies; import nextstep.org.apache.coyote.http11.Session; public class LoginController { private final LoginService loginService = new LoginService(); - public LoginResponseDto login(HttpCookie httpCookie, String account, String password) { + public LoginResponseDto login(Cookies cookies, String account, String password) { try { Session loginSession = loginService.login(account, password); - httpCookie.set("JSESSIONID", loginSession.getId()); + cookies.set("JSESSIONID", loginSession.getId()); return new LoginResponseDto("/index.html"); } catch (NoSuchElementException | InvalidLoginInfoException | NullPointerException e) { return new LoginResponseDto("/401.html"); diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpCookie.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Cookies.java similarity index 84% rename from tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpCookie.java rename to tomcat/src/main/java/nextstep/org/apache/coyote/http11/Cookies.java index 48bfa7fb91..884d1f796f 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpCookie.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Cookies.java @@ -6,7 +6,7 @@ import java.util.Map; import java.util.stream.Collectors; -public class HttpCookie { +public class Cookies { private static final String SET_COOKIE_HEADER = "Set-Cookie: %s \r\n"; private static final String COOKIE_VALUES_DELIMITER = "; "; @@ -36,9 +36,9 @@ public boolean hasCookie(String key) { } public String createSetCookieHeader() { - String cookies = cookie.entrySet().stream() + return cookie.entrySet().stream() .map(entry -> entry.getKey() + COOKIE_KEY_VALUE_DELIMITER + entry.getValue()) - .collect(Collectors.joining(COOKIE_VALUES_DELIMITER)); - return String.format(SET_COOKIE_HEADER, cookies); + .map(value -> String.format(SET_COOKIE_HEADER, value)) + .collect(Collectors.joining()); } } diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java index c14153beb1..8061249c8c 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java @@ -1,7 +1,5 @@ package nextstep.org.apache.coyote.http11; -import static nextstep.org.apache.coyote.http11.HttpUtil.parseMultipleValues; - import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -13,9 +11,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.Arrays; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; import nextstep.jwp.controller.LoginController; @@ -29,14 +25,9 @@ public class Http11Processor implements Runnable, Processor { private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); - private static final int KEY_INDEX = 0; - private static final int VALUE_INDEX = 1; - private static final String EMPTY_LINE = ""; private static final String RESOURCES_PATH_PREFIX = "static"; private static final int ACCEPT_HEADER_BEST_CONTENT_TYPE_INDEX = 0; - private static final String FORM_VALUES_DELIMITER = "&"; - private static final String FORM_KEY_VALUE_DELIMITER = "="; - private static final String NOT_FOUND_DEFALULT_MESSAGE = "404 Not Found"; + private static final String NOT_FOUND_DEFAULT_MESSAGE = "404 Not Found"; private final Socket connection; private final HandlerMapper handlerMapper; @@ -60,53 +51,39 @@ public void process(final Socket connection) { InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader) ) { - StartLine startLine = new StartLine(bufferedReader.readLine()); - Map requestHeaders = extractHeaders(bufferedReader); - - Map parsedBody = new HashMap<>(); - if (requestHeaders.containsKey("Content-Length")) { - String requestBody = extractRequestBody(bufferedReader, requestHeaders); - parseMultipleValues(parsedBody, - requestBody, FORM_VALUES_DELIMITER, FORM_KEY_VALUE_DELIMITER); - } - - HttpCookie httpCookie = new HttpCookie(); - if (requestHeaders.containsKey("Cookie")) { - httpCookie.parseCookieHeaders(requestHeaders.get("Cookie")); - } - - Map queryParams = new HashMap<>(); - if (startLine.hasQueryString()) { - parseMultipleValues(queryParams, - startLine.getQueryString(), "&", "="); - } + HttpRequest httpRequest = new HttpRequest(bufferedReader); + Cookies cookies = httpRequest.getCookies(); String response = null; - String requestPath = startLine.getPath(); + String requestPathInfo = httpRequest.getPathInfo(); - Object handler = handlerMapper.mapHandler(requestPath); - if (Objects.nonNull(handler) && startLine.getHttpMethod().equals("POST") - && requestPath.equals("/login")) { + Object handler = handlerMapper.mapHandler(requestPathInfo); + if (Objects.nonNull(handler) && httpRequest.getMethod().equals("POST") + && requestPathInfo.equals("/login")) { LoginController loginController = (LoginController) handler; - LoginResponseDto loginDto = loginController.login(httpCookie, - parsedBody.get("account"), - parsedBody.get("password")); + LoginResponseDto loginDto = loginController.login( + httpRequest.getCookies(), + httpRequest.getParsedBodyValue("account"), + httpRequest.getParsedBodyValue("password")); response = String.join("\r\n", "HTTP/1.1 302 Found ", String.format("Location: %s \r\n", loginDto.getRedirectUrl())); - - if (!httpCookie.isEmpty()) { - response += httpCookie.createSetCookieHeader(); + if (!cookies.isEmpty()) { + response += cookies.createSetCookieHeader(); } response += ""; } - if (Objects.nonNull(handler) && startLine.getHttpMethod().equals("POST") - && requestPath.equals("/register")) { + if (Objects.nonNull(handler) + && httpRequest.getMethod().equals("POST") + && requestPathInfo.equals("/register")) { LoginController loginController = (LoginController) handler; - LoginResponseDto loginDto = loginController.register(parsedBody.get("account"), - parsedBody.get("password"), parsedBody.get("email")); + LoginResponseDto loginDto = loginController.register( + httpRequest.getParsedBodyValue("account"), + httpRequest.getParsedBodyValue("password"), + httpRequest.getParsedBodyValue("email") + ); response = String.join("\r\n", "HTTP/1.1 302 Found ", @@ -114,12 +91,13 @@ public void process(final Socket connection) { ""); } - if (startLine.getHttpMethod().equals("GET") && Objects.isNull(response)) { - String contentType = selectFirstContentTypeOrDefault(requestHeaders.get("Accept")); + if (httpRequest.getMethod().equals("GET") && Objects.isNull(response)) { + String contentType = selectFirstContentTypeOrDefault( + httpRequest.getHeader("Accept")); // Todo: createResponseBody() pageController로 위임해보기 // Todo: 헤더에 담긴 sessionId 유효성 검증 - if (requestPath.contains("/login") && httpCookie.hasCookie("JSESSIONID")) { + if (requestPathInfo.contains("/login") && cookies.hasCookie("JSESSIONID")) { response = String.join("\r\n", "HTTP/1.1 302 Found ", "Location: /index.html ", @@ -129,10 +107,10 @@ public void process(final Socket connection) { return; } - Optional responseBody = createResponseBody(requestPath); + Optional responseBody = createResponseBody(requestPathInfo); if (responseBody.isEmpty()) { String notFoundPageBody = createResponseBody("/404.html") - .orElse(NOT_FOUND_DEFALULT_MESSAGE); + .orElse(NOT_FOUND_DEFAULT_MESSAGE); response = String.join("\r\n", "HTTP/1.1 404 Not Found ", @@ -160,16 +138,6 @@ public void process(final Socket connection) { } } - private String extractRequestBody( - BufferedReader bufferedReader, - Map requestHeaders - ) throws IOException { - int contentLength = Integer.parseInt(requestHeaders.get("Content-Length")); - char[] buffer = new char[contentLength]; - bufferedReader.read(buffer, 0, contentLength); - return new String(buffer); - } - private void writeResponse(OutputStream outputStream, String response) throws IOException { outputStream.write(response.getBytes()); outputStream.flush(); @@ -183,16 +151,6 @@ private String selectFirstContentTypeOrDefault(String acceptHeader) { return acceptHeaderValues.get(ACCEPT_HEADER_BEST_CONTENT_TYPE_INDEX); } - private Map extractHeaders(BufferedReader bufferedReader) throws IOException { - Map requestHeaders = new HashMap<>(); - String line; - while (!EMPTY_LINE.equals(line = bufferedReader.readLine())) { - String[] splited = line.split(": "); - requestHeaders.put(splited[KEY_INDEX], splited[VALUE_INDEX].strip()); - } - return requestHeaders; - } - private Optional createResponseBody(String requestPath) throws IOException { if (requestPath.equals("/")) { return Optional.of("Hello world!"); diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpRequest.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpRequest.java new file mode 100644 index 0000000000..16cfddd813 --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpRequest.java @@ -0,0 +1,81 @@ +package nextstep.org.apache.coyote.http11; + +import static nextstep.org.apache.coyote.http11.HttpUtil.parseMultipleValues; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class HttpRequest { + + private static final int KEY_INDEX = 0; + private static final int VALUE_INDEX = 1; + private static final String EMPTY_LINE = ""; + private static final String FORM_VALUES_DELIMITER = "&"; + private static final String FORM_KEY_VALUE_DELIMITER = "="; + + private StartLine startLine; + private Map queryParams = new HashMap<>(); + private Map requestHeaders = new HashMap<>(); + private final Cookies cookies = new Cookies(); + private Map parsedBody = null; + + public HttpRequest(BufferedReader bufferedReader) throws IOException { + this.startLine = new StartLine(bufferedReader.readLine()); + extractHeaders(bufferedReader); + + if (startLine.hasQueryString()) { + parseMultipleValues(queryParams, + startLine.getQueryString(), "&", "="); + } + if (requestHeaders.containsKey("Cookie")) { + cookies.parseCookieHeaders(requestHeaders.get("Cookie")); + } + if (requestHeaders.containsKey("Content-Length")) { + parseBody(bufferedReader); + } + } + + private void extractHeaders(BufferedReader bufferedReader) throws IOException { + String line; + while (!EMPTY_LINE.equals(line = bufferedReader.readLine())) { + String[] splited = line.split(": "); + requestHeaders.put(splited[KEY_INDEX], splited[VALUE_INDEX].strip()); + } + } + + private void parseBody(BufferedReader bufferedReader) throws IOException{ + parsedBody = new HashMap<>(); + String requestBody = extractRequestBody(bufferedReader); + parseMultipleValues(parsedBody, + requestBody, FORM_VALUES_DELIMITER, FORM_KEY_VALUE_DELIMITER); + } + + private String extractRequestBody(BufferedReader bufferedReader) throws IOException { + int contentLength = Integer.parseInt(requestHeaders.get("Content-Length")); + char[] buffer = new char[contentLength]; + bufferedReader.read(buffer, 0, contentLength); + return new String(buffer); + } + + public String getMethod() { + return startLine.getHttpMethod(); + } + + public String getPathInfo() { + return startLine.getPath(); + } + + public String getHeader(String name) { + return requestHeaders.get(name); + } + + public Cookies getCookies() { + return cookies; + } + + public String getParsedBodyValue(String name) { + return parsedBody.get(name); + } +} diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/StartLine.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/StartLine.java index d2db69776d..d9e0cf13bb 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/StartLine.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/StartLine.java @@ -14,14 +14,14 @@ public class StartLine { private String httpMethod; private String path; - private String httpVersion; private String queryString = null; + private String httpVersion; public StartLine(String startLine) { - List parsed = Arrays.asList(startLine.split(START_LINE_DELIMITER)); - httpMethod = parsed.get(HTTP_METHOD_INDEX); - parsePath(parsed); - httpVersion = parsed.get(HTTP_VERSION_INDEX); + List parsedStartLine = Arrays.asList(startLine.split(START_LINE_DELIMITER)); + httpMethod = parsedStartLine.get(HTTP_METHOD_INDEX); + parsePath(parsedStartLine); + httpVersion = parsedStartLine.get(HTTP_VERSION_INDEX); } private void parsePath(List parsed) { @@ -48,8 +48,4 @@ public String getPath() { public String getQueryString() { return queryString; } - - public String getHttpVersion() { - return httpVersion; - } } diff --git a/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java b/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java index 05dda79a10..74d2490d47 100644 --- a/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java +++ b/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java @@ -173,6 +173,8 @@ void loginSuccessTest() { "Location: /index.html \r\n" + "Set-Cookie: "; + System.out.println(socket.output()); + assertThat(socket.output()).contains(expected); } @@ -261,9 +263,9 @@ void sessionTest() { requestHeaders.put(splitedLine[0], splitedLine[1].strip()); } - HttpCookie httpCookie = new HttpCookie(); - httpCookie.parseCookieHeaders(requestHeaders.get("Set-Cookie")); - String jSessionId = httpCookie.get("JSESSIONID"); + Cookies cookies = new Cookies(); + cookies.parseCookieHeaders(requestHeaders.get("Set-Cookie")); + String jSessionId = cookies.get("JSESSIONID"); User user = (User) sessionManager.findSession(jSessionId).getAttribute("gugu"); assertThat(user).isNotNull(); @@ -349,8 +351,8 @@ private String loginAndGetSessionId() { requestHeaders.put(splitedLine[0], splitedLine[1].strip()); } - HttpCookie httpCookie = new HttpCookie(); - httpCookie.parseCookieHeaders(requestHeaders.get("Set-Cookie")); - return httpCookie.get("JSESSIONID"); + Cookies cookies = new Cookies(); + cookies.parseCookieHeaders(requestHeaders.get("Set-Cookie")); + return cookies.get("JSESSIONID"); } } From e6cf26386978780205dde7d030060b8c4881b193 Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Thu, 7 Sep 2023 22:19:51 +0900 Subject: [PATCH 02/24] =?UTF-8?q?refactor:=20InputStream=EC=9D=98=20?= =?UTF-8?q?=EB=B3=B4=EC=A1=B0=20=EC=8A=A4=ED=8A=B8=EB=A6=BC=EC=9D=80=20req?= =?UTF-8?q?uest=20=EA=B0=9D=EC=B2=B4=20=EB=82=B4=EB=B6=80=EB=A1=9C=20?= =?UTF-8?q?=EC=BA=A1=EC=8A=90=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/org/apache/coyote/http11/Http11Processor.java | 6 +----- .../java/nextstep/org/apache/coyote/http11/HttpRequest.java | 6 +++++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java index 8061249c8c..55cee302db 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java @@ -1,10 +1,8 @@ package nextstep.org.apache.coyote.http11; -import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; import java.net.URL; @@ -48,10 +46,8 @@ public void process(final Socket connection) { try ( InputStream inputStream = connection.getInputStream(); OutputStream outputStream = connection.getOutputStream(); - InputStreamReader inputStreamReader = new InputStreamReader(inputStream); - BufferedReader bufferedReader = new BufferedReader(inputStreamReader) ) { - HttpRequest httpRequest = new HttpRequest(bufferedReader); + HttpRequest httpRequest = new HttpRequest(inputStream); Cookies cookies = httpRequest.getCookies(); String response = null; diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpRequest.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpRequest.java index 16cfddd813..a04a735331 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpRequest.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpRequest.java @@ -4,6 +4,8 @@ import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; @@ -21,7 +23,9 @@ public class HttpRequest { private final Cookies cookies = new Cookies(); private Map parsedBody = null; - public HttpRequest(BufferedReader bufferedReader) throws IOException { + public HttpRequest(InputStream inputStream) throws IOException { + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + this.startLine = new StartLine(bufferedReader.readLine()); extractHeaders(bufferedReader); From e9be59d22888d079f50e2b9f357e6744c56f671e Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Fri, 8 Sep 2023 00:51:02 +0900 Subject: [PATCH 03/24] =?UTF-8?q?refactor:=20httpRequest=20=EC=97=90?= =?UTF-8?q?=EC=84=9C=20cookies=20=EB=B0=98=ED=99=98=20=EC=8B=9C=20?= =?UTF-8?q?=EB=B0=A9=EC=96=B4=EC=A0=81=20=EB=B3=B5=EC=82=AC=20=EA=B8=B0?= =?UTF-8?q?=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 --- .../org/apache/coyote/http11/Cookies.java | 6 ++++++ .../org/apache/coyote/http11/HttpRequest.java | 16 ++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Cookies.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Cookies.java index 884d1f796f..f16c0a1e61 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Cookies.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Cookies.java @@ -41,4 +41,10 @@ public String createSetCookieHeader() { .map(value -> String.format(SET_COOKIE_HEADER, value)) .collect(Collectors.joining()); } + + public Cookies defensiveCopy() { + Cookies newCookies = new Cookies(); + cookie.forEach((key, value) -> newCookies.set(key, value)); + return newCookies; + } } diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpRequest.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpRequest.java index a04a735331..1726b7d457 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpRequest.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpRequest.java @@ -19,7 +19,7 @@ public class HttpRequest { private StartLine startLine; private Map queryParams = new HashMap<>(); - private Map requestHeaders = new HashMap<>(); + private Map headers = new HashMap<>(); private final Cookies cookies = new Cookies(); private Map parsedBody = null; @@ -33,10 +33,10 @@ public HttpRequest(InputStream inputStream) throws IOException { parseMultipleValues(queryParams, startLine.getQueryString(), "&", "="); } - if (requestHeaders.containsKey("Cookie")) { - cookies.parseCookieHeaders(requestHeaders.get("Cookie")); + if (headers.containsKey("Cookie")) { + cookies.parseCookieHeaders(headers.get("Cookie")); } - if (requestHeaders.containsKey("Content-Length")) { + if (headers.containsKey("Content-Length")) { parseBody(bufferedReader); } } @@ -45,7 +45,7 @@ private void extractHeaders(BufferedReader bufferedReader) throws IOException { String line; while (!EMPTY_LINE.equals(line = bufferedReader.readLine())) { String[] splited = line.split(": "); - requestHeaders.put(splited[KEY_INDEX], splited[VALUE_INDEX].strip()); + headers.put(splited[KEY_INDEX], splited[VALUE_INDEX].strip()); } } @@ -57,7 +57,7 @@ private void parseBody(BufferedReader bufferedReader) throws IOException{ } private String extractRequestBody(BufferedReader bufferedReader) throws IOException { - int contentLength = Integer.parseInt(requestHeaders.get("Content-Length")); + int contentLength = Integer.parseInt(headers.get("Content-Length")); char[] buffer = new char[contentLength]; bufferedReader.read(buffer, 0, contentLength); return new String(buffer); @@ -72,11 +72,11 @@ public String getPathInfo() { } public String getHeader(String name) { - return requestHeaders.get(name); + return headers.get(name); } public Cookies getCookies() { - return cookies; + return cookies.defensiveCopy(); } public String getParsedBodyValue(String name) { From 51291fa3910426f47e8abc85000bb08805ada69e Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Fri, 8 Sep 2023 22:54:58 +0900 Subject: [PATCH 04/24] =?UTF-8?q?refactor:=20Http11Response=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 61 +++++++--------- .../apache/coyote/http11/Http11Response.java | 69 +++++++++++++++++++ .../org/apache/coyote/http11/Status.java | 20 ++++++ .../org/apache/coyote/http11/StatusLine.java | 16 +++++ 4 files changed, 130 insertions(+), 36 deletions(-) create mode 100644 tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Response.java create mode 100644 tomcat/src/main/java/nextstep/org/apache/coyote/http11/Status.java create mode 100644 tomcat/src/main/java/nextstep/org/apache/coyote/http11/StatusLine.java diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java index 55cee302db..4f326e1e61 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java @@ -45,30 +45,27 @@ public void run() { public void process(final Socket connection) { try ( InputStream inputStream = connection.getInputStream(); - OutputStream outputStream = connection.getOutputStream(); + OutputStream outputStream = connection.getOutputStream() ) { HttpRequest httpRequest = new HttpRequest(inputStream); Cookies cookies = httpRequest.getCookies(); - String response = null; + Http11Response response = null; String requestPathInfo = httpRequest.getPathInfo(); Object handler = handlerMapper.mapHandler(requestPathInfo); - if (Objects.nonNull(handler) && httpRequest.getMethod().equals("POST") + if (Objects.nonNull(handler) + && httpRequest.getMethod().equals("POST") && requestPathInfo.equals("/login")) { LoginController loginController = (LoginController) handler; LoginResponseDto loginDto = loginController.login( - httpRequest.getCookies(), + cookies, httpRequest.getParsedBodyValue("account"), httpRequest.getParsedBodyValue("password")); - response = String.join("\r\n", - "HTTP/1.1 302 Found ", - String.format("Location: %s \r\n", loginDto.getRedirectUrl())); - if (!cookies.isEmpty()) { - response += cookies.createSetCookieHeader(); - } - response += ""; + response = new Http11Response(Status.FOUND) + .setHeader("Location", loginDto.getRedirectUrl()) + .setCookies(cookies); } if (Objects.nonNull(handler) @@ -81,10 +78,8 @@ public void process(final Socket connection) { httpRequest.getParsedBodyValue("email") ); - response = String.join("\r\n", - "HTTP/1.1 302 Found ", - String.format("Location: %s ", loginDto.getRedirectUrl()), - ""); + response = new Http11Response(Status.FOUND) + .setHeader("Location", loginDto.getRedirectUrl()); } if (httpRequest.getMethod().equals("GET") && Objects.isNull(response)) { @@ -94,11 +89,8 @@ public void process(final Socket connection) { // Todo: createResponseBody() pageController로 위임해보기 // Todo: 헤더에 담긴 sessionId 유효성 검증 if (requestPathInfo.contains("/login") && cookies.hasCookie("JSESSIONID")) { - response = String.join("\r\n", - "HTTP/1.1 302 Found ", - "Location: /index.html ", - ""); - + response = new Http11Response(Status.FOUND) + .setHeader("Location", "/index.html"); writeResponse(outputStream, response); return; } @@ -108,24 +100,20 @@ public void process(final Socket connection) { String notFoundPageBody = createResponseBody("/404.html") .orElse(NOT_FOUND_DEFAULT_MESSAGE); - response = String.join("\r\n", - "HTTP/1.1 404 Not Found ", - String.format("Content-Type: %s;charset=utf-8 ", contentType), - String.format("Content-Length: %s ", - notFoundPageBody.getBytes(StandardCharsets.UTF_8).length), - "", - notFoundPageBody); + response = new Http11Response(Status.NOT_FOUND) + .setHeader("Content-Type", contentType + ";charset=utf-8") + .setHeader("Content-Length", String.valueOf( + notFoundPageBody.getBytes(StandardCharsets.UTF_8).length)) + .setBody(notFoundPageBody); writeResponse(outputStream, response); return; } - response = String.join("\r\n", - "HTTP/1.1 200 OK ", - String.format("Content-Type: %s;charset=utf-8 ", contentType), - String.format("Content-Length: %s ", - responseBody.get().getBytes(StandardCharsets.UTF_8).length), - "", - responseBody.get()); + response = new Http11Response(Status.OK) + .setHeader("Content-Type", contentType + ";charset=utf-8") + .setHeader("Content-Length", String.valueOf( + responseBody.get().getBytes(StandardCharsets.UTF_8).length)) + .setBody(responseBody.get()); } writeResponse(outputStream, response); @@ -134,8 +122,9 @@ public void process(final Socket connection) { } } - private void writeResponse(OutputStream outputStream, String response) throws IOException { - outputStream.write(response.getBytes()); + private void writeResponse(OutputStream outputStream, Http11Response response) + throws IOException { + outputStream.write(response.createResponseAsByteArray()); outputStream.flush(); } diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Response.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Response.java new file mode 100644 index 0000000000..c1300fc039 --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Response.java @@ -0,0 +1,69 @@ +package nextstep.org.apache.coyote.http11; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +public class Http11Response { + + private static final String HTTP_VERSION = "HTTP/1.1"; + private static final String HEADER_FORMAT = "%s: %s "; + private static final String EMPTY_LINE = ""; + private static final String LINEBREAK_DELIMITER = "\r\n"; + + private StatusLine statusLine; + private Map headers = new LinkedHashMap<>(); + private Cookies cookies = null; + private String body = null; + + public Http11Response(Status status) { + this.statusLine = new StatusLine(HTTP_VERSION, status); + } + + public byte[] createResponseAsByteArray() { + return createResponse().getBytes(); + } + + private String createResponse() { + String response = String.join(LINEBREAK_DELIMITER, + statusLine.toString(), + createHeadersResponse(), + EMPTY_LINE + ); + if (Objects.nonNull(body)) { + response += LINEBREAK_DELIMITER + body; + } + return response; + } + + private String createHeadersResponse() { + String headersResponse = this.headers.entrySet().stream() + .map(entry -> String.format(HEADER_FORMAT, entry.getKey(), entry.getValue())) + .collect(Collectors.joining(LINEBREAK_DELIMITER)); + if (Objects.nonNull(cookies) && !cookies.isEmpty()) { + headersResponse += LINEBREAK_DELIMITER + cookies.createSetCookieHeader(); + } + return headersResponse; + } + + public Http11Response setStatus(Status status) { + this.statusLine = new StatusLine(HTTP_VERSION, status); + return this; + } + + public Http11Response setHeader(String key, String value) { + this.headers.put(key, value); + return this; + } + + public Http11Response setCookies(Cookies cookies) { + this.cookies = cookies; + return this; + } + + public Http11Response setBody(String body) { + this.body = body; + return this; + } +} diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Status.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Status.java new file mode 100644 index 0000000000..6b887aaa75 --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Status.java @@ -0,0 +1,20 @@ +package nextstep.org.apache.coyote.http11; + +public enum Status { + + OK("200", "OK"), + FOUND("302", "Found"), + NOT_FOUND("404", "Not Found"); + + private final String code; + private final String message; + + Status(String code, String message) { + this.code = code; + this.message = message; + } + + public String getCodeMessage() { + return this.code + " " + this.message; + } +} diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/StatusLine.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/StatusLine.java new file mode 100644 index 0000000000..445a6b8640 --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/StatusLine.java @@ -0,0 +1,16 @@ +package nextstep.org.apache.coyote.http11; + +public class StatusLine { + + private final String httpVersion; + private final Status status; + + public StatusLine(String httpVersion, Status status) { + this.httpVersion = httpVersion; + this.status = status; + } + + public String toString() { + return httpVersion + " " + status.getCodeMessage() + " "; + } +} From c6cf4b2465ff53a17ec9d995f3213576d744be2e Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Fri, 8 Sep 2023 22:55:27 +0900 Subject: [PATCH 05/24] =?UTF-8?q?refactor:=20HttpRequest=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=EB=AA=85=20Http11Request=EB=A1=9C=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 --- .../apache/coyote/http11/Http11Processor.java | 24 +++++++++---------- .../{HttpRequest.java => Http11Request.java} | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) rename tomcat/src/main/java/nextstep/org/apache/coyote/http11/{HttpRequest.java => Http11Request.java} (96%) diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java index 4f326e1e61..9142e0c2c2 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java @@ -47,21 +47,21 @@ public void process(final Socket connection) { InputStream inputStream = connection.getInputStream(); OutputStream outputStream = connection.getOutputStream() ) { - HttpRequest httpRequest = new HttpRequest(inputStream); - Cookies cookies = httpRequest.getCookies(); + Http11Request http11Request = new Http11Request(inputStream); + Cookies cookies = http11Request.getCookies(); Http11Response response = null; - String requestPathInfo = httpRequest.getPathInfo(); + String requestPathInfo = http11Request.getPathInfo(); Object handler = handlerMapper.mapHandler(requestPathInfo); if (Objects.nonNull(handler) - && httpRequest.getMethod().equals("POST") + && http11Request.getMethod().equals("POST") && requestPathInfo.equals("/login")) { LoginController loginController = (LoginController) handler; LoginResponseDto loginDto = loginController.login( cookies, - httpRequest.getParsedBodyValue("account"), - httpRequest.getParsedBodyValue("password")); + http11Request.getParsedBodyValue("account"), + http11Request.getParsedBodyValue("password")); response = new Http11Response(Status.FOUND) .setHeader("Location", loginDto.getRedirectUrl()) @@ -69,22 +69,22 @@ public void process(final Socket connection) { } if (Objects.nonNull(handler) - && httpRequest.getMethod().equals("POST") + && http11Request.getMethod().equals("POST") && requestPathInfo.equals("/register")) { LoginController loginController = (LoginController) handler; LoginResponseDto loginDto = loginController.register( - httpRequest.getParsedBodyValue("account"), - httpRequest.getParsedBodyValue("password"), - httpRequest.getParsedBodyValue("email") + http11Request.getParsedBodyValue("account"), + http11Request.getParsedBodyValue("password"), + http11Request.getParsedBodyValue("email") ); response = new Http11Response(Status.FOUND) .setHeader("Location", loginDto.getRedirectUrl()); } - if (httpRequest.getMethod().equals("GET") && Objects.isNull(response)) { + if (http11Request.getMethod().equals("GET") && Objects.isNull(response)) { String contentType = selectFirstContentTypeOrDefault( - httpRequest.getHeader("Accept")); + http11Request.getHeader("Accept")); // Todo: createResponseBody() pageController로 위임해보기 // Todo: 헤더에 담긴 sessionId 유효성 검증 diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpRequest.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Request.java similarity index 96% rename from tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpRequest.java rename to tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Request.java index 1726b7d457..ee6bb491ea 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpRequest.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Request.java @@ -9,7 +9,7 @@ import java.util.HashMap; import java.util.Map; -public class HttpRequest { +public class Http11Request { private static final int KEY_INDEX = 0; private static final int VALUE_INDEX = 1; @@ -23,7 +23,7 @@ public class HttpRequest { private final Cookies cookies = new Cookies(); private Map parsedBody = null; - public HttpRequest(InputStream inputStream) throws IOException { + public Http11Request(InputStream inputStream) throws IOException { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); this.startLine = new StartLine(bufferedReader.readLine()); From 6b3ed16326cc0c8cd1495bed644ed5c31710b360 Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Fri, 8 Sep 2023 23:05:46 +0900 Subject: [PATCH 06/24] =?UTF-8?q?feat:=20501=20status=20enum=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/nextstep/org/apache/coyote/http11/Status.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Status.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Status.java index 6b887aaa75..dfab3f32c8 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Status.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Status.java @@ -4,7 +4,8 @@ public enum Status { OK("200", "OK"), FOUND("302", "Found"), - NOT_FOUND("404", "Not Found"); + NOT_FOUND("404", "Not Found"), + NOT_IMPLEMENTED("501", "Not Implemented"); private final String code; private final String message; From e4c9fb55ae214b2619f61e050d543df1d6762e13 Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Fri, 8 Sep 2023 23:06:24 +0900 Subject: [PATCH 07/24] =?UTF-8?q?feat:=20Servlet=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EB=B0=8F=20AbstractServlet=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../http11/servlet/AbstractServlet.java | 25 +++++++++++++++++++ .../apache/coyote/http11/servlet/Servlet.java | 9 +++++++ 2 files changed, 34 insertions(+) create mode 100644 tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/AbstractServlet.java create mode 100644 tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/Servlet.java diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/AbstractServlet.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/AbstractServlet.java new file mode 100644 index 0000000000..212ff3e63a --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/AbstractServlet.java @@ -0,0 +1,25 @@ +package nextstep.org.apache.coyote.http11.servlet; + +import nextstep.org.apache.coyote.http11.Http11Request; +import nextstep.org.apache.coyote.http11.Http11Response; +import nextstep.org.apache.coyote.http11.Status; + +public abstract class AbstractServlet implements Servlet{ + + @Override + public void Service(Http11Request request, Http11Response response) throws Exception { + String method = request.getMethod(); + + if (method.equals("GET")) { + doGet(request, response); + } else if (method.equals("POST")) { + doPost(request, response); + } else { + response.setStatus(Status.NOT_IMPLEMENTED); + } + } + + protected void doPost(Http11Request request, Http11Response response) throws Exception {} + + protected void doGet(Http11Request request, Http11Response response) throws Exception {} +} diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/Servlet.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/Servlet.java new file mode 100644 index 0000000000..31b200853c --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/Servlet.java @@ -0,0 +1,9 @@ +package nextstep.org.apache.coyote.http11.servlet; + +import nextstep.org.apache.coyote.http11.Http11Request; +import nextstep.org.apache.coyote.http11.Http11Response; + +public interface Servlet { + + void Service(Http11Request request, Http11Response response) throws Exception; +} From 23c3c393b0b554f9382d236540087dcd1a0714b3 Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Sat, 9 Sep 2023 01:01:37 +0900 Subject: [PATCH 08/24] =?UTF-8?q?feat:=20LoginServlet=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 79 +++++++++++-------- .../org/apache/coyote/http11/HttpUtil.java | 11 +++ .../org/apache/coyote/http11/Status.java | 3 +- .../http11/servlet/AbstractServlet.java | 63 ++++++++++++++- .../coyote/http11/servlet/LoginServlet.java | 70 ++++++++++++++++ .../apache/coyote/http11/servlet/Servlet.java | 2 +- 6 files changed, 189 insertions(+), 39 deletions(-) create mode 100644 tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/LoginServlet.java diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java index 9142e0c2c2..ac9b038ca1 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java @@ -1,5 +1,7 @@ package nextstep.org.apache.coyote.http11; +import static nextstep.org.apache.coyote.http11.HttpUtil.selectFirstContentTypeOrDefault; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -8,14 +10,13 @@ import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.util.Arrays; -import java.util.List; import java.util.Objects; import java.util.Optional; import nextstep.jwp.controller.LoginController; import nextstep.jwp.dto.LoginResponseDto; import nextstep.jwp.exception.UncheckedServletException; import nextstep.org.apache.coyote.Processor; +import nextstep.org.apache.coyote.http11.servlet.LoginServlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,8 +25,8 @@ public class Http11Processor implements Runnable, Processor { private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); private static final String RESOURCES_PATH_PREFIX = "static"; - private static final int ACCEPT_HEADER_BEST_CONTENT_TYPE_INDEX = 0; private static final String NOT_FOUND_DEFAULT_MESSAGE = "404 Not Found"; + private static final String INTERNAL_SERVER_ERROR_DEFAULT_MESSAGE = "500 Internal Server Error"; private final Socket connection; private final HandlerMapper handlerMapper; @@ -47,50 +48,55 @@ public void process(final Socket connection) { InputStream inputStream = connection.getInputStream(); OutputStream outputStream = connection.getOutputStream() ) { - Http11Request http11Request = new Http11Request(inputStream); - Cookies cookies = http11Request.getCookies(); + Http11Request request = new Http11Request(inputStream); + Cookies cookies = request.getCookies(); - Http11Response response = null; - String requestPathInfo = http11Request.getPathInfo(); + Http11Response response = new Http11Response(Status.OK); + String requestPathInfo = request.getPathInfo(); Object handler = handlerMapper.mapHandler(requestPathInfo); if (Objects.nonNull(handler) - && http11Request.getMethod().equals("POST") + && request.getMethod().equals("POST") && requestPathInfo.equals("/login")) { - LoginController loginController = (LoginController) handler; - LoginResponseDto loginDto = loginController.login( - cookies, - http11Request.getParsedBodyValue("account"), - http11Request.getParsedBodyValue("password")); + try { + LoginServlet loginServlet = new LoginServlet(); + loginServlet.service(request, response); + } catch (Exception e) { + responseInternalServerError(request, response); + } - response = new Http11Response(Status.FOUND) - .setHeader("Location", loginDto.getRedirectUrl()) - .setCookies(cookies); + writeResponse(outputStream, response); + return; } if (Objects.nonNull(handler) - && http11Request.getMethod().equals("POST") + && request.getMethod().equals("POST") && requestPathInfo.equals("/register")) { LoginController loginController = (LoginController) handler; LoginResponseDto loginDto = loginController.register( - http11Request.getParsedBodyValue("account"), - http11Request.getParsedBodyValue("password"), - http11Request.getParsedBodyValue("email") + request.getParsedBodyValue("account"), + request.getParsedBodyValue("password"), + request.getParsedBodyValue("email") ); response = new Http11Response(Status.FOUND) .setHeader("Location", loginDto.getRedirectUrl()); + writeResponse(outputStream, response); + return; } - if (http11Request.getMethod().equals("GET") && Objects.isNull(response)) { + if (request.getMethod().equals("GET")) { String contentType = selectFirstContentTypeOrDefault( - http11Request.getHeader("Accept")); + request.getHeader("Accept") + ); - // Todo: createResponseBody() pageController로 위임해보기 - // Todo: 헤더에 담긴 sessionId 유효성 검증 if (requestPathInfo.contains("/login") && cookies.hasCookie("JSESSIONID")) { - response = new Http11Response(Status.FOUND) - .setHeader("Location", "/index.html"); + LoginServlet loginServlet = new LoginServlet(); + try { + loginServlet.service(request, response); + } catch (Exception e) { + responseInternalServerError(request, response); + } writeResponse(outputStream, response); return; } @@ -122,20 +128,25 @@ public void process(final Socket connection) { } } + private void responseInternalServerError(Http11Request request, Http11Response response) + throws IOException { + String internalServerErrorPageBody = createResponseBody("/500.html") + .orElse(INTERNAL_SERVER_ERROR_DEFAULT_MESSAGE); + String contentType = selectFirstContentTypeOrDefault(request.getHeader("Accept")); + response.setStatus(Status.INTERNAL_SERVER_ERROR) + .setHeader("Content-Type", contentType + ";charset=utf-8") + .setHeader("Content-Length", String.valueOf( + internalServerErrorPageBody.getBytes( + StandardCharsets.UTF_8).length)) + .setBody(internalServerErrorPageBody); + } + private void writeResponse(OutputStream outputStream, Http11Response response) throws IOException { outputStream.write(response.createResponseAsByteArray()); outputStream.flush(); } - private String selectFirstContentTypeOrDefault(String acceptHeader) { - if (Objects.isNull(acceptHeader)) { - return "text/html"; - } - List acceptHeaderValues = Arrays.asList(acceptHeader.split(",")); - return acceptHeaderValues.get(ACCEPT_HEADER_BEST_CONTENT_TYPE_INDEX); - } - private Optional createResponseBody(String requestPath) throws IOException { if (requestPath.equals("/")) { return Optional.of("Hello world!"); diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpUtil.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpUtil.java index ae8314dbb6..bc6f3b10fe 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpUtil.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpUtil.java @@ -1,12 +1,15 @@ package nextstep.org.apache.coyote.http11; import java.util.Arrays; +import java.util.List; import java.util.Map; +import java.util.Objects; public class HttpUtil { private static final int KEY_INDEX = 0; private static final int VALUE_INDEX = 1; + private static final int ACCEPT_HEADER_BEST_CONTENT_TYPE_INDEX = 0; private HttpUtil() { } @@ -21,4 +24,12 @@ public static void parseMultipleValues( parsedValues.put(splited[KEY_INDEX], splited[VALUE_INDEX]); }); } + + public static String selectFirstContentTypeOrDefault(String acceptHeader) { + if (Objects.isNull(acceptHeader)) { + return "text/html"; + } + List acceptHeaderValues = Arrays.asList(acceptHeader.split(",")); + return acceptHeaderValues.get(ACCEPT_HEADER_BEST_CONTENT_TYPE_INDEX); + } } diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Status.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Status.java index dfab3f32c8..2b9cda9913 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Status.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Status.java @@ -5,7 +5,8 @@ public enum Status { OK("200", "OK"), FOUND("302", "Found"), NOT_FOUND("404", "Not Found"), - NOT_IMPLEMENTED("501", "Not Implemented"); + NOT_IMPLEMENTED("501", "Not Implemented"), + INTERNAL_SERVER_ERROR("500", "Internal Server Error"); private final String code; private final String message; diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/AbstractServlet.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/AbstractServlet.java index 212ff3e63a..abdc61bee5 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/AbstractServlet.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/AbstractServlet.java @@ -1,13 +1,25 @@ package nextstep.org.apache.coyote.http11.servlet; +import static nextstep.org.apache.coyote.http11.HttpUtil.selectFirstContentTypeOrDefault; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Objects; +import java.util.Optional; import nextstep.org.apache.coyote.http11.Http11Request; import nextstep.org.apache.coyote.http11.Http11Response; import nextstep.org.apache.coyote.http11.Status; public abstract class AbstractServlet implements Servlet{ + private static final String RESOURCES_PATH_PREFIX = "static"; + protected static final String NOT_FOUND_DEFAULT_MESSAGE = "404 Not Found"; + @Override - public void Service(Http11Request request, Http11Response response) throws Exception { + public void service(Http11Request request, Http11Response response) throws Exception { String method = request.getMethod(); if (method.equals("GET")) { @@ -19,7 +31,52 @@ public void Service(Http11Request request, Http11Response response) throws Excep } } - protected void doPost(Http11Request request, Http11Response response) throws Exception {} + protected abstract void doPost(Http11Request request, Http11Response response) throws Exception; + + protected abstract void doGet(Http11Request request, Http11Response response) throws Exception; + + protected Optional createResponseBody(String requestPath) throws IOException { + if (requestPath.equals("/")) { + return Optional.of("Hello world!"); + } - protected void doGet(Http11Request request, Http11Response response) throws Exception {} + String resourceName = RESOURCES_PATH_PREFIX + requestPath; + if (!resourceName.contains(".")) { + resourceName += ".html"; + } + URL resource = getClass().getClassLoader().getResource(resourceName); + + if (Objects.isNull(resource)) { + return Optional.empty(); + } + return Optional.of(new String(Files.readAllBytes(new File(resource.getFile()).toPath()))); + } + + protected void responseWithBody(Http11Request request, Http11Response response) throws IOException { + Optional responseBody = createResponseBody(request.getPathInfo()); + String contentType = selectFirstContentTypeOrDefault(request.getHeader("Accept")); + + if (responseBody.isEmpty()) { + responseWithNotFound(request, response); + return; + } + + response.setStatus(Status.OK) + .setHeader("Content-Type", contentType + ";charset=utf-8") + .setHeader("Content-Length", String.valueOf( + responseBody.get().getBytes(StandardCharsets.UTF_8).length)) + .setBody(responseBody.get()); + } + + private void responseWithNotFound(Http11Request request, Http11Response response) throws IOException { + String notFoundPageBody = createResponseBody("/404.html") + .orElse(NOT_FOUND_DEFAULT_MESSAGE); + String contentType = selectFirstContentTypeOrDefault(request.getHeader("Accept")); + + response.setStatus(Status.NOT_FOUND) + .setHeader("Content-Type", contentType + ";charset=utf-8") + .setHeader("Content-Length", String.valueOf( + notFoundPageBody.getBytes(StandardCharsets.UTF_8).length)) + .setBody(notFoundPageBody); + } } diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/LoginServlet.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/LoginServlet.java new file mode 100644 index 0000000000..8c19c8bd3f --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/LoginServlet.java @@ -0,0 +1,70 @@ +package nextstep.org.apache.coyote.http11.servlet; + +import java.util.NoSuchElementException; +import java.util.UUID; +import nextstep.jwp.db.InMemoryUserRepository; +import nextstep.jwp.exception.InvalidLoginInfoException; +import nextstep.jwp.model.User; +import nextstep.org.apache.coyote.http11.Cookies; +import nextstep.org.apache.coyote.http11.Http11Request; +import nextstep.org.apache.coyote.http11.Http11Response; +import nextstep.org.apache.coyote.http11.Session; +import nextstep.org.apache.coyote.http11.SessionManager; +import nextstep.org.apache.coyote.http11.Status; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LoginServlet extends AbstractServlet { + + private final Logger log = LoggerFactory.getLogger(LoginServlet.class); + + @Override + protected void doPost(Http11Request request, Http11Response response) { + Cookies cookies = request.getCookies(); + try { + Session loginSession = login( + request.getParsedBodyValue("account"), + request.getParsedBodyValue("password") + ); + cookies.set("JSESSIONID", loginSession.getId()); + + response.setStatus(Status.FOUND) + .setHeader("Location", "/index.html") + .setCookies(cookies); + } catch (NoSuchElementException | InvalidLoginInfoException | NullPointerException e) { + response.setStatus(Status.FOUND) + .setHeader("Location", "/401.html") + .setCookies(cookies); + } + } + + private Session login(String account, String password) { + User user = InMemoryUserRepository.findByAccount(account) + .orElseThrow(); + if (!user.checkPassword(password)) { + throw new InvalidLoginInfoException(); + } + log.info(user.toString()); + return createSession(user); + } + + private Session createSession(User user) { + SessionManager sessionManager = new SessionManager(); + Session session = new Session(UUID.randomUUID().toString()); + session.setAttribute(user.getAccount(), user); + sessionManager.add(session); + return session; + } + + @Override + protected void doGet(Http11Request request, Http11Response response) throws Exception { + // Todo: 헤더에 담긴 sessionId 유효성 검증 + Cookies cookies = request.getCookies(); + if (cookies.hasCookie("JSESSIONID")) { + response.setStatus(Status.FOUND) + .setHeader("Location", "/index.html"); + return; + } + responseWithBody(request, response); + } +} diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/Servlet.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/Servlet.java index 31b200853c..769583a895 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/Servlet.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/Servlet.java @@ -5,5 +5,5 @@ public interface Servlet { - void Service(Http11Request request, Http11Response response) throws Exception; + void service(Http11Request request, Http11Response response) throws Exception; } From 384b75507c3a8bd5dd2e40dc51158482dccb5ced Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Sat, 9 Sep 2023 01:34:00 +0900 Subject: [PATCH 09/24] =?UTF-8?q?feat:=20RegisterServlet=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jwp/controller/LoginController.java | 28 --------- .../nextstep/jwp/dto/LoginResponseDto.java | 14 ----- .../nextstep/jwp/service/LoginService.java | 37 ----------- .../apache/coyote/http11/Http11Processor.java | 61 ++++++++++--------- .../http11/servlet/RegisterServlet.java | 30 +++++++++ 5 files changed, 62 insertions(+), 108 deletions(-) delete mode 100644 tomcat/src/main/java/nextstep/jwp/controller/LoginController.java delete mode 100644 tomcat/src/main/java/nextstep/jwp/dto/LoginResponseDto.java delete mode 100644 tomcat/src/main/java/nextstep/jwp/service/LoginService.java create mode 100644 tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/RegisterServlet.java diff --git a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java deleted file mode 100644 index 88cbc92f32..0000000000 --- a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java +++ /dev/null @@ -1,28 +0,0 @@ -package nextstep.jwp.controller; - -import java.util.NoSuchElementException; -import nextstep.jwp.dto.LoginResponseDto; -import nextstep.jwp.exception.InvalidLoginInfoException; -import nextstep.jwp.service.LoginService; -import nextstep.org.apache.coyote.http11.Cookies; -import nextstep.org.apache.coyote.http11.Session; - -public class LoginController { - - private final LoginService loginService = new LoginService(); - - public LoginResponseDto login(Cookies cookies, String account, String password) { - try { - Session loginSession = loginService.login(account, password); - cookies.set("JSESSIONID", loginSession.getId()); - return new LoginResponseDto("/index.html"); - } catch (NoSuchElementException | InvalidLoginInfoException | NullPointerException e) { - return new LoginResponseDto("/401.html"); - } - } - - public LoginResponseDto register(String account, String password, String email) { - loginService.register(account, password, email); - return new LoginResponseDto("/index.html"); - } -} diff --git a/tomcat/src/main/java/nextstep/jwp/dto/LoginResponseDto.java b/tomcat/src/main/java/nextstep/jwp/dto/LoginResponseDto.java deleted file mode 100644 index 25e0b9e82b..0000000000 --- a/tomcat/src/main/java/nextstep/jwp/dto/LoginResponseDto.java +++ /dev/null @@ -1,14 +0,0 @@ -package nextstep.jwp.dto; - -public class LoginResponseDto { - - private final String redirectUrl; - - public LoginResponseDto(String redirectUrl) { - this.redirectUrl = redirectUrl; - } - - public String getRedirectUrl() { - return redirectUrl; - } -} diff --git a/tomcat/src/main/java/nextstep/jwp/service/LoginService.java b/tomcat/src/main/java/nextstep/jwp/service/LoginService.java deleted file mode 100644 index f2f1c0a678..0000000000 --- a/tomcat/src/main/java/nextstep/jwp/service/LoginService.java +++ /dev/null @@ -1,37 +0,0 @@ -package nextstep.jwp.service; - -import java.util.UUID; -import nextstep.jwp.db.InMemoryUserRepository; -import nextstep.jwp.exception.InvalidLoginInfoException; -import nextstep.jwp.model.User; -import nextstep.org.apache.coyote.http11.Session; -import nextstep.org.apache.coyote.http11.SessionManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class LoginService { - - private final Logger log = LoggerFactory.getLogger(LoginService.class); - - public Session login(String account, String password) { - User user = InMemoryUserRepository.findByAccount(account) - .orElseThrow(); - if (!user.checkPassword(password)) { - throw new InvalidLoginInfoException(); - } - log.info(user.toString()); - return createSession(user); - } - - private Session createSession(User user) { - SessionManager sessionManager = new SessionManager(); - Session session = new Session(UUID.randomUUID().toString()); - session.setAttribute(user.getAccount(), user); - sessionManager.add(session); - return session; - } - - public void register(String account, String password, String email) { - InMemoryUserRepository.save(new User(account, password, email)); - } -} diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java index ac9b038ca1..c430d8100a 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java @@ -12,11 +12,10 @@ import java.nio.file.Files; import java.util.Objects; import java.util.Optional; -import nextstep.jwp.controller.LoginController; -import nextstep.jwp.dto.LoginResponseDto; import nextstep.jwp.exception.UncheckedServletException; import nextstep.org.apache.coyote.Processor; import nextstep.org.apache.coyote.http11.servlet.LoginServlet; +import nextstep.org.apache.coyote.http11.servlet.RegisterServlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -72,15 +71,13 @@ public void process(final Socket connection) { if (Objects.nonNull(handler) && request.getMethod().equals("POST") && requestPathInfo.equals("/register")) { - LoginController loginController = (LoginController) handler; - LoginResponseDto loginDto = loginController.register( - request.getParsedBodyValue("account"), - request.getParsedBodyValue("password"), - request.getParsedBodyValue("email") - ); + RegisterServlet registerServlet = new RegisterServlet(); + try { + registerServlet.service(request, response); + } catch (Exception e) { + responseInternalServerError(request, response); + } - response = new Http11Response(Status.FOUND) - .setHeader("Location", loginDto.getRedirectUrl()); writeResponse(outputStream, response); return; } @@ -101,25 +98,7 @@ public void process(final Socket connection) { return; } - Optional responseBody = createResponseBody(requestPathInfo); - if (responseBody.isEmpty()) { - String notFoundPageBody = createResponseBody("/404.html") - .orElse(NOT_FOUND_DEFAULT_MESSAGE); - - response = new Http11Response(Status.NOT_FOUND) - .setHeader("Content-Type", contentType + ";charset=utf-8") - .setHeader("Content-Length", String.valueOf( - notFoundPageBody.getBytes(StandardCharsets.UTF_8).length)) - .setBody(notFoundPageBody); - writeResponse(outputStream, response); - return; - } - - response = new Http11Response(Status.OK) - .setHeader("Content-Type", contentType + ";charset=utf-8") - .setHeader("Content-Length", String.valueOf( - responseBody.get().getBytes(StandardCharsets.UTF_8).length)) - .setBody(responseBody.get()); + responseWithFoundResource(request, response); } writeResponse(outputStream, response); @@ -128,6 +107,30 @@ public void process(final Socket connection) { } } + private void responseWithFoundResource(Http11Request request, Http11Response response) + throws IOException { + Optional responseBody = createResponseBody(request.getPathInfo()); + String contentType = selectFirstContentTypeOrDefault(request.getHeader("Accept")); + + if (responseBody.isEmpty()) { + String notFoundPageBody = createResponseBody("/404.html") + .orElse(NOT_FOUND_DEFAULT_MESSAGE); + + response.setStatus(Status.NOT_FOUND) + .setHeader("Content-Type", contentType + ";charset=utf-8") + .setHeader("Content-Length", String.valueOf( + notFoundPageBody.getBytes(StandardCharsets.UTF_8).length)) + .setBody(notFoundPageBody); + return; + } + + response.setStatus(Status.OK) + .setHeader("Content-Type", contentType + ";charset=utf-8") + .setHeader("Content-Length", String.valueOf( + responseBody.get().getBytes(StandardCharsets.UTF_8).length)) + .setBody(responseBody.get()); + } + private void responseInternalServerError(Http11Request request, Http11Response response) throws IOException { String internalServerErrorPageBody = createResponseBody("/500.html") diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/RegisterServlet.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/RegisterServlet.java new file mode 100644 index 0000000000..4afa8f5777 --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/RegisterServlet.java @@ -0,0 +1,30 @@ +package nextstep.org.apache.coyote.http11.servlet; + +import nextstep.jwp.db.InMemoryUserRepository; +import nextstep.jwp.model.User; +import nextstep.org.apache.coyote.http11.Http11Request; +import nextstep.org.apache.coyote.http11.Http11Response; +import nextstep.org.apache.coyote.http11.Status; + +public class RegisterServlet extends AbstractServlet{ + + @Override + protected void doPost(Http11Request request, Http11Response response) { + + InMemoryUserRepository.save( + new User( + request.getParsedBodyValue("account"), + request.getParsedBodyValue("password"), + request.getParsedBodyValue("email") + ) + ); + + response.setStatus(Status.FOUND) + .setHeader("Location", "/index.html"); + } + + @Override + protected void doGet(Http11Request request, Http11Response response) throws Exception { + responseWithBody(request, response); + } +} From ae3ac590d27f32adbcea525de72b84925b644717 Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Sat, 9 Sep 2023 14:47:51 +0900 Subject: [PATCH 10/24] =?UTF-8?q?feat:=20=EC=84=9C=EB=B8=94=EB=A6=BF=20?= =?UTF-8?q?=EC=BB=A8=ED=85=8C=EC=9D=B4=EB=84=88=20Context=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20DefaultSe?= =?UTF-8?q?rvlet=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/HandlerMapper.java | 20 ----- .../apache/coyote/http11/Http11Processor.java | 86 ++----------------- .../apache/coyote/http11/servlet/Context.java | 32 +++++++ .../coyote/http11/servlet/DefaultServlet.java | 18 ++++ .../coyote/http11/HandlerMapperTest.java | 23 ----- 5 files changed, 58 insertions(+), 121 deletions(-) delete mode 100644 tomcat/src/main/java/nextstep/org/apache/coyote/http11/HandlerMapper.java create mode 100644 tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/Context.java create mode 100644 tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/DefaultServlet.java delete mode 100644 tomcat/src/test/java/nextstep/org/apache/coyote/http11/HandlerMapperTest.java diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HandlerMapper.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HandlerMapper.java deleted file mode 100644 index 15bdd9cac9..0000000000 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HandlerMapper.java +++ /dev/null @@ -1,20 +0,0 @@ -package nextstep.org.apache.coyote.http11; - -import java.util.HashMap; -import java.util.Map; -import nextstep.jwp.controller.LoginController; - -public class HandlerMapper { - - private static final Map handlerMapper = new HashMap<>(); - - static { - LoginController loginController = new LoginController(); - handlerMapper.put("/login", loginController); - handlerMapper.put("/register", loginController); - } - - public Object mapHandler(String requestedUrl) { - return handlerMapper.get(requestedUrl); - } -} diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java index c430d8100a..ce4cea098a 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java @@ -14,8 +14,8 @@ import java.util.Optional; import nextstep.jwp.exception.UncheckedServletException; import nextstep.org.apache.coyote.Processor; -import nextstep.org.apache.coyote.http11.servlet.LoginServlet; -import nextstep.org.apache.coyote.http11.servlet.RegisterServlet; +import nextstep.org.apache.coyote.http11.servlet.Context; +import nextstep.org.apache.coyote.http11.servlet.Servlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,15 +24,12 @@ public class Http11Processor implements Runnable, Processor { private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); private static final String RESOURCES_PATH_PREFIX = "static"; - private static final String NOT_FOUND_DEFAULT_MESSAGE = "404 Not Found"; private static final String INTERNAL_SERVER_ERROR_DEFAULT_MESSAGE = "500 Internal Server Error"; private final Socket connection; - private final HandlerMapper handlerMapper; public Http11Processor(final Socket connection) { this.connection = connection; - this.handlerMapper = new HandlerMapper(); } @Override @@ -48,57 +45,14 @@ public void process(final Socket connection) { OutputStream outputStream = connection.getOutputStream() ) { Http11Request request = new Http11Request(inputStream); - Cookies cookies = request.getCookies(); - Http11Response response = new Http11Response(Status.OK); - String requestPathInfo = request.getPathInfo(); - - Object handler = handlerMapper.mapHandler(requestPathInfo); - if (Objects.nonNull(handler) - && request.getMethod().equals("POST") - && requestPathInfo.equals("/login")) { - try { - LoginServlet loginServlet = new LoginServlet(); - loginServlet.service(request, response); - } catch (Exception e) { - responseInternalServerError(request, response); - } - - writeResponse(outputStream, response); - return; - } - - if (Objects.nonNull(handler) - && request.getMethod().equals("POST") - && requestPathInfo.equals("/register")) { - RegisterServlet registerServlet = new RegisterServlet(); - try { - registerServlet.service(request, response); - } catch (Exception e) { - responseInternalServerError(request, response); - } - - writeResponse(outputStream, response); - return; - } - if (request.getMethod().equals("GET")) { - String contentType = selectFirstContentTypeOrDefault( - request.getHeader("Accept") - ); - - if (requestPathInfo.contains("/login") && cookies.hasCookie("JSESSIONID")) { - LoginServlet loginServlet = new LoginServlet(); - try { - loginServlet.service(request, response); - } catch (Exception e) { - responseInternalServerError(request, response); - } - writeResponse(outputStream, response); - return; - } - - responseWithFoundResource(request, response); + Context servletContainer = new Context(); + Servlet servlet = servletContainer.getServlet(request.getPathInfo()); + try { + servlet.service(request, response); + } catch (Exception e) { + responseInternalServerError(request, response); } writeResponse(outputStream, response); @@ -107,30 +61,6 @@ public void process(final Socket connection) { } } - private void responseWithFoundResource(Http11Request request, Http11Response response) - throws IOException { - Optional responseBody = createResponseBody(request.getPathInfo()); - String contentType = selectFirstContentTypeOrDefault(request.getHeader("Accept")); - - if (responseBody.isEmpty()) { - String notFoundPageBody = createResponseBody("/404.html") - .orElse(NOT_FOUND_DEFAULT_MESSAGE); - - response.setStatus(Status.NOT_FOUND) - .setHeader("Content-Type", contentType + ";charset=utf-8") - .setHeader("Content-Length", String.valueOf( - notFoundPageBody.getBytes(StandardCharsets.UTF_8).length)) - .setBody(notFoundPageBody); - return; - } - - response.setStatus(Status.OK) - .setHeader("Content-Type", contentType + ";charset=utf-8") - .setHeader("Content-Length", String.valueOf( - responseBody.get().getBytes(StandardCharsets.UTF_8).length)) - .setBody(responseBody.get()); - } - private void responseInternalServerError(Http11Request request, Http11Response response) throws IOException { String internalServerErrorPageBody = createResponseBody("/500.html") diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/Context.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/Context.java new file mode 100644 index 0000000000..bb2613f480 --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/Context.java @@ -0,0 +1,32 @@ +package nextstep.org.apache.coyote.http11.servlet; + +import java.util.HashMap; +import java.util.Map; + +public class Context { + + private static final Map servletMappings; + private static final String EXTENSION_DELIMITER = "."; + + static { + servletMappings = new HashMap<>(); + servletMappings.put("/login", new LoginServlet()); + servletMappings.put("/register", new RegisterServlet()); + servletMappings.put("default", new DefaultServlet()); + } + + public Servlet getServlet(String pathInfo) { + if (servletMappings.containsKey(removeExtension(pathInfo))) { + return servletMappings.get(removeExtension(pathInfo)); + } + return servletMappings.get("default"); + } + + private String removeExtension(String pathInfo) { + int idx = pathInfo.indexOf(EXTENSION_DELIMITER); + if (idx == -1) { + return pathInfo; + } + return pathInfo.substring(0,idx); + } +} diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/DefaultServlet.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/DefaultServlet.java new file mode 100644 index 0000000000..48195267d8 --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/DefaultServlet.java @@ -0,0 +1,18 @@ +package nextstep.org.apache.coyote.http11.servlet; + +import nextstep.org.apache.coyote.http11.Http11Request; +import nextstep.org.apache.coyote.http11.Http11Response; +import nextstep.org.apache.coyote.http11.Status; + +public class DefaultServlet extends AbstractServlet { + + @Override + protected void doPost(Http11Request request, Http11Response response) { + response.setStatus(Status.NOT_IMPLEMENTED); + } + + @Override + protected void doGet(Http11Request request, Http11Response response) throws Exception { + responseWithBody(request, response); + } +} diff --git a/tomcat/src/test/java/nextstep/org/apache/coyote/http11/HandlerMapperTest.java b/tomcat/src/test/java/nextstep/org/apache/coyote/http11/HandlerMapperTest.java deleted file mode 100644 index dfda50e0af..0000000000 --- a/tomcat/src/test/java/nextstep/org/apache/coyote/http11/HandlerMapperTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package nextstep.org.apache.coyote.http11; - -import static org.junit.jupiter.api.Assertions.*; - -import nextstep.jwp.controller.LoginController; -import org.junit.jupiter.api.Test; - -class HandlerMapperTest { - - private HandlerMapper handlerMapper = new HandlerMapper(); - - @Test - void mapHandlerByUrlTest() { - // given - String requestedUrl = "/login"; - - // when - LoginController loginController = (LoginController) handlerMapper.mapHandler(requestedUrl); - - // then - assertNotNull(loginController); - } -} From 4aa7b939ecf8af00386a9638150507f673384cf6 Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Sun, 10 Sep 2023 12:21:32 +0900 Subject: [PATCH 11/24] =?UTF-8?q?feat:=20=EC=9D=91=EB=8B=B5=20=EB=A9=94?= =?UTF-8?q?=EC=84=B8=EC=A7=80=20=EB=B0=94=EB=94=94=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EA=B3=B5=ED=86=B5=20Util=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 28 ++----------------- .../org/apache/coyote/http11/HttpUtil.java | 27 ++++++++++++++++++ 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java index ce4cea098a..c096213633 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java @@ -1,17 +1,13 @@ package nextstep.org.apache.coyote.http11; +import static nextstep.org.apache.coyote.http11.HttpUtil.createResponseBody; import static nextstep.org.apache.coyote.http11.HttpUtil.selectFirstContentTypeOrDefault; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; -import java.net.URL; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.Objects; -import java.util.Optional; import nextstep.jwp.exception.UncheckedServletException; import nextstep.org.apache.coyote.Processor; import nextstep.org.apache.coyote.http11.servlet.Context; @@ -23,9 +19,6 @@ public class Http11Processor implements Runnable, Processor { private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); - private static final String RESOURCES_PATH_PREFIX = "static"; - private static final String INTERNAL_SERVER_ERROR_DEFAULT_MESSAGE = "500 Internal Server Error"; - private final Socket connection; public Http11Processor(final Socket connection) { @@ -64,7 +57,7 @@ public void process(final Socket connection) { private void responseInternalServerError(Http11Request request, Http11Response response) throws IOException { String internalServerErrorPageBody = createResponseBody("/500.html") - .orElse(INTERNAL_SERVER_ERROR_DEFAULT_MESSAGE); + .orElse(Status.INTERNAL_SERVER_ERROR.getCodeMessage()); String contentType = selectFirstContentTypeOrDefault(request.getHeader("Accept")); response.setStatus(Status.INTERNAL_SERVER_ERROR) .setHeader("Content-Type", contentType + ";charset=utf-8") @@ -79,21 +72,4 @@ private void writeResponse(OutputStream outputStream, Http11Response response) outputStream.write(response.createResponseAsByteArray()); outputStream.flush(); } - - private Optional createResponseBody(String requestPath) throws IOException { - if (requestPath.equals("/")) { - return Optional.of("Hello world!"); - } - - String resourceName = RESOURCES_PATH_PREFIX + requestPath; - if (!resourceName.contains(".")) { - resourceName += ".html"; - } - URL resource = getClass().getClassLoader().getResource(resourceName); - - if (Objects.isNull(resource)) { - return Optional.empty(); - } - return Optional.of(new String(Files.readAllBytes(new File(resource.getFile()).toPath()))); - } } diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpUtil.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpUtil.java index bc6f3b10fe..c9141498d7 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpUtil.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpUtil.java @@ -1,15 +1,21 @@ package nextstep.org.apache.coyote.http11; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; public class HttpUtil { private static final int KEY_INDEX = 0; private static final int VALUE_INDEX = 1; private static final int ACCEPT_HEADER_BEST_CONTENT_TYPE_INDEX = 0; + private static final String RESOURCES_PATH_PREFIX = "static"; private HttpUtil() { } @@ -32,4 +38,25 @@ public static String selectFirstContentTypeOrDefault(String acceptHeader) { List acceptHeaderValues = Arrays.asList(acceptHeader.split(",")); return acceptHeaderValues.get(ACCEPT_HEADER_BEST_CONTENT_TYPE_INDEX); } + + public static Optional createResponseBody(String requestPath) throws IOException { + if (requestPath.equals("/")) { + return Optional.of("Hello world!"); + } + + String resourceName = preprocessRequestPath(requestPath); + URL resource = HttpUtil.class.getClassLoader().getResource(resourceName); + if (Objects.isNull(resource)) { + return Optional.empty(); + } + return Optional.of(new String(Files.readAllBytes(new File(resource.getFile()).toPath()))); + } + + private static String preprocessRequestPath(String requestPath) { + String resourceName = RESOURCES_PATH_PREFIX + requestPath; + if (!resourceName.contains(".")) { + resourceName += ".html"; + } + return resourceName; + } } From 24ec96997134600f5082a0feff20285158769ea9 Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Sun, 10 Sep 2023 12:46:14 +0900 Subject: [PATCH 12/24] =?UTF-8?q?style:=20catalina,=20coyote=20=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=20=EC=97=AD=ED=95=A0=20=EB=B3=84=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{coyote/http11/servlet => catalina}/Context.java | 6 +++++- .../java/nextstep/org/apache/catalina/Manager.java | 2 +- .../{coyote/http11 => catalina/cookie}/Cookies.java | 4 ++-- .../http11 => catalina}/servlet/AbstractServlet.java | 6 +++--- .../http11 => catalina}/servlet/DefaultServlet.java | 6 +++--- .../http11 => catalina}/servlet/LoginServlet.java | 12 ++++++------ .../http11 => catalina}/servlet/RegisterServlet.java | 6 +++--- .../org/apache/catalina/servlet/Servlet.java | 9 +++++++++ .../{coyote/http11 => catalina/session}/Session.java | 2 +- .../http11 => catalina/session}/SessionManager.java | 4 +--- .../org/apache/coyote/http11/Http11Processor.java | 6 ++++-- .../coyote/http11/{ => request}/Http11Request.java | 3 ++- .../coyote/http11/{ => request}/StartLine.java | 2 +- .../coyote/http11/{ => response}/Http11Response.java | 4 +++- .../coyote/http11/{ => response}/StatusLine.java | 4 +++- .../org/apache/coyote/http11/servlet/Servlet.java | 9 --------- .../apache/coyote/http11/Http11ProcessorTest.java | 2 ++ 17 files changed, 49 insertions(+), 38 deletions(-) rename tomcat/src/main/java/nextstep/org/apache/{coyote/http11/servlet => catalina}/Context.java (77%) rename tomcat/src/main/java/nextstep/org/apache/{coyote/http11 => catalina/cookie}/Cookies.java (92%) rename tomcat/src/main/java/nextstep/org/apache/{coyote/http11 => catalina}/servlet/AbstractServlet.java (94%) rename tomcat/src/main/java/nextstep/org/apache/{coyote/http11 => catalina}/servlet/DefaultServlet.java (70%) rename tomcat/src/main/java/nextstep/org/apache/{coyote/http11 => catalina}/servlet/LoginServlet.java (87%) rename tomcat/src/main/java/nextstep/org/apache/{coyote/http11 => catalina}/servlet/RegisterServlet.java (82%) create mode 100644 tomcat/src/main/java/nextstep/org/apache/catalina/servlet/Servlet.java rename tomcat/src/main/java/nextstep/org/apache/{coyote/http11 => catalina/session}/Session.java (91%) rename tomcat/src/main/java/nextstep/org/apache/{coyote/http11 => catalina/session}/SessionManager.java (87%) rename tomcat/src/main/java/nextstep/org/apache/coyote/http11/{ => request}/Http11Request.java (96%) rename tomcat/src/main/java/nextstep/org/apache/coyote/http11/{ => request}/StartLine.java (96%) rename tomcat/src/main/java/nextstep/org/apache/coyote/http11/{ => response}/Http11Response.java (93%) rename tomcat/src/main/java/nextstep/org/apache/coyote/http11/{ => response}/StatusLine.java (76%) delete mode 100644 tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/Servlet.java diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/Context.java b/tomcat/src/main/java/nextstep/org/apache/catalina/Context.java similarity index 77% rename from tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/Context.java rename to tomcat/src/main/java/nextstep/org/apache/catalina/Context.java index bb2613f480..27ff402373 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/Context.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/Context.java @@ -1,7 +1,11 @@ -package nextstep.org.apache.coyote.http11.servlet; +package nextstep.org.apache.catalina; import java.util.HashMap; import java.util.Map; +import nextstep.org.apache.catalina.servlet.DefaultServlet; +import nextstep.org.apache.catalina.servlet.LoginServlet; +import nextstep.org.apache.catalina.servlet.RegisterServlet; +import nextstep.org.apache.catalina.servlet.Servlet; public class Context { diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/Manager.java b/tomcat/src/main/java/nextstep/org/apache/catalina/Manager.java index c3dd6088d5..678695694c 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/Manager.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/Manager.java @@ -1,7 +1,7 @@ package nextstep.org.apache.catalina; import java.io.IOException; -import nextstep.org.apache.coyote.http11.Session; +import nextstep.org.apache.catalina.session.Session; /** * A Manager manages the pool of Sessions that are associated with a diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Cookies.java b/tomcat/src/main/java/nextstep/org/apache/catalina/cookie/Cookies.java similarity index 92% rename from tomcat/src/main/java/nextstep/org/apache/coyote/http11/Cookies.java rename to tomcat/src/main/java/nextstep/org/apache/catalina/cookie/Cookies.java index f16c0a1e61..a7663dd45c 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Cookies.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/cookie/Cookies.java @@ -1,4 +1,4 @@ -package nextstep.org.apache.coyote.http11; +package nextstep.org.apache.catalina.cookie; import static nextstep.org.apache.coyote.http11.HttpUtil.parseMultipleValues; @@ -44,7 +44,7 @@ public String createSetCookieHeader() { public Cookies defensiveCopy() { Cookies newCookies = new Cookies(); - cookie.forEach((key, value) -> newCookies.set(key, value)); + cookie.forEach(newCookies::set); return newCookies; } } diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/AbstractServlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/AbstractServlet.java similarity index 94% rename from tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/AbstractServlet.java rename to tomcat/src/main/java/nextstep/org/apache/catalina/servlet/AbstractServlet.java index abdc61bee5..755bba4b0a 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/AbstractServlet.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/AbstractServlet.java @@ -1,4 +1,4 @@ -package nextstep.org.apache.coyote.http11.servlet; +package nextstep.org.apache.catalina.servlet; import static nextstep.org.apache.coyote.http11.HttpUtil.selectFirstContentTypeOrDefault; @@ -9,8 +9,8 @@ import java.nio.file.Files; import java.util.Objects; import java.util.Optional; -import nextstep.org.apache.coyote.http11.Http11Request; -import nextstep.org.apache.coyote.http11.Http11Response; +import nextstep.org.apache.coyote.http11.request.Http11Request; +import nextstep.org.apache.coyote.http11.response.Http11Response; import nextstep.org.apache.coyote.http11.Status; public abstract class AbstractServlet implements Servlet{ diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/DefaultServlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/DefaultServlet.java similarity index 70% rename from tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/DefaultServlet.java rename to tomcat/src/main/java/nextstep/org/apache/catalina/servlet/DefaultServlet.java index 48195267d8..4935718f4a 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/DefaultServlet.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/DefaultServlet.java @@ -1,7 +1,7 @@ -package nextstep.org.apache.coyote.http11.servlet; +package nextstep.org.apache.catalina.servlet; -import nextstep.org.apache.coyote.http11.Http11Request; -import nextstep.org.apache.coyote.http11.Http11Response; +import nextstep.org.apache.coyote.http11.request.Http11Request; +import nextstep.org.apache.coyote.http11.response.Http11Response; import nextstep.org.apache.coyote.http11.Status; public class DefaultServlet extends AbstractServlet { diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/LoginServlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java similarity index 87% rename from tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/LoginServlet.java rename to tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java index 8c19c8bd3f..886abc9e16 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/LoginServlet.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java @@ -1,15 +1,15 @@ -package nextstep.org.apache.coyote.http11.servlet; +package nextstep.org.apache.catalina.servlet; import java.util.NoSuchElementException; import java.util.UUID; import nextstep.jwp.db.InMemoryUserRepository; import nextstep.jwp.exception.InvalidLoginInfoException; import nextstep.jwp.model.User; -import nextstep.org.apache.coyote.http11.Cookies; -import nextstep.org.apache.coyote.http11.Http11Request; -import nextstep.org.apache.coyote.http11.Http11Response; -import nextstep.org.apache.coyote.http11.Session; -import nextstep.org.apache.coyote.http11.SessionManager; +import nextstep.org.apache.catalina.cookie.Cookies; +import nextstep.org.apache.coyote.http11.request.Http11Request; +import nextstep.org.apache.coyote.http11.response.Http11Response; +import nextstep.org.apache.catalina.session.Session; +import nextstep.org.apache.catalina.session.SessionManager; import nextstep.org.apache.coyote.http11.Status; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/RegisterServlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/RegisterServlet.java similarity index 82% rename from tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/RegisterServlet.java rename to tomcat/src/main/java/nextstep/org/apache/catalina/servlet/RegisterServlet.java index 4afa8f5777..3a17c8f092 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/RegisterServlet.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/RegisterServlet.java @@ -1,9 +1,9 @@ -package nextstep.org.apache.coyote.http11.servlet; +package nextstep.org.apache.catalina.servlet; import nextstep.jwp.db.InMemoryUserRepository; import nextstep.jwp.model.User; -import nextstep.org.apache.coyote.http11.Http11Request; -import nextstep.org.apache.coyote.http11.Http11Response; +import nextstep.org.apache.coyote.http11.request.Http11Request; +import nextstep.org.apache.coyote.http11.response.Http11Response; import nextstep.org.apache.coyote.http11.Status; public class RegisterServlet extends AbstractServlet{ diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/Servlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/Servlet.java new file mode 100644 index 0000000000..8bbaa4464b --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/Servlet.java @@ -0,0 +1,9 @@ +package nextstep.org.apache.catalina.servlet; + +import nextstep.org.apache.coyote.http11.request.Http11Request; +import nextstep.org.apache.coyote.http11.response.Http11Response; + +public interface Servlet { + + void service(Http11Request request, Http11Response response) throws Exception; +} diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Session.java b/tomcat/src/main/java/nextstep/org/apache/catalina/session/Session.java similarity index 91% rename from tomcat/src/main/java/nextstep/org/apache/coyote/http11/Session.java rename to tomcat/src/main/java/nextstep/org/apache/catalina/session/Session.java index 0622a2ec32..afdb360c65 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Session.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/session/Session.java @@ -1,4 +1,4 @@ -package nextstep.org.apache.coyote.http11; +package nextstep.org.apache.catalina.session; import java.util.HashMap; import java.util.Map; diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/SessionManager.java b/tomcat/src/main/java/nextstep/org/apache/catalina/session/SessionManager.java similarity index 87% rename from tomcat/src/main/java/nextstep/org/apache/coyote/http11/SessionManager.java rename to tomcat/src/main/java/nextstep/org/apache/catalina/session/SessionManager.java index cbafda4e68..580f4d9ef5 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/SessionManager.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/session/SessionManager.java @@ -1,4 +1,4 @@ -package nextstep.org.apache.coyote.http11; +package nextstep.org.apache.catalina.session; import java.util.HashMap; import java.util.Map; @@ -8,8 +8,6 @@ public class SessionManager implements Manager { private static final Map SESSIONS = new HashMap<>(); - public SessionManager() {} - @Override public void add(Session session) { SESSIONS.put(session.getId(), session); diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java index c096213633..6bd2887144 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java @@ -10,8 +10,10 @@ import java.nio.charset.StandardCharsets; import nextstep.jwp.exception.UncheckedServletException; import nextstep.org.apache.coyote.Processor; -import nextstep.org.apache.coyote.http11.servlet.Context; -import nextstep.org.apache.coyote.http11.servlet.Servlet; +import nextstep.org.apache.catalina.Context; +import nextstep.org.apache.catalina.servlet.Servlet; +import nextstep.org.apache.coyote.http11.request.Http11Request; +import nextstep.org.apache.coyote.http11.response.Http11Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Request.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/Http11Request.java similarity index 96% rename from tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Request.java rename to tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/Http11Request.java index ee6bb491ea..b27f99f643 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Request.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/Http11Request.java @@ -1,4 +1,4 @@ -package nextstep.org.apache.coyote.http11; +package nextstep.org.apache.coyote.http11.request; import static nextstep.org.apache.coyote.http11.HttpUtil.parseMultipleValues; @@ -8,6 +8,7 @@ import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; +import nextstep.org.apache.catalina.cookie.Cookies; public class Http11Request { diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/StartLine.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/StartLine.java similarity index 96% rename from tomcat/src/main/java/nextstep/org/apache/coyote/http11/StartLine.java rename to tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/StartLine.java index d9e0cf13bb..93389c7a87 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/StartLine.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/StartLine.java @@ -1,4 +1,4 @@ -package nextstep.org.apache.coyote.http11; +package nextstep.org.apache.coyote.http11.request; import java.util.Arrays; import java.util.List; diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Response.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/response/Http11Response.java similarity index 93% rename from tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Response.java rename to tomcat/src/main/java/nextstep/org/apache/coyote/http11/response/Http11Response.java index c1300fc039..5469247846 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Response.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/response/Http11Response.java @@ -1,9 +1,11 @@ -package nextstep.org.apache.coyote.http11; +package nextstep.org.apache.coyote.http11.response; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; +import nextstep.org.apache.catalina.cookie.Cookies; +import nextstep.org.apache.coyote.http11.Status; public class Http11Response { diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/StatusLine.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/response/StatusLine.java similarity index 76% rename from tomcat/src/main/java/nextstep/org/apache/coyote/http11/StatusLine.java rename to tomcat/src/main/java/nextstep/org/apache/coyote/http11/response/StatusLine.java index 445a6b8640..096d09aade 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/StatusLine.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/response/StatusLine.java @@ -1,4 +1,6 @@ -package nextstep.org.apache.coyote.http11; +package nextstep.org.apache.coyote.http11.response; + +import nextstep.org.apache.coyote.http11.Status; public class StatusLine { diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/Servlet.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/Servlet.java deleted file mode 100644 index 769583a895..0000000000 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/servlet/Servlet.java +++ /dev/null @@ -1,9 +0,0 @@ -package nextstep.org.apache.coyote.http11.servlet; - -import nextstep.org.apache.coyote.http11.Http11Request; -import nextstep.org.apache.coyote.http11.Http11Response; - -public interface Servlet { - - void service(Http11Request request, Http11Response response) throws Exception; -} diff --git a/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java b/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java index 74d2490d47..aaa800f0c5 100644 --- a/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java +++ b/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java @@ -11,6 +11,8 @@ import java.util.List; import java.util.Map; import nextstep.jwp.model.User; +import nextstep.org.apache.catalina.cookie.Cookies; +import nextstep.org.apache.catalina.session.SessionManager; import org.junit.jupiter.api.Test; import support.StubSocket; From 84bb261d9ca842c7cf2f440de6ad0647033ee078 Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Sun, 10 Sep 2023 23:29:40 +0900 Subject: [PATCH 13/24] =?UTF-8?q?test:=20thread=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=ED=95=99=EC=8A=B5=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=A7=84?= =?UTF-8?q?=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- study/src/main/resources/application.yml | 4 ++-- study/src/test/java/thread/stage0/SynchronizationTest.java | 2 +- study/src/test/java/thread/stage0/ThreadPoolsTest.java | 6 +++--- study/src/test/java/thread/stage1/ConcurrencyTest.java | 4 ++-- study/src/test/java/thread/stage1/UserServlet.java | 2 +- study/src/test/java/thread/stage2/AppTest.java | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/study/src/main/resources/application.yml b/study/src/main/resources/application.yml index 385c11d5f1..708e368729 100644 --- a/study/src/main/resources/application.yml +++ b/study/src/main/resources/application.yml @@ -4,9 +4,9 @@ handlebars: server: tomcat: accept-count: 1 - max-connections: 1 + max-connections: 10 threads: - max: 2 + max: 10 compression: enabled: true min-response-size: 10 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/ConcurrencyTest.java b/study/src/test/java/thread/stage1/ConcurrencyTest.java index f5e8ee070a..bc721f2db9 100644 --- a/study/src/test/java/thread/stage1/ConcurrencyTest.java +++ b/study/src/test/java/thread/stage1/ConcurrencyTest.java @@ -1,9 +1,9 @@ package thread.stage1; -import org.junit.jupiter.api.Test; - import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; + /** * 스레드를 다룰 때 어떤 상황을 조심해야 할까? * - 상태를 가진 한 객체를 여러 스레드에서 동시에 접근할 경우 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); } diff --git a/study/src/test/java/thread/stage2/AppTest.java b/study/src/test/java/thread/stage2/AppTest.java index e253c4a249..9e85357ac0 100644 --- a/study/src/test/java/thread/stage2/AppTest.java +++ b/study/src/test/java/thread/stage2/AppTest.java @@ -39,7 +39,7 @@ void test() throws Exception { thread.join(); } - assertThat(count.intValue()).isEqualTo(2); + assertThat(count.intValue()).isEqualTo(10); } private static void incrementIfOk(final HttpResponse response) { From 7f81abca073e5d8df258a1b9cdef438dcb91ef3e Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Mon, 11 Sep 2023 00:36:01 +0900 Subject: [PATCH 14/24] =?UTF-8?q?feat:=20tomcat=20connector=20=EC=93=B0?= =?UTF-8?q?=EB=A0=88=EB=93=9C=20=ED=92=80=20=EC=84=A4=EC=A0=95=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/catalina/connector/Connector.java | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java b/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java index 6df168a492..52ddc1d43c 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java @@ -1,13 +1,15 @@ package nextstep.org.apache.catalina.connector; -import nextstep.org.apache.coyote.http11.Http11Processor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.io.UncheckedIOException; import java.net.ServerSocket; import java.net.Socket; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import nextstep.org.apache.coyote.http11.Http11Processor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Connector implements Runnable { @@ -16,14 +18,20 @@ public class Connector implements Runnable { private static final int DEFAULT_PORT = 8080; private static final int DEFAULT_ACCEPT_COUNT = 100; + private final ThreadPoolExecutor threadPoolExecutor; private final ServerSocket serverSocket; private boolean stopped; public Connector() { - this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT); + this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, 250); } - public Connector(final int port, final int acceptCount) { + public Connector(final int port, final int acceptCount, final int maxThreads) { + this.threadPoolExecutor = new ThreadPoolExecutor( + maxThreads, maxThreads, + 1, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(100) + ); this.serverSocket = createServerSocket(port, acceptCount); this.stopped = false; } @@ -67,7 +75,7 @@ private void process(final Socket connection) { return; } var processor = new Http11Processor(connection); - new Thread(processor).start(); + this.threadPoolExecutor.submit(processor); } public void stop() { From 7d922b632acd181c046786a0c6003e07418d2a97 Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Mon, 11 Sep 2023 00:40:35 +0900 Subject: [PATCH 15/24] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20ses?= =?UTF-8?q?sion=20=EB=8F=99=EC=8B=9C=EC=84=B1=20=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=EB=A5=BC=20=EC=9C=84=ED=95=9C=20ConcurrentHashMap=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nextstep/org/apache/catalina/session/SessionManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/session/SessionManager.java b/tomcat/src/main/java/nextstep/org/apache/catalina/session/SessionManager.java index 580f4d9ef5..1ac8dab6f9 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/session/SessionManager.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/session/SessionManager.java @@ -1,12 +1,12 @@ package nextstep.org.apache.catalina.session; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import nextstep.org.apache.catalina.Manager; public class SessionManager implements Manager { - private static final Map SESSIONS = new HashMap<>(); + private static final Map SESSIONS = new ConcurrentHashMap<>(); @Override public void add(Session session) { From 77de1e61ddfa3a67a491a4379349a343edc18a46 Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Mon, 11 Sep 2023 01:41:48 +0900 Subject: [PATCH 16/24] =?UTF-8?q?feat:=20HttpHeader=20enum=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/apache/catalina/cookie/Cookies.java | 7 +++-- .../catalina/servlet/AbstractServlet.java | 13 ++++---- .../apache/catalina/servlet/LoginServlet.java | 7 +++-- .../catalina/servlet/RegisterServlet.java | 3 +- .../apache/coyote/http11/Http11Processor.java | 6 ++-- .../org/apache/coyote/http11/HttpHeader.java | 31 +++++++++++++++++++ .../coyote/http11/request/Http11Request.java | 24 +++++++++----- .../http11/response/Http11Response.java | 9 +++--- .../coyote/http11/Http11ProcessorTest.java | 2 -- 9 files changed, 73 insertions(+), 29 deletions(-) create mode 100644 tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpHeader.java diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/cookie/Cookies.java b/tomcat/src/main/java/nextstep/org/apache/catalina/cookie/Cookies.java index a7663dd45c..314c63f2f9 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/cookie/Cookies.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/cookie/Cookies.java @@ -5,10 +5,11 @@ import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; +import nextstep.org.apache.coyote.http11.HttpHeader; public class Cookies { - private static final String SET_COOKIE_HEADER = "Set-Cookie: %s \r\n"; + private static final String SET_COOKIE_HEADER_FORMAT = "%s: %s \r\n"; private static final String COOKIE_VALUES_DELIMITER = "; "; private static final String COOKIE_KEY_VALUE_DELIMITER = "="; @@ -38,7 +39,9 @@ public boolean hasCookie(String key) { public String createSetCookieHeader() { return cookie.entrySet().stream() .map(entry -> entry.getKey() + COOKIE_KEY_VALUE_DELIMITER + entry.getValue()) - .map(value -> String.format(SET_COOKIE_HEADER, value)) + .map(value -> String.format( + SET_COOKIE_HEADER_FORMAT, HttpHeader.SET_COOKIE.getValue(), value) + ) .collect(Collectors.joining()); } diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/AbstractServlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/AbstractServlet.java index 755bba4b0a..8897651203 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/AbstractServlet.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/AbstractServlet.java @@ -9,6 +9,7 @@ import java.nio.file.Files; import java.util.Objects; import java.util.Optional; +import nextstep.org.apache.coyote.http11.HttpHeader; import nextstep.org.apache.coyote.http11.request.Http11Request; import nextstep.org.apache.coyote.http11.response.Http11Response; import nextstep.org.apache.coyote.http11.Status; @@ -54,7 +55,7 @@ protected Optional createResponseBody(String requestPath) throws IOExcep protected void responseWithBody(Http11Request request, Http11Response response) throws IOException { Optional responseBody = createResponseBody(request.getPathInfo()); - String contentType = selectFirstContentTypeOrDefault(request.getHeader("Accept")); + String contentType = selectFirstContentTypeOrDefault(request.getHeader(HttpHeader.ACCEPT)); if (responseBody.isEmpty()) { responseWithNotFound(request, response); @@ -62,8 +63,8 @@ protected void responseWithBody(Http11Request request, Http11Response response) } response.setStatus(Status.OK) - .setHeader("Content-Type", contentType + ";charset=utf-8") - .setHeader("Content-Length", String.valueOf( + .setHeader(HttpHeader.CONTENT_TYPE, contentType + ";charset=utf-8") + .setHeader(HttpHeader.CONTENT_LENGTH, String.valueOf( responseBody.get().getBytes(StandardCharsets.UTF_8).length)) .setBody(responseBody.get()); } @@ -71,11 +72,11 @@ protected void responseWithBody(Http11Request request, Http11Response response) private void responseWithNotFound(Http11Request request, Http11Response response) throws IOException { String notFoundPageBody = createResponseBody("/404.html") .orElse(NOT_FOUND_DEFAULT_MESSAGE); - String contentType = selectFirstContentTypeOrDefault(request.getHeader("Accept")); + String contentType = selectFirstContentTypeOrDefault(request.getHeader(HttpHeader.ACCEPT)); response.setStatus(Status.NOT_FOUND) - .setHeader("Content-Type", contentType + ";charset=utf-8") - .setHeader("Content-Length", String.valueOf( + .setHeader(HttpHeader.CONTENT_TYPE, contentType + ";charset=utf-8") + .setHeader(HttpHeader.CONTENT_LENGTH, String.valueOf( notFoundPageBody.getBytes(StandardCharsets.UTF_8).length)) .setBody(notFoundPageBody); } diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java index 886abc9e16..5624e76988 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java @@ -6,6 +6,7 @@ import nextstep.jwp.exception.InvalidLoginInfoException; import nextstep.jwp.model.User; import nextstep.org.apache.catalina.cookie.Cookies; +import nextstep.org.apache.coyote.http11.HttpHeader; import nextstep.org.apache.coyote.http11.request.Http11Request; import nextstep.org.apache.coyote.http11.response.Http11Response; import nextstep.org.apache.catalina.session.Session; @@ -29,11 +30,11 @@ protected void doPost(Http11Request request, Http11Response response) { cookies.set("JSESSIONID", loginSession.getId()); response.setStatus(Status.FOUND) - .setHeader("Location", "/index.html") + .setHeader(HttpHeader.LOCATION, "/index.html") .setCookies(cookies); } catch (NoSuchElementException | InvalidLoginInfoException | NullPointerException e) { response.setStatus(Status.FOUND) - .setHeader("Location", "/401.html") + .setHeader(HttpHeader.LOCATION, "/401.html") .setCookies(cookies); } } @@ -62,7 +63,7 @@ protected void doGet(Http11Request request, Http11Response response) throws Exce Cookies cookies = request.getCookies(); if (cookies.hasCookie("JSESSIONID")) { response.setStatus(Status.FOUND) - .setHeader("Location", "/index.html"); + .setHeader(HttpHeader.LOCATION, "/index.html"); return; } responseWithBody(request, response); diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/RegisterServlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/RegisterServlet.java index 3a17c8f092..432e5cb544 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/RegisterServlet.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/RegisterServlet.java @@ -2,6 +2,7 @@ import nextstep.jwp.db.InMemoryUserRepository; import nextstep.jwp.model.User; +import nextstep.org.apache.coyote.http11.HttpHeader; import nextstep.org.apache.coyote.http11.request.Http11Request; import nextstep.org.apache.coyote.http11.response.Http11Response; import nextstep.org.apache.coyote.http11.Status; @@ -20,7 +21,7 @@ protected void doPost(Http11Request request, Http11Response response) { ); response.setStatus(Status.FOUND) - .setHeader("Location", "/index.html"); + .setHeader(HttpHeader.LOCATION, "/index.html"); } @Override diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java index 6bd2887144..bbe3db571a 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java @@ -60,10 +60,10 @@ private void responseInternalServerError(Http11Request request, Http11Response r throws IOException { String internalServerErrorPageBody = createResponseBody("/500.html") .orElse(Status.INTERNAL_SERVER_ERROR.getCodeMessage()); - String contentType = selectFirstContentTypeOrDefault(request.getHeader("Accept")); + String contentType = selectFirstContentTypeOrDefault(request.getHeader(HttpHeader.ACCEPT)); response.setStatus(Status.INTERNAL_SERVER_ERROR) - .setHeader("Content-Type", contentType + ";charset=utf-8") - .setHeader("Content-Length", String.valueOf( + .setHeader(HttpHeader.CONTENT_TYPE, contentType + ";charset=utf-8") + .setHeader(HttpHeader.CONTENT_LENGTH, String.valueOf( internalServerErrorPageBody.getBytes( StandardCharsets.UTF_8).length)) .setBody(internalServerErrorPageBody); diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpHeader.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpHeader.java new file mode 100644 index 0000000000..062d282743 --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpHeader.java @@ -0,0 +1,31 @@ +package nextstep.org.apache.coyote.http11; + +import java.util.Arrays; + +public enum HttpHeader { + ACCEPT("Accept"), + COOKIE("Cookie"), + CONTENT_LENGTH("Content-Length"), + CONTENT_TYPE("Content-Type"), + LOCATION("Location"), + SET_COOKIE("Set-Cookie"); + + + + private final String value; + + HttpHeader(String value) { + this.value = value; + } + + public static HttpHeader getHttpHeader(String value) { + return Arrays.stream(HttpHeader.values()) + .filter(header -> header.value.equals(value)) + .findFirst() + .orElseThrow(IllegalArgumentException::new); + } + + public String getValue() { + return value; + } +} diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/Http11Request.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/Http11Request.java index b27f99f643..4a3f753207 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/Http11Request.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/Http11Request.java @@ -9,6 +9,7 @@ import java.util.HashMap; import java.util.Map; import nextstep.org.apache.catalina.cookie.Cookies; +import nextstep.org.apache.coyote.http11.HttpHeader; public class Http11Request { @@ -20,7 +21,7 @@ public class Http11Request { private StartLine startLine; private Map queryParams = new HashMap<>(); - private Map headers = new HashMap<>(); + private Map headers = new HashMap<>(); private final Cookies cookies = new Cookies(); private Map parsedBody = null; @@ -34,10 +35,10 @@ public Http11Request(InputStream inputStream) throws IOException { parseMultipleValues(queryParams, startLine.getQueryString(), "&", "="); } - if (headers.containsKey("Cookie")) { - cookies.parseCookieHeaders(headers.get("Cookie")); + if (headers.containsKey(HttpHeader.COOKIE)) { + cookies.parseCookieHeaders(headers.get(HttpHeader.COOKIE)); } - if (headers.containsKey("Content-Length")) { + if (headers.containsKey(HttpHeader.CONTENT_LENGTH)) { parseBody(bufferedReader); } } @@ -45,8 +46,15 @@ public Http11Request(InputStream inputStream) throws IOException { private void extractHeaders(BufferedReader bufferedReader) throws IOException { String line; while (!EMPTY_LINE.equals(line = bufferedReader.readLine())) { + extractHeader(line); + } + } + + private void extractHeader(String line) { + try { String[] splited = line.split(": "); - headers.put(splited[KEY_INDEX], splited[VALUE_INDEX].strip()); + headers.put(HttpHeader.getHttpHeader(splited[KEY_INDEX]), splited[VALUE_INDEX].strip()); + } catch (IllegalArgumentException ignored) { } } @@ -58,7 +66,7 @@ private void parseBody(BufferedReader bufferedReader) throws IOException{ } private String extractRequestBody(BufferedReader bufferedReader) throws IOException { - int contentLength = Integer.parseInt(headers.get("Content-Length")); + int contentLength = Integer.parseInt(headers.get(HttpHeader.CONTENT_LENGTH)); char[] buffer = new char[contentLength]; bufferedReader.read(buffer, 0, contentLength); return new String(buffer); @@ -72,8 +80,8 @@ public String getPathInfo() { return startLine.getPath(); } - public String getHeader(String name) { - return headers.get(name); + public String getHeader(HttpHeader httpHeader) { + return headers.get(httpHeader); } public Cookies getCookies() { diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/response/Http11Response.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/response/Http11Response.java index 5469247846..aed87d6404 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/response/Http11Response.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/response/Http11Response.java @@ -5,6 +5,7 @@ import java.util.Objects; import java.util.stream.Collectors; import nextstep.org.apache.catalina.cookie.Cookies; +import nextstep.org.apache.coyote.http11.HttpHeader; import nextstep.org.apache.coyote.http11.Status; public class Http11Response { @@ -15,7 +16,7 @@ public class Http11Response { private static final String LINEBREAK_DELIMITER = "\r\n"; private StatusLine statusLine; - private Map headers = new LinkedHashMap<>(); + private Map headers = new LinkedHashMap<>(); private Cookies cookies = null; private String body = null; @@ -41,7 +42,7 @@ private String createResponse() { private String createHeadersResponse() { String headersResponse = this.headers.entrySet().stream() - .map(entry -> String.format(HEADER_FORMAT, entry.getKey(), entry.getValue())) + .map(entry -> String.format(HEADER_FORMAT, entry.getKey().getValue(), entry.getValue())) .collect(Collectors.joining(LINEBREAK_DELIMITER)); if (Objects.nonNull(cookies) && !cookies.isEmpty()) { headersResponse += LINEBREAK_DELIMITER + cookies.createSetCookieHeader(); @@ -54,8 +55,8 @@ public Http11Response setStatus(Status status) { return this; } - public Http11Response setHeader(String key, String value) { - this.headers.put(key, value); + public Http11Response setHeader(HttpHeader httpHeader, String value) { + this.headers.put(httpHeader, value); return this; } diff --git a/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java b/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java index aaa800f0c5..e25a8e999d 100644 --- a/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java +++ b/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java @@ -175,8 +175,6 @@ void loginSuccessTest() { "Location: /index.html \r\n" + "Set-Cookie: "; - System.out.println(socket.output()); - assertThat(socket.output()).contains(expected); } From 5016190ee8a6e02d153b3c853935e065c3686e67 Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Tue, 12 Sep 2023 09:12:41 +0900 Subject: [PATCH 17/24] =?UTF-8?q?feat:=20Connector=20=EC=83=81=EC=88=98=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/org/apache/catalina/connector/Connector.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java b/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java index 52ddc1d43c..20f32ee219 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java @@ -17,13 +17,14 @@ public class Connector implements Runnable { private static final int DEFAULT_PORT = 8080; private static final int DEFAULT_ACCEPT_COUNT = 100; + private static final int MAX_THREADS_COUNT = 250; private final ThreadPoolExecutor threadPoolExecutor; private final ServerSocket serverSocket; private boolean stopped; public Connector() { - this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, 250); + this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, MAX_THREADS_COUNT); } public Connector(final int port, final int acceptCount, final int maxThreads) { From 055e620dd7c6d6ac045ed680632f845129ee73ba Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Tue, 12 Sep 2023 09:26:52 +0900 Subject: [PATCH 18/24] =?UTF-8?q?feat:=20Connector=20=EC=93=B0=EB=A0=88?= =?UTF-8?q?=EB=93=9C=20=ED=92=80=20=EC=9E=AC=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/apache/catalina/connector/Connector.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java b/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java index 20f32ee219..c19f4bf665 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java @@ -4,9 +4,8 @@ import java.io.UncheckedIOException; import java.net.ServerSocket; import java.net.Socket; -import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; import nextstep.org.apache.coyote.http11.Http11Processor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,11 +27,7 @@ public Connector() { } public Connector(final int port, final int acceptCount, final int maxThreads) { - this.threadPoolExecutor = new ThreadPoolExecutor( - maxThreads, maxThreads, - 1, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue<>(100) - ); + this.threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(maxThreads); this.serverSocket = createServerSocket(port, acceptCount); this.stopped = false; } From 881b66d24b73f243f2a24f37f5c82bb6c286b79a Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Tue, 12 Sep 2023 09:28:37 +0900 Subject: [PATCH 19/24] =?UTF-8?q?refactor:=20Servlet=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=88=9C=EC=84=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../catalina/servlet/AbstractServlet.java | 4 ++-- .../catalina/servlet/DefaultServlet.java | 8 +++---- .../apache/catalina/servlet/LoginServlet.java | 24 +++++++++---------- .../catalina/servlet/RegisterServlet.java | 10 ++++---- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/AbstractServlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/AbstractServlet.java index 8897651203..3c157decaa 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/AbstractServlet.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/AbstractServlet.java @@ -32,10 +32,10 @@ public void service(Http11Request request, Http11Response response) throws Excep } } - protected abstract void doPost(Http11Request request, Http11Response response) throws Exception; - protected abstract void doGet(Http11Request request, Http11Response response) throws Exception; + protected abstract void doPost(Http11Request request, Http11Response response) throws Exception; + protected Optional createResponseBody(String requestPath) throws IOException { if (requestPath.equals("/")) { return Optional.of("Hello world!"); diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/DefaultServlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/DefaultServlet.java index 4935718f4a..c874b2149e 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/DefaultServlet.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/DefaultServlet.java @@ -7,12 +7,12 @@ public class DefaultServlet extends AbstractServlet { @Override - protected void doPost(Http11Request request, Http11Response response) { - response.setStatus(Status.NOT_IMPLEMENTED); + protected void doGet(Http11Request request, Http11Response response) throws Exception { + responseWithBody(request, response); } @Override - protected void doGet(Http11Request request, Http11Response response) throws Exception { - responseWithBody(request, response); + protected void doPost(Http11Request request, Http11Response response) { + response.setStatus(Status.NOT_IMPLEMENTED); } } diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java index 5624e76988..e11c17408e 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java @@ -19,6 +19,18 @@ public class LoginServlet extends AbstractServlet { private final Logger log = LoggerFactory.getLogger(LoginServlet.class); + @Override + protected void doGet(Http11Request request, Http11Response response) throws Exception { + // Todo: 헤더에 담긴 sessionId 유효성 검증 + Cookies cookies = request.getCookies(); + if (cookies.hasCookie("JSESSIONID")) { + response.setStatus(Status.FOUND) + .setHeader(HttpHeader.LOCATION, "/index.html"); + return; + } + responseWithBody(request, response); + } + @Override protected void doPost(Http11Request request, Http11Response response) { Cookies cookies = request.getCookies(); @@ -56,16 +68,4 @@ private Session createSession(User user) { sessionManager.add(session); return session; } - - @Override - protected void doGet(Http11Request request, Http11Response response) throws Exception { - // Todo: 헤더에 담긴 sessionId 유효성 검증 - Cookies cookies = request.getCookies(); - if (cookies.hasCookie("JSESSIONID")) { - response.setStatus(Status.FOUND) - .setHeader(HttpHeader.LOCATION, "/index.html"); - return; - } - responseWithBody(request, response); - } } diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/RegisterServlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/RegisterServlet.java index 432e5cb544..064b2b9c20 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/RegisterServlet.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/RegisterServlet.java @@ -9,6 +9,11 @@ public class RegisterServlet extends AbstractServlet{ + @Override + protected void doGet(Http11Request request, Http11Response response) throws Exception { + responseWithBody(request, response); + } + @Override protected void doPost(Http11Request request, Http11Response response) { @@ -23,9 +28,4 @@ protected void doPost(Http11Request request, Http11Response response) { response.setStatus(Status.FOUND) .setHeader(HttpHeader.LOCATION, "/index.html"); } - - @Override - protected void doGet(Http11Request request, Http11Response response) throws Exception { - responseWithBody(request, response); - } } From ad63ded5a58819b3b42de805e7e690dfd3b1820c Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Tue, 12 Sep 2023 09:35:06 +0900 Subject: [PATCH 20/24] =?UTF-8?q?refactor:=20AbstractServlet=20=EC=83=81?= =?UTF-8?q?=EC=88=98=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../catalina/servlet/AbstractServlet.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/AbstractServlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/AbstractServlet.java index 3c157decaa..1eed878187 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/AbstractServlet.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/AbstractServlet.java @@ -18,14 +18,19 @@ public abstract class AbstractServlet implements Servlet{ private static final String RESOURCES_PATH_PREFIX = "static"; protected static final String NOT_FOUND_DEFAULT_MESSAGE = "404 Not Found"; + private static final String CHARSET_UTF_8 = ";charset=utf-8"; + private static final String DEFAULT_EXTENSION = ".html"; + private static final String NOT_FOUND_PAGE = "/404.html"; + private static final String HTTP_GET_METHOD = "GET"; + private static final String HTTP_POST_METHOD = "POST"; @Override public void service(Http11Request request, Http11Response response) throws Exception { String method = request.getMethod(); - if (method.equals("GET")) { + if (method.equals(HTTP_GET_METHOD)) { doGet(request, response); - } else if (method.equals("POST")) { + } else if (method.equals(HTTP_POST_METHOD)) { doPost(request, response); } else { response.setStatus(Status.NOT_IMPLEMENTED); @@ -43,7 +48,7 @@ protected Optional createResponseBody(String requestPath) throws IOExcep String resourceName = RESOURCES_PATH_PREFIX + requestPath; if (!resourceName.contains(".")) { - resourceName += ".html"; + resourceName += DEFAULT_EXTENSION; } URL resource = getClass().getClassLoader().getResource(resourceName); @@ -63,19 +68,19 @@ protected void responseWithBody(Http11Request request, Http11Response response) } response.setStatus(Status.OK) - .setHeader(HttpHeader.CONTENT_TYPE, contentType + ";charset=utf-8") + .setHeader(HttpHeader.CONTENT_TYPE, contentType + CHARSET_UTF_8) .setHeader(HttpHeader.CONTENT_LENGTH, String.valueOf( responseBody.get().getBytes(StandardCharsets.UTF_8).length)) .setBody(responseBody.get()); } private void responseWithNotFound(Http11Request request, Http11Response response) throws IOException { - String notFoundPageBody = createResponseBody("/404.html") + String notFoundPageBody = createResponseBody(NOT_FOUND_PAGE) .orElse(NOT_FOUND_DEFAULT_MESSAGE); String contentType = selectFirstContentTypeOrDefault(request.getHeader(HttpHeader.ACCEPT)); response.setStatus(Status.NOT_FOUND) - .setHeader(HttpHeader.CONTENT_TYPE, contentType + ";charset=utf-8") + .setHeader(HttpHeader.CONTENT_TYPE, contentType + CHARSET_UTF_8) .setHeader(HttpHeader.CONTENT_LENGTH, String.valueOf( notFoundPageBody.getBytes(StandardCharsets.UTF_8).length)) .setBody(notFoundPageBody); From 25a5754153955e78b8347d890bfa7fbf1a7aaaa9 Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Tue, 12 Sep 2023 10:11:56 +0900 Subject: [PATCH 21/24] =?UTF-8?q?refactor:=20LoginServlet=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20static=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 --- .../nextstep/org/apache/catalina/servlet/LoginServlet.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java index e11c17408e..eddb990831 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java @@ -17,11 +17,10 @@ public class LoginServlet extends AbstractServlet { - private final Logger log = LoggerFactory.getLogger(LoginServlet.class); + private static final Logger log = LoggerFactory.getLogger(LoginServlet.class); @Override protected void doGet(Http11Request request, Http11Response response) throws Exception { - // Todo: 헤더에 담긴 sessionId 유효성 검증 Cookies cookies = request.getCookies(); if (cookies.hasCookie("JSESSIONID")) { response.setStatus(Status.FOUND) From 8c99b09caf4b35c24942ac6511aba4ae671eb275 Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Tue, 12 Sep 2023 10:18:07 +0900 Subject: [PATCH 22/24] =?UTF-8?q?refactor:=20LoginServlet=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EB=82=B4=20SessionManager=20=EC=A0=84=EC=97=AD?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EA=B4=80=EB=A6=AC=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/org/apache/catalina/servlet/LoginServlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java index eddb990831..7f39612fa9 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java @@ -18,6 +18,7 @@ public class LoginServlet extends AbstractServlet { private static final Logger log = LoggerFactory.getLogger(LoginServlet.class); + private static final SessionManager sessionManager = new SessionManager(); @Override protected void doGet(Http11Request request, Http11Response response) throws Exception { @@ -61,7 +62,6 @@ private Session login(String account, String password) { } private Session createSession(User user) { - SessionManager sessionManager = new SessionManager(); Session session = new Session(UUID.randomUUID().toString()); session.setAttribute(user.getAccount(), user); sessionManager.add(session); From 681e7fbb1cc7b8bfdf4183064e2f1c1cb00445a6 Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Tue, 12 Sep 2023 20:21:09 +0900 Subject: [PATCH 23/24] =?UTF-8?q?refactor:=20context=EB=A5=BC=20Tomcat=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=EC=97=90=EC=84=9C=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=ED=9B=84=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A3=BC=EC=9E=85?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=B0=A9=ED=96=A5=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/catalina/connector/Connector.java | 11 ++++-- .../org/apache/catalina/startup/Tomcat.java | 5 ++- .../apache/coyote/http11/Http11Processor.java | 7 ++-- .../coyote/http11/Http11ProcessorTest.java | 37 +++++++++++++------ 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java b/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java index c19f4bf665..9d89f9979e 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java @@ -6,6 +6,7 @@ import java.net.Socket; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; +import nextstep.org.apache.catalina.Context; import nextstep.org.apache.coyote.http11.Http11Processor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,15 +19,17 @@ public class Connector implements Runnable { private static final int DEFAULT_ACCEPT_COUNT = 100; private static final int MAX_THREADS_COUNT = 250; + private final Context context; private final ThreadPoolExecutor threadPoolExecutor; private final ServerSocket serverSocket; private boolean stopped; - public Connector() { - this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, MAX_THREADS_COUNT); + public Connector(Context context) { + this(context, DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, MAX_THREADS_COUNT); } - public Connector(final int port, final int acceptCount, final int maxThreads) { + public Connector(Context context, final int port, final int acceptCount, final int maxThreads) { + this.context = context; this.threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(maxThreads); this.serverSocket = createServerSocket(port, acceptCount); this.stopped = false; @@ -70,7 +73,7 @@ private void process(final Socket connection) { if (connection == null) { return; } - var processor = new Http11Processor(connection); + var processor = new Http11Processor(context, connection); this.threadPoolExecutor.submit(processor); } diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/startup/Tomcat.java b/tomcat/src/main/java/nextstep/org/apache/catalina/startup/Tomcat.java index c56485fd2b..1ccedd83c1 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/startup/Tomcat.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/startup/Tomcat.java @@ -1,5 +1,6 @@ package nextstep.org.apache.catalina.startup; +import nextstep.org.apache.catalina.Context; import nextstep.org.apache.catalina.connector.Connector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,8 +11,10 @@ public class Tomcat { private static final Logger log = LoggerFactory.getLogger(Tomcat.class); + private final Context context = new Context(); + public void start() { - var connector = new Connector(); + var connector = new Connector(context); connector.start(); try { diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java index bbe3db571a..07a009773a 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java @@ -21,9 +21,11 @@ public class Http11Processor implements Runnable, Processor { private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); + private final Context context; private final Socket connection; - public Http11Processor(final Socket connection) { + public Http11Processor(final Context context, final Socket connection) { + this.context = context; this.connection = connection; } @@ -42,8 +44,7 @@ public void process(final Socket connection) { Http11Request request = new Http11Request(inputStream); Http11Response response = new Http11Response(Status.OK); - Context servletContainer = new Context(); - Servlet servlet = servletContainer.getServlet(request.getPathInfo()); + Servlet servlet = context.getServlet(request.getPathInfo()); try { servlet.service(request, response); } catch (Exception e) { diff --git a/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java b/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java index e25a8e999d..75226c6600 100644 --- a/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java +++ b/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Map; import nextstep.jwp.model.User; +import nextstep.org.apache.catalina.Context; import nextstep.org.apache.catalina.cookie.Cookies; import nextstep.org.apache.catalina.session.SessionManager; import org.junit.jupiter.api.Test; @@ -22,7 +23,8 @@ class Http11ProcessorTest { void process() { // given final var socket = new StubSocket(); - final var processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -49,7 +51,8 @@ void index() throws IOException { ""); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -79,7 +82,8 @@ void css() throws IOException { ""); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -107,7 +111,8 @@ void login() throws IOException { ""); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -135,7 +140,8 @@ void register() throws IOException { ""); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -165,7 +171,8 @@ void loginSuccessTest() { requestBody); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -191,7 +198,8 @@ void loginFailTest() { requestBody); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -219,7 +227,8 @@ void registerSuccessTest() { requestBody); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -245,7 +254,8 @@ void sessionTest() { requestBody); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -286,7 +296,8 @@ void loginRedirectionWithSessionTest() { // when final var socket = new StubSocket(httpGetRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); processor.process(socket); @@ -309,7 +320,8 @@ void wrongPathReturnNotFoundPageTest() throws IOException { ""); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -337,7 +349,8 @@ private String loginAndGetSessionId() { requestBody); var preSocket = new StubSocket(httpRequest); - Http11Processor preProcessor = new Http11Processor(preSocket); + final var context = new Context(); + final var preProcessor = new Http11Processor(context, preSocket); preProcessor.process(preSocket); String preOutput = preSocket.output(); From af65667e8f5d8edd6e7be249d37ae000ebb6e0e8 Mon Sep 17 00:00:00 2001 From: MoonJeWoong Date: Tue, 12 Sep 2023 20:43:49 +0900 Subject: [PATCH 24/24] =?UTF-8?q?refactor:=20startLine=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EB=82=B4=20queryString=EC=9D=84=20queryParams?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coyote/http11/request/Http11Request.java | 7 +------ .../coyote/http11/request/StartLine.java | 20 ++++++++++--------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/Http11Request.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/Http11Request.java index 4a3f753207..dcd505cf45 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/Http11Request.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/Http11Request.java @@ -20,7 +20,6 @@ public class Http11Request { private static final String FORM_KEY_VALUE_DELIMITER = "="; private StartLine startLine; - private Map queryParams = new HashMap<>(); private Map headers = new HashMap<>(); private final Cookies cookies = new Cookies(); private Map parsedBody = null; @@ -31,10 +30,6 @@ public Http11Request(InputStream inputStream) throws IOException { this.startLine = new StartLine(bufferedReader.readLine()); extractHeaders(bufferedReader); - if (startLine.hasQueryString()) { - parseMultipleValues(queryParams, - startLine.getQueryString(), "&", "="); - } if (headers.containsKey(HttpHeader.COOKIE)) { cookies.parseCookieHeaders(headers.get(HttpHeader.COOKIE)); } @@ -58,7 +53,7 @@ private void extractHeader(String line) { } } - private void parseBody(BufferedReader bufferedReader) throws IOException{ + private void parseBody(BufferedReader bufferedReader) throws IOException { parsedBody = new HashMap<>(); String requestBody = extractRequestBody(bufferedReader); parseMultipleValues(parsedBody, diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/StartLine.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/StartLine.java index 93389c7a87..aec7a88320 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/StartLine.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/StartLine.java @@ -1,8 +1,11 @@ package nextstep.org.apache.coyote.http11.request; +import static nextstep.org.apache.coyote.http11.HttpUtil.parseMultipleValues; + import java.util.Arrays; +import java.util.HashMap; import java.util.List; -import java.util.Objects; +import java.util.Map; public class StartLine { @@ -11,10 +14,12 @@ public class StartLine { private static final int REQUEST_TARGET_INDEX = 1; private static final int HTTP_VERSION_INDEX = 2; private static final String QUERY_PARAM_DELIMITER = "?"; + private static final String VALUES_DELIMITER = "&"; + private static final String KEY_VALUE_DELIMITER = "="; private String httpMethod; private String path; - private String queryString = null; + private Map queryParams = new HashMap<>(); private String httpVersion; public StartLine(String startLine) { @@ -29,14 +34,11 @@ private void parsePath(List parsed) { if (path.contains(QUERY_PARAM_DELIMITER)) { int queryParamIndex = path.indexOf(QUERY_PARAM_DELIMITER); path = parsed.get(REQUEST_TARGET_INDEX).substring(0, queryParamIndex); - queryString = parsed.get(queryParamIndex + 1); + String queryString = parsed.get(queryParamIndex + 1); + parseMultipleValues(queryParams, queryString, VALUES_DELIMITER, KEY_VALUE_DELIMITER); } } - public boolean hasQueryString() { - return Objects.nonNull(queryString); - } - public String getHttpMethod() { return httpMethod; } @@ -45,7 +47,7 @@ public String getPath() { return path; } - public String getQueryString() { - return queryString; + public Map getQueryParams() { + return new HashMap<>(queryParams); } }