From 0570af3031c6bb39425214e6c197349044a316ed Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Wed, 4 Sep 2024 17:24:17 +0900 Subject: [PATCH 01/30] =?UTF-8?q?chore:=20=ED=95=99=EC=8A=B5=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- study/src/test/java/study/FileTest.java | 31 +++++++++++-------- study/src/test/java/study/IOStreamTest.java | 34 ++++++++++++++------- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/study/src/test/java/study/FileTest.java b/study/src/test/java/study/FileTest.java index e1b6cca042..e20b480062 100644 --- a/study/src/test/java/study/FileTest.java +++ b/study/src/test/java/study/FileTest.java @@ -1,13 +1,14 @@ package study; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collections; import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; /** * 웹서버는 사용자가 요청한 html 파일을 제공 할 수 있어야 한다. @@ -28,7 +29,7 @@ class FileTest { final String fileName = "nextstep.txt"; // todo - final String actual = ""; + final String actual = getClass().getClassLoader().getResource(fileName).getFile(); assertThat(actual).endsWith(fileName); } @@ -44,11 +45,17 @@ class FileTest { final String fileName = "nextstep.txt"; // todo - final Path path = null; - - // todo - final List actual = Collections.emptyList(); - - assertThat(actual).containsOnly("nextstep"); + try { + final Path path = Path.of(getClass().getClassLoader().getResource(fileName).toURI()); + // todo + try { + final List actual = Files.newBufferedReader(path).lines().toList(); + assertThat(actual).containsOnly("nextstep"); + } catch (IOException e) { + + } + } catch (URISyntaxException e) { + + } } } diff --git a/study/src/test/java/study/IOStreamTest.java b/study/src/test/java/study/IOStreamTest.java index 47a79356b6..13171b221a 100644 --- a/study/src/test/java/study/IOStreamTest.java +++ b/study/src/test/java/study/IOStreamTest.java @@ -1,14 +1,22 @@ package study; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import java.io.*; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; - /** * 자바는 스트림(Stream)으로부터 I/O를 사용한다. * 입출력(I/O)은 하나의 시스템에서 다른 시스템으로 데이터를 이동 시킬 때 사용한다. @@ -53,6 +61,7 @@ class OutputStream_학습_테스트 { * todo * OutputStream 객체의 write 메서드를 사용해서 테스트를 통과시킨다 */ + outputStream.write(bytes); final String actual = outputStream.toString(); @@ -78,6 +87,7 @@ class OutputStream_학습_테스트 { * flush를 사용해서 테스트를 통과시킨다. * ByteArrayOutputStream과 어떤 차이가 있을까? */ + outputStream.flush(); verify(outputStream, atLeastOnce()).flush(); outputStream.close(); @@ -96,6 +106,7 @@ class OutputStream_학습_테스트 { * try-with-resources를 사용한다. * java 9 이상에서는 변수를 try-with-resources로 처리할 수 있다. */ + outputStream.close(); verify(outputStream, atLeastOnce()).close(); } @@ -128,7 +139,7 @@ class InputStream_학습_테스트 { * todo * inputStream에서 바이트로 반환한 값을 문자열로 어떻게 바꿀까? */ - final String actual = ""; + final String actual = new String(inputStream.readAllBytes()); assertThat(actual).isEqualTo("🤩"); assertThat(inputStream.read()).isEqualTo(-1); @@ -148,6 +159,7 @@ class InputStream_학습_테스트 { * try-with-resources를 사용한다. * java 9 이상에서는 변수를 try-with-resources로 처리할 수 있다. */ + inputStream.close(); verify(inputStream, atLeastOnce()).close(); } @@ -169,12 +181,12 @@ class FilterStream_학습_테스트 { * 버퍼 크기를 지정하지 않으면 버퍼의 기본 사이즈는 얼마일까? */ @Test - void 필터인_BufferedInputStream를_사용해보자() { + void 필터인_BufferedInputStream를_사용해보자() throws IOException { final String text = "필터에 연결해보자."; final InputStream inputStream = new ByteArrayInputStream(text.getBytes()); - final InputStream bufferedInputStream = null; + final InputStream bufferedInputStream = new BufferedInputStream(inputStream); - final byte[] actual = new byte[0]; + final byte[] actual = bufferedInputStream.readAllBytes(); assertThat(bufferedInputStream).isInstanceOf(FilterInputStream.class); assertThat(actual).isEqualTo("필터에 연결해보자.".getBytes()); @@ -197,7 +209,7 @@ class InputStreamReader_학습_테스트 { * 필터인 BufferedReader를 사용하면 readLine 메서드를 사용해서 문자열(String)을 한 줄 씩 읽어올 수 있다. */ @Test - void BufferedReader를_사용하여_문자열을_읽어온다() { + void BufferedReader를_사용하여_문자열을_읽어온다() throws IOException { final String emoji = String.join("\r\n", "😀😃😄😁😆😅😂🤣🥲☺️😊", "😇🙂🙃😉😌😍🥰😘😗😙😚", @@ -205,7 +217,7 @@ class InputStreamReader_학습_테스트 { ""); final InputStream inputStream = new ByteArrayInputStream(emoji.getBytes()); - final StringBuilder actual = new StringBuilder(); + final StringBuilder actual = new StringBuilder(new String(inputStream.readAllBytes())); assertThat(actual).hasToString(emoji); } From 74b6cba1c7f0b976114119680c696f6525a15536 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Wed, 4 Sep 2024 17:24:43 +0900 Subject: [PATCH 02/30] =?UTF-8?q?feat:=201=EB=8B=A8=EA=B3=84=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 85 +++++++++++++++++-- .../coyote/http11/Http11ProcessorTest.java | 9 +- 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index bb14184757..4e4da61303 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -1,13 +1,19 @@ package org.apache.coyote.http11; +import com.techcourse.db.InMemoryUserRepository; import com.techcourse.exception.UncheckedServletException; +import com.techcourse.model.User; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.Socket; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; import org.apache.coyote.Processor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.net.Socket; - public class Http11Processor implements Runnable, Processor { private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); @@ -28,20 +34,89 @@ public void run() { public void process(final Socket connection) { try (final var inputStream = connection.getInputStream(); final var outputStream = connection.getOutputStream()) { + var bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + String line = bufferedReader.readLine(); + System.out.println(line); + if (line == null || line.isEmpty()) { + return; + } + + String[] requestLine = line.split(" "); + if (requestLine.length < 3) { + return; + } + String uri = requestLine[1]; + String path = uri; + String queryString = ""; + if (uri.contains("?")) { + int index = uri.indexOf("?"); + path = uri.substring(0, index); + queryString = uri.substring(index + 1); + } + + var responseBody = ""; + var contentType = "text/html;charset=utf-8 \r\n"; + if (!path.equals("/") && !path.contains(".")) { + path += ".html"; + } + + if (path.equals("/")) { + responseBody = "Hello world!"; + } else { + String fileName = "static" + path; + var resourceUrl = getClass().getClassLoader().getResource(fileName); + if (resourceUrl == null) { + final var response = String.join("\r\n", + "HTTP/1.1 404 Not Found", + "Content-Type: text/html;charset=utf-8 \r\n", + "", + "

404 Not Found

"); + outputStream.write(response.getBytes()); + outputStream.flush(); + return; + } + Path filePath = Path.of(resourceUrl.toURI()); + responseBody = new String(Files.readAllBytes(filePath)); + } - final var responseBody = "Hello world!"; + if (path.endsWith(".css")) { + contentType = "text/css;charset=utf-8 \r\n"; + } else if (path.endsWith(".js")) { + contentType = "application/javascript;charset=utf-8 \r\n"; + } else if (path.endsWith(".html")) { + contentType = "text/html;charset=utf-8 \r\n"; + } else if (path.endsWith(".png")) { + contentType = "image/png \r\n"; + } else if (path.endsWith(".jpg") || path.endsWith(".jpeg")) { + contentType = "image/jpeg \r\n"; + } final var response = String.join("\r\n", "HTTP/1.1 200 OK ", - "Content-Type: text/html;charset=utf-8 ", + "Content-Type: " + contentType + "Content-Length: " + responseBody.getBytes().length + " ", "", responseBody); + if (!queryString.isEmpty()) { + String[] query = queryString.split("&"); + String account = query[0].split("=")[1]; + String password = query[1].split("=")[1]; + System.out.println(account + " " + password); + User user = InMemoryUserRepository.findByAccount(account) + .orElseThrow(); + if (user.checkPassword(password)) { + log.info(user.toString()); + } else { + log.error("비밀번호 불일치"); + } + } outputStream.write(response.getBytes()); outputStream.flush(); } catch (IOException | UncheckedServletException e) { log.error(e.getMessage(), e); + } catch (URISyntaxException e) { + throw new RuntimeException(e); } } } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java b/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java index 2aba8c56e0..9d9071b6f9 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java @@ -1,14 +1,13 @@ package org.apache.coyote.http11; -import org.junit.jupiter.api.Test; -import support.StubSocket; +import static org.assertj.core.api.Assertions.assertThat; import java.io.File; import java.io.IOException; import java.net.URL; import java.nio.file.Files; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; +import support.StubSocket; class Http11ProcessorTest { @@ -52,7 +51,7 @@ void index() throws IOException { final URL resource = getClass().getClassLoader().getResource("static/index.html"); var expected = "HTTP/1.1 200 OK \r\n" + "Content-Type: text/html;charset=utf-8 \r\n" + - "Content-Length: 5564 \r\n" + + "Content-Length: 5670 \r\n" + "\r\n"+ new String(Files.readAllBytes(new File(resource.getFile()).toPath())); From 0327a97abec70c4b9050701d886915f89f3ee90f Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Wed, 4 Sep 2024 19:16:50 +0900 Subject: [PATCH 03/30] =?UTF-8?q?feat:=20login=20=EC=84=B1=EA=B3=B5=20?= =?UTF-8?q?=EC=8B=9C=20index.html=EB=A1=9C=20redirect=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/apache/coyote/http11/Http11Processor.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 4e4da61303..b083228547 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -91,7 +91,7 @@ public void process(final Socket connection) { contentType = "image/jpeg \r\n"; } - final var response = String.join("\r\n", + var response = String.join("\r\n", "HTTP/1.1 200 OK ", "Content-Type: " + contentType + "Content-Length: " + responseBody.getBytes().length + " ", @@ -105,8 +105,14 @@ public void process(final Socket connection) { User user = InMemoryUserRepository.findByAccount(account) .orElseThrow(); if (user.checkPassword(password)) { + response = String.join("\r\n", + "HTTP/1.1 302 Found ", + "Location: " + "/index.html"); log.info(user.toString()); } else { + response = String.join("\r\n", + "HTTP/1.1 302 Found ", + "Location: " + "/401.html"); log.error("비밀번호 불일치"); } } From 2f07450bd2bd353dc6635b46055d6844ac369505 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Wed, 4 Sep 2024 20:44:05 +0900 Subject: [PATCH 04/30] =?UTF-8?q?feat:=20login=20=EC=84=B1=EA=B3=B5=20?= =?UTF-8?q?=EC=8B=9C=20=EC=BF=A0=ED=82=A4=20=EC=84=A4=EC=A0=95=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 41 +++++++++++++++---- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index b083228547..a0ebb3a220 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -10,6 +10,7 @@ import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.UUID; import org.apache.coyote.Processor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,19 +36,37 @@ public void process(final Socket connection) { try (final var inputStream = connection.getInputStream(); final var outputStream = connection.getOutputStream()) { var bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); - String line = bufferedReader.readLine(); - System.out.println(line); - if (line == null || line.isEmpty()) { - return; + + var requestBuilder = new StringBuilder(); + String line; + + while ((line = bufferedReader.readLine()) != null && !line.isEmpty()) { + requestBuilder.append(line + System.lineSeparator()); } +// System.out.println(requestBuilder); + line = requestBuilder.toString(); - String[] requestLine = line.split(" "); + String[] requestLine = line.split(System.lineSeparator())[0].split(" "); if (requestLine.length < 3) { return; } + String method = requestLine[0]; String uri = requestLine[1]; String path = uri; String queryString = ""; + if (method.equals("POST") && path.equals("/register")) { + String contentLengthS = line.split(System.lineSeparator())[3].split(": ")[1]; + int contentLength = Integer.parseInt(contentLengthS); + char[] buffer = new char[contentLength]; + bufferedReader.read(buffer, 0, contentLength); + String requestBody = new String(buffer); + String[] token = requestBody.split("&"); + String account = token[0].split("=")[1]; + String email = token[1].split("=")[1]; + String password = token[2].split("=")[1]; + User user = new User(account, password, email); + InMemoryUserRepository.save(user); + } if (uri.contains("?")) { int index = uri.indexOf("?"); path = uri.substring(0, index); @@ -56,13 +75,13 @@ public void process(final Socket connection) { var responseBody = ""; var contentType = "text/html;charset=utf-8 \r\n"; - if (!path.equals("/") && !path.contains(".")) { + if (!path.equals("/") && !path.contains(".") && method.equals("GET")) { path += ".html"; } if (path.equals("/")) { responseBody = "Hello world!"; - } else { + } else if (method.equals("GET")) { String fileName = "static" + path; var resourceUrl = getClass().getClassLoader().getResource(fileName); if (resourceUrl == null) { @@ -101,12 +120,13 @@ public void process(final Socket connection) { String[] query = queryString.split("&"); String account = query[0].split("=")[1]; String password = query[1].split("=")[1]; - System.out.println(account + " " + password); User user = InMemoryUserRepository.findByAccount(account) .orElseThrow(); + UUID uuid = UUID.randomUUID(); if (user.checkPassword(password)) { response = String.join("\r\n", "HTTP/1.1 302 Found ", + "Set-Cookie: JSESSIONID=" + uuid, "Location: " + "/index.html"); log.info(user.toString()); } else { @@ -116,6 +136,11 @@ public void process(final Socket connection) { log.error("비밀번호 불일치"); } } + if (method.equals("POST") && path.equals("/register")) { + response = String.join("\r\n", + "HTTP/1.1 302 Found ", + "Location: " + "/index.html"); + } outputStream.write(response.getBytes()); outputStream.flush(); From a16adfac68bf78f58778494b64e1390a16d5a2fe Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 5 Sep 2024 10:03:43 +0900 Subject: [PATCH 05/30] =?UTF-8?q?feat:=20=EC=BF=A0=ED=82=A4=EA=B0=80=20?= =?UTF-8?q?=EC=9E=88=EC=9D=84=20=EC=8B=9C=20=EC=9E=90=EB=8F=99=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 87 ++++++++++--------- .../org/apache/coyote/http11/HttpRequest.java | 33 +++++++ .../apache/coyote/http11/HttpRequestBody.java | 30 +++++++ .../coyote/http11/HttpRequestHeader.java | 68 +++++++++++++++ .../apache/coyote/http11/HttpResponse.java | 4 + .../org/apache/coyote/http11/Session.java | 36 ++++++++ tomcat/src/main/resources/static/login.html | 2 +- 7 files changed, 216 insertions(+), 44 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/Session.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index a0ebb3a220..d2b5008cf8 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -20,9 +20,11 @@ public class Http11Processor implements Runnable, Processor { private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); private final Socket connection; + private final Session session; public Http11Processor(final Socket connection) { this.connection = connection; + this.session = Session.getInstance(); } @Override @@ -37,29 +39,11 @@ public void process(final Socket connection) { final var outputStream = connection.getOutputStream()) { var bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); - var requestBuilder = new StringBuilder(); - String line; + HttpRequest httpRequest = new HttpRequest(bufferedReader); + System.out.println(httpRequest); - while ((line = bufferedReader.readLine()) != null && !line.isEmpty()) { - requestBuilder.append(line + System.lineSeparator()); - } -// System.out.println(requestBuilder); - line = requestBuilder.toString(); - - String[] requestLine = line.split(System.lineSeparator())[0].split(" "); - if (requestLine.length < 3) { - return; - } - String method = requestLine[0]; - String uri = requestLine[1]; - String path = uri; - String queryString = ""; - if (method.equals("POST") && path.equals("/register")) { - String contentLengthS = line.split(System.lineSeparator())[3].split(": ")[1]; - int contentLength = Integer.parseInt(contentLengthS); - char[] buffer = new char[contentLength]; - bufferedReader.read(buffer, 0, contentLength); - String requestBody = new String(buffer); + if (httpRequest.getHttpRequestHeader().getMethod().equals("POST") && httpRequest.getHttpRequestHeader().getPath().equals("/register")) { + String requestBody = httpRequest.getHttpRequestBody().getBody(); String[] token = requestBody.split("&"); String account = token[0].split("=")[1]; String email = token[1].split("=")[1]; @@ -67,22 +51,17 @@ public void process(final Socket connection) { User user = new User(account, password, email); InMemoryUserRepository.save(user); } - if (uri.contains("?")) { - int index = uri.indexOf("?"); - path = uri.substring(0, index); - queryString = uri.substring(index + 1); - } var responseBody = ""; var contentType = "text/html;charset=utf-8 \r\n"; - if (!path.equals("/") && !path.contains(".") && method.equals("GET")) { - path += ".html"; + if (!httpRequest.getHttpRequestHeader().getPath().equals("/") && !httpRequest.getHttpRequestHeader().getPath().contains(".") && httpRequest.getHttpRequestHeader().getMethod().equals("GET")) { + httpRequest.getHttpRequestHeader().setDefaultPath(); } - if (path.equals("/")) { + if (httpRequest.getHttpRequestHeader().getPath().equals("/")) { responseBody = "Hello world!"; - } else if (method.equals("GET")) { - String fileName = "static" + path; + } else if (httpRequest.getHttpRequestHeader().getMethod().equals("GET")) { + String fileName = "static" + httpRequest.getHttpRequestHeader().getPath(); var resourceUrl = getClass().getClassLoader().getResource(fileName); if (resourceUrl == null) { final var response = String.join("\r\n", @@ -98,32 +77,35 @@ public void process(final Socket connection) { responseBody = new String(Files.readAllBytes(filePath)); } - if (path.endsWith(".css")) { + if (httpRequest.getHttpRequestHeader().getPath().endsWith(".css")) { contentType = "text/css;charset=utf-8 \r\n"; - } else if (path.endsWith(".js")) { + } else if (httpRequest.getHttpRequestHeader().getPath().endsWith(".js")) { contentType = "application/javascript;charset=utf-8 \r\n"; - } else if (path.endsWith(".html")) { + } else if (httpRequest.getHttpRequestHeader().getPath().endsWith(".html")) { contentType = "text/html;charset=utf-8 \r\n"; - } else if (path.endsWith(".png")) { + } else if (httpRequest.getHttpRequestHeader().getPath().endsWith(".png")) { contentType = "image/png \r\n"; - } else if (path.endsWith(".jpg") || path.endsWith(".jpeg")) { + } else if (httpRequest.getHttpRequestHeader().getPath().endsWith(".jpg") || httpRequest.getHttpRequestHeader().getPath().endsWith(".jpeg")) { contentType = "image/jpeg \r\n"; } var response = String.join("\r\n", "HTTP/1.1 200 OK ", "Content-Type: " + contentType + - "Content-Length: " + responseBody.getBytes().length + " ", + "Content-Length: " + responseBody.getBytes().length + " ", "", responseBody); - if (!queryString.isEmpty()) { - String[] query = queryString.split("&"); - String account = query[0].split("=")[1]; - String password = query[1].split("=")[1]; + if (httpRequest.getHttpRequestHeader().getMethod().equals("POST") && httpRequest.getHttpRequestHeader().getPath().equals("/login")) { + String requestBody = httpRequest.getHttpRequestBody().getBody(); + String[] token = requestBody.split("&"); + String account = token[0].split("=")[1]; + String password = token[1].split("=")[1]; + User user = InMemoryUserRepository.findByAccount(account) .orElseThrow(); UUID uuid = UUID.randomUUID(); if (user.checkPassword(password)) { + session.save(uuid.toString(), user); response = String.join("\r\n", "HTTP/1.1 302 Found ", "Set-Cookie: JSESSIONID=" + uuid, @@ -136,11 +118,30 @@ public void process(final Socket connection) { log.error("비밀번호 불일치"); } } - if (method.equals("POST") && path.equals("/register")) { + if (httpRequest.getHttpRequestHeader().getMethod().equals("POST") && httpRequest.getHttpRequestHeader().getPath().equals("/register")) { response = String.join("\r\n", "HTTP/1.1 302 Found ", "Location: " + "/index.html"); } + if (httpRequest.getHttpRequestHeader().getMethod().equals("GET") && httpRequest.getHttpRequestHeader().getPath().equals("/login.html")) { + if (!httpRequest.getHttpRequestHeader().containsKey("Cookie")) { + String[] cookies = httpRequest.getHttpRequestHeader().getValue("Cookie").split("; "); + String cookie = ""; + for (String c : cookies) { + if (c.contains("JSESSIONID")) { + cookie = c.split("=")[1]; + } + } + if (session.containsUser(cookie)) { + User user = session.getUser(cookie); + response = String.join("\r\n", + "HTTP/1.1 302 Found ", + "Set-Cookie: JSESSIONID=" + cookie, + "Location: " + "/index.html"); + log.info(user.toString()); + } + } + } outputStream.write(response.getBytes()); outputStream.flush(); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java new file mode 100644 index 0000000000..ac63352a15 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java @@ -0,0 +1,33 @@ +package org.apache.coyote.http11; + +import java.io.BufferedReader; + +public class HttpRequest { + + private final HttpRequestHeader httpRequestHeader; + private HttpRequestBody httpRequestBody; + + public HttpRequest(BufferedReader bufferedReader) { + this.httpRequestHeader = new HttpRequestHeader(bufferedReader); + if (httpRequestHeader.containsKey("Content-Length")) { + int contentLength = Integer.parseInt(httpRequestHeader.getValue("Content-Length")); + this.httpRequestBody = new HttpRequestBody(contentLength, bufferedReader); + } + } + + public HttpRequestHeader getHttpRequestHeader() { + return httpRequestHeader; + } + + public HttpRequestBody getHttpRequestBody() { + return httpRequestBody; + } + + @Override + public String toString() { + return "HttpRequest{\n" + + "httpRequestHeader=" + httpRequestHeader + + ",\n httpRequestBody=" + httpRequestBody + + "\n}"; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java new file mode 100644 index 0000000000..1e17f50227 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java @@ -0,0 +1,30 @@ +package org.apache.coyote.http11; + +import java.io.BufferedReader; +import java.io.IOException; + +public class HttpRequestBody { + + private final String body; + + public HttpRequestBody(int contentLength, BufferedReader bufferedReader) { + try { + char[] buffer = new char[contentLength]; + bufferedReader.read(buffer, 0, contentLength); + this.body = new String(buffer); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public String getBody() { + return body; + } + + @Override + public String toString() { + return "HttpRequestBody{\n" + + "body='" + body + '\'' + + "\n}"; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java new file mode 100644 index 0000000000..e1f4532543 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java @@ -0,0 +1,68 @@ +package org.apache.coyote.http11; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class HttpRequestHeader { + + private final String method; + private String path; + private final String version; + private final Map headers; + + public HttpRequestHeader(BufferedReader bufferedReader) { + try { + String[] headerFirstLine = bufferedReader.readLine().split(" "); + if (headerFirstLine.length < 3) { + throw new RuntimeException("제대로 된 요청이 아닙니다. header의 첫 줄의 값이 3개 미만입니다."); + } + this.method = headerFirstLine[0]; + this.path = headerFirstLine[1]; + this.version = headerFirstLine[2]; + String line; + headers = new HashMap<>(); + while ((line = bufferedReader.readLine()) != null && !line.isEmpty()) { + String[] requestLine = line.split(": "); + headers.put(requestLine[0], requestLine[1]); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public boolean containsKey(String key) { + return headers.containsKey(key); + } + + public void setDefaultPath() { + path += ".html"; + } + + public String getValue(String key) { + return headers.get(key); + } + + public String getMethod() { + return method; + } + + public String getPath() { + return path; + } + + public String getVersion() { + return version; + } + + @Override + public String toString() { + return "HttpRequestHeader{" + + "\nmethod='" + method + '\'' + + ", \npath='" + path + '\'' + + ", \nversion='" + version + '\'' + + ", \nheaders=" + headers + + '}'; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java new file mode 100644 index 0000000000..9ac8eece44 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java @@ -0,0 +1,4 @@ +package org.apache.coyote.http11; + +public class HttpResponse { +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Session.java b/tomcat/src/main/java/org/apache/coyote/http11/Session.java new file mode 100644 index 0000000000..424b9cf6f9 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/Session.java @@ -0,0 +1,36 @@ +package org.apache.coyote.http11; + +import com.techcourse.model.User; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class Session { + + private static final Session INSTANCE = new Session(); + private final Map userMap; + + private Session() { + this.userMap = new HashMap<>(); + } + + public static Session getInstance() { + return INSTANCE; + } + + public void save(String uuid, User user) { + userMap.put(uuid, user); + } + + public boolean containsUser(String uuid) { + return userMap.containsKey(uuid); + } + + public User getUser(String uuid) { + return userMap.get(uuid); + } + + public Set getKeySet() { + return userMap.keySet(); + } +} diff --git a/tomcat/src/main/resources/static/login.html b/tomcat/src/main/resources/static/login.html index f4ed9de875..bc933357f2 100644 --- a/tomcat/src/main/resources/static/login.html +++ b/tomcat/src/main/resources/static/login.html @@ -20,7 +20,7 @@

로그인

-
+
From fed02f6f5f4308400e55c160d9495cad010f5bfb Mon Sep 17 00:00:00 2001 From: Gyeongho Yang Date: Thu, 5 Sep 2024 11:11:09 +0900 Subject: [PATCH 06/30] fix: remove implementation logback-classic on gradle (#501) --- study/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/study/build.gradle b/study/build.gradle index 5c69542f84..87a1f0313c 100644 --- a/study/build.gradle +++ b/study/build.gradle @@ -19,7 +19,6 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-webflux' - implementation 'ch.qos.logback:logback-classic:1.5.7' implementation 'org.apache.commons:commons-lang3:3.14.0' implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1' implementation 'pl.allegro.tech.boot:handlebars-spring-boot-starter:0.4.1' From 7e9135698878932274ddc1f523ba817ed9c56c70 Mon Sep 17 00:00:00 2001 From: Gyeongho Yang Date: Thu, 5 Sep 2024 13:51:07 +0900 Subject: [PATCH 07/30] fix: add threads min-spare configuration on properties (#502) --- study/src/main/resources/application.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/study/src/main/resources/application.yml b/study/src/main/resources/application.yml index 4e8655a962..e3503a5fb9 100644 --- a/study/src/main/resources/application.yml +++ b/study/src/main/resources/application.yml @@ -6,4 +6,5 @@ server: accept-count: 1 max-connections: 1 threads: + min-spare: 2 max: 2 From 1f851588644dd198c113cdddd2812c6e5d06a502 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 5 Sep 2024 13:51:14 +0900 Subject: [PATCH 08/30] =?UTF-8?q?refactor:=20HttpRequest=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../techcourse/db/InMemoryUserRepository.java | 2 +- .../apache/coyote/http11/Http11Processor.java | 43 ++++++++--------- .../org/apache/coyote/http11/HttpMethod.java | 27 +++++++++++ .../org/apache/coyote/http11/HttpRequest.java | 47 +++++++++++++++---- .../apache/coyote/http11/HttpRequestBody.java | 13 +---- .../coyote/http11/HttpRequestConvertor.java | 43 +++++++++++++++++ .../coyote/http11/HttpRequestHeader.java | 40 ++++++---------- 7 files changed, 146 insertions(+), 69 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java diff --git a/tomcat/src/main/java/com/techcourse/db/InMemoryUserRepository.java b/tomcat/src/main/java/com/techcourse/db/InMemoryUserRepository.java index d3fa57feeb..b041a264ea 100644 --- a/tomcat/src/main/java/com/techcourse/db/InMemoryUserRepository.java +++ b/tomcat/src/main/java/com/techcourse/db/InMemoryUserRepository.java @@ -11,7 +11,7 @@ public class InMemoryUserRepository { private static final Map database = new ConcurrentHashMap<>(); static { - final User user = new User(1L, "gugu", "password", "hkkang@woowahan.com"); + final User user = new User(1L, "gugu", "1", "hkkang@woowahan.com"); database.put(user.getAccount(), user); } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index d2b5008cf8..260329e143 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -35,14 +35,14 @@ public void run() { @Override public void process(final Socket connection) { - try (final var inputStream = connection.getInputStream(); - final var outputStream = connection.getOutputStream()) { + try ( + final var inputStream = connection.getInputStream(); + final var outputStream = connection.getOutputStream() + ) { var bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + HttpRequest httpRequest = HttpRequestConvertor.convertHttpRequest(bufferedReader); - HttpRequest httpRequest = new HttpRequest(bufferedReader); - System.out.println(httpRequest); - - if (httpRequest.getHttpRequestHeader().getMethod().equals("POST") && httpRequest.getHttpRequestHeader().getPath().equals("/register")) { + if (httpRequest.isMethod("POST") && httpRequest.isPath("/register")) { String requestBody = httpRequest.getHttpRequestBody().getBody(); String[] token = requestBody.split("&"); String account = token[0].split("=")[1]; @@ -54,14 +54,11 @@ public void process(final Socket connection) { var responseBody = ""; var contentType = "text/html;charset=utf-8 \r\n"; - if (!httpRequest.getHttpRequestHeader().getPath().equals("/") && !httpRequest.getHttpRequestHeader().getPath().contains(".") && httpRequest.getHttpRequestHeader().getMethod().equals("GET")) { - httpRequest.getHttpRequestHeader().setDefaultPath(); - } - if (httpRequest.getHttpRequestHeader().getPath().equals("/")) { + if (httpRequest.isPath("/")) { responseBody = "Hello world!"; - } else if (httpRequest.getHttpRequestHeader().getMethod().equals("GET")) { - String fileName = "static" + httpRequest.getHttpRequestHeader().getPath(); + } else if (httpRequest.isMethod("GET")) { + String fileName = "static" + httpRequest.getPath(); var resourceUrl = getClass().getClassLoader().getResource(fileName); if (resourceUrl == null) { final var response = String.join("\r\n", @@ -77,15 +74,15 @@ public void process(final Socket connection) { responseBody = new String(Files.readAllBytes(filePath)); } - if (httpRequest.getHttpRequestHeader().getPath().endsWith(".css")) { + if (httpRequest.getPath().endsWith(".css")) { contentType = "text/css;charset=utf-8 \r\n"; - } else if (httpRequest.getHttpRequestHeader().getPath().endsWith(".js")) { + } else if (httpRequest.getPath().endsWith(".js")) { contentType = "application/javascript;charset=utf-8 \r\n"; - } else if (httpRequest.getHttpRequestHeader().getPath().endsWith(".html")) { + } else if (httpRequest.getPath().endsWith(".html")) { contentType = "text/html;charset=utf-8 \r\n"; - } else if (httpRequest.getHttpRequestHeader().getPath().endsWith(".png")) { + } else if (httpRequest.getPath().endsWith(".png")) { contentType = "image/png \r\n"; - } else if (httpRequest.getHttpRequestHeader().getPath().endsWith(".jpg") || httpRequest.getHttpRequestHeader().getPath().endsWith(".jpeg")) { + } else if (httpRequest.getPath().endsWith(".jpg")) { contentType = "image/jpeg \r\n"; } @@ -95,8 +92,8 @@ public void process(final Socket connection) { "Content-Length: " + responseBody.getBytes().length + " ", "", responseBody); - if (httpRequest.getHttpRequestHeader().getMethod().equals("POST") && httpRequest.getHttpRequestHeader().getPath().equals("/login")) { - String requestBody = httpRequest.getHttpRequestBody().getBody(); + if (httpRequest.isMethod("POST") && httpRequest.isPath("/login")) { + String requestBody = httpRequest.getBody(); String[] token = requestBody.split("&"); String account = token[0].split("=")[1]; String password = token[1].split("=")[1]; @@ -118,14 +115,14 @@ public void process(final Socket connection) { log.error("비밀번호 불일치"); } } - if (httpRequest.getHttpRequestHeader().getMethod().equals("POST") && httpRequest.getHttpRequestHeader().getPath().equals("/register")) { + if (httpRequest.isMethod("POST") && httpRequest.isPath("/register")) { response = String.join("\r\n", "HTTP/1.1 302 Found ", "Location: " + "/index.html"); } - if (httpRequest.getHttpRequestHeader().getMethod().equals("GET") && httpRequest.getHttpRequestHeader().getPath().equals("/login.html")) { - if (!httpRequest.getHttpRequestHeader().containsKey("Cookie")) { - String[] cookies = httpRequest.getHttpRequestHeader().getValue("Cookie").split("; "); + if (httpRequest.isMethod("GET") && httpRequest.isPath("/login.html")) { + if (httpRequest.containsKey("Cookie")) { + String[] cookies = httpRequest.getValue("Cookie").split("; "); String cookie = ""; for (String c : cookies) { if (c.contains("JSESSIONID")) { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java new file mode 100644 index 0000000000..4cb9976c42 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java @@ -0,0 +1,27 @@ +package org.apache.coyote.http11; + +import java.util.Arrays; + +public enum HttpMethod { + + GET("GET"), + POST("POST") + ; + + private final String name; + + HttpMethod(String name) { + this.name = name; + } + + public static HttpMethod getHttpMethod(String name) { + return Arrays.stream(values()) + .filter(httpMethod -> httpMethod.name.equals(name)) + .findAny() + .orElseThrow(); + } + + public boolean isMethod(String name) { + return this.name.equals(name); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java index ac63352a15..f2970d2967 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java @@ -1,18 +1,49 @@ package org.apache.coyote.http11; -import java.io.BufferedReader; - public class HttpRequest { private final HttpRequestHeader httpRequestHeader; private HttpRequestBody httpRequestBody; - public HttpRequest(BufferedReader bufferedReader) { - this.httpRequestHeader = new HttpRequestHeader(bufferedReader); - if (httpRequestHeader.containsKey("Content-Length")) { - int contentLength = Integer.parseInt(httpRequestHeader.getValue("Content-Length")); - this.httpRequestBody = new HttpRequestBody(contentLength, bufferedReader); - } + public HttpRequest(HttpRequestHeader httpRequestHeader, HttpRequestBody httpRequestBody) { + this.httpRequestHeader = httpRequestHeader; + this.httpRequestBody = httpRequestBody; + } + + public HttpRequest(HttpRequestHeader httpRequestHeader) { + this(httpRequestHeader, null); + } + + public boolean isMethod(String name) { + return httpRequestHeader.isMethod(name); + } + + public boolean isPath(String path) { + return httpRequestHeader.isPath(path); + } + + public boolean containsKey(String key) { + return httpRequestHeader.containsKey(key); + } + + public String getValue(String key) { + return httpRequestHeader.getValue(key); + } + + public HttpMethod getMethod() { + return httpRequestHeader.getMethod(); + } + + public String getPath() { + return httpRequestHeader.getPath(); + } + + public String getVersion() { + return httpRequestHeader.getVersion(); + } + + public String getBody() { + return httpRequestBody.getBody(); } public HttpRequestHeader getHttpRequestHeader() { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java index 1e17f50227..2a59b78576 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java @@ -1,20 +1,11 @@ package org.apache.coyote.http11; -import java.io.BufferedReader; -import java.io.IOException; - public class HttpRequestBody { private final String body; - public HttpRequestBody(int contentLength, BufferedReader bufferedReader) { - try { - char[] buffer = new char[contentLength]; - bufferedReader.read(buffer, 0, contentLength); - this.body = new String(buffer); - } catch (IOException e) { - throw new RuntimeException(e); - } + public HttpRequestBody(String body) { + this.body = body; } public String getBody() { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java new file mode 100644 index 0000000000..f038703557 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java @@ -0,0 +1,43 @@ +package org.apache.coyote.http11; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class HttpRequestConvertor { + + public static HttpRequest convertHttpRequest(BufferedReader bufferedReader) { + try { + String firstLine = bufferedReader.readLine(); + if (firstLine == null) { + throw new RuntimeException("요청이 비어 있습니다."); + } + String[] headerFirstLine = firstLine.split(" "); + HttpMethod method = HttpMethod.getHttpMethod(headerFirstLine[0]); + String path = headerFirstLine[1]; + if (method.isMethod("GET") && !path.contains(".")) { + path += ".html"; + } + String version = headerFirstLine[2]; + String line; + Map headers = new HashMap<>(); + while ((line = bufferedReader.readLine()) != null && !line.isEmpty()) { + String[] requestLine = line.split(": "); + headers.put(requestLine[0], requestLine[1]); + } + HttpRequestHeader httpRequestHeader = new HttpRequestHeader(method, path, version, headers); + if (httpRequestHeader.containsKey("Content-Length")) { + int contentLength = Integer.parseInt(httpRequestHeader.getValue("Content-Length")); + char[] buffer = new char[contentLength]; + bufferedReader.read(buffer, 0, contentLength); + String body = new String(buffer); + HttpRequestBody httpRequestBody = new HttpRequestBody(body); + return new HttpRequest(httpRequestHeader, httpRequestBody); + } + return new HttpRequest(httpRequestHeader); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java index e1f4532543..a63821e8d5 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java @@ -1,50 +1,38 @@ package org.apache.coyote.http11; -import java.io.BufferedReader; -import java.io.IOException; -import java.util.HashMap; import java.util.Map; public class HttpRequestHeader { - private final String method; - private String path; + private final HttpMethod method; + private final String path; private final String version; private final Map headers; - public HttpRequestHeader(BufferedReader bufferedReader) { - try { - String[] headerFirstLine = bufferedReader.readLine().split(" "); - if (headerFirstLine.length < 3) { - throw new RuntimeException("제대로 된 요청이 아닙니다. header의 첫 줄의 값이 3개 미만입니다."); - } - this.method = headerFirstLine[0]; - this.path = headerFirstLine[1]; - this.version = headerFirstLine[2]; - String line; - headers = new HashMap<>(); - while ((line = bufferedReader.readLine()) != null && !line.isEmpty()) { - String[] requestLine = line.split(": "); - headers.put(requestLine[0], requestLine[1]); - } - } catch (IOException e) { - throw new RuntimeException(e); - } + public HttpRequestHeader(HttpMethod method, String path, String version, Map headers) { + this.method = method; + this.path = path; + this.version = version; + this.headers = headers; } public boolean containsKey(String key) { return headers.containsKey(key); } - public void setDefaultPath() { - path += ".html"; + public boolean isMethod(String name) { + return method.isMethod(name); + } + + public boolean isPath(String path) { + return this.path.equals(path); } public String getValue(String key) { return headers.get(key); } - public String getMethod() { + public HttpMethod getMethod() { return method; } From 3666831c6efde83824e2fde5930de07fbed756a8 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 5 Sep 2024 17:14:17 +0900 Subject: [PATCH 09/30] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coyote/http11/HttpRequestConvertor.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java index f038703557..462c62e7eb 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java @@ -13,6 +13,7 @@ public static HttpRequest convertHttpRequest(BufferedReader bufferedReader) { if (firstLine == null) { throw new RuntimeException("요청이 비어 있습니다."); } + String[] headerFirstLine = firstLine.split(" "); HttpMethod method = HttpMethod.getHttpMethod(headerFirstLine[0]); String path = headerFirstLine[1]; @@ -20,24 +21,34 @@ public static HttpRequest convertHttpRequest(BufferedReader bufferedReader) { path += ".html"; } String version = headerFirstLine[2]; - String line; - Map headers = new HashMap<>(); - while ((line = bufferedReader.readLine()) != null && !line.isEmpty()) { - String[] requestLine = line.split(": "); - headers.put(requestLine[0], requestLine[1]); - } + Map headers = getHeaders(bufferedReader); + HttpRequestHeader httpRequestHeader = new HttpRequestHeader(method, path, version, headers); + if (httpRequestHeader.containsKey("Content-Length")) { int contentLength = Integer.parseInt(httpRequestHeader.getValue("Content-Length")); char[] buffer = new char[contentLength]; bufferedReader.read(buffer, 0, contentLength); String body = new String(buffer); HttpRequestBody httpRequestBody = new HttpRequestBody(body); + return new HttpRequest(httpRequestHeader, httpRequestBody); } + return new HttpRequest(httpRequestHeader); } catch (IOException e) { throw new RuntimeException(e); } } + + private static Map getHeaders(BufferedReader bufferedReader) throws IOException { + String line; + Map headers = new HashMap<>(); + while ((line = bufferedReader.readLine()) != null && !line.isEmpty()) { + String[] requestLine = line.split(": "); + headers.put(requestLine[0], requestLine[1]); + } + + return headers; + } } From 95f3203f7ac26ee91c73ccedb22662588990fb2d Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 5 Sep 2024 17:15:44 +0900 Subject: [PATCH 10/30] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coyote/http11/HttpRequestConvertor.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java index 462c62e7eb..38c2d8660b 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java @@ -26,11 +26,7 @@ public static HttpRequest convertHttpRequest(BufferedReader bufferedReader) { HttpRequestHeader httpRequestHeader = new HttpRequestHeader(method, path, version, headers); if (httpRequestHeader.containsKey("Content-Length")) { - int contentLength = Integer.parseInt(httpRequestHeader.getValue("Content-Length")); - char[] buffer = new char[contentLength]; - bufferedReader.read(buffer, 0, contentLength); - String body = new String(buffer); - HttpRequestBody httpRequestBody = new HttpRequestBody(body); + HttpRequestBody httpRequestBody = getHttpRequestBody(bufferedReader, httpRequestHeader); return new HttpRequest(httpRequestHeader, httpRequestBody); } @@ -41,6 +37,18 @@ public static HttpRequest convertHttpRequest(BufferedReader bufferedReader) { } } + private static HttpRequestBody getHttpRequestBody( + BufferedReader bufferedReader, + HttpRequestHeader httpRequestHeader + ) throws IOException { + int contentLength = Integer.parseInt(httpRequestHeader.getValue("Content-Length")); + char[] buffer = new char[contentLength]; + bufferedReader.read(buffer, 0, contentLength); + String body = new String(buffer); + HttpRequestBody httpRequestBody = new HttpRequestBody(body); + return httpRequestBody; + } + private static Map getHeaders(BufferedReader bufferedReader) throws IOException { String line; Map headers = new HashMap<>(); From e0adfe5fb25f07514a40f3ace882f535ca7f7eed Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 5 Sep 2024 22:42:13 +0900 Subject: [PATCH 11/30] =?UTF-8?q?refactor:=20Controller=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/apache/coyote/http11/ContentType.java | 32 +++++++ .../apache/coyote/http11/Http11Processor.java | 44 ++------- .../coyote/http11/HttpRequestConvertor.java | 16 +++- .../coyote/http11/HttpRequestHeader.java | 56 ------------ .../apache/coyote/http11/HttpResponse.java | 4 - .../apache/coyote/http11/HttpStatusCode.java | 24 +++++ .../apache/coyote/http11/RequestMapping.java | 27 ++++++ .../http11/controller/AbstractController.java | 22 +++++ .../coyote/http11/controller/Controller.java | 9 ++ .../http11/controller/LoginController.java | 91 +++++++++++++++++++ .../http11/controller/PageController.java | 45 +++++++++ .../http11/controller/RegisterController.java | 55 +++++++++++ .../http11/{ => httprequest}/HttpRequest.java | 24 +++-- .../{ => httprequest}/HttpRequestBody.java | 2 +- .../http11/httprequest/HttpRequestHeader.java | 27 ++++++ .../http11/httprequest/HttpRequestLine.java | 36 ++++++++ .../http11/httpresponse/HttpResponse.java | 73 +++++++++++++++ .../http11/httpresponse/HttpResponseBody.java | 14 +++ .../httpresponse/HttpResponseHeader.java | 21 +++++ .../http11/httpresponse/HttpStatusLine.java | 22 +++++ 20 files changed, 533 insertions(+), 111 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/ContentType.java delete mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java delete mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpStatusCode.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/RequestMapping.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/controller/AbstractController.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/controller/Controller.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/controller/LoginController.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/controller/PageController.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/controller/RegisterController.java rename tomcat/src/main/java/org/apache/coyote/http11/{ => httprequest}/HttpRequest.java (59%) rename tomcat/src/main/java/org/apache/coyote/http11/{ => httprequest}/HttpRequestBody.java (88%) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestHeader.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestLine.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponse.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponseBody.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponseHeader.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpStatusLine.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java b/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java new file mode 100644 index 0000000000..cd36e4a10c --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java @@ -0,0 +1,32 @@ +package org.apache.coyote.http11; + +import java.util.Arrays; + +public enum ContentType { + + CSS("text/css;charset=utf-8", "css"), + JS("application/javascript;charset=utf-8", "js"), + HTML("text/html;charset=utf-8", "html"), + PNG("image/png", "png"), + JPG("image/jpeg", "jpeg") + ; + + private final String contentType; + private final String extention; + + ContentType(String contentType, String extention) { + this.contentType = contentType; + this.extention = extention; + } + + public String getContentType() { + return contentType; + } + + public static ContentType getContentType(String extention) { + return Arrays.stream(values()) + .filter(contentType1 -> contentType1.extention.equals(extention)) + .findAny() + .orElseThrow(); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 260329e143..ba84e05659 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -1,6 +1,5 @@ package org.apache.coyote.http11; -import com.techcourse.db.InMemoryUserRepository; import com.techcourse.exception.UncheckedServletException; import com.techcourse.model.User; import java.io.BufferedReader; @@ -10,8 +9,10 @@ import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.UUID; import org.apache.coyote.Processor; +import org.apache.coyote.http11.controller.Controller; +import org.apache.coyote.http11.httprequest.HttpRequest; +import org.apache.coyote.http11.httpresponse.HttpResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,15 +43,11 @@ public void process(final Socket connection) { var bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); HttpRequest httpRequest = HttpRequestConvertor.convertHttpRequest(bufferedReader); - if (httpRequest.isMethod("POST") && httpRequest.isPath("/register")) { - String requestBody = httpRequest.getHttpRequestBody().getBody(); - String[] token = requestBody.split("&"); - String account = token[0].split("=")[1]; - String email = token[1].split("=")[1]; - String password = token[2].split("=")[1]; - User user = new User(account, password, email); - InMemoryUserRepository.save(user); - } + RequestMapping requestMapping = new RequestMapping(); + + Controller controller = requestMapping.getController(httpRequest.getPath()); + + HttpResponse httpResponse = controller.service(httpRequest); var responseBody = ""; var contentType = "text/html;charset=utf-8 \r\n"; @@ -92,29 +89,6 @@ public void process(final Socket connection) { "Content-Length: " + responseBody.getBytes().length + " ", "", responseBody); - if (httpRequest.isMethod("POST") && httpRequest.isPath("/login")) { - String requestBody = httpRequest.getBody(); - String[] token = requestBody.split("&"); - String account = token[0].split("=")[1]; - String password = token[1].split("=")[1]; - - User user = InMemoryUserRepository.findByAccount(account) - .orElseThrow(); - UUID uuid = UUID.randomUUID(); - if (user.checkPassword(password)) { - session.save(uuid.toString(), user); - response = String.join("\r\n", - "HTTP/1.1 302 Found ", - "Set-Cookie: JSESSIONID=" + uuid, - "Location: " + "/index.html"); - log.info(user.toString()); - } else { - response = String.join("\r\n", - "HTTP/1.1 302 Found ", - "Location: " + "/401.html"); - log.error("비밀번호 불일치"); - } - } if (httpRequest.isMethod("POST") && httpRequest.isPath("/register")) { response = String.join("\r\n", "HTTP/1.1 302 Found ", @@ -140,7 +114,7 @@ public void process(final Socket connection) { } } - outputStream.write(response.getBytes()); + outputStream.write(httpResponse.getBytes()); outputStream.flush(); } catch (IOException | UncheckedServletException e) { log.error(e.getMessage(), e); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java index 38c2d8660b..69460d079e 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java @@ -4,6 +4,10 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; +import org.apache.coyote.http11.httprequest.HttpRequest; +import org.apache.coyote.http11.httprequest.HttpRequestBody; +import org.apache.coyote.http11.httprequest.HttpRequestHeader; +import org.apache.coyote.http11.httprequest.HttpRequestLine; public class HttpRequestConvertor { @@ -21,17 +25,20 @@ public static HttpRequest convertHttpRequest(BufferedReader bufferedReader) { path += ".html"; } String version = headerFirstLine[2]; + + HttpRequestLine httpRequestLine = new HttpRequestLine(method, path, version); + Map headers = getHeaders(bufferedReader); - HttpRequestHeader httpRequestHeader = new HttpRequestHeader(method, path, version, headers); + HttpRequestHeader httpRequestHeader = new HttpRequestHeader(headers); if (httpRequestHeader.containsKey("Content-Length")) { HttpRequestBody httpRequestBody = getHttpRequestBody(bufferedReader, httpRequestHeader); - return new HttpRequest(httpRequestHeader, httpRequestBody); + return new HttpRequest(httpRequestLine, httpRequestHeader, httpRequestBody); } - return new HttpRequest(httpRequestHeader); + return new HttpRequest(httpRequestLine, httpRequestHeader); } catch (IOException e) { throw new RuntimeException(e); } @@ -45,8 +52,7 @@ private static HttpRequestBody getHttpRequestBody( char[] buffer = new char[contentLength]; bufferedReader.read(buffer, 0, contentLength); String body = new String(buffer); - HttpRequestBody httpRequestBody = new HttpRequestBody(body); - return httpRequestBody; + return new HttpRequestBody(body); } private static Map getHeaders(BufferedReader bufferedReader) throws IOException { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java deleted file mode 100644 index a63821e8d5..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.apache.coyote.http11; - -import java.util.Map; - -public class HttpRequestHeader { - - private final HttpMethod method; - private final String path; - private final String version; - private final Map headers; - - public HttpRequestHeader(HttpMethod method, String path, String version, Map headers) { - this.method = method; - this.path = path; - this.version = version; - this.headers = headers; - } - - public boolean containsKey(String key) { - return headers.containsKey(key); - } - - public boolean isMethod(String name) { - return method.isMethod(name); - } - - public boolean isPath(String path) { - return this.path.equals(path); - } - - public String getValue(String key) { - return headers.get(key); - } - - public HttpMethod getMethod() { - return method; - } - - public String getPath() { - return path; - } - - public String getVersion() { - return version; - } - - @Override - public String toString() { - return "HttpRequestHeader{" + - "\nmethod='" + method + '\'' + - ", \npath='" + path + '\'' + - ", \nversion='" + version + '\'' + - ", \nheaders=" + headers + - '}'; - } -} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java deleted file mode 100644 index 9ac8eece44..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.apache.coyote.http11; - -public class HttpResponse { -} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpStatusCode.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpStatusCode.java new file mode 100644 index 0000000000..a51eac9edf --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpStatusCode.java @@ -0,0 +1,24 @@ +package org.apache.coyote.http11; + +public enum HttpStatusCode { + + OK(200, "OK"), + FOUND(302, "Found") + ; + + private final int code; + private final String message; + + HttpStatusCode(int code, String message) { + this.code = code; + this.message = message; + } + + public int getCode() { + return code; + } + + public String getMessage() { + return message; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/RequestMapping.java b/tomcat/src/main/java/org/apache/coyote/http11/RequestMapping.java new file mode 100644 index 0000000000..fa012d5ca8 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/RequestMapping.java @@ -0,0 +1,27 @@ +package org.apache.coyote.http11; + +import java.util.HashMap; +import java.util.Map; +import org.apache.coyote.http11.controller.Controller; +import org.apache.coyote.http11.controller.LoginController; +import org.apache.coyote.http11.controller.PageController; +import org.apache.coyote.http11.controller.RegisterController; + +public class RequestMapping { + + private final Map controllers = new HashMap<>(); + + public RequestMapping() { + controllers.put("/login", new LoginController()); + controllers.put("/register", new RegisterController()); + controllers.put("page", new PageController()); + } + + public Controller getController(String path) { + if (controllers.containsKey(path)) { + return controllers.get(path); + } + + return new PageController(); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/AbstractController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/AbstractController.java new file mode 100644 index 0000000000..28e5765e11 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/AbstractController.java @@ -0,0 +1,22 @@ +package org.apache.coyote.http11.controller; + +import org.apache.coyote.http11.httprequest.HttpRequest; +import org.apache.coyote.http11.httpresponse.HttpResponse; + +public abstract class AbstractController implements Controller { + + @Override + public HttpResponse service(HttpRequest httpRequest) { + if (httpRequest.isMethod("GET")) { + return serviceGet(httpRequest); + } else if (httpRequest.isMethod("POST")) { + return servicePost(httpRequest); + } + + throw new RuntimeException(); + } + + abstract protected HttpResponse servicePost(HttpRequest httpRequest); + + abstract protected HttpResponse serviceGet(HttpRequest httpRequest); +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/Controller.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/Controller.java new file mode 100644 index 0000000000..3f2f18697a --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/Controller.java @@ -0,0 +1,9 @@ +package org.apache.coyote.http11.controller; + +import org.apache.coyote.http11.httprequest.HttpRequest; +import org.apache.coyote.http11.httpresponse.HttpResponse; + +public interface Controller { + + HttpResponse service(HttpRequest httpRequest); +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/LoginController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/LoginController.java new file mode 100644 index 0000000000..72dfa0cba9 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/LoginController.java @@ -0,0 +1,91 @@ +package org.apache.coyote.http11.controller; + +import com.techcourse.db.InMemoryUserRepository; +import com.techcourse.model.User; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.UUID; +import org.apache.coyote.http11.ContentType; +import org.apache.coyote.http11.HttpStatusCode; +import org.apache.coyote.http11.Session; +import org.apache.coyote.http11.httprequest.HttpRequest; +import org.apache.coyote.http11.httpresponse.HttpResponse; +import org.apache.coyote.http11.httpresponse.HttpResponseBody; +import org.apache.coyote.http11.httpresponse.HttpResponseHeader; +import org.apache.coyote.http11.httpresponse.HttpStatusLine; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LoginController extends AbstractController { + + private static final Logger log = LoggerFactory.getLogger(LoginController.class); + + private final Session session = Session.getInstance(); + + @Override + protected HttpResponse servicePost(HttpRequest httpRequest) { + String requestBody = httpRequest.getBody(); + String[] token = requestBody.split("&"); + String account = token[0].split("=")[1]; + String password = token[1].split("=")[1]; + + User user = InMemoryUserRepository.findByAccount(account) + .orElseThrow(); + UUID uuid = UUID.randomUUID(); + + HttpStatusLine httpStatusLine = new HttpStatusLine(httpRequest.getVersion(), HttpStatusCode.FOUND); + HttpResponseHeader httpResponseHeader = new HttpResponseHeader(); + if (user.checkPassword(password)) { + session.save(uuid.toString(), user); + httpResponseHeader.addHeaders("Set-Cookie", "JSESSIONID=" + uuid); + httpResponseHeader.addHeaders("Location", "/index.html"); + log.info(user.toString()); + } else { + httpResponseHeader.addHeaders("Location", "/401.html"); + log.error("비밀번호 불일치"); + } + + return new HttpResponse(httpStatusLine, httpResponseHeader); + } + + @Override + protected HttpResponse serviceGet(HttpRequest httpRequest) { + try { + HttpStatusLine httpStatusLine = new HttpStatusLine(httpRequest.getVersion(), HttpStatusCode.OK); + + if (httpRequest.containsKey("Cookie")) { + String[] cookies = httpRequest.getValue("Cookie").split("; "); + String cookie = ""; + for (String c : cookies) { + if (c.contains("JSESSIONID")) { + cookie = c.split("=")[1]; + } + } + if (session.containsUser(cookie)) { + User user = session.getUser(cookie); + log.info(user.toString()); + httpStatusLine = new HttpStatusLine(httpStatusLine.getVersion(), HttpStatusCode.FOUND); + HttpResponseHeader httpResponseHeader = new HttpResponseHeader(); + httpResponseHeader.addHeaders("Set-Cookie", "JSESSIONID=" + cookie); + httpResponseHeader.addHeaders("Location", "/index.html"); + return new HttpResponse(httpStatusLine, httpResponseHeader); + } + } + + String fileName = "static/login.html"; + var resourceUrl = getClass().getClassLoader().getResource(fileName); + Path filePath = Path.of(resourceUrl.toURI()); + String responseBody = new String(Files.readAllBytes(filePath)); + HttpResponseHeader httpResponseHeader = new HttpResponseHeader(); + httpResponseHeader.addHeaders("Content-Type", ContentType.HTML.getContentType()); + httpResponseHeader.addHeaders("Content-Length", String.valueOf(responseBody.getBytes().length)); + HttpResponseBody httpResponseBody = new HttpResponseBody(responseBody); + + return new HttpResponse(httpStatusLine, httpResponseHeader, httpResponseBody); + } catch (URISyntaxException | IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/PageController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/PageController.java new file mode 100644 index 0000000000..32bd4f9532 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/PageController.java @@ -0,0 +1,45 @@ +package org.apache.coyote.http11.controller; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.apache.coyote.http11.ContentType; +import org.apache.coyote.http11.HttpStatusCode; +import org.apache.coyote.http11.httprequest.HttpRequest; +import org.apache.coyote.http11.httpresponse.HttpResponse; +import org.apache.coyote.http11.httpresponse.HttpResponseBody; +import org.apache.coyote.http11.httpresponse.HttpResponseHeader; +import org.apache.coyote.http11.httpresponse.HttpStatusLine; + +public class PageController extends AbstractController { + + @Override + protected HttpResponse servicePost(HttpRequest httpRequest) { + return null; + } + + @Override + protected HttpResponse serviceGet(HttpRequest httpRequest) { + try { + HttpStatusLine httpStatusLine = new HttpStatusLine(httpRequest.getVersion(), HttpStatusCode.OK); + + String path = httpRequest.getPath(); + if (!httpRequest.getPath().contains(".")) { + path += ".html"; + } + String fileName = "static" + path; + var resourceUrl = getClass().getClassLoader().getResource(fileName); + Path filePath = Path.of(resourceUrl.toURI()); + String responseBody = new String(Files.readAllBytes(filePath)); + HttpResponseHeader httpResponseHeader = new HttpResponseHeader(); + httpResponseHeader.addHeaders("Content-Type", ContentType.getContentType(path.split("\\.")[1]).getContentType()); + httpResponseHeader.addHeaders("Content-Length", String.valueOf(responseBody.getBytes().length)); + HttpResponseBody httpResponseBody = new HttpResponseBody(responseBody); + + return new HttpResponse(httpStatusLine, httpResponseHeader, httpResponseBody); + } catch (URISyntaxException | IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/RegisterController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/RegisterController.java new file mode 100644 index 0000000000..7890787d48 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/RegisterController.java @@ -0,0 +1,55 @@ +package org.apache.coyote.http11.controller; + +import com.techcourse.db.InMemoryUserRepository; +import com.techcourse.model.User; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.apache.coyote.http11.ContentType; +import org.apache.coyote.http11.HttpStatusCode; +import org.apache.coyote.http11.httprequest.HttpRequest; +import org.apache.coyote.http11.httpresponse.HttpResponse; +import org.apache.coyote.http11.httpresponse.HttpResponseBody; +import org.apache.coyote.http11.httpresponse.HttpResponseHeader; +import org.apache.coyote.http11.httpresponse.HttpStatusLine; + +public class RegisterController extends AbstractController { + + @Override + protected HttpResponse servicePost(HttpRequest httpRequest) { + String requestBody = httpRequest.getBody(); + String[] token = requestBody.split("&"); + String account = token[0].split("=")[1]; + String email = token[1].split("=")[1]; + String password = token[2].split("=")[1]; + User user = new User(account, password, email); + InMemoryUserRepository.save(user); + + HttpStatusLine httpStatusLine = new HttpStatusLine(httpRequest.getVersion(), HttpStatusCode.FOUND); + HttpResponseHeader httpResponseHeader = new HttpResponseHeader(); + httpResponseHeader.addHeaders("Location", "/index.html"); + + return new HttpResponse(httpStatusLine, httpResponseHeader); + } + + @Override + protected HttpResponse serviceGet(HttpRequest httpRequest) { + try { + HttpStatusLine httpStatusLine = new HttpStatusLine(httpRequest.getVersion(), HttpStatusCode.OK); + + String fileName = "static/register.html"; + var resourceUrl = getClass().getClassLoader().getResource(fileName); + Path filePath = Path.of(resourceUrl.toURI()); + String responseBody = new String(Files.readAllBytes(filePath)); + HttpResponseHeader httpResponseHeader = new HttpResponseHeader(); + httpResponseHeader.addHeaders("Content-Type", ContentType.HTML.getContentType()); + httpResponseHeader.addHeaders("Content-Length", String.valueOf(responseBody.getBytes().length)); + HttpResponseBody httpResponseBody = new HttpResponseBody(responseBody); + + return new HttpResponse(httpStatusLine, httpResponseHeader, httpResponseBody); + } catch (URISyntaxException | IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequest.java similarity index 59% rename from tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java rename to tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequest.java index f2970d2967..1e614078b8 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequest.java @@ -1,25 +1,29 @@ -package org.apache.coyote.http11; +package org.apache.coyote.http11.httprequest; + +import org.apache.coyote.http11.HttpMethod; public class HttpRequest { + private final HttpRequestLine httpRequestLine; private final HttpRequestHeader httpRequestHeader; - private HttpRequestBody httpRequestBody; + private final HttpRequestBody httpRequestBody; - public HttpRequest(HttpRequestHeader httpRequestHeader, HttpRequestBody httpRequestBody) { + public HttpRequest(HttpRequestLine httpRequestLine, HttpRequestHeader httpRequestHeader, HttpRequestBody httpRequestBody) { + this.httpRequestLine = httpRequestLine; this.httpRequestHeader = httpRequestHeader; this.httpRequestBody = httpRequestBody; } - public HttpRequest(HttpRequestHeader httpRequestHeader) { - this(httpRequestHeader, null); + public HttpRequest(HttpRequestLine httpRequestLine, HttpRequestHeader httpRequestHeader) { + this(httpRequestLine, httpRequestHeader, null); } public boolean isMethod(String name) { - return httpRequestHeader.isMethod(name); + return httpRequestLine.isMethod(name); } public boolean isPath(String path) { - return httpRequestHeader.isPath(path); + return httpRequestLine.isPath(path); } public boolean containsKey(String key) { @@ -31,15 +35,15 @@ public String getValue(String key) { } public HttpMethod getMethod() { - return httpRequestHeader.getMethod(); + return httpRequestLine.getMethod(); } public String getPath() { - return httpRequestHeader.getPath(); + return httpRequestLine.getPath(); } public String getVersion() { - return httpRequestHeader.getVersion(); + return httpRequestLine.getVersion(); } public String getBody() { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestBody.java similarity index 88% rename from tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java rename to tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestBody.java index 2a59b78576..6c303051c2 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestBody.java @@ -1,4 +1,4 @@ -package org.apache.coyote.http11; +package org.apache.coyote.http11.httprequest; public class HttpRequestBody { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestHeader.java b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestHeader.java new file mode 100644 index 0000000000..bf29b0cdc4 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestHeader.java @@ -0,0 +1,27 @@ +package org.apache.coyote.http11.httprequest; + +import java.util.Map; + +public class HttpRequestHeader { + + private final Map headers; + + public HttpRequestHeader(Map headers) { + this.headers = headers; + } + + public boolean containsKey(String key) { + return headers.containsKey(key); + } + + public String getValue(String key) { + return headers.get(key); + } + + @Override + public String toString() { + return "HttpRequestHeader{" + + ", \nheaders=" + headers + + '}'; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestLine.java b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestLine.java new file mode 100644 index 0000000000..407015edbb --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestLine.java @@ -0,0 +1,36 @@ +package org.apache.coyote.http11.httprequest; + +import org.apache.coyote.http11.HttpMethod; + +public class HttpRequestLine { + + private final HttpMethod method; + private final String path; + private final String version; + + public HttpRequestLine(HttpMethod method, String path, String version) { + this.method = method; + this.path = path; + this.version = version; + } + + public boolean isMethod(String name) { + return method.isMethod(name); + } + + public boolean isPath(String path) { + return this.path.equals(path); + } + + public HttpMethod getMethod() { + return method; + } + + public String getPath() { + return path; + } + + public String getVersion() { + return version; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponse.java new file mode 100644 index 0000000000..e1e705a65e --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponse.java @@ -0,0 +1,73 @@ +package org.apache.coyote.http11.httpresponse; + +import java.util.Map; + +public class HttpResponse { + + private final HttpStatusLine httpStatusLine; + private final HttpResponseHeader httpResponseHeader; + private final HttpResponseBody httpResponseBody; + + public HttpResponse( + HttpStatusLine httpStatusLine, + HttpResponseHeader httpResponseHeader, + HttpResponseBody httpResponseBody + ) { + this.httpStatusLine = httpStatusLine; + this.httpResponseHeader = httpResponseHeader; + this.httpResponseBody = httpResponseBody; + } + + public HttpResponse(HttpStatusLine httpStatusLine, HttpResponseHeader httpResponseHeader) { + this(httpStatusLine, httpResponseHeader, null); + } + + /*var response = String.join("\r\n", + "HTTP/1.1 200 OK ", + "Content-Type: " + contentType + + "Content-Length: " + responseBody.getBytes().length + " ", + "", + responseBody);*/ + public byte[] getBytes() { + String statusLine = httpStatusLine.getVersion() + " " + httpStatusLine.getHttpStatusCode().getCode() + " " + + httpStatusLine.getHttpStatusCode().getMessage(); + Map headers = httpResponseHeader.getHeaders(); + StringBuilder sb = new StringBuilder(); + int size = headers.keySet().size(); + int i = 1; + for (String key : headers.keySet()) { + if (i < size) { + sb.append(key).append(": ").append(headers.get(key)).append(" \r\n"); + size++; + } else { + sb.append(key).append(": ").append(headers.get(key)); + } + } + if (httpResponseBody != null) { + String responseBody = httpResponseBody.getBody(); + String join = String.join("\r\n", + statusLine, + sb.toString(), + responseBody); + System.out.println(join); + return join.getBytes(); + } + String join = String.join("\r\n", + statusLine, + sb.toString()); + System.out.println(join); + return join.getBytes(); + } + + public HttpStatusLine getHttpStatusLine() { + return httpStatusLine; + } + + public HttpResponseHeader getHttpResponseHeader() { + return httpResponseHeader; + } + + public HttpResponseBody getHttpResponseBody() { + return httpResponseBody; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponseBody.java b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponseBody.java new file mode 100644 index 0000000000..4c4a5cb111 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponseBody.java @@ -0,0 +1,14 @@ +package org.apache.coyote.http11.httpresponse; + +public class HttpResponseBody { + + private final String body; + + public HttpResponseBody(String body) { + this.body = body; + } + + public String getBody() { + return body; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponseHeader.java b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponseHeader.java new file mode 100644 index 0000000000..e350a1b84a --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponseHeader.java @@ -0,0 +1,21 @@ +package org.apache.coyote.http11.httpresponse; + +import java.util.HashMap; +import java.util.Map; + +public class HttpResponseHeader { + + private final Map headers; + + public HttpResponseHeader() { + this.headers = new HashMap<>(); + } + + public void addHeaders(String key, String value) { + headers.put(key, value); + } + + public Map getHeaders() { + return headers; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpStatusLine.java b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpStatusLine.java new file mode 100644 index 0000000000..4eaf9eba69 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpStatusLine.java @@ -0,0 +1,22 @@ +package org.apache.coyote.http11.httpresponse; + +import org.apache.coyote.http11.HttpStatusCode; + +public class HttpStatusLine { + + private final String version; + private final HttpStatusCode httpStatusCode; + + public HttpStatusLine(String version, HttpStatusCode httpStatusCode) { + this.version = version; + this.httpStatusCode = httpStatusCode; + } + + public String getVersion() { + return version; + } + + public HttpStatusCode getHttpStatusCode() { + return httpStatusCode; + } +} From 5e975b04b12a587d0c5e737d04cf43909b965acb Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Fri, 6 Sep 2024 07:45:35 +0900 Subject: [PATCH 12/30] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 71 ------------------- .../coyote/http11/HttpRequestConvertor.java | 13 +++- .../http11/httpresponse/HttpResponse.java | 8 --- 3 files changed, 11 insertions(+), 81 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index ba84e05659..028ca63d7e 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -1,14 +1,10 @@ package org.apache.coyote.http11; import com.techcourse.exception.UncheckedServletException; -import com.techcourse.model.User; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Path; import org.apache.coyote.Processor; import org.apache.coyote.http11.controller.Controller; import org.apache.coyote.http11.httprequest.HttpRequest; @@ -49,77 +45,10 @@ public void process(final Socket connection) { HttpResponse httpResponse = controller.service(httpRequest); - var responseBody = ""; - var contentType = "text/html;charset=utf-8 \r\n"; - - if (httpRequest.isPath("/")) { - responseBody = "Hello world!"; - } else if (httpRequest.isMethod("GET")) { - String fileName = "static" + httpRequest.getPath(); - var resourceUrl = getClass().getClassLoader().getResource(fileName); - if (resourceUrl == null) { - final var response = String.join("\r\n", - "HTTP/1.1 404 Not Found", - "Content-Type: text/html;charset=utf-8 \r\n", - "", - "

404 Not Found

"); - outputStream.write(response.getBytes()); - outputStream.flush(); - return; - } - Path filePath = Path.of(resourceUrl.toURI()); - responseBody = new String(Files.readAllBytes(filePath)); - } - - if (httpRequest.getPath().endsWith(".css")) { - contentType = "text/css;charset=utf-8 \r\n"; - } else if (httpRequest.getPath().endsWith(".js")) { - contentType = "application/javascript;charset=utf-8 \r\n"; - } else if (httpRequest.getPath().endsWith(".html")) { - contentType = "text/html;charset=utf-8 \r\n"; - } else if (httpRequest.getPath().endsWith(".png")) { - contentType = "image/png \r\n"; - } else if (httpRequest.getPath().endsWith(".jpg")) { - contentType = "image/jpeg \r\n"; - } - - var response = String.join("\r\n", - "HTTP/1.1 200 OK ", - "Content-Type: " + contentType + - "Content-Length: " + responseBody.getBytes().length + " ", - "", - responseBody); - if (httpRequest.isMethod("POST") && httpRequest.isPath("/register")) { - response = String.join("\r\n", - "HTTP/1.1 302 Found ", - "Location: " + "/index.html"); - } - if (httpRequest.isMethod("GET") && httpRequest.isPath("/login.html")) { - if (httpRequest.containsKey("Cookie")) { - String[] cookies = httpRequest.getValue("Cookie").split("; "); - String cookie = ""; - for (String c : cookies) { - if (c.contains("JSESSIONID")) { - cookie = c.split("=")[1]; - } - } - if (session.containsUser(cookie)) { - User user = session.getUser(cookie); - response = String.join("\r\n", - "HTTP/1.1 302 Found ", - "Set-Cookie: JSESSIONID=" + cookie, - "Location: " + "/index.html"); - log.info(user.toString()); - } - } - } - outputStream.write(httpResponse.getBytes()); outputStream.flush(); } catch (IOException | UncheckedServletException e) { log.error(e.getMessage(), e); - } catch (URISyntaxException e) { - throw new RuntimeException(e); } } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java index 69460d079e..9b49768341 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java @@ -59,10 +59,19 @@ private static Map getHeaders(BufferedReader bufferedReader) thr String line; Map headers = new HashMap<>(); while ((line = bufferedReader.readLine()) != null && !line.isEmpty()) { - String[] requestLine = line.split(": "); - headers.put(requestLine[0], requestLine[1]); + String[] requestLine = line.split(":"); + headers.put(requestLine[0], parseHeaderValue(requestLine)); } return headers; } + + private static String parseHeaderValue(String[] requestLine) { + StringBuilder sb = new StringBuilder(); + for (int i = 1; i < requestLine.length; i++) { + sb.append(requestLine[i].strip()); + } + + return sb.toString(); + } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponse.java index e1e705a65e..2960b502c7 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponse.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponse.java @@ -22,12 +22,6 @@ public HttpResponse(HttpStatusLine httpStatusLine, HttpResponseHeader httpRespon this(httpStatusLine, httpResponseHeader, null); } - /*var response = String.join("\r\n", - "HTTP/1.1 200 OK ", - "Content-Type: " + contentType + - "Content-Length: " + responseBody.getBytes().length + " ", - "", - responseBody);*/ public byte[] getBytes() { String statusLine = httpStatusLine.getVersion() + " " + httpStatusLine.getHttpStatusCode().getCode() + " " + httpStatusLine.getHttpStatusCode().getMessage(); @@ -49,13 +43,11 @@ public byte[] getBytes() { statusLine, sb.toString(), responseBody); - System.out.println(join); return join.getBytes(); } String join = String.join("\r\n", statusLine, sb.toString()); - System.out.println(join); return join.getBytes(); } From 450414beb472797740467cc3dfec748411ea69a7 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Fri, 6 Sep 2024 07:54:21 +0900 Subject: [PATCH 13/30] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/org/apache/coyote/http11/Http11Processor.java | 2 -- .../java/org/apache/coyote/http11/HttpRequestConvertor.java | 3 --- 2 files changed, 5 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 028ca63d7e..d1486f2416 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -17,11 +17,9 @@ public class Http11Processor implements Runnable, Processor { private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); private final Socket connection; - private final Session session; public Http11Processor(final Socket connection) { this.connection = connection; - this.session = Session.getInstance(); } @Override diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java index 9b49768341..136201b2e1 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java @@ -21,9 +21,6 @@ public static HttpRequest convertHttpRequest(BufferedReader bufferedReader) { String[] headerFirstLine = firstLine.split(" "); HttpMethod method = HttpMethod.getHttpMethod(headerFirstLine[0]); String path = headerFirstLine[1]; - if (method.isMethod("GET") && !path.contains(".")) { - path += ".html"; - } String version = headerFirstLine[2]; HttpRequestLine httpRequestLine = new HttpRequestLine(method, path, version); From 15e8bfb2bc12b5a5aa85a5de3ee71e5f4e9be82d Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Fri, 6 Sep 2024 07:55:30 +0900 Subject: [PATCH 14/30] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coyote/http11/controller/AbstractController.java | 8 ++++---- .../apache/coyote/http11/controller/LoginController.java | 4 ++-- .../apache/coyote/http11/controller/PageController.java | 4 ++-- .../coyote/http11/controller/RegisterController.java | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/AbstractController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/AbstractController.java index 28e5765e11..e52a2508da 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/controller/AbstractController.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/AbstractController.java @@ -8,15 +8,15 @@ public abstract class AbstractController implements Controller { @Override public HttpResponse service(HttpRequest httpRequest) { if (httpRequest.isMethod("GET")) { - return serviceGet(httpRequest); + return doGet(httpRequest); } else if (httpRequest.isMethod("POST")) { - return servicePost(httpRequest); + return doPost(httpRequest); } throw new RuntimeException(); } - abstract protected HttpResponse servicePost(HttpRequest httpRequest); + abstract protected HttpResponse doPost(HttpRequest httpRequest); - abstract protected HttpResponse serviceGet(HttpRequest httpRequest); + abstract protected HttpResponse doGet(HttpRequest httpRequest); } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/LoginController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/LoginController.java index 72dfa0cba9..62d0b29b6f 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/controller/LoginController.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/LoginController.java @@ -25,7 +25,7 @@ public class LoginController extends AbstractController { private final Session session = Session.getInstance(); @Override - protected HttpResponse servicePost(HttpRequest httpRequest) { + protected HttpResponse doPost(HttpRequest httpRequest) { String requestBody = httpRequest.getBody(); String[] token = requestBody.split("&"); String account = token[0].split("=")[1]; @@ -51,7 +51,7 @@ protected HttpResponse servicePost(HttpRequest httpRequest) { } @Override - protected HttpResponse serviceGet(HttpRequest httpRequest) { + protected HttpResponse doGet(HttpRequest httpRequest) { try { HttpStatusLine httpStatusLine = new HttpStatusLine(httpRequest.getVersion(), HttpStatusCode.OK); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/PageController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/PageController.java index 32bd4f9532..cda0b2f59a 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/controller/PageController.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/PageController.java @@ -15,12 +15,12 @@ public class PageController extends AbstractController { @Override - protected HttpResponse servicePost(HttpRequest httpRequest) { + protected HttpResponse doPost(HttpRequest httpRequest) { return null; } @Override - protected HttpResponse serviceGet(HttpRequest httpRequest) { + protected HttpResponse doGet(HttpRequest httpRequest) { try { HttpStatusLine httpStatusLine = new HttpStatusLine(httpRequest.getVersion(), HttpStatusCode.OK); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/RegisterController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/RegisterController.java index 7890787d48..736aa4d852 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/controller/RegisterController.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/RegisterController.java @@ -17,7 +17,7 @@ public class RegisterController extends AbstractController { @Override - protected HttpResponse servicePost(HttpRequest httpRequest) { + protected HttpResponse doPost(HttpRequest httpRequest) { String requestBody = httpRequest.getBody(); String[] token = requestBody.split("&"); String account = token[0].split("=")[1]; @@ -34,7 +34,7 @@ protected HttpResponse servicePost(HttpRequest httpRequest) { } @Override - protected HttpResponse serviceGet(HttpRequest httpRequest) { + protected HttpResponse doGet(HttpRequest httpRequest) { try { HttpStatusLine httpStatusLine = new HttpStatusLine(httpRequest.getVersion(), HttpStatusCode.OK); From 8dc695354a7e242af06fc8a1b8a9403d4ee84cd0 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Fri, 6 Sep 2024 07:59:43 +0900 Subject: [PATCH 15/30] =?UTF-8?q?refactor:=20requestLine=20=ED=8C=8C?= =?UTF-8?q?=EC=8B=B1=20=EB=A1=9C=EC=A7=81=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/HttpRequestConvertor.java | 11 +++-------- .../coyote/http11/httprequest/HttpRequestLine.java | 9 +++++---- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java index 136201b2e1..cd992e7d10 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java @@ -13,17 +13,12 @@ public class HttpRequestConvertor { public static HttpRequest convertHttpRequest(BufferedReader bufferedReader) { try { - String firstLine = bufferedReader.readLine(); - if (firstLine == null) { + String requestLine = bufferedReader.readLine(); + if (requestLine == null) { throw new RuntimeException("요청이 비어 있습니다."); } - String[] headerFirstLine = firstLine.split(" "); - HttpMethod method = HttpMethod.getHttpMethod(headerFirstLine[0]); - String path = headerFirstLine[1]; - String version = headerFirstLine[2]; - - HttpRequestLine httpRequestLine = new HttpRequestLine(method, path, version); + HttpRequestLine httpRequestLine = new HttpRequestLine(requestLine); Map headers = getHeaders(bufferedReader); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestLine.java b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestLine.java index 407015edbb..6befc619d4 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestLine.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestLine.java @@ -8,10 +8,11 @@ public class HttpRequestLine { private final String path; private final String version; - public HttpRequestLine(HttpMethod method, String path, String version) { - this.method = method; - this.path = path; - this.version = version; + public HttpRequestLine(String requestLine) { + String[] headerFirstLine = requestLine.split(" "); + this.method = HttpMethod.getHttpMethod(headerFirstLine[0]); + this.path = headerFirstLine[1]; + this.version = headerFirstLine[2]; } public boolean isMethod(String name) { From 2e1b3947cab0a3782a4ebff33e34c3d8af6f3cbd Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Wed, 4 Sep 2024 17:24:17 +0900 Subject: [PATCH 16/30] =?UTF-8?q?chore:=20=ED=95=99=EC=8A=B5=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- study/src/test/java/study/FileTest.java | 31 +++++++++++-------- study/src/test/java/study/IOStreamTest.java | 34 ++++++++++++++------- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/study/src/test/java/study/FileTest.java b/study/src/test/java/study/FileTest.java index e1b6cca042..e20b480062 100644 --- a/study/src/test/java/study/FileTest.java +++ b/study/src/test/java/study/FileTest.java @@ -1,13 +1,14 @@ package study; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collections; import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; /** * 웹서버는 사용자가 요청한 html 파일을 제공 할 수 있어야 한다. @@ -28,7 +29,7 @@ class FileTest { final String fileName = "nextstep.txt"; // todo - final String actual = ""; + final String actual = getClass().getClassLoader().getResource(fileName).getFile(); assertThat(actual).endsWith(fileName); } @@ -44,11 +45,17 @@ class FileTest { final String fileName = "nextstep.txt"; // todo - final Path path = null; - - // todo - final List actual = Collections.emptyList(); - - assertThat(actual).containsOnly("nextstep"); + try { + final Path path = Path.of(getClass().getClassLoader().getResource(fileName).toURI()); + // todo + try { + final List actual = Files.newBufferedReader(path).lines().toList(); + assertThat(actual).containsOnly("nextstep"); + } catch (IOException e) { + + } + } catch (URISyntaxException e) { + + } } } diff --git a/study/src/test/java/study/IOStreamTest.java b/study/src/test/java/study/IOStreamTest.java index 47a79356b6..13171b221a 100644 --- a/study/src/test/java/study/IOStreamTest.java +++ b/study/src/test/java/study/IOStreamTest.java @@ -1,14 +1,22 @@ package study; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import java.io.*; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; - /** * 자바는 스트림(Stream)으로부터 I/O를 사용한다. * 입출력(I/O)은 하나의 시스템에서 다른 시스템으로 데이터를 이동 시킬 때 사용한다. @@ -53,6 +61,7 @@ class OutputStream_학습_테스트 { * todo * OutputStream 객체의 write 메서드를 사용해서 테스트를 통과시킨다 */ + outputStream.write(bytes); final String actual = outputStream.toString(); @@ -78,6 +87,7 @@ class OutputStream_학습_테스트 { * flush를 사용해서 테스트를 통과시킨다. * ByteArrayOutputStream과 어떤 차이가 있을까? */ + outputStream.flush(); verify(outputStream, atLeastOnce()).flush(); outputStream.close(); @@ -96,6 +106,7 @@ class OutputStream_학습_테스트 { * try-with-resources를 사용한다. * java 9 이상에서는 변수를 try-with-resources로 처리할 수 있다. */ + outputStream.close(); verify(outputStream, atLeastOnce()).close(); } @@ -128,7 +139,7 @@ class InputStream_학습_테스트 { * todo * inputStream에서 바이트로 반환한 값을 문자열로 어떻게 바꿀까? */ - final String actual = ""; + final String actual = new String(inputStream.readAllBytes()); assertThat(actual).isEqualTo("🤩"); assertThat(inputStream.read()).isEqualTo(-1); @@ -148,6 +159,7 @@ class InputStream_학습_테스트 { * try-with-resources를 사용한다. * java 9 이상에서는 변수를 try-with-resources로 처리할 수 있다. */ + inputStream.close(); verify(inputStream, atLeastOnce()).close(); } @@ -169,12 +181,12 @@ class FilterStream_학습_테스트 { * 버퍼 크기를 지정하지 않으면 버퍼의 기본 사이즈는 얼마일까? */ @Test - void 필터인_BufferedInputStream를_사용해보자() { + void 필터인_BufferedInputStream를_사용해보자() throws IOException { final String text = "필터에 연결해보자."; final InputStream inputStream = new ByteArrayInputStream(text.getBytes()); - final InputStream bufferedInputStream = null; + final InputStream bufferedInputStream = new BufferedInputStream(inputStream); - final byte[] actual = new byte[0]; + final byte[] actual = bufferedInputStream.readAllBytes(); assertThat(bufferedInputStream).isInstanceOf(FilterInputStream.class); assertThat(actual).isEqualTo("필터에 연결해보자.".getBytes()); @@ -197,7 +209,7 @@ class InputStreamReader_학습_테스트 { * 필터인 BufferedReader를 사용하면 readLine 메서드를 사용해서 문자열(String)을 한 줄 씩 읽어올 수 있다. */ @Test - void BufferedReader를_사용하여_문자열을_읽어온다() { + void BufferedReader를_사용하여_문자열을_읽어온다() throws IOException { final String emoji = String.join("\r\n", "😀😃😄😁😆😅😂🤣🥲☺️😊", "😇🙂🙃😉😌😍🥰😘😗😙😚", @@ -205,7 +217,7 @@ class InputStreamReader_학습_테스트 { ""); final InputStream inputStream = new ByteArrayInputStream(emoji.getBytes()); - final StringBuilder actual = new StringBuilder(); + final StringBuilder actual = new StringBuilder(new String(inputStream.readAllBytes())); assertThat(actual).hasToString(emoji); } From a3b0989a6e6e48ab656bc360b52de297bb1d9bc8 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Wed, 4 Sep 2024 17:24:43 +0900 Subject: [PATCH 17/30] =?UTF-8?q?feat:=201=EB=8B=A8=EA=B3=84=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 85 +++++++++++++++++-- .../coyote/http11/Http11ProcessorTest.java | 9 +- 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index bb14184757..4e4da61303 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -1,13 +1,19 @@ package org.apache.coyote.http11; +import com.techcourse.db.InMemoryUserRepository; import com.techcourse.exception.UncheckedServletException; +import com.techcourse.model.User; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.Socket; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; import org.apache.coyote.Processor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.net.Socket; - public class Http11Processor implements Runnable, Processor { private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); @@ -28,20 +34,89 @@ public void run() { public void process(final Socket connection) { try (final var inputStream = connection.getInputStream(); final var outputStream = connection.getOutputStream()) { + var bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + String line = bufferedReader.readLine(); + System.out.println(line); + if (line == null || line.isEmpty()) { + return; + } + + String[] requestLine = line.split(" "); + if (requestLine.length < 3) { + return; + } + String uri = requestLine[1]; + String path = uri; + String queryString = ""; + if (uri.contains("?")) { + int index = uri.indexOf("?"); + path = uri.substring(0, index); + queryString = uri.substring(index + 1); + } + + var responseBody = ""; + var contentType = "text/html;charset=utf-8 \r\n"; + if (!path.equals("/") && !path.contains(".")) { + path += ".html"; + } + + if (path.equals("/")) { + responseBody = "Hello world!"; + } else { + String fileName = "static" + path; + var resourceUrl = getClass().getClassLoader().getResource(fileName); + if (resourceUrl == null) { + final var response = String.join("\r\n", + "HTTP/1.1 404 Not Found", + "Content-Type: text/html;charset=utf-8 \r\n", + "", + "

404 Not Found

"); + outputStream.write(response.getBytes()); + outputStream.flush(); + return; + } + Path filePath = Path.of(resourceUrl.toURI()); + responseBody = new String(Files.readAllBytes(filePath)); + } - final var responseBody = "Hello world!"; + if (path.endsWith(".css")) { + contentType = "text/css;charset=utf-8 \r\n"; + } else if (path.endsWith(".js")) { + contentType = "application/javascript;charset=utf-8 \r\n"; + } else if (path.endsWith(".html")) { + contentType = "text/html;charset=utf-8 \r\n"; + } else if (path.endsWith(".png")) { + contentType = "image/png \r\n"; + } else if (path.endsWith(".jpg") || path.endsWith(".jpeg")) { + contentType = "image/jpeg \r\n"; + } final var response = String.join("\r\n", "HTTP/1.1 200 OK ", - "Content-Type: text/html;charset=utf-8 ", + "Content-Type: " + contentType + "Content-Length: " + responseBody.getBytes().length + " ", "", responseBody); + if (!queryString.isEmpty()) { + String[] query = queryString.split("&"); + String account = query[0].split("=")[1]; + String password = query[1].split("=")[1]; + System.out.println(account + " " + password); + User user = InMemoryUserRepository.findByAccount(account) + .orElseThrow(); + if (user.checkPassword(password)) { + log.info(user.toString()); + } else { + log.error("비밀번호 불일치"); + } + } outputStream.write(response.getBytes()); outputStream.flush(); } catch (IOException | UncheckedServletException e) { log.error(e.getMessage(), e); + } catch (URISyntaxException e) { + throw new RuntimeException(e); } } } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java b/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java index 2aba8c56e0..9d9071b6f9 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java @@ -1,14 +1,13 @@ package org.apache.coyote.http11; -import org.junit.jupiter.api.Test; -import support.StubSocket; +import static org.assertj.core.api.Assertions.assertThat; import java.io.File; import java.io.IOException; import java.net.URL; import java.nio.file.Files; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; +import support.StubSocket; class Http11ProcessorTest { @@ -52,7 +51,7 @@ void index() throws IOException { final URL resource = getClass().getClassLoader().getResource("static/index.html"); var expected = "HTTP/1.1 200 OK \r\n" + "Content-Type: text/html;charset=utf-8 \r\n" + - "Content-Length: 5564 \r\n" + + "Content-Length: 5670 \r\n" + "\r\n"+ new String(Files.readAllBytes(new File(resource.getFile()).toPath())); From 70e07cea2447a7b806334913ee9b9c3ca54e0a3f Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Wed, 4 Sep 2024 19:16:50 +0900 Subject: [PATCH 18/30] =?UTF-8?q?feat:=20login=20=EC=84=B1=EA=B3=B5=20?= =?UTF-8?q?=EC=8B=9C=20index.html=EB=A1=9C=20redirect=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/apache/coyote/http11/Http11Processor.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 4e4da61303..b083228547 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -91,7 +91,7 @@ public void process(final Socket connection) { contentType = "image/jpeg \r\n"; } - final var response = String.join("\r\n", + var response = String.join("\r\n", "HTTP/1.1 200 OK ", "Content-Type: " + contentType + "Content-Length: " + responseBody.getBytes().length + " ", @@ -105,8 +105,14 @@ public void process(final Socket connection) { User user = InMemoryUserRepository.findByAccount(account) .orElseThrow(); if (user.checkPassword(password)) { + response = String.join("\r\n", + "HTTP/1.1 302 Found ", + "Location: " + "/index.html"); log.info(user.toString()); } else { + response = String.join("\r\n", + "HTTP/1.1 302 Found ", + "Location: " + "/401.html"); log.error("비밀번호 불일치"); } } From d36c2f6130f4bb2169b020aee86973cb179a6572 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Wed, 4 Sep 2024 20:44:05 +0900 Subject: [PATCH 19/30] =?UTF-8?q?feat:=20login=20=EC=84=B1=EA=B3=B5=20?= =?UTF-8?q?=EC=8B=9C=20=EC=BF=A0=ED=82=A4=20=EC=84=A4=EC=A0=95=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 41 +++++++++++++++---- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index b083228547..a0ebb3a220 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -10,6 +10,7 @@ import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.UUID; import org.apache.coyote.Processor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,19 +36,37 @@ public void process(final Socket connection) { try (final var inputStream = connection.getInputStream(); final var outputStream = connection.getOutputStream()) { var bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); - String line = bufferedReader.readLine(); - System.out.println(line); - if (line == null || line.isEmpty()) { - return; + + var requestBuilder = new StringBuilder(); + String line; + + while ((line = bufferedReader.readLine()) != null && !line.isEmpty()) { + requestBuilder.append(line + System.lineSeparator()); } +// System.out.println(requestBuilder); + line = requestBuilder.toString(); - String[] requestLine = line.split(" "); + String[] requestLine = line.split(System.lineSeparator())[0].split(" "); if (requestLine.length < 3) { return; } + String method = requestLine[0]; String uri = requestLine[1]; String path = uri; String queryString = ""; + if (method.equals("POST") && path.equals("/register")) { + String contentLengthS = line.split(System.lineSeparator())[3].split(": ")[1]; + int contentLength = Integer.parseInt(contentLengthS); + char[] buffer = new char[contentLength]; + bufferedReader.read(buffer, 0, contentLength); + String requestBody = new String(buffer); + String[] token = requestBody.split("&"); + String account = token[0].split("=")[1]; + String email = token[1].split("=")[1]; + String password = token[2].split("=")[1]; + User user = new User(account, password, email); + InMemoryUserRepository.save(user); + } if (uri.contains("?")) { int index = uri.indexOf("?"); path = uri.substring(0, index); @@ -56,13 +75,13 @@ public void process(final Socket connection) { var responseBody = ""; var contentType = "text/html;charset=utf-8 \r\n"; - if (!path.equals("/") && !path.contains(".")) { + if (!path.equals("/") && !path.contains(".") && method.equals("GET")) { path += ".html"; } if (path.equals("/")) { responseBody = "Hello world!"; - } else { + } else if (method.equals("GET")) { String fileName = "static" + path; var resourceUrl = getClass().getClassLoader().getResource(fileName); if (resourceUrl == null) { @@ -101,12 +120,13 @@ public void process(final Socket connection) { String[] query = queryString.split("&"); String account = query[0].split("=")[1]; String password = query[1].split("=")[1]; - System.out.println(account + " " + password); User user = InMemoryUserRepository.findByAccount(account) .orElseThrow(); + UUID uuid = UUID.randomUUID(); if (user.checkPassword(password)) { response = String.join("\r\n", "HTTP/1.1 302 Found ", + "Set-Cookie: JSESSIONID=" + uuid, "Location: " + "/index.html"); log.info(user.toString()); } else { @@ -116,6 +136,11 @@ public void process(final Socket connection) { log.error("비밀번호 불일치"); } } + if (method.equals("POST") && path.equals("/register")) { + response = String.join("\r\n", + "HTTP/1.1 302 Found ", + "Location: " + "/index.html"); + } outputStream.write(response.getBytes()); outputStream.flush(); From 9dd932d7e729e09440562e3c09ba904959e5b97a Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 5 Sep 2024 10:03:43 +0900 Subject: [PATCH 20/30] =?UTF-8?q?feat:=20=EC=BF=A0=ED=82=A4=EA=B0=80=20?= =?UTF-8?q?=EC=9E=88=EC=9D=84=20=EC=8B=9C=20=EC=9E=90=EB=8F=99=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 87 ++++++++++--------- .../org/apache/coyote/http11/HttpRequest.java | 33 +++++++ .../apache/coyote/http11/HttpRequestBody.java | 30 +++++++ .../coyote/http11/HttpRequestHeader.java | 68 +++++++++++++++ .../apache/coyote/http11/HttpResponse.java | 4 + .../org/apache/coyote/http11/Session.java | 36 ++++++++ tomcat/src/main/resources/static/login.html | 2 +- 7 files changed, 216 insertions(+), 44 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/Session.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index a0ebb3a220..d2b5008cf8 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -20,9 +20,11 @@ public class Http11Processor implements Runnable, Processor { private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); private final Socket connection; + private final Session session; public Http11Processor(final Socket connection) { this.connection = connection; + this.session = Session.getInstance(); } @Override @@ -37,29 +39,11 @@ public void process(final Socket connection) { final var outputStream = connection.getOutputStream()) { var bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); - var requestBuilder = new StringBuilder(); - String line; + HttpRequest httpRequest = new HttpRequest(bufferedReader); + System.out.println(httpRequest); - while ((line = bufferedReader.readLine()) != null && !line.isEmpty()) { - requestBuilder.append(line + System.lineSeparator()); - } -// System.out.println(requestBuilder); - line = requestBuilder.toString(); - - String[] requestLine = line.split(System.lineSeparator())[0].split(" "); - if (requestLine.length < 3) { - return; - } - String method = requestLine[0]; - String uri = requestLine[1]; - String path = uri; - String queryString = ""; - if (method.equals("POST") && path.equals("/register")) { - String contentLengthS = line.split(System.lineSeparator())[3].split(": ")[1]; - int contentLength = Integer.parseInt(contentLengthS); - char[] buffer = new char[contentLength]; - bufferedReader.read(buffer, 0, contentLength); - String requestBody = new String(buffer); + if (httpRequest.getHttpRequestHeader().getMethod().equals("POST") && httpRequest.getHttpRequestHeader().getPath().equals("/register")) { + String requestBody = httpRequest.getHttpRequestBody().getBody(); String[] token = requestBody.split("&"); String account = token[0].split("=")[1]; String email = token[1].split("=")[1]; @@ -67,22 +51,17 @@ public void process(final Socket connection) { User user = new User(account, password, email); InMemoryUserRepository.save(user); } - if (uri.contains("?")) { - int index = uri.indexOf("?"); - path = uri.substring(0, index); - queryString = uri.substring(index + 1); - } var responseBody = ""; var contentType = "text/html;charset=utf-8 \r\n"; - if (!path.equals("/") && !path.contains(".") && method.equals("GET")) { - path += ".html"; + if (!httpRequest.getHttpRequestHeader().getPath().equals("/") && !httpRequest.getHttpRequestHeader().getPath().contains(".") && httpRequest.getHttpRequestHeader().getMethod().equals("GET")) { + httpRequest.getHttpRequestHeader().setDefaultPath(); } - if (path.equals("/")) { + if (httpRequest.getHttpRequestHeader().getPath().equals("/")) { responseBody = "Hello world!"; - } else if (method.equals("GET")) { - String fileName = "static" + path; + } else if (httpRequest.getHttpRequestHeader().getMethod().equals("GET")) { + String fileName = "static" + httpRequest.getHttpRequestHeader().getPath(); var resourceUrl = getClass().getClassLoader().getResource(fileName); if (resourceUrl == null) { final var response = String.join("\r\n", @@ -98,32 +77,35 @@ public void process(final Socket connection) { responseBody = new String(Files.readAllBytes(filePath)); } - if (path.endsWith(".css")) { + if (httpRequest.getHttpRequestHeader().getPath().endsWith(".css")) { contentType = "text/css;charset=utf-8 \r\n"; - } else if (path.endsWith(".js")) { + } else if (httpRequest.getHttpRequestHeader().getPath().endsWith(".js")) { contentType = "application/javascript;charset=utf-8 \r\n"; - } else if (path.endsWith(".html")) { + } else if (httpRequest.getHttpRequestHeader().getPath().endsWith(".html")) { contentType = "text/html;charset=utf-8 \r\n"; - } else if (path.endsWith(".png")) { + } else if (httpRequest.getHttpRequestHeader().getPath().endsWith(".png")) { contentType = "image/png \r\n"; - } else if (path.endsWith(".jpg") || path.endsWith(".jpeg")) { + } else if (httpRequest.getHttpRequestHeader().getPath().endsWith(".jpg") || httpRequest.getHttpRequestHeader().getPath().endsWith(".jpeg")) { contentType = "image/jpeg \r\n"; } var response = String.join("\r\n", "HTTP/1.1 200 OK ", "Content-Type: " + contentType + - "Content-Length: " + responseBody.getBytes().length + " ", + "Content-Length: " + responseBody.getBytes().length + " ", "", responseBody); - if (!queryString.isEmpty()) { - String[] query = queryString.split("&"); - String account = query[0].split("=")[1]; - String password = query[1].split("=")[1]; + if (httpRequest.getHttpRequestHeader().getMethod().equals("POST") && httpRequest.getHttpRequestHeader().getPath().equals("/login")) { + String requestBody = httpRequest.getHttpRequestBody().getBody(); + String[] token = requestBody.split("&"); + String account = token[0].split("=")[1]; + String password = token[1].split("=")[1]; + User user = InMemoryUserRepository.findByAccount(account) .orElseThrow(); UUID uuid = UUID.randomUUID(); if (user.checkPassword(password)) { + session.save(uuid.toString(), user); response = String.join("\r\n", "HTTP/1.1 302 Found ", "Set-Cookie: JSESSIONID=" + uuid, @@ -136,11 +118,30 @@ public void process(final Socket connection) { log.error("비밀번호 불일치"); } } - if (method.equals("POST") && path.equals("/register")) { + if (httpRequest.getHttpRequestHeader().getMethod().equals("POST") && httpRequest.getHttpRequestHeader().getPath().equals("/register")) { response = String.join("\r\n", "HTTP/1.1 302 Found ", "Location: " + "/index.html"); } + if (httpRequest.getHttpRequestHeader().getMethod().equals("GET") && httpRequest.getHttpRequestHeader().getPath().equals("/login.html")) { + if (!httpRequest.getHttpRequestHeader().containsKey("Cookie")) { + String[] cookies = httpRequest.getHttpRequestHeader().getValue("Cookie").split("; "); + String cookie = ""; + for (String c : cookies) { + if (c.contains("JSESSIONID")) { + cookie = c.split("=")[1]; + } + } + if (session.containsUser(cookie)) { + User user = session.getUser(cookie); + response = String.join("\r\n", + "HTTP/1.1 302 Found ", + "Set-Cookie: JSESSIONID=" + cookie, + "Location: " + "/index.html"); + log.info(user.toString()); + } + } + } outputStream.write(response.getBytes()); outputStream.flush(); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java new file mode 100644 index 0000000000..ac63352a15 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java @@ -0,0 +1,33 @@ +package org.apache.coyote.http11; + +import java.io.BufferedReader; + +public class HttpRequest { + + private final HttpRequestHeader httpRequestHeader; + private HttpRequestBody httpRequestBody; + + public HttpRequest(BufferedReader bufferedReader) { + this.httpRequestHeader = new HttpRequestHeader(bufferedReader); + if (httpRequestHeader.containsKey("Content-Length")) { + int contentLength = Integer.parseInt(httpRequestHeader.getValue("Content-Length")); + this.httpRequestBody = new HttpRequestBody(contentLength, bufferedReader); + } + } + + public HttpRequestHeader getHttpRequestHeader() { + return httpRequestHeader; + } + + public HttpRequestBody getHttpRequestBody() { + return httpRequestBody; + } + + @Override + public String toString() { + return "HttpRequest{\n" + + "httpRequestHeader=" + httpRequestHeader + + ",\n httpRequestBody=" + httpRequestBody + + "\n}"; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java new file mode 100644 index 0000000000..1e17f50227 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java @@ -0,0 +1,30 @@ +package org.apache.coyote.http11; + +import java.io.BufferedReader; +import java.io.IOException; + +public class HttpRequestBody { + + private final String body; + + public HttpRequestBody(int contentLength, BufferedReader bufferedReader) { + try { + char[] buffer = new char[contentLength]; + bufferedReader.read(buffer, 0, contentLength); + this.body = new String(buffer); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public String getBody() { + return body; + } + + @Override + public String toString() { + return "HttpRequestBody{\n" + + "body='" + body + '\'' + + "\n}"; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java new file mode 100644 index 0000000000..e1f4532543 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java @@ -0,0 +1,68 @@ +package org.apache.coyote.http11; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class HttpRequestHeader { + + private final String method; + private String path; + private final String version; + private final Map headers; + + public HttpRequestHeader(BufferedReader bufferedReader) { + try { + String[] headerFirstLine = bufferedReader.readLine().split(" "); + if (headerFirstLine.length < 3) { + throw new RuntimeException("제대로 된 요청이 아닙니다. header의 첫 줄의 값이 3개 미만입니다."); + } + this.method = headerFirstLine[0]; + this.path = headerFirstLine[1]; + this.version = headerFirstLine[2]; + String line; + headers = new HashMap<>(); + while ((line = bufferedReader.readLine()) != null && !line.isEmpty()) { + String[] requestLine = line.split(": "); + headers.put(requestLine[0], requestLine[1]); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public boolean containsKey(String key) { + return headers.containsKey(key); + } + + public void setDefaultPath() { + path += ".html"; + } + + public String getValue(String key) { + return headers.get(key); + } + + public String getMethod() { + return method; + } + + public String getPath() { + return path; + } + + public String getVersion() { + return version; + } + + @Override + public String toString() { + return "HttpRequestHeader{" + + "\nmethod='" + method + '\'' + + ", \npath='" + path + '\'' + + ", \nversion='" + version + '\'' + + ", \nheaders=" + headers + + '}'; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java new file mode 100644 index 0000000000..9ac8eece44 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java @@ -0,0 +1,4 @@ +package org.apache.coyote.http11; + +public class HttpResponse { +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Session.java b/tomcat/src/main/java/org/apache/coyote/http11/Session.java new file mode 100644 index 0000000000..424b9cf6f9 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/Session.java @@ -0,0 +1,36 @@ +package org.apache.coyote.http11; + +import com.techcourse.model.User; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class Session { + + private static final Session INSTANCE = new Session(); + private final Map userMap; + + private Session() { + this.userMap = new HashMap<>(); + } + + public static Session getInstance() { + return INSTANCE; + } + + public void save(String uuid, User user) { + userMap.put(uuid, user); + } + + public boolean containsUser(String uuid) { + return userMap.containsKey(uuid); + } + + public User getUser(String uuid) { + return userMap.get(uuid); + } + + public Set getKeySet() { + return userMap.keySet(); + } +} diff --git a/tomcat/src/main/resources/static/login.html b/tomcat/src/main/resources/static/login.html index f4ed9de875..bc933357f2 100644 --- a/tomcat/src/main/resources/static/login.html +++ b/tomcat/src/main/resources/static/login.html @@ -20,7 +20,7 @@

로그인

- +
From 640b619018a871d4232c7397c080ee482b1ba175 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 5 Sep 2024 13:51:14 +0900 Subject: [PATCH 21/30] =?UTF-8?q?refactor:=20HttpRequest=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../techcourse/db/InMemoryUserRepository.java | 2 +- .../apache/coyote/http11/Http11Processor.java | 43 ++++++++--------- .../org/apache/coyote/http11/HttpMethod.java | 27 +++++++++++ .../org/apache/coyote/http11/HttpRequest.java | 47 +++++++++++++++---- .../apache/coyote/http11/HttpRequestBody.java | 13 +---- .../coyote/http11/HttpRequestConvertor.java | 43 +++++++++++++++++ .../coyote/http11/HttpRequestHeader.java | 40 ++++++---------- 7 files changed, 146 insertions(+), 69 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java diff --git a/tomcat/src/main/java/com/techcourse/db/InMemoryUserRepository.java b/tomcat/src/main/java/com/techcourse/db/InMemoryUserRepository.java index d3fa57feeb..b041a264ea 100644 --- a/tomcat/src/main/java/com/techcourse/db/InMemoryUserRepository.java +++ b/tomcat/src/main/java/com/techcourse/db/InMemoryUserRepository.java @@ -11,7 +11,7 @@ public class InMemoryUserRepository { private static final Map database = new ConcurrentHashMap<>(); static { - final User user = new User(1L, "gugu", "password", "hkkang@woowahan.com"); + final User user = new User(1L, "gugu", "1", "hkkang@woowahan.com"); database.put(user.getAccount(), user); } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index d2b5008cf8..260329e143 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -35,14 +35,14 @@ public void run() { @Override public void process(final Socket connection) { - try (final var inputStream = connection.getInputStream(); - final var outputStream = connection.getOutputStream()) { + try ( + final var inputStream = connection.getInputStream(); + final var outputStream = connection.getOutputStream() + ) { var bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + HttpRequest httpRequest = HttpRequestConvertor.convertHttpRequest(bufferedReader); - HttpRequest httpRequest = new HttpRequest(bufferedReader); - System.out.println(httpRequest); - - if (httpRequest.getHttpRequestHeader().getMethod().equals("POST") && httpRequest.getHttpRequestHeader().getPath().equals("/register")) { + if (httpRequest.isMethod("POST") && httpRequest.isPath("/register")) { String requestBody = httpRequest.getHttpRequestBody().getBody(); String[] token = requestBody.split("&"); String account = token[0].split("=")[1]; @@ -54,14 +54,11 @@ public void process(final Socket connection) { var responseBody = ""; var contentType = "text/html;charset=utf-8 \r\n"; - if (!httpRequest.getHttpRequestHeader().getPath().equals("/") && !httpRequest.getHttpRequestHeader().getPath().contains(".") && httpRequest.getHttpRequestHeader().getMethod().equals("GET")) { - httpRequest.getHttpRequestHeader().setDefaultPath(); - } - if (httpRequest.getHttpRequestHeader().getPath().equals("/")) { + if (httpRequest.isPath("/")) { responseBody = "Hello world!"; - } else if (httpRequest.getHttpRequestHeader().getMethod().equals("GET")) { - String fileName = "static" + httpRequest.getHttpRequestHeader().getPath(); + } else if (httpRequest.isMethod("GET")) { + String fileName = "static" + httpRequest.getPath(); var resourceUrl = getClass().getClassLoader().getResource(fileName); if (resourceUrl == null) { final var response = String.join("\r\n", @@ -77,15 +74,15 @@ public void process(final Socket connection) { responseBody = new String(Files.readAllBytes(filePath)); } - if (httpRequest.getHttpRequestHeader().getPath().endsWith(".css")) { + if (httpRequest.getPath().endsWith(".css")) { contentType = "text/css;charset=utf-8 \r\n"; - } else if (httpRequest.getHttpRequestHeader().getPath().endsWith(".js")) { + } else if (httpRequest.getPath().endsWith(".js")) { contentType = "application/javascript;charset=utf-8 \r\n"; - } else if (httpRequest.getHttpRequestHeader().getPath().endsWith(".html")) { + } else if (httpRequest.getPath().endsWith(".html")) { contentType = "text/html;charset=utf-8 \r\n"; - } else if (httpRequest.getHttpRequestHeader().getPath().endsWith(".png")) { + } else if (httpRequest.getPath().endsWith(".png")) { contentType = "image/png \r\n"; - } else if (httpRequest.getHttpRequestHeader().getPath().endsWith(".jpg") || httpRequest.getHttpRequestHeader().getPath().endsWith(".jpeg")) { + } else if (httpRequest.getPath().endsWith(".jpg")) { contentType = "image/jpeg \r\n"; } @@ -95,8 +92,8 @@ public void process(final Socket connection) { "Content-Length: " + responseBody.getBytes().length + " ", "", responseBody); - if (httpRequest.getHttpRequestHeader().getMethod().equals("POST") && httpRequest.getHttpRequestHeader().getPath().equals("/login")) { - String requestBody = httpRequest.getHttpRequestBody().getBody(); + if (httpRequest.isMethod("POST") && httpRequest.isPath("/login")) { + String requestBody = httpRequest.getBody(); String[] token = requestBody.split("&"); String account = token[0].split("=")[1]; String password = token[1].split("=")[1]; @@ -118,14 +115,14 @@ public void process(final Socket connection) { log.error("비밀번호 불일치"); } } - if (httpRequest.getHttpRequestHeader().getMethod().equals("POST") && httpRequest.getHttpRequestHeader().getPath().equals("/register")) { + if (httpRequest.isMethod("POST") && httpRequest.isPath("/register")) { response = String.join("\r\n", "HTTP/1.1 302 Found ", "Location: " + "/index.html"); } - if (httpRequest.getHttpRequestHeader().getMethod().equals("GET") && httpRequest.getHttpRequestHeader().getPath().equals("/login.html")) { - if (!httpRequest.getHttpRequestHeader().containsKey("Cookie")) { - String[] cookies = httpRequest.getHttpRequestHeader().getValue("Cookie").split("; "); + if (httpRequest.isMethod("GET") && httpRequest.isPath("/login.html")) { + if (httpRequest.containsKey("Cookie")) { + String[] cookies = httpRequest.getValue("Cookie").split("; "); String cookie = ""; for (String c : cookies) { if (c.contains("JSESSIONID")) { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java new file mode 100644 index 0000000000..4cb9976c42 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpMethod.java @@ -0,0 +1,27 @@ +package org.apache.coyote.http11; + +import java.util.Arrays; + +public enum HttpMethod { + + GET("GET"), + POST("POST") + ; + + private final String name; + + HttpMethod(String name) { + this.name = name; + } + + public static HttpMethod getHttpMethod(String name) { + return Arrays.stream(values()) + .filter(httpMethod -> httpMethod.name.equals(name)) + .findAny() + .orElseThrow(); + } + + public boolean isMethod(String name) { + return this.name.equals(name); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java index ac63352a15..f2970d2967 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java @@ -1,18 +1,49 @@ package org.apache.coyote.http11; -import java.io.BufferedReader; - public class HttpRequest { private final HttpRequestHeader httpRequestHeader; private HttpRequestBody httpRequestBody; - public HttpRequest(BufferedReader bufferedReader) { - this.httpRequestHeader = new HttpRequestHeader(bufferedReader); - if (httpRequestHeader.containsKey("Content-Length")) { - int contentLength = Integer.parseInt(httpRequestHeader.getValue("Content-Length")); - this.httpRequestBody = new HttpRequestBody(contentLength, bufferedReader); - } + public HttpRequest(HttpRequestHeader httpRequestHeader, HttpRequestBody httpRequestBody) { + this.httpRequestHeader = httpRequestHeader; + this.httpRequestBody = httpRequestBody; + } + + public HttpRequest(HttpRequestHeader httpRequestHeader) { + this(httpRequestHeader, null); + } + + public boolean isMethod(String name) { + return httpRequestHeader.isMethod(name); + } + + public boolean isPath(String path) { + return httpRequestHeader.isPath(path); + } + + public boolean containsKey(String key) { + return httpRequestHeader.containsKey(key); + } + + public String getValue(String key) { + return httpRequestHeader.getValue(key); + } + + public HttpMethod getMethod() { + return httpRequestHeader.getMethod(); + } + + public String getPath() { + return httpRequestHeader.getPath(); + } + + public String getVersion() { + return httpRequestHeader.getVersion(); + } + + public String getBody() { + return httpRequestBody.getBody(); } public HttpRequestHeader getHttpRequestHeader() { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java index 1e17f50227..2a59b78576 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java @@ -1,20 +1,11 @@ package org.apache.coyote.http11; -import java.io.BufferedReader; -import java.io.IOException; - public class HttpRequestBody { private final String body; - public HttpRequestBody(int contentLength, BufferedReader bufferedReader) { - try { - char[] buffer = new char[contentLength]; - bufferedReader.read(buffer, 0, contentLength); - this.body = new String(buffer); - } catch (IOException e) { - throw new RuntimeException(e); - } + public HttpRequestBody(String body) { + this.body = body; } public String getBody() { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java new file mode 100644 index 0000000000..f038703557 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java @@ -0,0 +1,43 @@ +package org.apache.coyote.http11; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class HttpRequestConvertor { + + public static HttpRequest convertHttpRequest(BufferedReader bufferedReader) { + try { + String firstLine = bufferedReader.readLine(); + if (firstLine == null) { + throw new RuntimeException("요청이 비어 있습니다."); + } + String[] headerFirstLine = firstLine.split(" "); + HttpMethod method = HttpMethod.getHttpMethod(headerFirstLine[0]); + String path = headerFirstLine[1]; + if (method.isMethod("GET") && !path.contains(".")) { + path += ".html"; + } + String version = headerFirstLine[2]; + String line; + Map headers = new HashMap<>(); + while ((line = bufferedReader.readLine()) != null && !line.isEmpty()) { + String[] requestLine = line.split(": "); + headers.put(requestLine[0], requestLine[1]); + } + HttpRequestHeader httpRequestHeader = new HttpRequestHeader(method, path, version, headers); + if (httpRequestHeader.containsKey("Content-Length")) { + int contentLength = Integer.parseInt(httpRequestHeader.getValue("Content-Length")); + char[] buffer = new char[contentLength]; + bufferedReader.read(buffer, 0, contentLength); + String body = new String(buffer); + HttpRequestBody httpRequestBody = new HttpRequestBody(body); + return new HttpRequest(httpRequestHeader, httpRequestBody); + } + return new HttpRequest(httpRequestHeader); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java index e1f4532543..a63821e8d5 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java @@ -1,50 +1,38 @@ package org.apache.coyote.http11; -import java.io.BufferedReader; -import java.io.IOException; -import java.util.HashMap; import java.util.Map; public class HttpRequestHeader { - private final String method; - private String path; + private final HttpMethod method; + private final String path; private final String version; private final Map headers; - public HttpRequestHeader(BufferedReader bufferedReader) { - try { - String[] headerFirstLine = bufferedReader.readLine().split(" "); - if (headerFirstLine.length < 3) { - throw new RuntimeException("제대로 된 요청이 아닙니다. header의 첫 줄의 값이 3개 미만입니다."); - } - this.method = headerFirstLine[0]; - this.path = headerFirstLine[1]; - this.version = headerFirstLine[2]; - String line; - headers = new HashMap<>(); - while ((line = bufferedReader.readLine()) != null && !line.isEmpty()) { - String[] requestLine = line.split(": "); - headers.put(requestLine[0], requestLine[1]); - } - } catch (IOException e) { - throw new RuntimeException(e); - } + public HttpRequestHeader(HttpMethod method, String path, String version, Map headers) { + this.method = method; + this.path = path; + this.version = version; + this.headers = headers; } public boolean containsKey(String key) { return headers.containsKey(key); } - public void setDefaultPath() { - path += ".html"; + public boolean isMethod(String name) { + return method.isMethod(name); + } + + public boolean isPath(String path) { + return this.path.equals(path); } public String getValue(String key) { return headers.get(key); } - public String getMethod() { + public HttpMethod getMethod() { return method; } From f015d37706dd3ec0b4b169e327931a61a475a8f5 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 5 Sep 2024 17:14:17 +0900 Subject: [PATCH 22/30] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coyote/http11/HttpRequestConvertor.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java index f038703557..462c62e7eb 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java @@ -13,6 +13,7 @@ public static HttpRequest convertHttpRequest(BufferedReader bufferedReader) { if (firstLine == null) { throw new RuntimeException("요청이 비어 있습니다."); } + String[] headerFirstLine = firstLine.split(" "); HttpMethod method = HttpMethod.getHttpMethod(headerFirstLine[0]); String path = headerFirstLine[1]; @@ -20,24 +21,34 @@ public static HttpRequest convertHttpRequest(BufferedReader bufferedReader) { path += ".html"; } String version = headerFirstLine[2]; - String line; - Map headers = new HashMap<>(); - while ((line = bufferedReader.readLine()) != null && !line.isEmpty()) { - String[] requestLine = line.split(": "); - headers.put(requestLine[0], requestLine[1]); - } + Map headers = getHeaders(bufferedReader); + HttpRequestHeader httpRequestHeader = new HttpRequestHeader(method, path, version, headers); + if (httpRequestHeader.containsKey("Content-Length")) { int contentLength = Integer.parseInt(httpRequestHeader.getValue("Content-Length")); char[] buffer = new char[contentLength]; bufferedReader.read(buffer, 0, contentLength); String body = new String(buffer); HttpRequestBody httpRequestBody = new HttpRequestBody(body); + return new HttpRequest(httpRequestHeader, httpRequestBody); } + return new HttpRequest(httpRequestHeader); } catch (IOException e) { throw new RuntimeException(e); } } + + private static Map getHeaders(BufferedReader bufferedReader) throws IOException { + String line; + Map headers = new HashMap<>(); + while ((line = bufferedReader.readLine()) != null && !line.isEmpty()) { + String[] requestLine = line.split(": "); + headers.put(requestLine[0], requestLine[1]); + } + + return headers; + } } From c36e04a7f5748cfdc701c71b748fc503892c416c Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 5 Sep 2024 17:15:44 +0900 Subject: [PATCH 23/30] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coyote/http11/HttpRequestConvertor.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java index 462c62e7eb..38c2d8660b 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java @@ -26,11 +26,7 @@ public static HttpRequest convertHttpRequest(BufferedReader bufferedReader) { HttpRequestHeader httpRequestHeader = new HttpRequestHeader(method, path, version, headers); if (httpRequestHeader.containsKey("Content-Length")) { - int contentLength = Integer.parseInt(httpRequestHeader.getValue("Content-Length")); - char[] buffer = new char[contentLength]; - bufferedReader.read(buffer, 0, contentLength); - String body = new String(buffer); - HttpRequestBody httpRequestBody = new HttpRequestBody(body); + HttpRequestBody httpRequestBody = getHttpRequestBody(bufferedReader, httpRequestHeader); return new HttpRequest(httpRequestHeader, httpRequestBody); } @@ -41,6 +37,18 @@ public static HttpRequest convertHttpRequest(BufferedReader bufferedReader) { } } + private static HttpRequestBody getHttpRequestBody( + BufferedReader bufferedReader, + HttpRequestHeader httpRequestHeader + ) throws IOException { + int contentLength = Integer.parseInt(httpRequestHeader.getValue("Content-Length")); + char[] buffer = new char[contentLength]; + bufferedReader.read(buffer, 0, contentLength); + String body = new String(buffer); + HttpRequestBody httpRequestBody = new HttpRequestBody(body); + return httpRequestBody; + } + private static Map getHeaders(BufferedReader bufferedReader) throws IOException { String line; Map headers = new HashMap<>(); From 7fa779c2222e500808a82baf2513e4e780de8867 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Thu, 5 Sep 2024 22:42:13 +0900 Subject: [PATCH 24/30] =?UTF-8?q?refactor:=20Controller=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/apache/coyote/http11/ContentType.java | 32 +++++++ .../apache/coyote/http11/Http11Processor.java | 44 ++------- .../coyote/http11/HttpRequestConvertor.java | 16 +++- .../coyote/http11/HttpRequestHeader.java | 56 ------------ .../apache/coyote/http11/HttpResponse.java | 4 - .../apache/coyote/http11/HttpStatusCode.java | 24 +++++ .../apache/coyote/http11/RequestMapping.java | 27 ++++++ .../http11/controller/AbstractController.java | 22 +++++ .../coyote/http11/controller/Controller.java | 9 ++ .../http11/controller/LoginController.java | 91 +++++++++++++++++++ .../http11/controller/PageController.java | 45 +++++++++ .../http11/controller/RegisterController.java | 55 +++++++++++ .../http11/{ => httprequest}/HttpRequest.java | 24 +++-- .../{ => httprequest}/HttpRequestBody.java | 2 +- .../http11/httprequest/HttpRequestHeader.java | 27 ++++++ .../http11/httprequest/HttpRequestLine.java | 36 ++++++++ .../http11/httpresponse/HttpResponse.java | 73 +++++++++++++++ .../http11/httpresponse/HttpResponseBody.java | 14 +++ .../httpresponse/HttpResponseHeader.java | 21 +++++ .../http11/httpresponse/HttpStatusLine.java | 22 +++++ 20 files changed, 533 insertions(+), 111 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/ContentType.java delete mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java delete mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/HttpStatusCode.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/RequestMapping.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/controller/AbstractController.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/controller/Controller.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/controller/LoginController.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/controller/PageController.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/controller/RegisterController.java rename tomcat/src/main/java/org/apache/coyote/http11/{ => httprequest}/HttpRequest.java (59%) rename tomcat/src/main/java/org/apache/coyote/http11/{ => httprequest}/HttpRequestBody.java (88%) create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestHeader.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestLine.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponse.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponseBody.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponseHeader.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpStatusLine.java diff --git a/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java b/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java new file mode 100644 index 0000000000..cd36e4a10c --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java @@ -0,0 +1,32 @@ +package org.apache.coyote.http11; + +import java.util.Arrays; + +public enum ContentType { + + CSS("text/css;charset=utf-8", "css"), + JS("application/javascript;charset=utf-8", "js"), + HTML("text/html;charset=utf-8", "html"), + PNG("image/png", "png"), + JPG("image/jpeg", "jpeg") + ; + + private final String contentType; + private final String extention; + + ContentType(String contentType, String extention) { + this.contentType = contentType; + this.extention = extention; + } + + public String getContentType() { + return contentType; + } + + public static ContentType getContentType(String extention) { + return Arrays.stream(values()) + .filter(contentType1 -> contentType1.extention.equals(extention)) + .findAny() + .orElseThrow(); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 260329e143..ba84e05659 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -1,6 +1,5 @@ package org.apache.coyote.http11; -import com.techcourse.db.InMemoryUserRepository; import com.techcourse.exception.UncheckedServletException; import com.techcourse.model.User; import java.io.BufferedReader; @@ -10,8 +9,10 @@ import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.UUID; import org.apache.coyote.Processor; +import org.apache.coyote.http11.controller.Controller; +import org.apache.coyote.http11.httprequest.HttpRequest; +import org.apache.coyote.http11.httpresponse.HttpResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,15 +43,11 @@ public void process(final Socket connection) { var bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); HttpRequest httpRequest = HttpRequestConvertor.convertHttpRequest(bufferedReader); - if (httpRequest.isMethod("POST") && httpRequest.isPath("/register")) { - String requestBody = httpRequest.getHttpRequestBody().getBody(); - String[] token = requestBody.split("&"); - String account = token[0].split("=")[1]; - String email = token[1].split("=")[1]; - String password = token[2].split("=")[1]; - User user = new User(account, password, email); - InMemoryUserRepository.save(user); - } + RequestMapping requestMapping = new RequestMapping(); + + Controller controller = requestMapping.getController(httpRequest.getPath()); + + HttpResponse httpResponse = controller.service(httpRequest); var responseBody = ""; var contentType = "text/html;charset=utf-8 \r\n"; @@ -92,29 +89,6 @@ public void process(final Socket connection) { "Content-Length: " + responseBody.getBytes().length + " ", "", responseBody); - if (httpRequest.isMethod("POST") && httpRequest.isPath("/login")) { - String requestBody = httpRequest.getBody(); - String[] token = requestBody.split("&"); - String account = token[0].split("=")[1]; - String password = token[1].split("=")[1]; - - User user = InMemoryUserRepository.findByAccount(account) - .orElseThrow(); - UUID uuid = UUID.randomUUID(); - if (user.checkPassword(password)) { - session.save(uuid.toString(), user); - response = String.join("\r\n", - "HTTP/1.1 302 Found ", - "Set-Cookie: JSESSIONID=" + uuid, - "Location: " + "/index.html"); - log.info(user.toString()); - } else { - response = String.join("\r\n", - "HTTP/1.1 302 Found ", - "Location: " + "/401.html"); - log.error("비밀번호 불일치"); - } - } if (httpRequest.isMethod("POST") && httpRequest.isPath("/register")) { response = String.join("\r\n", "HTTP/1.1 302 Found ", @@ -140,7 +114,7 @@ public void process(final Socket connection) { } } - outputStream.write(response.getBytes()); + outputStream.write(httpResponse.getBytes()); outputStream.flush(); } catch (IOException | UncheckedServletException e) { log.error(e.getMessage(), e); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java index 38c2d8660b..69460d079e 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java @@ -4,6 +4,10 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; +import org.apache.coyote.http11.httprequest.HttpRequest; +import org.apache.coyote.http11.httprequest.HttpRequestBody; +import org.apache.coyote.http11.httprequest.HttpRequestHeader; +import org.apache.coyote.http11.httprequest.HttpRequestLine; public class HttpRequestConvertor { @@ -21,17 +25,20 @@ public static HttpRequest convertHttpRequest(BufferedReader bufferedReader) { path += ".html"; } String version = headerFirstLine[2]; + + HttpRequestLine httpRequestLine = new HttpRequestLine(method, path, version); + Map headers = getHeaders(bufferedReader); - HttpRequestHeader httpRequestHeader = new HttpRequestHeader(method, path, version, headers); + HttpRequestHeader httpRequestHeader = new HttpRequestHeader(headers); if (httpRequestHeader.containsKey("Content-Length")) { HttpRequestBody httpRequestBody = getHttpRequestBody(bufferedReader, httpRequestHeader); - return new HttpRequest(httpRequestHeader, httpRequestBody); + return new HttpRequest(httpRequestLine, httpRequestHeader, httpRequestBody); } - return new HttpRequest(httpRequestHeader); + return new HttpRequest(httpRequestLine, httpRequestHeader); } catch (IOException e) { throw new RuntimeException(e); } @@ -45,8 +52,7 @@ private static HttpRequestBody getHttpRequestBody( char[] buffer = new char[contentLength]; bufferedReader.read(buffer, 0, contentLength); String body = new String(buffer); - HttpRequestBody httpRequestBody = new HttpRequestBody(body); - return httpRequestBody; + return new HttpRequestBody(body); } private static Map getHeaders(BufferedReader bufferedReader) throws IOException { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java deleted file mode 100644 index a63821e8d5..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestHeader.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.apache.coyote.http11; - -import java.util.Map; - -public class HttpRequestHeader { - - private final HttpMethod method; - private final String path; - private final String version; - private final Map headers; - - public HttpRequestHeader(HttpMethod method, String path, String version, Map headers) { - this.method = method; - this.path = path; - this.version = version; - this.headers = headers; - } - - public boolean containsKey(String key) { - return headers.containsKey(key); - } - - public boolean isMethod(String name) { - return method.isMethod(name); - } - - public boolean isPath(String path) { - return this.path.equals(path); - } - - public String getValue(String key) { - return headers.get(key); - } - - public HttpMethod getMethod() { - return method; - } - - public String getPath() { - return path; - } - - public String getVersion() { - return version; - } - - @Override - public String toString() { - return "HttpRequestHeader{" + - "\nmethod='" + method + '\'' + - ", \npath='" + path + '\'' + - ", \nversion='" + version + '\'' + - ", \nheaders=" + headers + - '}'; - } -} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java deleted file mode 100644 index 9ac8eece44..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.apache.coyote.http11; - -public class HttpResponse { -} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpStatusCode.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpStatusCode.java new file mode 100644 index 0000000000..a51eac9edf --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpStatusCode.java @@ -0,0 +1,24 @@ +package org.apache.coyote.http11; + +public enum HttpStatusCode { + + OK(200, "OK"), + FOUND(302, "Found") + ; + + private final int code; + private final String message; + + HttpStatusCode(int code, String message) { + this.code = code; + this.message = message; + } + + public int getCode() { + return code; + } + + public String getMessage() { + return message; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/RequestMapping.java b/tomcat/src/main/java/org/apache/coyote/http11/RequestMapping.java new file mode 100644 index 0000000000..fa012d5ca8 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/RequestMapping.java @@ -0,0 +1,27 @@ +package org.apache.coyote.http11; + +import java.util.HashMap; +import java.util.Map; +import org.apache.coyote.http11.controller.Controller; +import org.apache.coyote.http11.controller.LoginController; +import org.apache.coyote.http11.controller.PageController; +import org.apache.coyote.http11.controller.RegisterController; + +public class RequestMapping { + + private final Map controllers = new HashMap<>(); + + public RequestMapping() { + controllers.put("/login", new LoginController()); + controllers.put("/register", new RegisterController()); + controllers.put("page", new PageController()); + } + + public Controller getController(String path) { + if (controllers.containsKey(path)) { + return controllers.get(path); + } + + return new PageController(); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/AbstractController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/AbstractController.java new file mode 100644 index 0000000000..28e5765e11 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/AbstractController.java @@ -0,0 +1,22 @@ +package org.apache.coyote.http11.controller; + +import org.apache.coyote.http11.httprequest.HttpRequest; +import org.apache.coyote.http11.httpresponse.HttpResponse; + +public abstract class AbstractController implements Controller { + + @Override + public HttpResponse service(HttpRequest httpRequest) { + if (httpRequest.isMethod("GET")) { + return serviceGet(httpRequest); + } else if (httpRequest.isMethod("POST")) { + return servicePost(httpRequest); + } + + throw new RuntimeException(); + } + + abstract protected HttpResponse servicePost(HttpRequest httpRequest); + + abstract protected HttpResponse serviceGet(HttpRequest httpRequest); +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/Controller.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/Controller.java new file mode 100644 index 0000000000..3f2f18697a --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/Controller.java @@ -0,0 +1,9 @@ +package org.apache.coyote.http11.controller; + +import org.apache.coyote.http11.httprequest.HttpRequest; +import org.apache.coyote.http11.httpresponse.HttpResponse; + +public interface Controller { + + HttpResponse service(HttpRequest httpRequest); +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/LoginController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/LoginController.java new file mode 100644 index 0000000000..72dfa0cba9 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/LoginController.java @@ -0,0 +1,91 @@ +package org.apache.coyote.http11.controller; + +import com.techcourse.db.InMemoryUserRepository; +import com.techcourse.model.User; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.UUID; +import org.apache.coyote.http11.ContentType; +import org.apache.coyote.http11.HttpStatusCode; +import org.apache.coyote.http11.Session; +import org.apache.coyote.http11.httprequest.HttpRequest; +import org.apache.coyote.http11.httpresponse.HttpResponse; +import org.apache.coyote.http11.httpresponse.HttpResponseBody; +import org.apache.coyote.http11.httpresponse.HttpResponseHeader; +import org.apache.coyote.http11.httpresponse.HttpStatusLine; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LoginController extends AbstractController { + + private static final Logger log = LoggerFactory.getLogger(LoginController.class); + + private final Session session = Session.getInstance(); + + @Override + protected HttpResponse servicePost(HttpRequest httpRequest) { + String requestBody = httpRequest.getBody(); + String[] token = requestBody.split("&"); + String account = token[0].split("=")[1]; + String password = token[1].split("=")[1]; + + User user = InMemoryUserRepository.findByAccount(account) + .orElseThrow(); + UUID uuid = UUID.randomUUID(); + + HttpStatusLine httpStatusLine = new HttpStatusLine(httpRequest.getVersion(), HttpStatusCode.FOUND); + HttpResponseHeader httpResponseHeader = new HttpResponseHeader(); + if (user.checkPassword(password)) { + session.save(uuid.toString(), user); + httpResponseHeader.addHeaders("Set-Cookie", "JSESSIONID=" + uuid); + httpResponseHeader.addHeaders("Location", "/index.html"); + log.info(user.toString()); + } else { + httpResponseHeader.addHeaders("Location", "/401.html"); + log.error("비밀번호 불일치"); + } + + return new HttpResponse(httpStatusLine, httpResponseHeader); + } + + @Override + protected HttpResponse serviceGet(HttpRequest httpRequest) { + try { + HttpStatusLine httpStatusLine = new HttpStatusLine(httpRequest.getVersion(), HttpStatusCode.OK); + + if (httpRequest.containsKey("Cookie")) { + String[] cookies = httpRequest.getValue("Cookie").split("; "); + String cookie = ""; + for (String c : cookies) { + if (c.contains("JSESSIONID")) { + cookie = c.split("=")[1]; + } + } + if (session.containsUser(cookie)) { + User user = session.getUser(cookie); + log.info(user.toString()); + httpStatusLine = new HttpStatusLine(httpStatusLine.getVersion(), HttpStatusCode.FOUND); + HttpResponseHeader httpResponseHeader = new HttpResponseHeader(); + httpResponseHeader.addHeaders("Set-Cookie", "JSESSIONID=" + cookie); + httpResponseHeader.addHeaders("Location", "/index.html"); + return new HttpResponse(httpStatusLine, httpResponseHeader); + } + } + + String fileName = "static/login.html"; + var resourceUrl = getClass().getClassLoader().getResource(fileName); + Path filePath = Path.of(resourceUrl.toURI()); + String responseBody = new String(Files.readAllBytes(filePath)); + HttpResponseHeader httpResponseHeader = new HttpResponseHeader(); + httpResponseHeader.addHeaders("Content-Type", ContentType.HTML.getContentType()); + httpResponseHeader.addHeaders("Content-Length", String.valueOf(responseBody.getBytes().length)); + HttpResponseBody httpResponseBody = new HttpResponseBody(responseBody); + + return new HttpResponse(httpStatusLine, httpResponseHeader, httpResponseBody); + } catch (URISyntaxException | IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/PageController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/PageController.java new file mode 100644 index 0000000000..32bd4f9532 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/PageController.java @@ -0,0 +1,45 @@ +package org.apache.coyote.http11.controller; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.apache.coyote.http11.ContentType; +import org.apache.coyote.http11.HttpStatusCode; +import org.apache.coyote.http11.httprequest.HttpRequest; +import org.apache.coyote.http11.httpresponse.HttpResponse; +import org.apache.coyote.http11.httpresponse.HttpResponseBody; +import org.apache.coyote.http11.httpresponse.HttpResponseHeader; +import org.apache.coyote.http11.httpresponse.HttpStatusLine; + +public class PageController extends AbstractController { + + @Override + protected HttpResponse servicePost(HttpRequest httpRequest) { + return null; + } + + @Override + protected HttpResponse serviceGet(HttpRequest httpRequest) { + try { + HttpStatusLine httpStatusLine = new HttpStatusLine(httpRequest.getVersion(), HttpStatusCode.OK); + + String path = httpRequest.getPath(); + if (!httpRequest.getPath().contains(".")) { + path += ".html"; + } + String fileName = "static" + path; + var resourceUrl = getClass().getClassLoader().getResource(fileName); + Path filePath = Path.of(resourceUrl.toURI()); + String responseBody = new String(Files.readAllBytes(filePath)); + HttpResponseHeader httpResponseHeader = new HttpResponseHeader(); + httpResponseHeader.addHeaders("Content-Type", ContentType.getContentType(path.split("\\.")[1]).getContentType()); + httpResponseHeader.addHeaders("Content-Length", String.valueOf(responseBody.getBytes().length)); + HttpResponseBody httpResponseBody = new HttpResponseBody(responseBody); + + return new HttpResponse(httpStatusLine, httpResponseHeader, httpResponseBody); + } catch (URISyntaxException | IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/RegisterController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/RegisterController.java new file mode 100644 index 0000000000..7890787d48 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/RegisterController.java @@ -0,0 +1,55 @@ +package org.apache.coyote.http11.controller; + +import com.techcourse.db.InMemoryUserRepository; +import com.techcourse.model.User; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.apache.coyote.http11.ContentType; +import org.apache.coyote.http11.HttpStatusCode; +import org.apache.coyote.http11.httprequest.HttpRequest; +import org.apache.coyote.http11.httpresponse.HttpResponse; +import org.apache.coyote.http11.httpresponse.HttpResponseBody; +import org.apache.coyote.http11.httpresponse.HttpResponseHeader; +import org.apache.coyote.http11.httpresponse.HttpStatusLine; + +public class RegisterController extends AbstractController { + + @Override + protected HttpResponse servicePost(HttpRequest httpRequest) { + String requestBody = httpRequest.getBody(); + String[] token = requestBody.split("&"); + String account = token[0].split("=")[1]; + String email = token[1].split("=")[1]; + String password = token[2].split("=")[1]; + User user = new User(account, password, email); + InMemoryUserRepository.save(user); + + HttpStatusLine httpStatusLine = new HttpStatusLine(httpRequest.getVersion(), HttpStatusCode.FOUND); + HttpResponseHeader httpResponseHeader = new HttpResponseHeader(); + httpResponseHeader.addHeaders("Location", "/index.html"); + + return new HttpResponse(httpStatusLine, httpResponseHeader); + } + + @Override + protected HttpResponse serviceGet(HttpRequest httpRequest) { + try { + HttpStatusLine httpStatusLine = new HttpStatusLine(httpRequest.getVersion(), HttpStatusCode.OK); + + String fileName = "static/register.html"; + var resourceUrl = getClass().getClassLoader().getResource(fileName); + Path filePath = Path.of(resourceUrl.toURI()); + String responseBody = new String(Files.readAllBytes(filePath)); + HttpResponseHeader httpResponseHeader = new HttpResponseHeader(); + httpResponseHeader.addHeaders("Content-Type", ContentType.HTML.getContentType()); + httpResponseHeader.addHeaders("Content-Length", String.valueOf(responseBody.getBytes().length)); + HttpResponseBody httpResponseBody = new HttpResponseBody(responseBody); + + return new HttpResponse(httpStatusLine, httpResponseHeader, httpResponseBody); + } catch (URISyntaxException | IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequest.java similarity index 59% rename from tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java rename to tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequest.java index f2970d2967..1e614078b8 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequest.java @@ -1,25 +1,29 @@ -package org.apache.coyote.http11; +package org.apache.coyote.http11.httprequest; + +import org.apache.coyote.http11.HttpMethod; public class HttpRequest { + private final HttpRequestLine httpRequestLine; private final HttpRequestHeader httpRequestHeader; - private HttpRequestBody httpRequestBody; + private final HttpRequestBody httpRequestBody; - public HttpRequest(HttpRequestHeader httpRequestHeader, HttpRequestBody httpRequestBody) { + public HttpRequest(HttpRequestLine httpRequestLine, HttpRequestHeader httpRequestHeader, HttpRequestBody httpRequestBody) { + this.httpRequestLine = httpRequestLine; this.httpRequestHeader = httpRequestHeader; this.httpRequestBody = httpRequestBody; } - public HttpRequest(HttpRequestHeader httpRequestHeader) { - this(httpRequestHeader, null); + public HttpRequest(HttpRequestLine httpRequestLine, HttpRequestHeader httpRequestHeader) { + this(httpRequestLine, httpRequestHeader, null); } public boolean isMethod(String name) { - return httpRequestHeader.isMethod(name); + return httpRequestLine.isMethod(name); } public boolean isPath(String path) { - return httpRequestHeader.isPath(path); + return httpRequestLine.isPath(path); } public boolean containsKey(String key) { @@ -31,15 +35,15 @@ public String getValue(String key) { } public HttpMethod getMethod() { - return httpRequestHeader.getMethod(); + return httpRequestLine.getMethod(); } public String getPath() { - return httpRequestHeader.getPath(); + return httpRequestLine.getPath(); } public String getVersion() { - return httpRequestHeader.getVersion(); + return httpRequestLine.getVersion(); } public String getBody() { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestBody.java similarity index 88% rename from tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java rename to tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestBody.java index 2a59b78576..6c303051c2 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestBody.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestBody.java @@ -1,4 +1,4 @@ -package org.apache.coyote.http11; +package org.apache.coyote.http11.httprequest; public class HttpRequestBody { diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestHeader.java b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestHeader.java new file mode 100644 index 0000000000..bf29b0cdc4 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestHeader.java @@ -0,0 +1,27 @@ +package org.apache.coyote.http11.httprequest; + +import java.util.Map; + +public class HttpRequestHeader { + + private final Map headers; + + public HttpRequestHeader(Map headers) { + this.headers = headers; + } + + public boolean containsKey(String key) { + return headers.containsKey(key); + } + + public String getValue(String key) { + return headers.get(key); + } + + @Override + public String toString() { + return "HttpRequestHeader{" + + ", \nheaders=" + headers + + '}'; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestLine.java b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestLine.java new file mode 100644 index 0000000000..407015edbb --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestLine.java @@ -0,0 +1,36 @@ +package org.apache.coyote.http11.httprequest; + +import org.apache.coyote.http11.HttpMethod; + +public class HttpRequestLine { + + private final HttpMethod method; + private final String path; + private final String version; + + public HttpRequestLine(HttpMethod method, String path, String version) { + this.method = method; + this.path = path; + this.version = version; + } + + public boolean isMethod(String name) { + return method.isMethod(name); + } + + public boolean isPath(String path) { + return this.path.equals(path); + } + + public HttpMethod getMethod() { + return method; + } + + public String getPath() { + return path; + } + + public String getVersion() { + return version; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponse.java new file mode 100644 index 0000000000..e1e705a65e --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponse.java @@ -0,0 +1,73 @@ +package org.apache.coyote.http11.httpresponse; + +import java.util.Map; + +public class HttpResponse { + + private final HttpStatusLine httpStatusLine; + private final HttpResponseHeader httpResponseHeader; + private final HttpResponseBody httpResponseBody; + + public HttpResponse( + HttpStatusLine httpStatusLine, + HttpResponseHeader httpResponseHeader, + HttpResponseBody httpResponseBody + ) { + this.httpStatusLine = httpStatusLine; + this.httpResponseHeader = httpResponseHeader; + this.httpResponseBody = httpResponseBody; + } + + public HttpResponse(HttpStatusLine httpStatusLine, HttpResponseHeader httpResponseHeader) { + this(httpStatusLine, httpResponseHeader, null); + } + + /*var response = String.join("\r\n", + "HTTP/1.1 200 OK ", + "Content-Type: " + contentType + + "Content-Length: " + responseBody.getBytes().length + " ", + "", + responseBody);*/ + public byte[] getBytes() { + String statusLine = httpStatusLine.getVersion() + " " + httpStatusLine.getHttpStatusCode().getCode() + " " + + httpStatusLine.getHttpStatusCode().getMessage(); + Map headers = httpResponseHeader.getHeaders(); + StringBuilder sb = new StringBuilder(); + int size = headers.keySet().size(); + int i = 1; + for (String key : headers.keySet()) { + if (i < size) { + sb.append(key).append(": ").append(headers.get(key)).append(" \r\n"); + size++; + } else { + sb.append(key).append(": ").append(headers.get(key)); + } + } + if (httpResponseBody != null) { + String responseBody = httpResponseBody.getBody(); + String join = String.join("\r\n", + statusLine, + sb.toString(), + responseBody); + System.out.println(join); + return join.getBytes(); + } + String join = String.join("\r\n", + statusLine, + sb.toString()); + System.out.println(join); + return join.getBytes(); + } + + public HttpStatusLine getHttpStatusLine() { + return httpStatusLine; + } + + public HttpResponseHeader getHttpResponseHeader() { + return httpResponseHeader; + } + + public HttpResponseBody getHttpResponseBody() { + return httpResponseBody; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponseBody.java b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponseBody.java new file mode 100644 index 0000000000..4c4a5cb111 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponseBody.java @@ -0,0 +1,14 @@ +package org.apache.coyote.http11.httpresponse; + +public class HttpResponseBody { + + private final String body; + + public HttpResponseBody(String body) { + this.body = body; + } + + public String getBody() { + return body; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponseHeader.java b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponseHeader.java new file mode 100644 index 0000000000..e350a1b84a --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponseHeader.java @@ -0,0 +1,21 @@ +package org.apache.coyote.http11.httpresponse; + +import java.util.HashMap; +import java.util.Map; + +public class HttpResponseHeader { + + private final Map headers; + + public HttpResponseHeader() { + this.headers = new HashMap<>(); + } + + public void addHeaders(String key, String value) { + headers.put(key, value); + } + + public Map getHeaders() { + return headers; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpStatusLine.java b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpStatusLine.java new file mode 100644 index 0000000000..4eaf9eba69 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpStatusLine.java @@ -0,0 +1,22 @@ +package org.apache.coyote.http11.httpresponse; + +import org.apache.coyote.http11.HttpStatusCode; + +public class HttpStatusLine { + + private final String version; + private final HttpStatusCode httpStatusCode; + + public HttpStatusLine(String version, HttpStatusCode httpStatusCode) { + this.version = version; + this.httpStatusCode = httpStatusCode; + } + + public String getVersion() { + return version; + } + + public HttpStatusCode getHttpStatusCode() { + return httpStatusCode; + } +} From 2f23bfc0846c6781f4a80ff75ce1fc7639bd9ef8 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Fri, 6 Sep 2024 07:45:35 +0900 Subject: [PATCH 25/30] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/Http11Processor.java | 71 ------------------- .../coyote/http11/HttpRequestConvertor.java | 13 +++- .../http11/httpresponse/HttpResponse.java | 8 --- 3 files changed, 11 insertions(+), 81 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index ba84e05659..028ca63d7e 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -1,14 +1,10 @@ package org.apache.coyote.http11; import com.techcourse.exception.UncheckedServletException; -import com.techcourse.model.User; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Path; import org.apache.coyote.Processor; import org.apache.coyote.http11.controller.Controller; import org.apache.coyote.http11.httprequest.HttpRequest; @@ -49,77 +45,10 @@ public void process(final Socket connection) { HttpResponse httpResponse = controller.service(httpRequest); - var responseBody = ""; - var contentType = "text/html;charset=utf-8 \r\n"; - - if (httpRequest.isPath("/")) { - responseBody = "Hello world!"; - } else if (httpRequest.isMethod("GET")) { - String fileName = "static" + httpRequest.getPath(); - var resourceUrl = getClass().getClassLoader().getResource(fileName); - if (resourceUrl == null) { - final var response = String.join("\r\n", - "HTTP/1.1 404 Not Found", - "Content-Type: text/html;charset=utf-8 \r\n", - "", - "

404 Not Found

"); - outputStream.write(response.getBytes()); - outputStream.flush(); - return; - } - Path filePath = Path.of(resourceUrl.toURI()); - responseBody = new String(Files.readAllBytes(filePath)); - } - - if (httpRequest.getPath().endsWith(".css")) { - contentType = "text/css;charset=utf-8 \r\n"; - } else if (httpRequest.getPath().endsWith(".js")) { - contentType = "application/javascript;charset=utf-8 \r\n"; - } else if (httpRequest.getPath().endsWith(".html")) { - contentType = "text/html;charset=utf-8 \r\n"; - } else if (httpRequest.getPath().endsWith(".png")) { - contentType = "image/png \r\n"; - } else if (httpRequest.getPath().endsWith(".jpg")) { - contentType = "image/jpeg \r\n"; - } - - var response = String.join("\r\n", - "HTTP/1.1 200 OK ", - "Content-Type: " + contentType + - "Content-Length: " + responseBody.getBytes().length + " ", - "", - responseBody); - if (httpRequest.isMethod("POST") && httpRequest.isPath("/register")) { - response = String.join("\r\n", - "HTTP/1.1 302 Found ", - "Location: " + "/index.html"); - } - if (httpRequest.isMethod("GET") && httpRequest.isPath("/login.html")) { - if (httpRequest.containsKey("Cookie")) { - String[] cookies = httpRequest.getValue("Cookie").split("; "); - String cookie = ""; - for (String c : cookies) { - if (c.contains("JSESSIONID")) { - cookie = c.split("=")[1]; - } - } - if (session.containsUser(cookie)) { - User user = session.getUser(cookie); - response = String.join("\r\n", - "HTTP/1.1 302 Found ", - "Set-Cookie: JSESSIONID=" + cookie, - "Location: " + "/index.html"); - log.info(user.toString()); - } - } - } - outputStream.write(httpResponse.getBytes()); outputStream.flush(); } catch (IOException | UncheckedServletException e) { log.error(e.getMessage(), e); - } catch (URISyntaxException e) { - throw new RuntimeException(e); } } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java index 69460d079e..9b49768341 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java @@ -59,10 +59,19 @@ private static Map getHeaders(BufferedReader bufferedReader) thr String line; Map headers = new HashMap<>(); while ((line = bufferedReader.readLine()) != null && !line.isEmpty()) { - String[] requestLine = line.split(": "); - headers.put(requestLine[0], requestLine[1]); + String[] requestLine = line.split(":"); + headers.put(requestLine[0], parseHeaderValue(requestLine)); } return headers; } + + private static String parseHeaderValue(String[] requestLine) { + StringBuilder sb = new StringBuilder(); + for (int i = 1; i < requestLine.length; i++) { + sb.append(requestLine[i].strip()); + } + + return sb.toString(); + } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponse.java index e1e705a65e..2960b502c7 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponse.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/httpresponse/HttpResponse.java @@ -22,12 +22,6 @@ public HttpResponse(HttpStatusLine httpStatusLine, HttpResponseHeader httpRespon this(httpStatusLine, httpResponseHeader, null); } - /*var response = String.join("\r\n", - "HTTP/1.1 200 OK ", - "Content-Type: " + contentType + - "Content-Length: " + responseBody.getBytes().length + " ", - "", - responseBody);*/ public byte[] getBytes() { String statusLine = httpStatusLine.getVersion() + " " + httpStatusLine.getHttpStatusCode().getCode() + " " + httpStatusLine.getHttpStatusCode().getMessage(); @@ -49,13 +43,11 @@ public byte[] getBytes() { statusLine, sb.toString(), responseBody); - System.out.println(join); return join.getBytes(); } String join = String.join("\r\n", statusLine, sb.toString()); - System.out.println(join); return join.getBytes(); } From 8dc940e94bc209303aef363596ce27230d93e77b Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Fri, 6 Sep 2024 07:54:21 +0900 Subject: [PATCH 26/30] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/org/apache/coyote/http11/Http11Processor.java | 2 -- .../java/org/apache/coyote/http11/HttpRequestConvertor.java | 3 --- 2 files changed, 5 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java index 028ca63d7e..d1486f2416 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -17,11 +17,9 @@ public class Http11Processor implements Runnable, Processor { private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); private final Socket connection; - private final Session session; public Http11Processor(final Socket connection) { this.connection = connection; - this.session = Session.getInstance(); } @Override diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java index 9b49768341..136201b2e1 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java @@ -21,9 +21,6 @@ public static HttpRequest convertHttpRequest(BufferedReader bufferedReader) { String[] headerFirstLine = firstLine.split(" "); HttpMethod method = HttpMethod.getHttpMethod(headerFirstLine[0]); String path = headerFirstLine[1]; - if (method.isMethod("GET") && !path.contains(".")) { - path += ".html"; - } String version = headerFirstLine[2]; HttpRequestLine httpRequestLine = new HttpRequestLine(method, path, version); From 43cb5ba60163c1e2eca0b50f4dbf659d527e20a6 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Fri, 6 Sep 2024 07:55:30 +0900 Subject: [PATCH 27/30] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coyote/http11/controller/AbstractController.java | 8 ++++---- .../apache/coyote/http11/controller/LoginController.java | 4 ++-- .../apache/coyote/http11/controller/PageController.java | 4 ++-- .../coyote/http11/controller/RegisterController.java | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/AbstractController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/AbstractController.java index 28e5765e11..e52a2508da 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/controller/AbstractController.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/AbstractController.java @@ -8,15 +8,15 @@ public abstract class AbstractController implements Controller { @Override public HttpResponse service(HttpRequest httpRequest) { if (httpRequest.isMethod("GET")) { - return serviceGet(httpRequest); + return doGet(httpRequest); } else if (httpRequest.isMethod("POST")) { - return servicePost(httpRequest); + return doPost(httpRequest); } throw new RuntimeException(); } - abstract protected HttpResponse servicePost(HttpRequest httpRequest); + abstract protected HttpResponse doPost(HttpRequest httpRequest); - abstract protected HttpResponse serviceGet(HttpRequest httpRequest); + abstract protected HttpResponse doGet(HttpRequest httpRequest); } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/LoginController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/LoginController.java index 72dfa0cba9..62d0b29b6f 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/controller/LoginController.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/LoginController.java @@ -25,7 +25,7 @@ public class LoginController extends AbstractController { private final Session session = Session.getInstance(); @Override - protected HttpResponse servicePost(HttpRequest httpRequest) { + protected HttpResponse doPost(HttpRequest httpRequest) { String requestBody = httpRequest.getBody(); String[] token = requestBody.split("&"); String account = token[0].split("=")[1]; @@ -51,7 +51,7 @@ protected HttpResponse servicePost(HttpRequest httpRequest) { } @Override - protected HttpResponse serviceGet(HttpRequest httpRequest) { + protected HttpResponse doGet(HttpRequest httpRequest) { try { HttpStatusLine httpStatusLine = new HttpStatusLine(httpRequest.getVersion(), HttpStatusCode.OK); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/PageController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/PageController.java index 32bd4f9532..cda0b2f59a 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/controller/PageController.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/PageController.java @@ -15,12 +15,12 @@ public class PageController extends AbstractController { @Override - protected HttpResponse servicePost(HttpRequest httpRequest) { + protected HttpResponse doPost(HttpRequest httpRequest) { return null; } @Override - protected HttpResponse serviceGet(HttpRequest httpRequest) { + protected HttpResponse doGet(HttpRequest httpRequest) { try { HttpStatusLine httpStatusLine = new HttpStatusLine(httpRequest.getVersion(), HttpStatusCode.OK); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/RegisterController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/RegisterController.java index 7890787d48..736aa4d852 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/controller/RegisterController.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/RegisterController.java @@ -17,7 +17,7 @@ public class RegisterController extends AbstractController { @Override - protected HttpResponse servicePost(HttpRequest httpRequest) { + protected HttpResponse doPost(HttpRequest httpRequest) { String requestBody = httpRequest.getBody(); String[] token = requestBody.split("&"); String account = token[0].split("=")[1]; @@ -34,7 +34,7 @@ protected HttpResponse servicePost(HttpRequest httpRequest) { } @Override - protected HttpResponse serviceGet(HttpRequest httpRequest) { + protected HttpResponse doGet(HttpRequest httpRequest) { try { HttpStatusLine httpStatusLine = new HttpStatusLine(httpRequest.getVersion(), HttpStatusCode.OK); From 2b068a458edcad74ccadd6f135fba9b91dd398b3 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Fri, 6 Sep 2024 07:59:43 +0900 Subject: [PATCH 28/30] =?UTF-8?q?refactor:=20requestLine=20=ED=8C=8C?= =?UTF-8?q?=EC=8B=B1=20=EB=A1=9C=EC=A7=81=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apache/coyote/http11/HttpRequestConvertor.java | 11 +++-------- .../coyote/http11/httprequest/HttpRequestLine.java | 9 +++++---- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java index 136201b2e1..cd992e7d10 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequestConvertor.java @@ -13,17 +13,12 @@ public class HttpRequestConvertor { public static HttpRequest convertHttpRequest(BufferedReader bufferedReader) { try { - String firstLine = bufferedReader.readLine(); - if (firstLine == null) { + String requestLine = bufferedReader.readLine(); + if (requestLine == null) { throw new RuntimeException("요청이 비어 있습니다."); } - String[] headerFirstLine = firstLine.split(" "); - HttpMethod method = HttpMethod.getHttpMethod(headerFirstLine[0]); - String path = headerFirstLine[1]; - String version = headerFirstLine[2]; - - HttpRequestLine httpRequestLine = new HttpRequestLine(method, path, version); + HttpRequestLine httpRequestLine = new HttpRequestLine(requestLine); Map headers = getHeaders(bufferedReader); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestLine.java b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestLine.java index 407015edbb..6befc619d4 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestLine.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/httprequest/HttpRequestLine.java @@ -8,10 +8,11 @@ public class HttpRequestLine { private final String path; private final String version; - public HttpRequestLine(HttpMethod method, String path, String version) { - this.method = method; - this.path = path; - this.version = version; + public HttpRequestLine(String requestLine) { + String[] headerFirstLine = requestLine.split(" "); + this.method = HttpMethod.getHttpMethod(headerFirstLine[0]); + this.path = headerFirstLine[1]; + this.version = headerFirstLine[2]; } public boolean isMethod(String name) { From 0d10b581134391b413a91d0611d80e4458b0ac67 Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Fri, 6 Sep 2024 14:42:32 +0900 Subject: [PATCH 29/30] =?UTF-8?q?feat:=20=ED=95=99=EC=8A=B5=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=ED=86=B5=EA=B3=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cache/com/example/GreetingController.java | 26 +++++++++++++++---- .../example/cachecontrol/CacheWebConfig.java | 5 ++++ .../example/etag/EtagFilterConfiguration.java | 17 +++++++++--- .../version/CacheBustingWebConfig.java | 8 +++++- study/src/main/resources/application.yml | 4 +++ study/src/main/resources/static/index.html | 10 +++++++ .../resources/static/resource-versioning.html | 11 ++++++++ 7 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 study/src/main/resources/static/index.html create mode 100644 study/src/main/resources/static/resource-versioning.html diff --git a/study/src/main/java/cache/com/example/GreetingController.java b/study/src/main/java/cache/com/example/GreetingController.java index c0053cda42..d6c6c27bdf 100644 --- a/study/src/main/java/cache/com/example/GreetingController.java +++ b/study/src/main/java/cache/com/example/GreetingController.java @@ -1,18 +1,34 @@ package cache.com.example; +import java.io.IOException; +import java.io.OutputStream; import org.springframework.http.CacheControl; import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; @Controller public class GreetingController { @GetMapping("/") - public String index() { - return "index"; + public ResponseEntity index() { + StreamingResponseBody stream = new StreamingResponseBody() { + @Override + public void writeTo(OutputStream outputStream) throws IOException { + String htmlContent = "

Hello, World!

"; + outputStream.write(htmlContent.getBytes()); + outputStream.flush(); + } + }; + + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_HTML_VALUE) + .body(stream); } /** @@ -25,16 +41,16 @@ public String cacheControl(final HttpServletResponse response) { .cachePrivate() .getHeaderValue(); response.addHeader(HttpHeaders.CACHE_CONTROL, cacheControl); - return "index"; + return "index.html"; } @GetMapping("/etag") public String etag() { - return "index"; + return "index.html"; } @GetMapping("/resource-versioning") public String resourceVersioning() { - return "resource-versioning"; + return "resource-versioning.html"; } } diff --git a/study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java b/study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java index 305b1f1e1e..89068404bc 100644 --- a/study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java +++ b/study/src/main/java/cache/com/example/cachecontrol/CacheWebConfig.java @@ -1,13 +1,18 @@ package cache.com.example.cachecontrol; import org.springframework.context.annotation.Configuration; +import org.springframework.http.CacheControl; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.mvc.WebContentInterceptor; @Configuration public class CacheWebConfig implements WebMvcConfigurer { @Override public void addInterceptors(final InterceptorRegistry registry) { + WebContentInterceptor interceptor = new WebContentInterceptor(); + interceptor.addCacheMapping(CacheControl.noCache().cachePrivate(), "/"); + registry.addInterceptor(interceptor); } } diff --git a/study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java b/study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java index 41ef7a3d9a..21b921e09d 100644 --- a/study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java +++ b/study/src/main/java/cache/com/example/etag/EtagFilterConfiguration.java @@ -1,12 +1,21 @@ package cache.com.example.etag; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.filter.ShallowEtagHeaderFilter; @Configuration public class EtagFilterConfiguration { -// @Bean -// public FilterRegistrationBean shallowEtagHeaderFilter() { -// return null; -// } + @Bean + public FilterRegistrationBean shallowEtagHeaderFilter() { + FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(); + filterRegistrationBean.setFilter(new ShallowEtagHeaderFilter()); + filterRegistrationBean.addUrlPatterns("/*"); // 모든 URL 패턴에 대해 적용 + filterRegistrationBean.setName("etagFilter"); + filterRegistrationBean.setOrder(1); // 필터 순서 설정 + + return filterRegistrationBean; + } } diff --git a/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java b/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java index 6da6d2c795..a763a37078 100644 --- a/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java +++ b/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java @@ -1,7 +1,9 @@ package cache.com.example.version; +import java.util.concurrent.TimeUnit; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; +import org.springframework.http.CacheControl; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -20,6 +22,10 @@ public CacheBustingWebConfig(ResourceVersion version) { @Override public void addResourceHandlers(final ResourceHandlerRegistry registry) { registry.addResourceHandler(PREFIX_STATIC_RESOURCES + "/" + version.getVersion() + "/**") - .addResourceLocations("classpath:/static/"); + .addResourceLocations("classpath:/static/") + .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS).cachePublic()) // Cache for one year and set public + .resourceChain(true) + .addResolver(new org.springframework.web.servlet.resource.VersionResourceResolver() + .addContentVersionStrategy("/**")); } } diff --git a/study/src/main/resources/application.yml b/study/src/main/resources/application.yml index e3503a5fb9..c152bb20db 100644 --- a/study/src/main/resources/application.yml +++ b/study/src/main/resources/application.yml @@ -8,3 +8,7 @@ server: threads: min-spare: 2 max: 2 + compression: + enabled: true + mime-types: application/json,application/xml,text/html,text/xml,text/plain + min-response-size: 1024 diff --git a/study/src/main/resources/static/index.html b/study/src/main/resources/static/index.html new file mode 100644 index 0000000000..46cbef0f24 --- /dev/null +++ b/study/src/main/resources/static/index.html @@ -0,0 +1,10 @@ + + + + Getting Started: Serving Web Content + + + +Hello, World! + + diff --git a/study/src/main/resources/static/resource-versioning.html b/study/src/main/resources/static/resource-versioning.html new file mode 100644 index 0000000000..cb8323ebf9 --- /dev/null +++ b/study/src/main/resources/static/resource-versioning.html @@ -0,0 +1,11 @@ + + + + + + Document + + +html > head > script 태그의 src에 version 디렉터리가 생겼다. + + From 0b35ac01417e7d53ba1d787f1d169b1590d99e8a Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Fri, 6 Sep 2024 14:43:19 +0900 Subject: [PATCH 30/30] =?UTF-8?q?refactor:=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cache/com/example/version/CacheBustingWebConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java b/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java index a763a37078..fe3489b728 100644 --- a/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java +++ b/study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java @@ -23,7 +23,7 @@ public CacheBustingWebConfig(ResourceVersion version) { public void addResourceHandlers(final ResourceHandlerRegistry registry) { registry.addResourceHandler(PREFIX_STATIC_RESOURCES + "/" + version.getVersion() + "/**") .addResourceLocations("classpath:/static/") - .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS).cachePublic()) // Cache for one year and set public + .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS).cachePublic()) .resourceChain(true) .addResolver(new org.springframework.web.servlet.resource.VersionResourceResolver() .addContentVersionStrategy("/**"));