diff --git a/study/src/test/java/thread/stage0/SynchronizationTest.java b/study/src/test/java/thread/stage0/SynchronizationTest.java
index 0333c18e3b..b463c2b984 100644
--- a/study/src/test/java/thread/stage0/SynchronizationTest.java
+++ b/study/src/test/java/thread/stage0/SynchronizationTest.java
@@ -41,7 +41,7 @@ private static final class SynchronizedMethods {
private int sum = 0;
- public void calculate() {
+ public synchronized void calculate() {
setSum(getSum() + 1);
}
diff --git a/study/src/test/java/thread/stage0/ThreadPoolsTest.java b/study/src/test/java/thread/stage0/ThreadPoolsTest.java
index 238611ebfe..28b6e8f11c 100644
--- a/study/src/test/java/thread/stage0/ThreadPoolsTest.java
+++ b/study/src/test/java/thread/stage0/ThreadPoolsTest.java
@@ -28,11 +28,14 @@ void testNewFixedThreadPool() {
final var executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
executor.submit(logWithSleep("hello fixed thread pools"));
executor.submit(logWithSleep("hello fixed thread pools"));
+ //() -> {} 로 바꾸면 queueSize 가 0으로 줄어듬
executor.submit(logWithSleep("hello fixed thread pools"));
// 올바른 값으로 바꿔서 테스트를 통과시키자.
- final int expectedPoolSize = 0;
- final int expectedQueueSize = 0;
+ final int expectedPoolSize = 2;
+ //-> 스레드 개수를 2개로 지정해 두었으니 expectedPoolSize 는 2개
+ final int expectedQueueSize = 1;
+ //-> 아직 2개의 스레드가 모두 종료되지 않았기 때문에 대기중인 스레드 Queue 의 size() 값은 1
assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize());
assertThat(expectedQueueSize).isEqualTo(executor.getQueue().size());
@@ -41,16 +44,27 @@ void testNewFixedThreadPool() {
@Test
void testNewCachedThreadPool() {
final var executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
+ //이 테스트는 뭐지??
+ //https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newCachedThreadPool--
executor.submit(logWithSleep("hello cached thread pools"));
executor.submit(logWithSleep("hello cached thread pools"));
executor.submit(logWithSleep("hello cached thread pools"));
// 올바른 값으로 바꿔서 테스트를 통과시키자.
- final int expectedPoolSize = 0;
+ final int expectedPoolSize = 3;
final int expectedQueueSize = 0;
assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize());
assertThat(expectedQueueSize).isEqualTo(executor.getQueue().size());
+ /**
+ * 정리하면
+ * newFixedThreadPool : 최대 스레드 크기를 지정
+ * 매번 새로운 스레드 생성?
+ *
+ * Cached~
+ * : 가용 시스템 리소스가 없을 때 까지 생성 가능
+ * : 재활용할 수 있음
+ */
}
private Runnable logWithSleep(final String message) {
diff --git a/study/src/test/java/thread/stage0/ThreadTest.java b/study/src/test/java/thread/stage0/ThreadTest.java
index 3ffef18869..c145ad4a55 100644
--- a/study/src/test/java/thread/stage0/ThreadTest.java
+++ b/study/src/test/java/thread/stage0/ThreadTest.java
@@ -1,5 +1,9 @@
package thread.stage0;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.lang.Thread.State;
+
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -8,10 +12,10 @@
* 자바로 동시에 여러 작업을 처리할 때 스레드를 사용한다.
* 스레드 객체를 직접 생성하는 방법부터 알아보자.
* 진행하면서 막히는 부분은 아래 링크를 참고해서 해결한다.
- *
+ *
* Thread Objects
* https://docs.oracle.com/javase/tutorial/essential/concurrency/threads.html
- *
+ *
* Defining and Starting a Thread
* https://docs.oracle.com/javase/tutorial/essential/concurrency/runthread.html
*/
@@ -28,12 +32,15 @@ class ThreadTest {
void testExtendedThread() throws InterruptedException {
// 하단의 ExtendedThread 클래스를 Thread 클래스로 상속하고 스레드 객체를 생성한다.
Thread thread = new ExtendedThread("hello thread");
+ assertThat(thread.getState()).isEqualTo(State.NEW);
// 생성한 thread 객체를 시작한다.
- thread.start();
+ thread.start();
+ assertThat(thread.getState()).isEqualTo(State.RUNNABLE);
// thread의 작업이 완료될 때까지 기다린다.
- thread.join();
+ thread.join();
+ assertThat(thread.getState()).isEqualTo(State.TERMINATED);
}
/**
@@ -46,10 +53,10 @@ void testRunnableThread() throws InterruptedException {
Thread thread = new Thread(new RunnableThread("hello thread"));
// 생성한 thread 객체를 시작한다.
- thread.start();
+ thread.start();
// thread의 작업이 완료될 때까지 기다린다.
- thread.join();
+ thread.join();
}
private static final class ExtendedThread extends Thread {
diff --git a/study/src/test/java/thread/stage1/UserServlet.java b/study/src/test/java/thread/stage1/UserServlet.java
index b180a84c32..a78e7b4ad4 100644
--- a/study/src/test/java/thread/stage1/UserServlet.java
+++ b/study/src/test/java/thread/stage1/UserServlet.java
@@ -11,7 +11,7 @@ public void service(final User user) {
join(user);
}
- private void join(final User user) {
+ private synchronized void join(final User user) {
if (!users.contains(user)) {
users.add(user);
}
diff --git a/tomcat/build.gradle b/tomcat/build.gradle
index ba598c228d..c5f1fbb5ca 100644
--- a/tomcat/build.gradle
+++ b/tomcat/build.gradle
@@ -25,6 +25,7 @@ dependencies {
testImplementation 'org.mockito:mockito-core:5.12.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.2'
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.2'
+ testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.2'
}
test {
diff --git a/tomcat/src/main/java/com/techcourse/Application.java b/tomcat/src/main/java/com/techcourse/Application.java
index f874a4efb5..c4b9e86137 100644
--- a/tomcat/src/main/java/com/techcourse/Application.java
+++ b/tomcat/src/main/java/com/techcourse/Application.java
@@ -1,11 +1,17 @@
package com.techcourse;
+import org.apache.catalina.servlet.PathMatchServletContainer;
+import org.apache.catalina.servlet.RequestMapping;
import org.apache.catalina.startup.Tomcat;
+import com.techcourse.config.RequestMappingConfig;
+
public class Application {
public static void main(String[] args) {
- var tomcat = new Tomcat();
+ RequestMapping requestMapping = RequestMappingConfig.getRequestMapping();
+ PathMatchServletContainer servletContainer = new PathMatchServletContainer(requestMapping);
+ var tomcat = new Tomcat(servletContainer);
tomcat.start();
}
}
diff --git a/tomcat/src/main/java/com/techcourse/config/RequestMappingConfig.java b/tomcat/src/main/java/com/techcourse/config/RequestMappingConfig.java
new file mode 100644
index 0000000000..50649c1eb9
--- /dev/null
+++ b/tomcat/src/main/java/com/techcourse/config/RequestMappingConfig.java
@@ -0,0 +1,23 @@
+package com.techcourse.config;
+
+import java.util.Map;
+
+import org.apache.catalina.servlet.RequestMapping;
+
+import com.techcourse.controller.LoginController;
+import com.techcourse.controller.RegisterController;
+import com.techcourse.controller.StaticResourceController;
+
+public class RequestMappingConfig {
+ public static RequestMapping getRequestMapping() {
+ var staticResourceController = new StaticResourceController();
+ return new RequestMapping(
+ Map.of(
+ "/login", new LoginController(),
+ "/register", new RegisterController(),
+ "/", staticResourceController,
+ ".*\\.(js|html|css)$", staticResourceController
+ )
+ );
+ }
+}
diff --git a/tomcat/src/main/java/com/techcourse/controller/LoginController.java b/tomcat/src/main/java/com/techcourse/controller/LoginController.java
new file mode 100644
index 0000000000..c2b8933b2b
--- /dev/null
+++ b/tomcat/src/main/java/com/techcourse/controller/LoginController.java
@@ -0,0 +1,50 @@
+package com.techcourse.controller;
+
+import static org.apache.coyote.http11.httpmessage.HttpHeaders.JSESSIONID;
+
+import java.util.Objects;
+
+import org.apache.catalina.servlet.AbstractController;
+import org.apache.coyote.http11.httpmessage.request.HttpRequest;
+import org.apache.coyote.http11.httpmessage.request.HttpRequestParameters;
+import org.apache.coyote.http11.httpmessage.response.HttpResponse;
+import org.apache.coyote.http11.httpmessage.response.StaticResource;
+import org.apache.coyote.session.Session;
+import org.apache.coyote.session.SessionManager;
+
+import com.techcourse.db.InMemoryUserRepository;
+import com.techcourse.model.User;
+
+public class LoginController extends AbstractController {
+ @Override
+ protected void doPost(HttpRequest request, HttpResponse response) throws Exception {
+ HttpRequestParameters requestParams = HttpRequestParameters.parseFrom(request.getBody());
+ String account = requestParams.getParam("account");
+ String password = requestParams.getParam("password");
+ User user = InMemoryUserRepository.fetchByAccount(account);
+ if (user.checkPassword(password)) {
+ Session session = request.getSession(true);
+ session.setAttribute("user", user);
+ SessionManager.getInstance().add(session);
+
+ response.addCookie(JSESSIONID, session.getId());
+ response.setStatusFound("/index.html");
+ return;
+ }
+
+ response.setStatusFound("/401.html");
+ }
+
+ @Override
+ protected void doGet(HttpRequest request, HttpResponse response) throws Exception {
+ if (isLoggedIn(request)) {
+ response.setStatusFound("/index.html");
+ return;
+ }
+ response.setResponseOfStaticResource(new StaticResource("/login.html"));
+ }
+
+ private boolean isLoggedIn(HttpRequest httpRequest) {
+ return Objects.nonNull(httpRequest.getSession(false));
+ }
+}
diff --git a/tomcat/src/main/java/com/techcourse/controller/RegisterController.java b/tomcat/src/main/java/com/techcourse/controller/RegisterController.java
new file mode 100644
index 0000000000..701643895c
--- /dev/null
+++ b/tomcat/src/main/java/com/techcourse/controller/RegisterController.java
@@ -0,0 +1,42 @@
+package com.techcourse.controller;
+
+import org.apache.catalina.servlet.AbstractController;
+import org.apache.coyote.http11.httpmessage.request.HttpRequest;
+import org.apache.coyote.http11.httpmessage.request.HttpRequestParameters;
+import org.apache.coyote.http11.httpmessage.response.HttpResponse;
+import org.apache.coyote.http11.httpmessage.response.StaticResource;
+import org.apache.coyote.session.Session;
+import org.apache.coyote.session.SessionManager;
+
+import com.techcourse.db.InMemoryUserRepository;
+import com.techcourse.model.User;
+
+public class RegisterController extends AbstractController {
+ @Override
+ protected void doPost(HttpRequest request, HttpResponse response) throws Exception {
+ HttpRequestParameters methodRequest = HttpRequestParameters.parseFrom(request.getBody());
+ User user = register(methodRequest);
+ Session session = request.getSession(true);
+ session.setAttribute("user", user);
+ SessionManager.getInstance().add(session);
+
+ response.addCookie("JSESSIONID", session.getId());
+ response.setStatusFound("/index.html");
+ }
+
+ private User register(HttpRequestParameters requestParams) {
+ String account = requestParams.getParam("account");
+ User user = new User(
+ account,
+ requestParams.getParam("password"),
+ requestParams.getParam("email")
+ );
+ InMemoryUserRepository.save(user);
+ return InMemoryUserRepository.fetchByAccount(account);
+ }
+
+ @Override
+ protected void doGet(HttpRequest request, HttpResponse response) throws Exception {
+ response.setResponseOfStaticResource(new StaticResource("/register.html"));
+ }
+}
diff --git a/tomcat/src/main/java/com/techcourse/controller/StaticResourceController.java b/tomcat/src/main/java/com/techcourse/controller/StaticResourceController.java
new file mode 100644
index 0000000000..2bab1ab70f
--- /dev/null
+++ b/tomcat/src/main/java/com/techcourse/controller/StaticResourceController.java
@@ -0,0 +1,27 @@
+package com.techcourse.controller;
+
+import org.apache.catalina.servlet.AbstractController;
+import org.apache.coyote.http11.exception.CantHandleRequestException;
+import org.apache.coyote.http11.httpmessage.request.HttpRequest;
+import org.apache.coyote.http11.httpmessage.response.HttpResponse;
+import org.apache.coyote.http11.httpmessage.response.StaticResource;
+
+public class StaticResourceController extends AbstractController {
+ @Override
+ protected void doPost(HttpRequest request, HttpResponse response) throws Exception {
+ throw new CantHandleRequestException(
+ String.format("%s %s 요청을 처리할 수 없습니다.", request.getMethod().name(), request.getTarget())
+ );
+ }
+
+ @Override
+ protected void doGet(HttpRequest request, HttpResponse response) throws Exception {
+ if (request.getTarget().equals("/")) {
+ StaticResource staticResource = new StaticResource("/index.html");
+ response.setResponseOfStaticResource(staticResource);
+ return;
+ }
+ StaticResource staticResource = new StaticResource(request.getTarget());
+ response.setResponseOfStaticResource(staticResource);
+ }
+}
diff --git a/tomcat/src/main/java/org/apache/catalina/Manager.java b/tomcat/src/main/java/org/apache/catalina/Manager.java
index fd850c6558..4c5392254c 100644
--- a/tomcat/src/main/java/org/apache/catalina/Manager.java
+++ b/tomcat/src/main/java/org/apache/catalina/Manager.java
@@ -2,7 +2,7 @@
import java.io.IOException;
-import org.apache.coyote.http11.session.Session;
+import org.apache.coyote.session.Session;
/**
* A Manager manages the pool of Sessions that are associated with a
diff --git a/tomcat/src/main/java/org/apache/catalina/connector/Connector.java b/tomcat/src/main/java/org/apache/catalina/connector/Connector.java
index 2e2de79386..447c9853cd 100644
--- a/tomcat/src/main/java/org/apache/catalina/connector/Connector.java
+++ b/tomcat/src/main/java/org/apache/catalina/connector/Connector.java
@@ -5,6 +5,7 @@
import java.net.ServerSocket;
import java.net.Socket;
+import org.apache.coyote.ServletContainer;
import org.apache.coyote.http11.Http11Processor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -17,13 +18,16 @@ public class Connector implements Runnable {
private static final int DEFAULT_ACCEPT_COUNT = 100;
private final ServerSocket serverSocket;
+ private final ServletContainer servletContainer;
+
private boolean stopped;
- public Connector() {
- this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT);
+ public Connector(ServletContainer servletContainer) {
+ this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, servletContainer);
}
- public Connector(int port, int acceptCount) {
+ public Connector(int port, int acceptCount, ServletContainer servletContainer) {
+ this.servletContainer = servletContainer;
this.serverSocket = createServerSocket(port, acceptCount);
this.stopped = false;
}
@@ -66,7 +70,7 @@ private void process(Socket connection) {
if (connection == null) {
return;
}
- var processor = new Http11Processor(connection);
+ var processor = new Http11Processor(servletContainer, connection);
new Thread(processor).start();
}
diff --git a/tomcat/src/main/java/org/apache/catalina/exception/NoMatchedControllerException.java b/tomcat/src/main/java/org/apache/catalina/exception/NoMatchedControllerException.java
new file mode 100644
index 0000000000..851114b214
--- /dev/null
+++ b/tomcat/src/main/java/org/apache/catalina/exception/NoMatchedControllerException.java
@@ -0,0 +1,7 @@
+package org.apache.catalina.exception;
+
+public class NoMatchedControllerException extends RuntimeException {
+ public NoMatchedControllerException(String message) {
+ super(message);
+ }
+}
diff --git a/tomcat/src/main/java/org/apache/catalina/servlet/AbstractController.java b/tomcat/src/main/java/org/apache/catalina/servlet/AbstractController.java
new file mode 100644
index 0000000000..a71171118e
--- /dev/null
+++ b/tomcat/src/main/java/org/apache/catalina/servlet/AbstractController.java
@@ -0,0 +1,21 @@
+package org.apache.catalina.servlet;
+
+import org.apache.coyote.http11.httpmessage.request.HttpRequest;
+import org.apache.coyote.http11.httpmessage.response.HttpResponse;
+
+public abstract class AbstractController implements Controller {
+
+ @Override
+ public void service(HttpRequest request, HttpResponse response) throws Exception {
+ if (request.isGet()) {
+ doGet(request, response);
+ }
+ if (request.isPost()) {
+ doPost(request, response);
+ }
+ }
+
+ protected abstract void doPost(HttpRequest request, HttpResponse response) throws Exception;
+
+ protected abstract void doGet(HttpRequest request, HttpResponse response) throws Exception;
+}
diff --git a/tomcat/src/main/java/org/apache/catalina/servlet/Controller.java b/tomcat/src/main/java/org/apache/catalina/servlet/Controller.java
new file mode 100644
index 0000000000..e873e618ee
--- /dev/null
+++ b/tomcat/src/main/java/org/apache/catalina/servlet/Controller.java
@@ -0,0 +1,8 @@
+package org.apache.catalina.servlet;
+
+import org.apache.coyote.http11.httpmessage.request.HttpRequest;
+import org.apache.coyote.http11.httpmessage.response.HttpResponse;
+
+public interface Controller {
+ void service(HttpRequest request, HttpResponse response) throws Exception;
+}
diff --git a/tomcat/src/main/java/org/apache/catalina/servlet/PathMatchServletContainer.java b/tomcat/src/main/java/org/apache/catalina/servlet/PathMatchServletContainer.java
new file mode 100644
index 0000000000..23fb280aad
--- /dev/null
+++ b/tomcat/src/main/java/org/apache/catalina/servlet/PathMatchServletContainer.java
@@ -0,0 +1,19 @@
+package org.apache.catalina.servlet;
+
+import org.apache.coyote.ServletContainer;
+import org.apache.coyote.http11.httpmessage.request.HttpRequest;
+import org.apache.coyote.http11.httpmessage.response.HttpResponse;
+
+public class PathMatchServletContainer implements ServletContainer {
+ private final RequestMapping requestMapping;
+
+ public PathMatchServletContainer(RequestMapping requestMapping) {
+ this.requestMapping = requestMapping;
+ }
+
+ @Override
+ public void service(HttpRequest request, HttpResponse response) throws Exception {
+ Controller controller = requestMapping.getController(request);
+ controller.service(request, response);
+ }
+}
diff --git a/tomcat/src/main/java/org/apache/catalina/servlet/RequestMapping.java b/tomcat/src/main/java/org/apache/catalina/servlet/RequestMapping.java
new file mode 100644
index 0000000000..dbc7cdef21
--- /dev/null
+++ b/tomcat/src/main/java/org/apache/catalina/servlet/RequestMapping.java
@@ -0,0 +1,24 @@
+package org.apache.catalina.servlet;
+
+import java.util.Map;
+
+import org.apache.catalina.exception.NoMatchedControllerException;
+import org.apache.coyote.http11.httpmessage.request.HttpRequest;
+
+public class RequestMapping {
+ private final Map mappedController;
+
+ public RequestMapping(Map mappedController) {
+ this.mappedController = mappedController;
+ }
+
+ public Controller getController(HttpRequest request) {
+ String controllerKey = mappedController.keySet().stream()
+ .filter(pathRegex -> request.getTarget().matches(pathRegex))
+ .findFirst()
+ .orElseThrow(() -> new NoMatchedControllerException(
+ request.getTarget() + " 요청을 처리할 컨트롤러가 존재하지 않습니다.")
+ );
+ return mappedController.get(controllerKey);
+ }
+}
diff --git a/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java b/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java
index 61afa8df71..eddcdf8b0c 100644
--- a/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java
+++ b/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java
@@ -3,6 +3,7 @@
import java.io.IOException;
import org.apache.catalina.connector.Connector;
+import org.apache.coyote.ServletContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -10,8 +11,14 @@ public class Tomcat {
private static final Logger log = LoggerFactory.getLogger(Tomcat.class);
+ private final ServletContainer servletContainer;
+
+ public Tomcat(ServletContainer servletContainer) {
+ this.servletContainer = servletContainer;
+ }
+
public void start() {
- var connector = new Connector();
+ var connector = new Connector(servletContainer);
connector.start();
try {
diff --git a/tomcat/src/main/java/org/apache/coyote/ServletContainer.java b/tomcat/src/main/java/org/apache/coyote/ServletContainer.java
new file mode 100644
index 0000000000..dafc782b31
--- /dev/null
+++ b/tomcat/src/main/java/org/apache/coyote/ServletContainer.java
@@ -0,0 +1,8 @@
+package org.apache.coyote;
+
+import org.apache.coyote.http11.httpmessage.request.HttpRequest;
+import org.apache.coyote.http11.httpmessage.response.HttpResponse;
+
+public interface ServletContainer {
+ void service(HttpRequest request, HttpResponse response) throws Exception;
+}
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 e61d416570..cd60a86516 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java
+++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java
@@ -6,8 +6,9 @@
import java.net.Socket;
import org.apache.coyote.Processor;
-import org.apache.coyote.http11.handler.DefaultResourceHandler;
-import org.apache.coyote.http11.httpmessage.request.Request;
+import org.apache.coyote.ServletContainer;
+import org.apache.coyote.http11.httpmessage.request.HttpRequest;
+import org.apache.coyote.http11.httpmessage.response.HttpResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -16,12 +17,12 @@
public class Http11Processor implements Runnable, Processor {
private static final Logger log = LoggerFactory.getLogger(Http11Processor.class);
- private final RequestHandler requestHandler;
+ private final ServletContainer servletContainer;
private final Socket connection;
- public Http11Processor(Socket connection) {
+ public Http11Processor(ServletContainer servletContainer, Socket connection) {
+ this.servletContainer = servletContainer;
this.connection = connection;
- this.requestHandler = new DefaultResourceHandler();
}
@Override
@@ -37,18 +38,29 @@ public void process(Socket connection) {
BufferedReader requestBufferedReader = new BufferedReader(inputStreamReader);
var outputStream = connection.getOutputStream()) {
- Request request = Request.readFrom(requestBufferedReader);
- log.info("request : {}", request);
- String response = getResponse(request);
+ HttpRequest httpRequest = HttpRequest.readFrom(requestBufferedReader);
+ log.info("request : {}", httpRequest);
+ HttpResponse httpResponse = new HttpResponse(httpRequest);
- outputStream.write(response.getBytes());
+ service(httpRequest, httpResponse);
+
+ outputStream.write(httpResponse.toHttpMessage().getBytes());
outputStream.flush();
} catch (IOException | UncheckedServletException e) {
log.error(e.getMessage(), e);
}
}
- private String getResponse(Request request) throws IOException {
- return requestHandler.handle(request);
+ private void service(HttpRequest httpRequest, HttpResponse httpResponse) {
+ try {
+ servletContainer.service(httpRequest, httpResponse);
+ if(!httpResponse.containsHeader("Cache-Control")) {
+ httpResponse.addHeader("Cache-Control", "no-cache, private");
+ }
+
+ } catch (Exception e) {
+ log.error("request 처리 실패 : ", e);
+ httpResponse.setStatusBadRequest();
+ }
}
}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/RequestHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/RequestHandler.java
deleted file mode 100644
index 89ac542ac2..0000000000
--- a/tomcat/src/main/java/org/apache/coyote/http11/RequestHandler.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.apache.coyote.http11;
-
-import java.io.IOException;
-
-import org.apache.coyote.http11.httpmessage.request.Request;
-
-@FunctionalInterface
-public interface RequestHandler {
- String handle(Request request) throws IOException;
-}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/exception/CanNotHandleRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/exception/CanNotHandleRequest.java
deleted file mode 100644
index 922fbc0c82..0000000000
--- a/tomcat/src/main/java/org/apache/coyote/http11/exception/CanNotHandleRequest.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.apache.coyote.http11.exception;
-
-public class CanNotHandleRequest extends RuntimeException {
- public CanNotHandleRequest(String message) {
- super(message);
- }
-}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/exception/CantHandleRequestException.java b/tomcat/src/main/java/org/apache/coyote/http11/exception/CantHandleRequestException.java
new file mode 100644
index 0000000000..f81aa9cff8
--- /dev/null
+++ b/tomcat/src/main/java/org/apache/coyote/http11/exception/CantHandleRequestException.java
@@ -0,0 +1,7 @@
+package org.apache.coyote.http11.exception;
+
+public class CantHandleRequestException extends RuntimeException {
+ public CantHandleRequestException(String message) {
+ super(message);
+ }
+}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/exception/IllegalHttpMessageException.java b/tomcat/src/main/java/org/apache/coyote/http11/exception/IllegalHttpMessageException.java
new file mode 100644
index 0000000000..206ed000b1
--- /dev/null
+++ b/tomcat/src/main/java/org/apache/coyote/http11/exception/IllegalHttpMessageException.java
@@ -0,0 +1,7 @@
+package org.apache.coyote.http11.exception;
+
+public class IllegalHttpMessageException extends RuntimeException {
+ public IllegalHttpMessageException(String message) {
+ super(message);
+ }
+}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/exception/NoHandlerException.java b/tomcat/src/main/java/org/apache/coyote/http11/exception/NoHandlerException.java
deleted file mode 100644
index a8f36e5d4e..0000000000
--- a/tomcat/src/main/java/org/apache/coyote/http11/exception/NoHandlerException.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.apache.coyote.http11.exception;
-
-public class NoHandlerException extends RuntimeException {
- public NoHandlerException(String message) {
- super(message);
- }
-}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/DefaultResourceHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/DefaultResourceHandler.java
deleted file mode 100644
index fcca082d83..0000000000
--- a/tomcat/src/main/java/org/apache/coyote/http11/handler/DefaultResourceHandler.java
+++ /dev/null
@@ -1,113 +0,0 @@
-package org.apache.coyote.http11.handler;
-
-import java.io.IOException;
-import java.util.Objects;
-
-import org.apache.coyote.http11.RequestHandler;
-import org.apache.coyote.http11.exception.CanNotHandleRequest;
-import org.apache.coyote.http11.exception.NoSuchUserException;
-import org.apache.coyote.http11.httpmessage.request.Request;
-import org.apache.coyote.http11.httpmessage.response.Response;
-import org.apache.coyote.http11.httpmessage.response.StaticResource;
-import org.apache.coyote.http11.session.Session;
-import org.apache.coyote.http11.session.SessionManager;
-
-import com.techcourse.db.InMemoryUserRepository;
-import com.techcourse.model.User;
-
-public class DefaultResourceHandler implements RequestHandler {
-
- @Override
- public String handle(Request request) throws IOException {
- if (request.isStaticResourceRequest()) {
- return Response.builder()
- .versionOf(request.getHttpVersion())
- .ofStaticResource(new StaticResource(request.getTarget()))
- .toHttpMessage();
- }
- if (request.getTarget().equals("/")) {
- return Response.builder()
- .versionOf(request.getHttpVersion())
- .ofStaticResource(new StaticResource("/index.html"))
- .toHttpMessage();
- }
- if (request.getTarget().equals("/login")) {
- return loginResponse(request);
- }
- if (request.getTarget().contains("register")) {
- return registerResponse(request);
- }
- throw new CanNotHandleRequest("처리할 수 없는 요청입니다. : " + request.getTarget());
- }
-
- private String loginResponse(Request request) throws IOException {
- if (request.isPost()) {
- return login(request).toHttpMessage();
- }
- if (isLoggedIn(request)) {
- return Response.builder()
- .versionOf(request.getHttpVersion())
- .found("/index.html")
- .toHttpMessage();
- }
- return Response.builder()
- .versionOf(request.getHttpVersion())
- .ofStaticResource(new StaticResource("/login.html"))
- .toHttpMessage();
- }
-
- private boolean isLoggedIn(Request request) {
- return Objects.nonNull(request.getSession(false));
- }
-
- private Response login(Request request) throws NoSuchUserException {
- RequestParameters requestParams = RequestParameters.parseFrom(request.getBody());
- String account = requestParams.getParam("account");
- String password = requestParams.getParam("password");
- User user = InMemoryUserRepository.fetchByAccount(account);
- if (user.checkPassword(password)) {
- Session session = request.getSession(true);
- session.setAttribute("user", user);
- SessionManager.getInstance().add(session);
- return Response.builder()
- .versionOf(request.getHttpVersion())
- .addCookie("JSESSIONID", session.getId())
- .found("/index.html");
- }
-
- return Response.builder()
- .versionOf(request.getHttpVersion())
- .found("/401.html");
- }
-
- private String registerResponse(Request request) throws IOException {
- if (request.isPost()) {
- RequestParameters methodRequest = RequestParameters.parseFrom(request.getBody());
- User user = register(methodRequest);
- Session session = request.getSession(true);
- session.setAttribute("user", user);
- SessionManager.getInstance().add(session);
- return Response.builder()
- .versionOf(request.getHttpVersion())
- .addCookie("JSESSIONID", session.getId())
- .found("/index.html")
- .toHttpMessage();
- }
-
- return Response.builder()
- .versionOf(request.getHttpVersion())
- .ofStaticResource(new StaticResource("/register.html"))
- .toHttpMessage();
- }
-
- private User register(RequestParameters requestParams) {
- String account = requestParams.getParam("account");
- User user = new User(
- account,
- requestParams.getParam("password"),
- requestParams.getParam("email")
- );
- InMemoryUserRepository.save(user);
- return InMemoryUserRepository.fetchByAccount(account);
- }
-}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/Method.java b/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/HttpMethod.java
similarity index 81%
rename from tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/Method.java
rename to tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/HttpMethod.java
index 46e3b4ed9a..995138f632 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/Method.java
+++ b/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/HttpMethod.java
@@ -2,10 +2,10 @@
import java.util.Arrays;
-public enum Method {
+public enum HttpMethod {
GET, POST, PUT, PATCH, DELETE;
- public static Method findByName(String name) {
+ public static HttpMethod findByName(String name) {
return Arrays.stream(values())
.filter(method -> method.name().equals(name))
.findFirst()
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/Request.java b/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/HttpRequest.java
similarity index 73%
rename from tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/Request.java
rename to tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/HttpRequest.java
index 83798f2c7d..f0db2f9b5f 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/Request.java
+++ b/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/HttpRequest.java
@@ -8,20 +8,20 @@
import org.apache.coyote.http11.httpmessage.HttpCookie;
import org.apache.coyote.http11.httpmessage.HttpHeaders;
-import org.apache.coyote.http11.session.Session;
-import org.apache.coyote.http11.session.SessionManager;
+import org.apache.coyote.session.Session;
+import org.apache.coyote.session.SessionManager;
-public class Request {
+public class HttpRequest {
- private final RequestLine requestLine;
+ private final HttpRequestLine httpRequestLine;
private final HttpHeaders headers;
private final HttpCookie cookies;
private final String body;
private Session session;
- private Request(RequestLine requestLine, HttpHeaders headers, String body) {
- this.requestLine = requestLine;
+ private HttpRequest(HttpRequestLine httpRequestLine, HttpHeaders headers, String body) {
+ this.httpRequestLine = httpRequestLine;
this.headers = headers;
if (headers.contains(HttpHeaders.COOKIE)) {
this.cookies = HttpCookie.parseFrom(headers.get(HttpHeaders.COOKIE));
@@ -36,11 +36,11 @@ private Request(RequestLine requestLine, HttpHeaders headers, String body) {
}
}
- public static Request readFrom(BufferedReader reader) throws IOException {
- RequestLine requestLine = RequestLine.parseFrom(reader.readLine());
+ public static HttpRequest readFrom(BufferedReader reader) throws IOException {
+ HttpRequestLine httpRequestLine = HttpRequestLine.parseFrom(reader.readLine());
HttpHeaders header = new HttpHeaders(readHeader(reader));
String requestBody = readBody(reader, header.getContentLength());
- return new Request(requestLine, header, requestBody);
+ return new HttpRequest(httpRequestLine, header, requestBody);
}
private static Map readHeader(BufferedReader reader) throws IOException {
@@ -70,24 +70,24 @@ public Session getSession(boolean created) {
return this.session;
}
- public boolean isPost() {
- return requestLine.isPost();
+ public boolean isGet() {
+ return httpRequestLine.isGet();
}
- public boolean isStaticResourceRequest() {
- return requestLine.isStaticResourceRequest();
+ public boolean isPost() {
+ return httpRequestLine.isPost();
}
- public Method getMethod() {
- return requestLine.method();
+ public HttpMethod getMethod() {
+ return httpRequestLine.httpMethod();
}
public String getTarget() {
- return requestLine.target();
+ return httpRequestLine.target();
}
public String getHttpVersion() {
- return requestLine.httpVersion();
+ return httpRequestLine.httpVersion();
}
public String getBody() {
@@ -97,7 +97,7 @@ public String getBody() {
@Override
public String toString() {
return "Request{" +
- "requestLine=" + requestLine +
+ "requestLine=" + httpRequestLine +
", headers=" + headers +
", body='" + body + '\'' +
'}';
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/HttpRequestLine.java b/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/HttpRequestLine.java
new file mode 100644
index 0000000000..e3cc362f9a
--- /dev/null
+++ b/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/HttpRequestLine.java
@@ -0,0 +1,41 @@
+package org.apache.coyote.http11.httpmessage.request;
+
+import org.apache.coyote.http11.exception.IllegalHttpMessageException;
+
+public record HttpRequestLine(
+ HttpMethod httpMethod,
+ String target,
+ String httpVersion
+) {
+
+ public static HttpRequestLine parseFrom(String httpRequestLineText) {
+ String[] token = httpRequestLineText.split(" ");
+ if (token.length != 3) {
+ throw new IllegalHttpMessageException("잘못된 헤더 형식입니다.");
+ }
+ return new HttpRequestLine(HttpMethod.findByName(token[0]), token[1], token[2]);
+ }
+
+ public boolean isGet() {
+ return this.httpMethod == HttpMethod.GET;
+ }
+
+ public boolean isPost() {
+ return httpMethod == HttpMethod.POST;
+ }
+
+ public boolean isStaticResourceRequest() {
+ return target.contains(".css") ||
+ target.contains(".html") ||
+ target.contains(".js");
+ }
+
+ @Override
+ public String toString() {
+ return "RequestLine{" +
+ "httpMethod=" + httpMethod +
+ ", target='" + target + '\'' +
+ ", httpVersion='" + httpVersion + '\'' +
+ '}';
+ }
+}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/RequestParameters.java b/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/HttpRequestParameters.java
similarity index 72%
rename from tomcat/src/main/java/org/apache/coyote/http11/handler/RequestParameters.java
rename to tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/HttpRequestParameters.java
index f75590253a..0494ff1720 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/handler/RequestParameters.java
+++ b/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/HttpRequestParameters.java
@@ -1,25 +1,25 @@
-package org.apache.coyote.http11.handler;
+package org.apache.coyote.http11.httpmessage.request;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
-public class RequestParameters {
+public class HttpRequestParameters {
private final Map requestParams;
- private RequestParameters(Map requestParams) {
+ private HttpRequestParameters(Map requestParams) {
this.requestParams = Map.copyOf(requestParams);
}
- public static RequestParameters parseFrom(String paramString) {
+ public static HttpRequestParameters parseFrom(String paramString) {
Map requestParams = new HashMap<>();
String[] requestParamTokens = paramString.split("&");
for (String requestParam : requestParamTokens) {
String[] split = requestParam.split("=");
requestParams.put(split[0], split[1]);
}
- return new RequestParameters(requestParams);
+ return new HttpRequestParameters(requestParams);
}
public String getParam(String key) {
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/RequestLine.java b/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/RequestLine.java
deleted file mode 100644
index 66cad8037e..0000000000
--- a/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/request/RequestLine.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.apache.coyote.http11.httpmessage.request;
-
-public record RequestLine (
- Method method,
- String target,
- String httpVersion
-) {
-
- public static RequestLine parseFrom(String requestLineText) {
- String[] token = requestLineText.split(" ");
- return new RequestLine(Method.findByName(token[0]), token[1], token[2]);
- }
-
- public boolean isPost() {
- return method == Method.POST;
- }
-
- public boolean isStaticResourceRequest() {
- return target.contains(".css") ||
- target.contains(".html") ||
- target.contains(".js");
- }
-
- @Override
- public String toString() {
- return "RequestLine{" +
- "method=" + method +
- ", target='" + target + '\'' +
- ", httpVersion='" + httpVersion + '\'' +
- '}';
- }
-}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/response/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/response/HttpResponse.java
new file mode 100644
index 0000000000..1f8829ac82
--- /dev/null
+++ b/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/response/HttpResponse.java
@@ -0,0 +1,87 @@
+package org.apache.coyote.http11.httpmessage.response;
+
+import java.io.IOException;
+
+import org.apache.coyote.http11.exception.NotCompleteResponseException;
+import org.apache.coyote.http11.httpmessage.HttpCookie;
+import org.apache.coyote.http11.httpmessage.HttpHeaders;
+import org.apache.coyote.http11.httpmessage.request.HttpRequest;
+
+public class HttpResponse {
+
+ private final HttpCookie httpCookie;
+ private final HttpStatusLine httpStatusLine;
+ private final HttpHeaders headers;
+ private String body;
+
+
+ public HttpResponse(HttpStatusLine httpStatusLine, HttpHeaders headers, String body) {
+ this.httpStatusLine = httpStatusLine;
+ this.headers = headers;
+ this.body = body;
+ httpCookie = new HttpCookie();
+ }
+
+ public HttpResponse(HttpRequest httpRequest) {
+
+ this(new HttpStatusLine(httpRequest.getHttpVersion(), HttpStatus.NOT_FOUND),
+ new HttpHeaders(),
+ "");
+ }
+
+ public boolean containsHeader(String headerName) {
+ return headers.contains(headerName);
+ }
+
+ public void addHeader(String name, String value) {
+ headers.addHeader(name, value);
+ }
+
+ public String toHttpMessage() {
+ if (httpStatusLine == null) {
+ throw new NotCompleteResponseException("응답이 완성되지 않았습니다.");
+ }
+ setCookie();
+ return String.join("\r\n",
+ httpStatusLine.toHttpMessage(),
+ headers.toHttpMessage(),
+ "",
+ body);
+ }
+
+ private void setCookie() {
+ if(!httpCookie.isEmpty()) {
+ headers.addHeader(HttpHeaders.SET_COOKIE, httpCookie.toHttpMessage());
+ }
+ }
+
+ public void setStatusFound(String target) {
+ this.httpStatusLine.setStatus(HttpStatus.FOUND);
+ this.headers.addHeader(HttpHeaders.LOCATION, target);
+ }
+
+ public void setStatusBadRequest() {
+ this.httpStatusLine.setStatus(HttpStatus.BAD_REQUEST);
+ }
+
+ public void setResponseOfStaticResource(StaticResource resource) throws IOException {
+ this.httpStatusLine.setStatus(HttpStatus.OK);
+ headers.addHeader(HttpHeaders.CONTENT_TYPE, resource.getContentType() + ";charset=utf-8");
+ headers.addHeader(HttpHeaders.CONTENT_LENGTH, Long.toString(resource.getContentLength()));
+ this.body = resource.getContent();
+ }
+
+ public void addCookie(String key, String value) {
+ httpCookie.addCookie(key, value);
+ }
+
+ @Override
+ public String toString() {
+ return "HttpResponse{" +
+ "httpCookie=" + httpCookie +
+ ", httpStatusLine=" + httpStatusLine +
+ ", headers=" + headers +
+ ", body='" + body + '\'' +
+ '}';
+ }
+}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/response/HttpStatus.java b/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/response/HttpStatus.java
new file mode 100644
index 0000000000..e1595c8a08
--- /dev/null
+++ b/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/response/HttpStatus.java
@@ -0,0 +1,24 @@
+package org.apache.coyote.http11.httpmessage.response;
+
+public enum HttpStatus {
+ OK(200, "OK"),
+ FOUND(301, "FOUND"),
+ BAD_REQUEST(400, "BAD REQUEST"),
+ NOT_FOUND(404, "NOT FOUND");
+
+ private final int statusCode;
+ private final String statusText;
+
+ HttpStatus(int statusCode, String statusText) {
+ this.statusCode = statusCode;
+ this.statusText = statusText;
+ }
+
+ public int getStatusCode() {
+ return statusCode;
+ }
+
+ public String getStatusText() {
+ return statusText;
+ }
+}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/response/HttpStatusLine.java b/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/response/HttpStatusLine.java
new file mode 100644
index 0000000000..b0f4e0f376
--- /dev/null
+++ b/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/response/HttpStatusLine.java
@@ -0,0 +1,24 @@
+package org.apache.coyote.http11.httpmessage.response;
+
+public class HttpStatusLine {
+
+ private final String httpVersion;
+
+ private int statusCode;
+ private String statusText;
+
+ public HttpStatusLine(String httpVersion, HttpStatus httpStatus) {
+ this.httpVersion = httpVersion;
+ this.statusCode = httpStatus.getStatusCode();
+ this.statusText = httpStatus.getStatusText();
+ }
+
+ public String toHttpMessage() {
+ return String.format("%s %s %s ", httpVersion, statusCode, statusText);
+ }
+
+ public void setStatus(HttpStatus status) {
+ this.statusCode = status.getStatusCode();
+ this.statusText = status.getStatusText();
+ }
+}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/response/Response.java b/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/response/Response.java
deleted file mode 100644
index 13e65f1303..0000000000
--- a/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/response/Response.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package org.apache.coyote.http11.httpmessage.response;
-
-import java.io.IOException;
-
-import org.apache.coyote.http11.exception.NotCompleteResponseException;
-import org.apache.coyote.http11.httpmessage.HttpCookie;
-import org.apache.coyote.http11.httpmessage.HttpHeaders;
-
-public class Response {
-
- private final StatusLine statusLine;
- private final HttpHeaders headers;
- private final String content;
-
- public static ResponseBuilder builder() {
- return new ResponseBuilder();
- }
-
- public Response(StatusLine statusLine, HttpHeaders headers, String content) {
- this.statusLine = statusLine;
- this.headers = headers;
- this.content = content;
- }
-
- public static class ResponseBuilder {
- private final HttpCookie httpCookie;
- private final HttpHeaders headers;
- private String ProtocolVersion;
-
- private ResponseBuilder() {
- ProtocolVersion = "HTTP/1.1";
- httpCookie = new HttpCookie();
- headers = new HttpHeaders();
- }
-
- public ResponseBuilder versionOf(String protocolVersion) {
- this.ProtocolVersion = protocolVersion;
- return this;
- }
-
- public ResponseBuilder addCookie(String key, String value) {
- httpCookie.addCookie(key, value);
- return this;
- }
-
- public Response found(String target) {
- this.headers.addHeader(HttpHeaders.LOCATION, target);
-
- return build(
- new StatusLine(this.ProtocolVersion, 301, "FOUND"),
- this.headers,
- ""
- );
- }
-
- public Response ofStaticResource(StaticResource resource) throws IOException {
- headers.addHeader(HttpHeaders.CONTENT_TYPE, resource.getContentType() + ";charset=utf-8");
- headers.addHeader(HttpHeaders.CONTENT_LENGTH, Long.toString(resource.getContentLength()));
-
- return build(
- new StatusLine(this.ProtocolVersion, 200, "OK"),
- this.headers,
- resource.getContent()
- );
- }
-
- public Response build(StatusLine statusLine, HttpHeaders headers, String content) {
- setCookie(headers);
- return new Response(statusLine, headers, content);
- }
-
- private void setCookie(HttpHeaders headers) {
- if(!httpCookie.isEmpty()) {
- headers.addHeader(HttpHeaders.SET_COOKIE, httpCookie.toHttpMessage());
- }
- }
- }
-
- public String toHttpMessage() {
- if (statusLine == null) {
- throw new NotCompleteResponseException("응답이 완성되지 않았습니다.");
- }
- return String.join("\r\n",
- statusLine.toHttpMessage(),
- headers.toHttpMessage(),
- "",
- content);
- }
-}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/response/StatusLine.java b/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/response/StatusLine.java
deleted file mode 100644
index 21b1a87990..0000000000
--- a/tomcat/src/main/java/org/apache/coyote/http11/httpmessage/response/StatusLine.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package org.apache.coyote.http11.httpmessage.response;
-
-public class StatusLine {
-
- private final String protocolVersion;
- private final int statusCode;
- private final String statusText;
-
- public StatusLine(String protocolVersion, int statusCode, String statusText) {
- this.protocolVersion = protocolVersion;
- this.statusCode = statusCode;
- this.statusText = statusText;
- }
-
- public String toHttpMessage() {
- return String.format("%s %s %s ", protocolVersion, statusCode, statusText);
- }
-}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/session/Session.java b/tomcat/src/main/java/org/apache/coyote/session/Session.java
similarity index 93%
rename from tomcat/src/main/java/org/apache/coyote/http11/session/Session.java
rename to tomcat/src/main/java/org/apache/coyote/session/Session.java
index ba63df869d..c1c2f41b55 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/session/Session.java
+++ b/tomcat/src/main/java/org/apache/coyote/session/Session.java
@@ -1,4 +1,4 @@
-package org.apache.coyote.http11.session;
+package org.apache.coyote.session;
import java.util.HashMap;
import java.util.Map;
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/session/SessionManager.java b/tomcat/src/main/java/org/apache/coyote/session/SessionManager.java
similarity index 94%
rename from tomcat/src/main/java/org/apache/coyote/http11/session/SessionManager.java
rename to tomcat/src/main/java/org/apache/coyote/session/SessionManager.java
index 277c87c3e1..482a7a8967 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/session/SessionManager.java
+++ b/tomcat/src/main/java/org/apache/coyote/session/SessionManager.java
@@ -1,4 +1,4 @@
-package org.apache.coyote.http11.session;
+package org.apache.coyote.session;
import java.util.HashMap;
import java.util.Map;
diff --git a/tomcat/src/test/java/org/apache/catalina/TestPathServletContainerConfig.java b/tomcat/src/test/java/org/apache/catalina/TestPathServletContainerConfig.java
new file mode 100644
index 0000000000..a636d3678a
--- /dev/null
+++ b/tomcat/src/test/java/org/apache/catalina/TestPathServletContainerConfig.java
@@ -0,0 +1,37 @@
+package org.apache.catalina;
+
+import java.util.Map;
+
+import org.apache.catalina.servlet.AbstractController;
+import org.apache.catalina.servlet.RequestMapping;
+import org.apache.coyote.http11.httpmessage.request.HttpRequest;
+import org.apache.coyote.http11.httpmessage.response.HttpResponse;
+import org.apache.coyote.http11.httpmessage.response.StaticResource;
+
+import com.techcourse.controller.StaticResourceController;
+
+public class TestPathServletContainerConfig {
+
+ public static final String SUCCESS_PATH = "/success";
+
+ public static RequestMapping getRequestMapping() {
+ return new RequestMapping(
+ Map.of(
+ SUCCESS_PATH, new SuccessController()
+ )
+ );
+ }
+
+ private static class SuccessController extends AbstractController {
+
+ @Override
+ protected void doPost(HttpRequest request, HttpResponse response) throws Exception {
+ response.setResponseOfStaticResource(new StaticResource("/index.html"));
+ }
+
+ @Override
+ protected void doGet(HttpRequest request, HttpResponse response) throws Exception {
+ response.setResponseOfStaticResource(new StaticResource("/index.html"));
+ }
+ }
+}
diff --git a/tomcat/src/test/java/org/apache/catalina/servlet/PathMatchServletContainerTest.java b/tomcat/src/test/java/org/apache/catalina/servlet/PathMatchServletContainerTest.java
new file mode 100644
index 0000000000..f840e3432b
--- /dev/null
+++ b/tomcat/src/test/java/org/apache/catalina/servlet/PathMatchServletContainerTest.java
@@ -0,0 +1,55 @@
+package org.apache.catalina.servlet;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import org.apache.catalina.TestPathServletContainerConfig;
+import org.apache.catalina.exception.NoMatchedControllerException;
+import org.apache.coyote.http11.httpmessage.request.HttpRequest;
+import org.apache.coyote.http11.httpmessage.response.HttpResponse;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+class PathMatchServletContainerTest {
+
+ private PathMatchServletContainer pathMatchServletContainer = new PathMatchServletContainer(
+ TestPathServletContainerConfig.getRequestMapping()
+ );
+
+ @Test
+ @DisplayName("매핑 되는 경로의 컨트롤러가 존재하면 요청을 처리할 수 있다.")
+ void successService() throws IOException {
+ //given
+ HttpRequest httpRequest = makeRequestFrom("GET /success HTTP/1.1 ");
+ HttpResponse httpResponse = new HttpResponse(httpRequest);
+
+ //when & then
+ assertThatCode(() -> pathMatchServletContainer.service(httpRequest, httpResponse))
+ .doesNotThrowAnyException();
+ }
+
+ @Test
+ @DisplayName("매핑 되는 경로의 컨트롤러가 존재하지 않으면 예외가 발생한다.")
+ void failServiceNotMatchController() throws IOException {
+ //given
+ HttpRequest httpRequest = makeRequestFrom("GET /fail HTTP/1.1 ");
+ HttpResponse httpResponse = new HttpResponse(httpRequest);
+
+ //when & then
+ assertThatThrownBy(() -> pathMatchServletContainer.service(httpRequest, httpResponse))
+ .isInstanceOf(NoMatchedControllerException.class)
+ .hasMessage(httpRequest.getTarget() + " 요청을 처리할 컨트롤러가 존재하지 않습니다.");
+ }
+
+ private HttpRequest makeRequestFrom(String requestMessage) throws IOException {
+ var bufferedInputStream = new BufferedReader(
+ new InputStreamReader(new ByteArrayInputStream(requestMessage.getBytes()))
+ );
+ return HttpRequest.readFrom(bufferedInputStream);
+ }
+}
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 a521e0059a..ad21dbbbe4 100644
--- a/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java
+++ b/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java
@@ -7,13 +7,17 @@
import java.net.URL;
import java.nio.file.Files;
-import org.apache.coyote.http11.session.Session;
-import org.apache.coyote.http11.session.SessionManager;
+import org.apache.catalina.servlet.PathMatchServletContainer;
+import org.apache.catalina.servlet.RequestMapping;
+import org.apache.coyote.ServletContainer;
+import org.apache.coyote.session.Session;
+import org.apache.coyote.session.SessionManager;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
+import com.techcourse.config.RequestMappingConfig;
import com.techcourse.db.InMemoryUserRepository;
import com.techcourse.model.User;
@@ -21,12 +25,15 @@
class Http11ProcessorTest {
+ private final RequestMapping requestMapping = RequestMappingConfig.getRequestMapping();
+ private final ServletContainer servletContainer = new PathMatchServletContainer(requestMapping);
+
@Test
@Disabled
void process() {
// given
final var socket = new StubSocket();
- final var processor = new Http11Processor(socket);
+ final var processor = new Http11Processor(servletContainer, socket);
// when
processor.process(socket);
@@ -51,7 +58,7 @@ void index() throws IOException {
"");
final var socket = new StubSocket(httpRequest);
- final Http11Processor processor = new Http11Processor(socket);
+ final Http11Processor processor = new Http11Processor(servletContainer, socket);
// when
processor.process(socket);
@@ -85,7 +92,7 @@ void index() throws IOException {
"");
final var socket = new StubSocket(httpRequest);
- final Http11Processor processor = new Http11Processor(socket);
+ final Http11Processor processor = new Http11Processor(servletContainer, socket);
// when
processor.process(socket);
@@ -115,7 +122,7 @@ void css() throws IOException {
"");
final var socket = new StubSocket(httpRequest);
- final Http11Processor processor = new Http11Processor(socket);
+ final Http11Processor processor = new Http11Processor(servletContainer, socket);
// when
processor.process(socket);
@@ -149,7 +156,7 @@ void login() throws IOException {
"");
final var socket = new StubSocket(httpRequest);
- final Http11Processor processor = new Http11Processor(socket);
+ final Http11Processor processor = new Http11Processor(servletContainer, socket);
// when
processor.process(socket);
@@ -184,7 +191,7 @@ void loginViewWhenLoggedIn() throws IOException {
"");
final var socket = new StubSocket(httpRequest);
- final Http11Processor processor = new Http11Processor(socket);
+ final Http11Processor processor = new Http11Processor(servletContainer, socket);
// when
processor.process(socket);
@@ -207,7 +214,7 @@ void loginSuccess() throws IOException {
"",
requestBody);
final var socket = new StubSocket(httpRequest);
- final Http11Processor processor = new Http11Processor(socket);
+ final Http11Processor processor = new Http11Processor(servletContainer, socket);
// when
processor.process(socket);
@@ -232,7 +239,7 @@ void loginFailed() throws IOException {
requestBody);
final var socket = new StubSocket(httpRequest);
- final Http11Processor processor = new Http11Processor(socket);
+ final Http11Processor processor = new Http11Processor(servletContainer, socket);
// when
processor.process(socket);
@@ -260,7 +267,7 @@ void registerView() throws IOException {
"");
final var socket = new StubSocket(httpRequest);
- final Http11Processor processor = new Http11Processor(socket);
+ final Http11Processor processor = new Http11Processor(servletContainer, socket);
// when
processor.process(socket);
@@ -290,7 +297,7 @@ void registerSuccess() throws IOException {
requestBody);
final var socket = new StubSocket(httpRequest);
- final Http11Processor processor = new Http11Processor(socket);
+ final Http11Processor processor = new Http11Processor(servletContainer, socket);
// when
processor.process(socket);
diff --git a/tomcat/src/test/java/org/apache/coyote/http11/httpmessage/request/HttpHttpRequestLineTest.java b/tomcat/src/test/java/org/apache/coyote/http11/httpmessage/request/HttpHttpRequestLineTest.java
new file mode 100644
index 0000000000..ce36c0eb81
--- /dev/null
+++ b/tomcat/src/test/java/org/apache/coyote/http11/httpmessage/request/HttpHttpRequestLineTest.java
@@ -0,0 +1,95 @@
+package org.apache.coyote.http11.httpmessage.request;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertAll;
+
+import org.apache.coyote.http11.exception.IllegalHttpMessageException;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+class HttpHttpRequestLineTest {
+
+ @Nested
+ @DisplayName("파싱 테스트")
+ class ParseFromTest {
+ @Test
+ @DisplayName("파싱 성공")
+ void success() {
+ // given
+ String requestLineText = "GET /index.html HTTP/1.1 ";
+
+ // when
+ HttpRequestLine httpRequestLine = HttpRequestLine.parseFrom(requestLineText);
+
+ //then
+ assertAll(
+ () -> assertThat(httpRequestLine.httpVersion()).isEqualTo("HTTP/1.1"),
+ () -> assertThat(httpRequestLine.httpMethod()).isEqualTo(HttpMethod.GET),
+ () -> assertThat(httpRequestLine.target()).isEqualTo("/index.html")
+ );
+ }
+
+ @Test
+ @DisplayName("잘못된 형식 파싱 실패")
+ void IllegalFormTest() {
+ // given
+ String wrongRequestLine = "GET /index.html";
+
+ // when & then
+ assertThatThrownBy(() -> HttpRequestLine.parseFrom(wrongRequestLine))
+ .isInstanceOf(IllegalHttpMessageException.class)
+ .hasMessageContaining("잘못된 헤더 형식입니다.");
+ }
+ }
+
+ @Nested
+ @DisplayName("정적 리소스 확인 테스트")
+ class IsStaticResourceHttpRequestTest {
+
+ @ParameterizedTest(name = "{0} 확장자 파일을 요청하면 정적 리소스로 판단한다.")
+ @ValueSource(strings = {".css", ".html", ".js"})
+ void staticResourceTest(String extension) {
+ // given
+ String target = "/somePath" + extension;
+ String requestLineText = "GET " + target + " HTTP/1.1";
+
+ //when
+ HttpRequestLine httpRequestLine = HttpRequestLine.parseFrom(requestLineText);
+
+ //then
+ assertThat(httpRequestLine.isStaticResourceRequest()).isTrue();
+ }
+
+ @Test
+ @DisplayName("파일 확장자가 없으면 정적 리소스로 판단하지 않는다.")
+ void noExtensionPathTest() {
+ // given
+ String target = "/somePath";
+ String requestLineText = "GET " + target + " HTTP/1.1";
+
+ //when
+ HttpRequestLine httpRequestLine = HttpRequestLine.parseFrom(requestLineText);
+
+ //then
+ assertThat(httpRequestLine.isStaticResourceRequest()).isFalse();
+ }
+
+ @Test
+ @DisplayName("css, js, html 이외의 파일 확장자가 있으면 정적 리소스로 판단하지 않는다.")
+ void otherExtensionPathTest() {
+ // given
+ String target = "/somePath.java";
+ String requestLineText = "GET " + target + " HTTP/1.1";
+
+ //when
+ HttpRequestLine httpRequestLine = HttpRequestLine.parseFrom(requestLineText);
+
+ //then
+ assertThat(httpRequestLine.isStaticResourceRequest()).isFalse();
+ }
+ }
+}
diff --git a/tomcat/src/test/java/org/apache/coyote/http11/httpmessage/request/HttpHttpRequestParametersTest.java b/tomcat/src/test/java/org/apache/coyote/http11/httpmessage/request/HttpHttpRequestParametersTest.java
new file mode 100644
index 0000000000..3f21357596
--- /dev/null
+++ b/tomcat/src/test/java/org/apache/coyote/http11/httpmessage/request/HttpHttpRequestParametersTest.java
@@ -0,0 +1,64 @@
+package org.apache.coyote.http11.httpmessage.request;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertAll;
+
+import java.util.NoSuchElementException;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+class HttpHttpRequestParametersTest {
+
+ @Nested
+ @DisplayName("파싱 테스트")
+ class ParseTest {
+
+ @Test
+ @DisplayName("key=value 형식의 문자열을 정상적으로 파싱할 수 있다.")
+ void oneKeyValueFromParseTest() {
+ //given
+ String parameter = "key=value";
+
+ //when
+ HttpRequestParameters httpRequestParameters = HttpRequestParameters.parseFrom(parameter);
+
+ //then
+ assertThat(httpRequestParameters.getParam("key")).isEqualTo("value");
+ }
+
+ @ParameterizedTest
+ @DisplayName("key=value 형식의 문자열 여러개를 & 로 이어 붙이면 정상적으로 파싱할 수 있다.")
+ @ValueSource(strings = {"key1=value1&key2=value2", "key1=value1&key2=value2&key3=value3"})
+ void multipleKeyValueFromParseTest(String parameterText) {
+ assertThatCode(() -> HttpRequestParameters.parseFrom(parameterText))
+ .doesNotThrowAnyException();
+ }
+
+ @ParameterizedTest(name = "{0}을 이용해 문자열을 이어붙이면 제대로 파싱되지 않는다.")
+ @ValueSource(strings = {",", "|", ";"})
+ void wrongDelimiterTest(String delimiter) {
+ //given
+ String parameterText = String.join(delimiter, "key1=value1",
+ "key2=value2");
+
+ //when
+ HttpRequestParameters httpRequestParameters = HttpRequestParameters.parseFrom(parameterText);
+
+ //then
+ assertAll(
+ () -> assertThat(httpRequestParameters.getParam("key1"))
+ .isNotEqualTo("value1"),
+ () -> assertThatThrownBy(() -> httpRequestParameters.getParam("key2"))
+ .isInstanceOf(NoSuchElementException.class)
+ .hasMessage("key2 에 해당하는 값이 존재하지 않습니다.")
+ );
+
+ }
+ }
+}
diff --git a/tomcat/src/test/java/org/apache/coyote/http11/httpmessage/request/HttpMethodTest.java b/tomcat/src/test/java/org/apache/coyote/http11/httpmessage/request/HttpMethodTest.java
new file mode 100644
index 0000000000..b83201eb4b
--- /dev/null
+++ b/tomcat/src/test/java/org/apache/coyote/http11/httpmessage/request/HttpMethodTest.java
@@ -0,0 +1,43 @@
+package org.apache.coyote.http11.httpmessage.request;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+class HttpMethodTest {
+
+ @Nested
+ @DisplayName("이름 조회 테스트")
+ class FindByName {
+
+ @ParameterizedTest
+ @EnumSource(value = HttpMethod.class)
+ @DisplayName("Http 메서드 이름으로 HttpMethod 객체를 조회할 수 있다.")
+ void successTest(HttpMethod givenMethod) {
+ HttpMethod findMethod = HttpMethod.findByName(givenMethod.name());
+
+ assertThat(findMethod).isEqualTo(givenMethod);
+ }
+
+ @Test
+ @DisplayName("잘못된 이름으로 HttpMethod 를 조회하면 예외가 발생한다.")
+ void wrongNameTest() {
+ assertThatThrownBy(() -> HttpMethod.findByName("wrongName"))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("잘못된 Http 메서드 입니다.");
+ }
+
+ @Test
+ @DisplayName("null 로 HttpMethod 를 조회하면 예외가 발생한다.")
+ void nullNameTest() {
+ assertThatThrownBy(() -> HttpMethod.findByName(null))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("잘못된 Http 메서드 입니다.");
+ }
+ }
+}
diff --git a/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestTest.java b/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestTest.java
new file mode 100644
index 0000000000..6c9eada100
--- /dev/null
+++ b/tomcat/src/test/java/org/apache/coyote/http11/request/HttpRequestTest.java
@@ -0,0 +1,138 @@
+package org.apache.coyote.http11.request;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertAll;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import org.apache.coyote.http11.httpmessage.request.HttpMethod;
+import org.apache.coyote.http11.httpmessage.request.HttpRequest;
+import org.apache.coyote.session.Session;
+import org.apache.coyote.session.SessionManager;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+class HttpRequestTest {
+
+ @Nested
+ @DisplayName("생성 테스트")
+ class ConstructorTest {
+
+ @Test
+ @DisplayName("요청 생성 테스트")
+ void getRequestConstructTest() throws IOException {
+ //given
+ String requestBody = "requestBody";
+ String requestMessage = String.join("\r\n",
+ "POST /target HTTP/1.1",
+ "Host: localhost:8080",
+ "Connection: keep-alive",
+ "Content-Length: " + requestBody.getBytes().length,
+ "",
+ requestBody);
+
+ //when
+ HttpRequest request = createRequest(requestMessage);
+
+ //then
+ assertAll(
+ () -> assertThat(request.getMethod()).isEqualTo(HttpMethod.POST),
+ () -> assertThat(request.getTarget()).isEqualTo("/target"),
+ () -> assertThat(request.getHttpVersion()).isEqualTo("HTTP/1.1"),
+ () -> assertThat(request.getBody()).isEqualTo(requestBody)
+ );
+ }
+ }
+
+ @Nested
+ @DisplayName("세션 생성 테스트")
+ class SessionTest {
+
+ @Nested
+ @DisplayName("쿠키로 JSESSIONID 를 가지고 있는 HttpRequest 객체는")
+ class ContainCookieTest {
+ String requestMessage = String.join("\r\n",
+ "GET /target HTTP/1.1",
+ "Host: localhost:8080",
+ "Connection: keep-alive",
+ "Cookie: JSESSIONID=1234",
+ "");
+
+ @Test
+ @DisplayName("세션을 조회할 수 있다.")
+ void findSessionTest() throws IOException {
+ //given
+ SessionManager.getInstance().add(new Session("1234"));
+ HttpRequest request = createRequest(requestMessage);
+
+ //when
+ Session session = request.getSession(false);
+
+ //then
+ assertThat(session.getId()).isEqualTo("1234");
+ }
+
+ @Test
+ @DisplayName("세션을 생성해도 원래의 세션이 조회된다.")
+ void createSessionTest() throws IOException {
+ //given
+ SessionManager.getInstance().add(new Session("1234"));
+ HttpRequest request = createRequest(requestMessage);
+
+ //when
+ Session session = request.getSession(true);
+
+ //then
+ assertThat(session.getId()).isEqualTo("1234");
+ }
+ }
+
+ @Nested
+ @DisplayName("쿠키로 JSESSIONID 를 가지고 있지 않은 HttpRequest 객체는")
+ class NotContainCookieTest {
+ String requestMessage = String.join("\r\n",
+ "GET /target HTTP/1.1",
+ "Host: localhost:8080",
+ "Connection: keep-alive",
+ "");
+
+ @Test
+ @DisplayName("세션을 조회하면 null 이 반환된다.")
+ void findSessionTest() throws IOException {
+ //given
+ HttpRequest request = createRequest(requestMessage);
+
+ //when
+ Session session = request.getSession(false);
+
+ //then
+ assertThat(session).isNull();
+ }
+
+ @Test
+ @DisplayName("세션을 새로 생성할 수 있다.")
+ void createSessionTest() throws IOException {
+ //given
+ HttpRequest request = createRequest(requestMessage);
+
+ //when
+ Session session = request.getSession(true);
+
+ //then
+ assertThat(session.getId()).isNotNull();
+ }
+ }
+ }
+
+ private HttpRequest createRequest(String httpRequestText) throws IOException {
+ InputStream requestStream = new ByteArrayInputStream(httpRequestText.getBytes());
+ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(requestStream));
+
+ return HttpRequest.readFrom(bufferedReader);
+ }
+}
diff --git a/tomcat/src/test/java/org/apache/coyote/http11/request/RequestTest.java b/tomcat/src/test/java/org/apache/coyote/http11/request/RequestTest.java
deleted file mode 100644
index 8438feca69..0000000000
--- a/tomcat/src/test/java/org/apache/coyote/http11/request/RequestTest.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package org.apache.coyote.http11.request;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertAll;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-
-import org.apache.coyote.http11.httpmessage.request.Method;
-import org.apache.coyote.http11.httpmessage.request.Request;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-
-class RequestTest {
-
- @Nested
- @DisplayName("생성 테스트")
- class ConstructorTest {
-
- @Test
- @DisplayName("GET 요청 생성 테스트")
- void getRequestConstructTest() throws IOException {
- //given
- String input = """
- GET /index.html HTTP/1.1
- Host: localhost:8080
- Connection: keep-alive
-
- """;
-
- //when
- InputStream requestStream = new ByteArrayInputStream(input.getBytes());
- BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(requestStream));
-
-
- Request request = Request.readFrom(bufferedReader);
-
- //then
- assertAll(
- () -> assertThat(request.getMethod()).isEqualTo(Method.GET),
- () -> assertThat(request.getTarget()).isEqualTo("/index.html")
- );
- }
- }
-}
diff --git a/tomcat/src/test/java/org/apache/coyote/session/SessionManagerTest.java b/tomcat/src/test/java/org/apache/coyote/session/SessionManagerTest.java
new file mode 100644
index 0000000000..86069c4c09
--- /dev/null
+++ b/tomcat/src/test/java/org/apache/coyote/session/SessionManagerTest.java
@@ -0,0 +1,45 @@
+package org.apache.coyote.session;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+class SessionManagerTest {
+
+ @Test
+ @DisplayName("SessionManager 를 싱글톤으로 관리한다.")
+ void singletonTest() {
+ SessionManager sessionManager1 = SessionManager.getInstance();
+ SessionManager sessionManager2 = SessionManager.getInstance();
+
+ assertThat(sessionManager1).isSameAs(sessionManager2);
+ }
+
+ @Test
+ @DisplayName("Session 을 저장하고 session id 값으로 다시 조회할 수 있다.")
+ void sessionAddAndFindTest() {
+ String sessionId = "sessionId";
+ Session session = new Session(sessionId);
+ SessionManager sessionManager = SessionManager.getInstance();
+
+ sessionManager.add(session);
+
+ assertThat(sessionManager.findSession(sessionId))
+ .isEqualTo(session);
+ }
+
+ @Test
+ @DisplayName("Session 을 삭제하면 session id 값으로 조회했을 때 null 이 나온다.")
+ void sessionRemoveAndFindTest() {
+ String sessionId = "sessionId";
+ Session session = new Session(sessionId);
+ SessionManager sessionManager = SessionManager.getInstance();
+
+ sessionManager.add(session);
+ sessionManager.remove(sessionId);
+
+ assertThat(sessionManager.findSession(sessionId))
+ .isNull();
+ }
+}
diff --git a/tomcat/src/test/java/org/apache/coyote/session/SessionTest.java b/tomcat/src/test/java/org/apache/coyote/session/SessionTest.java
new file mode 100644
index 0000000000..f8a5c0500f
--- /dev/null
+++ b/tomcat/src/test/java/org/apache/coyote/session/SessionTest.java
@@ -0,0 +1,53 @@
+package org.apache.coyote.session;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+class SessionTest {
+
+ @Test
+ @DisplayName("세션에 정보를 저장하고 조회할 수 있다.")
+ void sessionAddTest () {
+ Session session = new Session("id");
+
+ session.setAttribute("key", "value");
+
+ assertThat(session.getAttribute("key"))
+ .isEqualTo("value");
+ }
+
+ @Test
+ @DisplayName("세션에 정보를 삭제하면 정보 조회시 null 이 반환된다.")
+ void sessionAttributeRemoveTest () {
+ Session session = new Session("id");
+
+ session.setAttribute("key", "value");
+ session.removeAttribute("key");
+
+ assertThat(session.getAttribute("key"))
+ .isNull();
+ }
+
+ @Test
+ @DisplayName("세션을 만료 시키면 모든 데이터가 삭제된다..")
+ void sessionInvalidateTest () {
+ Session session = new Session("id");
+
+ session.setAttribute("key1", "value1");
+ session.setAttribute("key2", "value2");
+ session.setAttribute("key3", "value3");
+ session.invalidate();
+
+ assertAll(
+ () -> assertThat(session.getAttribute("key1"))
+ .isNull(),
+ () -> assertThat(session.getAttribute("key2"))
+ .isNull(),
+ () -> assertThat(session.getAttribute("key3"))
+ .isNull()
+ );
+ }
+}