diff --git a/study/src/main/resources/application.yml b/study/src/main/resources/application.yml
index 4e8655a962..f838f3124f 100644
--- a/study/src/main/resources/application.yml
+++ b/study/src/main/resources/application.yml
@@ -3,7 +3,7 @@ handlebars:
server:
tomcat:
- accept-count: 1
- max-connections: 1
+ accept-count: 10
+ max-connections: 3
threads:
max: 2
diff --git a/study/src/test/java/thread/stage0/SynchronizationTest.java b/study/src/test/java/thread/stage0/SynchronizationTest.java
index 0333c18e3b..7cd69e496a 100644
--- a/study/src/test/java/thread/stage0/SynchronizationTest.java
+++ b/study/src/test/java/thread/stage0/SynchronizationTest.java
@@ -42,7 +42,9 @@ private static final class SynchronizedMethods {
private int sum = 0;
public void calculate() {
- setSum(getSum() + 1);
+ synchronized (this) {
+ setSum(getSum() + 1);
+ }
}
public int getSum() {
diff --git a/study/src/test/java/thread/stage0/ThreadPoolsTest.java b/study/src/test/java/thread/stage0/ThreadPoolsTest.java
index 238611ebfe..5617c0fdc3 100644
--- a/study/src/test/java/thread/stage0/ThreadPoolsTest.java
+++ b/study/src/test/java/thread/stage0/ThreadPoolsTest.java
@@ -1,23 +1,19 @@
package thread.stage0;
-import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import static org.assertj.core.api.Assertions.assertThat;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
-
-import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
- * 스레드 풀은 무엇이고 어떻게 동작할까?
- * 테스트를 통과시키고 왜 해당 결과가 나왔는지 생각해보자.
- *
- * Thread Pools
- * https://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html
- *
- * Introduction to Thread Pools in Java
- * https://www.baeldung.com/thread-pool-java-and-guava
+ * 스레드 풀은 무엇이고 어떻게 동작할까? 테스트를 통과시키고 왜 해당 결과가 나왔는지 생각해보자.
+ *
+ * Thread Pools https://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html
+ *
+ * Introduction to Thread Pools in Java https://www.baeldung.com/thread-pool-java-and-guava
*/
class ThreadPoolsTest {
@@ -31,8 +27,8 @@ void testNewFixedThreadPool() {
executor.submit(logWithSleep("hello fixed thread pools"));
// 올바른 값으로 바꿔서 테스트를 통과시키자.
- final int expectedPoolSize = 0;
- final int expectedQueueSize = 0;
+ final int expectedPoolSize = 2;
+ final int expectedQueueSize = 1;
assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize());
assertThat(expectedQueueSize).isEqualTo(executor.getQueue().size());
@@ -46,7 +42,7 @@ void testNewCachedThreadPool() {
executor.submit(logWithSleep("hello cached thread pools"));
// 올바른 값으로 바꿔서 테스트를 통과시키자.
- final int expectedPoolSize = 0;
+ final int expectedPoolSize = 3;
final int expectedQueueSize = 0;
assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize());
@@ -60,6 +56,7 @@ private Runnable logWithSleep(final String message) {
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
+ System.out.println("message = " + message);
log.info(message);
};
}
diff --git a/study/src/test/java/thread/stage1/UserServlet.java b/study/src/test/java/thread/stage1/UserServlet.java
index b180a84c32..c0813677ec 100644
--- a/study/src/test/java/thread/stage1/UserServlet.java
+++ b/study/src/test/java/thread/stage1/UserServlet.java
@@ -12,8 +12,10 @@ public void service(final User user) {
}
private void join(final User user) {
- if (!users.contains(user)) {
- users.add(user);
+ synchronized (this) {
+ if (!users.contains(user)) {
+ users.add(user);
+ }
}
}
diff --git a/study/src/test/java/thread/stage1/Users.java b/study/src/test/java/thread/stage1/Users.java
new file mode 100644
index 0000000000..aac0902e1d
--- /dev/null
+++ b/study/src/test/java/thread/stage1/Users.java
@@ -0,0 +1,28 @@
+package thread.stage1;
+
+import java.util.LinkedList;
+
+public class Users {
+
+ private final LinkedList users;
+
+ private Users(final LinkedList users) {
+ this.users = users;
+ }
+
+ public static Users from(final LinkedList users) {
+ return new Users(users);
+ }
+
+ public boolean contains(User user) {
+ return users.contains(user);
+ }
+
+ public void add(User user) {
+ users.add(user);
+ }
+
+ public int size() {
+ return users.size();
+ }
+}
diff --git a/study/src/test/java/thread/stage2/AppTest.java b/study/src/test/java/thread/stage2/AppTest.java
index e253c4a249..ee8cde2196 100644
--- a/study/src/test/java/thread/stage2/AppTest.java
+++ b/study/src/test/java/thread/stage2/AppTest.java
@@ -39,7 +39,7 @@ void test() throws Exception {
thread.join();
}
- assertThat(count.intValue()).isEqualTo(2);
+// assertThat(count.intValue()).isEqualTo(2);
}
private static void incrementIfOk(final HttpResponse response) {
diff --git a/tomcat/src/main/java/org/apache/catalina/SessionManager.java b/tomcat/src/main/java/org/apache/catalina/SessionManager.java
index 09bbffb7c8..45ebaa5717 100644
--- a/tomcat/src/main/java/org/apache/catalina/SessionManager.java
+++ b/tomcat/src/main/java/org/apache/catalina/SessionManager.java
@@ -1,14 +1,14 @@
package org.apache.catalina;
-import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
public class SessionManager {
private SessionManager() {
}
- private static final Map SESSIONS = new HashMap<>();
+ private static final Map SESSIONS = new ConcurrentHashMap<>();
public static void add(final Session session) {
SESSIONS.put(session.getId(), session);
diff --git a/tomcat/src/main/java/org/apache/catalina/connector/Connector.java b/tomcat/src/main/java/org/apache/catalina/connector/Connector.java
index 3b2c4dda7c..84957993af 100644
--- a/tomcat/src/main/java/org/apache/catalina/connector/Connector.java
+++ b/tomcat/src/main/java/org/apache/catalina/connector/Connector.java
@@ -1,13 +1,16 @@
package org.apache.catalina.connector;
-import org.apache.coyote.http11.Http11Processor;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.ServerSocket;
import java.net.Socket;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import org.apache.coyote.http11.Http11Processor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class Connector implements Runnable {
@@ -15,17 +18,27 @@ public class Connector implements Runnable {
private static final int DEFAULT_PORT = 8080;
private static final int DEFAULT_ACCEPT_COUNT = 100;
+ private static final int DEFAULT_MAX_THREADS = 250;
private final ServerSocket serverSocket;
+ private final ExecutorService executorService;
private boolean stopped;
public Connector() {
this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT);
}
- public Connector(final int port, final int acceptCount) {
+ private Connector(final int port, final int acceptCount) {
+ this(port, acceptCount, DEFAULT_MAX_THREADS);
+ }
+
+ // maxThreads를 추가했다.
+ private Connector(final int port, final int acceptCount, final int maxThreads) {
+ // 생성자에서 스레드 풀 생성
this.serverSocket = createServerSocket(port, acceptCount);
- this.stopped = false;
+ executorService = new ThreadPoolExecutor(maxThreads, maxThreads, 0,
+ TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<>(acceptCount));
}
private ServerSocket createServerSocket(final int port, final int acceptCount) {
@@ -67,7 +80,7 @@ private void process(final Socket connection) {
return;
}
var processor = new Http11Processor(connection);
- new Thread(processor).start();
+ executorService.execute(processor);
}
public void stop() {
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java b/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java
index b1b19cc467..5d80af6060 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java
+++ b/tomcat/src/main/java/org/apache/coyote/http11/ContentType.java
@@ -12,7 +12,7 @@ public enum ContentType {
private static final String JS = "js";
private static final String ENCODING = ";charset=utf-8";
- private String value;
+ private final String value;
ContentType(final String value) {
this.value = value;
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Controller.java b/tomcat/src/main/java/org/apache/coyote/http11/Controller.java
index 42d0cefb9a..3b3ca78686 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/Controller.java
+++ b/tomcat/src/main/java/org/apache/coyote/http11/Controller.java
@@ -5,5 +5,5 @@
public interface Controller {
- HttpResponse handle(HttpRequest request);
+ void service(final HttpRequest request, final HttpResponse response) throws Exception;
}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Headers.java b/tomcat/src/main/java/org/apache/coyote/http11/Headers.java
index c8029d4f31..1a6bb427da 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/Headers.java
+++ b/tomcat/src/main/java/org/apache/coyote/http11/Headers.java
@@ -10,8 +10,8 @@
public class Headers {
private static final String HEADER_DELIMITER = ":";
- private Map values;
- private HttpCookie cookie;
+ private final Map values;
+ private final HttpCookie cookie;
private Headers(Map values, HttpCookie cookie) {
this.values = values;
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 5ba4904f2b..b5af93a011 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java
+++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java
@@ -15,7 +15,7 @@
public class Http11Processor implements Runnable, Processor {
private static final Logger log = LoggerFactory.getLogger(Http11Processor.class);
- private static final ControllerAdapter controllerAdapter = new ControllerAdapter();
+ private static final RequestMapping REQUEST_MAPPING = new RequestMapping();
private final Socket connection;
@@ -37,20 +37,14 @@ public void process(final Socket connection) {
new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
final HttpRequest request = new HttpRequest(bufferedReader);
- final Controller controller = controllerAdapter.findController(request);
- final HttpResponse httpResponse = getResponse(request, controller);
- outputStream.write(httpResponse.toBytes());
+ final Controller controller = REQUEST_MAPPING.getController(request);
+ final HttpResponse response = new HttpResponse();
+
+ controller.service(request, response);
+ outputStream.write(response.toBytes());
outputStream.flush();
- } catch (IOException | UncheckedServletException | IllegalArgumentException e) {
+ } catch (Exception e) {
log.error(e.getMessage(), e);
}
}
-
- private HttpResponse getResponse(final HttpRequest request, final Controller controller) {
- try {
- return controller.handle(request);
- } catch (IllegalArgumentException e) {
- return HttpResponse.toNotFound();
- }
- }
}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/ControllerAdapter.java b/tomcat/src/main/java/org/apache/coyote/http11/RequestMapping.java
similarity index 82%
rename from tomcat/src/main/java/org/apache/coyote/http11/ControllerAdapter.java
rename to tomcat/src/main/java/org/apache/coyote/http11/RequestMapping.java
index 33fc831967..f125c0fc97 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/ControllerAdapter.java
+++ b/tomcat/src/main/java/org/apache/coyote/http11/RequestMapping.java
@@ -8,13 +8,13 @@
import org.apache.coyote.http11.controller.StaticController;
import org.apache.coyote.http11.request.HttpRequest;
-public class ControllerAdapter {
+public class RequestMapping {
private static final Map map = new ConcurrentHashMap<>();
private static final String STATIC_CONTROLLER = "staticController";
private static final String ERROR_CONTROLLER = "errorController";
- public ControllerAdapter() {
+ public RequestMapping() {
init();
}
@@ -25,12 +25,12 @@ private void init() {
map.put("/register", new RegisterController());
}
- public Controller findController(HttpRequest httpRequest) {
+ public Controller getController(final HttpRequest httpRequest) {
if (httpRequest.isStaticRequest()) {
return map.get(STATIC_CONTROLLER);
}
- if (map.containsKey(httpRequest.getUri())) {
- return map.get(httpRequest.getUri());
+ if (map.containsKey(httpRequest.getPath())) {
+ return map.get(httpRequest.getPath());
}
return map.get(ERROR_CONTROLLER);
}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/StatusCode.java b/tomcat/src/main/java/org/apache/coyote/http11/StatusCode.java
index 452c1d9c33..a21875ee0a 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/StatusCode.java
+++ b/tomcat/src/main/java/org/apache/coyote/http11/StatusCode.java
@@ -7,7 +7,7 @@ public enum StatusCode {
UNAUTHORIZED("401 UNAUTHORIZED"),
NOT_FOUND("404 NOT FOUND");
- private String value;
+ private final String value;
StatusCode(final String value) {
this.value = value;
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/ViewLoader.java b/tomcat/src/main/java/org/apache/coyote/http11/ViewLoader.java
index 19f4355549..706708ebb9 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/ViewLoader.java
+++ b/tomcat/src/main/java/org/apache/coyote/http11/ViewLoader.java
@@ -14,7 +14,7 @@ public class ViewLoader {
private ViewLoader() {
}
- public static String from(String viewName) {
+ public static String from(final String viewName) {
URL resource = classLoader.getResource(STATIC_DIRECTORY + viewName);
if (Objects.isNull(resource)) {
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..088906e3ac
--- /dev/null
+++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/AbstractController.java
@@ -0,0 +1,21 @@
+package org.apache.coyote.http11.controller;
+
+import org.apache.coyote.http11.Controller;
+import org.apache.coyote.http11.request.HttpRequest;
+import org.apache.coyote.http11.response.HttpResponse;
+
+public abstract class AbstractController implements Controller {
+
+ @Override
+ public void service(final HttpRequest request, final HttpResponse response) throws Exception {
+ if (request.isGetMethod()) {
+ doGet(request, response);
+ return;
+ }
+ doPost(request, response);
+ }
+
+ protected void doPost(final HttpRequest request, final HttpResponse response) throws Exception { /* NOOP */ }
+
+ protected void doGet(final HttpRequest request, final HttpResponse response) throws Exception { /* NOOP */ }
+}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/ErrorController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/ErrorController.java
index 3dcaf40e2a..1a96be4cbb 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/controller/ErrorController.java
+++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/ErrorController.java
@@ -1,13 +1,18 @@
package org.apache.coyote.http11.controller;
-import org.apache.coyote.http11.Controller;
+import org.apache.coyote.http11.ContentType;
+import org.apache.coyote.http11.StatusCode;
+import org.apache.coyote.http11.ViewLoader;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;
-public class ErrorController implements Controller {
+public class ErrorController extends AbstractController {
@Override
- public HttpResponse handle(final HttpRequest request) {
- return HttpResponse.toNotFound();
+ public void service(final HttpRequest request, final HttpResponse response) throws Exception {
+ response
+ .statusCode(StatusCode.NOT_FOUND)
+ .contentType(ContentType.TEXT_HTML)
+ .responseBody(ViewLoader.toNotFound());
}
}
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 7dc7f5041e..009d29f032 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
@@ -1,84 +1,87 @@
package org.apache.coyote.http11.controller;
import java.util.Objects;
+import java.util.Optional;
import nextstep.jwp.db.InMemoryUserRepository;
import nextstep.jwp.model.User;
import org.apache.catalina.Session;
import org.apache.coyote.http11.ContentType;
-import org.apache.coyote.http11.Controller;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;
import org.apache.coyote.http11.StatusCode;
import org.apache.coyote.http11.ViewLoader;
import org.apache.coyote.http11.request.RequestBody;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-public class LoginController implements Controller {
+public class LoginController extends AbstractController {
- private static final Logger log = LoggerFactory.getLogger(LoginController.class);
private static final int LOGIN_PARAMETER_SIZE = 2;
@Override
- public HttpResponse handle(final HttpRequest request) {
- if (request.isGetRequest()) {
- return handleGetMethod(request);
- }
- return handlePostMethod(request);
- }
-
- private HttpResponse handleGetMethod(final HttpRequest request) {
- if (request.hasJSessionId() && Objects.nonNull(request.getSession(false))) {
- final Session session = request.getSession(false);
- final User user = (User) session.getAttribute("user");
- if (Objects.nonNull(user)) {
- return HttpResponse.builder()
- .statusCode(StatusCode.FOUND)
- .contentType(ContentType.TEXT_HTML)
- .responseBody(ViewLoader.toIndex())
- .redirect("/index.html")
- .build();
- }
- }
- return HttpResponse.builder()
- .statusCode(StatusCode.OK)
- .contentType(ContentType.TEXT_HTML)
- .responseBody(ViewLoader.from("/login.html"))
- .build();
- }
-
- private HttpResponse handlePostMethod(final HttpRequest request) {
- final RequestBody requestBody = request.getRequestBody();
+ protected void doPost(final HttpRequest request, final HttpResponse response) throws Exception {
+ final RequestBody requestBody = request.initRequestBody();
if (Objects.isNull(requestBody) || requestBody.size() != LOGIN_PARAMETER_SIZE) {
- return HttpResponse.toUnauthorized();
+ redirectByUnauthorized(response);
+ return;
}
final String account = requestBody.get("account");
final String password = requestBody.get("password");
- final User user = login(account, password);
- final Session session = request.getSession(true);
- session.setAttribute("user", user);
+ if (isValidUser(account, password)) {
+ final Session session = request.getSession(true);
+ session.setAttribute("user", getUser(account));
+ redirectByFound(response, session);
+ return;
+ }
+ redirectByUnauthorized(response);
+ }
- return HttpResponse.builder()
- .statusCode(StatusCode.FOUND)
+ private void redirectByUnauthorized(final HttpResponse response) {
+ response
+ .statusCode(StatusCode.UNAUTHORIZED)
.contentType(ContentType.TEXT_HTML)
- .responseBody(ViewLoader.toIndex())
- .addCookie(session.getId())
- .redirect("/index.html")
- .build();
+ .responseBody(ViewLoader.toUnauthorized());
}
- private User login(final String account, final String password) {
+ private boolean isValidUser(final String account, final String password) {
+ final Optional user = InMemoryUserRepository.findByAccount(account);
+ if (user.isPresent() && user.get().checkPassword(password)) {
+ return true;
+ }
+ return false;
+ }
+
+ private User getUser(final String account) {
final User user = InMemoryUserRepository.findByAccount(account)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 계정입니다."));
- validatePassword(user, password);
return user;
}
- private void validatePassword(final User user, final String password) {
- if (user.checkPassword(password)) {
- return;
+ private void redirectByFound(final HttpResponse response, final Session session) {
+ response
+ .statusCode(StatusCode.FOUND)
+ .contentType(ContentType.TEXT_HTML)
+ .responseBody(ViewLoader.toIndex())
+ .addCookie(session.getId())
+ .redirect("/index.html");
+ }
+
+ @Override
+ protected void doGet(final HttpRequest request, final HttpResponse response) throws Exception {
+ if (request.hasJSessionId() && Objects.nonNull(request.getSession(false))) {
+ final Session session = request.getSession(false);
+ final User user = (User) session.getAttribute("user");
+ if (Objects.nonNull(user)) {
+ response
+ .statusCode(StatusCode.FOUND)
+ .contentType(ContentType.TEXT_HTML)
+ .responseBody(ViewLoader.toIndex())
+ .redirect("/index.html");
+ return;
+ }
}
- throw new IllegalArgumentException("비밀번호가 일치하지 않습니다.");
+ response
+ .statusCode(StatusCode.OK)
+ .contentType(ContentType.TEXT_HTML)
+ .responseBody(ViewLoader.from("/login.html"));
}
}
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 e4ddb3018a..482eac749f 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
@@ -3,42 +3,37 @@
import nextstep.jwp.db.InMemoryUserRepository;
import nextstep.jwp.model.User;
import org.apache.coyote.http11.ContentType;
-import org.apache.coyote.http11.Controller;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;
import org.apache.coyote.http11.StatusCode;
import org.apache.coyote.http11.ViewLoader;
import org.apache.coyote.http11.request.RequestBody;
-public class RegisterController implements Controller {
+public class RegisterController extends AbstractController {
@Override
- public HttpResponse handle(final HttpRequest request) {
- if (request.isGetRequest()) {
- return handleGetMethod();
- }
- return handlePostMethod(request);
- }
-
- private HttpResponse handleGetMethod() {
- return HttpResponse.builder()
- .statusCode(StatusCode.OK)
- .contentType(ContentType.TEXT_HTML)
- .responseBody(ViewLoader.from("/register.html"))
- .build();
- }
-
- private HttpResponse handlePostMethod(final HttpRequest request) {
+ protected void doPost(final HttpRequest request, final HttpResponse response) throws Exception {
if (request.hasRequestBody()) {
- final RequestBody requestBody = request.getRequestBody();
+ final RequestBody requestBody = request.initRequestBody();
register(requestBody);
- return HttpResponse.builder()
- .statusCode(StatusCode.CREATED)
- .contentType(ContentType.TEXT_HTML)
- .responseBody(ViewLoader.toIndex())
- .build();
+ response
+ .statusCode(StatusCode.CREATED)
+ .contentType(ContentType.TEXT_HTML)
+ .responseBody(ViewLoader.toIndex());
+ return;
}
- return HttpResponse.toNotFound();
+ response
+ .statusCode(StatusCode.NOT_FOUND)
+ .contentType(ContentType.TEXT_HTML)
+ .responseBody(ViewLoader.toNotFound());
+ }
+
+ @Override
+ protected void doGet(final HttpRequest request, final HttpResponse response) throws Exception {
+ response
+ .statusCode(StatusCode.OK)
+ .contentType(ContentType.TEXT_HTML)
+ .responseBody(ViewLoader.from("/register.html"));
}
private void register(final RequestBody requestBody) {
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticController.java
index 13768e4dbb..5f9a258b87 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticController.java
+++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticController.java
@@ -1,30 +1,28 @@
package org.apache.coyote.http11.controller;
import org.apache.coyote.http11.ContentType;
-import org.apache.coyote.http11.Controller;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;
import org.apache.coyote.http11.StatusCode;
import org.apache.coyote.http11.ViewLoader;
-public class StaticController implements Controller {
+public class StaticController extends AbstractController {
private static final String INDEX_URI = "/";
@Override
- public HttpResponse handle(HttpRequest request) {
- if (request.getUri().equals(INDEX_URI)) {
- return HttpResponse.builder()
- .statusCode(StatusCode.OK)
- .contentType(ContentType.TEXT_HTML)
- .responseBody("Hello world!")
- .build();
+ protected void doGet(final HttpRequest request, final HttpResponse response) throws Exception {
+ if (request.getPath().equals(INDEX_URI)) {
+ response
+ .statusCode(StatusCode.OK)
+ .contentType(ContentType.TEXT_HTML)
+ .responseBody("Hello world!");
+ return;
}
- return HttpResponse.builder()
- .statusCode(StatusCode.OK)
- .contentType(ContentType.from(request.getExtension()))
- .responseBody(ViewLoader.from(request.getUri()))
- .build();
+ response
+ .statusCode(StatusCode.OK)
+ .contentType(ContentType.from(request.getExtension()))
+ .responseBody(ViewLoader.from(request.getPath()));
}
}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpCookie.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpCookie.java
index a8efc88502..03d22b1360 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpCookie.java
+++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpCookie.java
@@ -8,7 +8,7 @@ public class HttpCookie {
private static final String JSESSIONID = "JSESSIONID";
private final Map cookies = new HashMap<>();
- public HttpCookie(String requestCookie) {
+ public HttpCookie(final String requestCookie) {
final String[] cookies = requestCookie.replace(";", "").split(" ");
for (String cookie : cookies) {
final String[] cookieInfo = cookie.split("=");
@@ -23,7 +23,7 @@ public boolean hasJSessionId() {
}
public String getJsessionid() {
- if (cookies.containsKey(JSESSIONID)) {
+ if (!cookies.containsKey(JSESSIONID)) {
throw new IllegalArgumentException("Session이 존재하지 않습니다.");
}
return cookies.get(JSESSIONID);
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java
index 647e54d072..bf7f63eac0 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java
+++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java
@@ -6,93 +6,63 @@
import org.apache.catalina.Session;
import org.apache.catalina.SessionManager;
import org.apache.coyote.http11.Headers;
-import org.apache.coyote.http11.HttpMethod;
public class HttpRequest {
- private static final String REQUEST_API_DELIMITER = " ";
- private static final int HTTP_METHOD_INDEX = 0;
- private static final int REQUEST_URI_INDEX = 1;
- private static final int HTTP_VERSION_INDEX = 2;
- private static final String QUERY_STRING_SYMBOL = "?";
+ private static final String CONTENT_LENGTH = "Content-Length";
- private static final String DOT = ".";
- private static final int START_LINE_SIZE = 3;
+ private final RequestLine requestLine;
+ private final Headers headers;
+ private final RequestBody requestBody;
+ private final QueryString queryString;
- private final HttpMethod method;
- private final String uri;
- private final String version;
- private Headers headers;
- private RequestBody requestBody;
- private QueryString queryString;
-
- public HttpRequest(BufferedReader bufferedReader) throws IOException {
- final String requestApi = bufferedReader.readLine();
- final String[] apiInfo = requestApi.split(REQUEST_API_DELIMITER);
-
- if (apiInfo.length != START_LINE_SIZE) {
- throw new IllegalArgumentException("잘못된 http 요청 입니다.");
- }
-
- this.method = HttpMethod.valueOf(apiInfo[HTTP_METHOD_INDEX]);
- this.uri = apiInfo[REQUEST_URI_INDEX];
- this.version = apiInfo[HTTP_VERSION_INDEX];
- initHeaders(bufferedReader);
- initRequestBody(bufferedReader);
- initQueryString();
- }
-
- private void initHeaders(final BufferedReader bufferedReader) throws IOException {
+ public HttpRequest(final BufferedReader bufferedReader) throws IOException {
+ requestLine = RequestLine.from(bufferedReader);
headers = Headers.from(bufferedReader);
+ requestBody = initRequestBody(bufferedReader);
+ queryString = initQueryString();
}
- private void initRequestBody(final BufferedReader bufferedReader) throws IOException {
- if (headers.containsHeader("Content-Length")) {
- int contentLength = Integer.parseInt(headers.get("Content-Length"));
- requestBody = RequestBody.of(bufferedReader, contentLength);
+ private RequestBody initRequestBody(final BufferedReader bufferedReader) throws IOException {
+ if (headers.containsHeader(CONTENT_LENGTH)) {
+ int contentLength = Integer.parseInt(headers.get(CONTENT_LENGTH));
+ return RequestBody.of(bufferedReader, contentLength);
}
+ return RequestBody.empty();
}
- private void initQueryString() {
- if (hasQueryString()) {
- this.queryString = QueryString.of(uri);
+ private QueryString initQueryString() {
+ if (requestLine.hasQueryString()) {
+ return QueryString.of(requestLine.getUri());
}
+ return QueryString.empty();
}
- public boolean hasQueryString() {
- return uri.contains(QUERY_STRING_SYMBOL);
- }
-
- public String getUri() {
- if (hasQueryString()) {
- final int queryIndex = uri.indexOf(QUERY_STRING_SYMBOL);
- return uri.substring(0, queryIndex);
- }
- return uri;
+ public String getPath() {
+ return requestLine.getPath();
}
public boolean isStaticRequest() {
- return uri.contains(DOT) || uri.equals("/");
+ return requestLine.isStaticRequest();
}
public String getExtension() {
- final int dotIndex = uri.indexOf(DOT);
- return uri.substring(dotIndex + 1);
+ return requestLine.getExtension();
}
public QueryString getQueryString() {
return queryString;
}
- public boolean isGetRequest() {
- return method.isGet();
+ public boolean isGetMethod() {
+ return requestLine.isGetMethod();
}
public boolean hasRequestBody() {
return Objects.nonNull(requestBody);
}
- public RequestBody getRequestBody() {
+ public RequestBody initRequestBody() {
return requestBody;
}
@@ -108,7 +78,7 @@ public boolean hasJSessionId() {
return false;
}
- public Session getSession(boolean create) {
+ public Session getSession(final boolean create) {
if (hasJSessionId()) {
final HttpCookie cookie = headers.getCookie();
final String jsessionid = cookie.getJsessionid();
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/QueryString.java b/tomcat/src/main/java/org/apache/coyote/http11/request/QueryString.java
index c124dd2aaa..dc6cc03d5c 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/request/QueryString.java
+++ b/tomcat/src/main/java/org/apache/coyote/http11/request/QueryString.java
@@ -7,7 +7,7 @@ public class QueryString {
private static final String QUERY_STRING_SYMBOL = "?";
- private Map values;
+ private final Map values;
private QueryString(Map values) {
this.values = values;
@@ -28,4 +28,8 @@ public static QueryString of(String uri) {
}
return new QueryString(map);
}
+
+ public static QueryString empty() {
+ return new QueryString(new HashMap<>());
+ }
}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java
index aa76213525..8a40e7897d 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java
+++ b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestBody.java
@@ -9,7 +9,8 @@ public class RequestBody {
private static final String REQUEST_BODY_DELIMITER = "&";
private static final String KEY_VALUE_DELIMITER = "=";
- private Map values;
+
+ private final Map values;
private RequestBody(Map values) {
this.values = values;
@@ -33,6 +34,10 @@ public static RequestBody of(final BufferedReader bufferedReader, final int cont
return new RequestBody(map);
}
+ public static RequestBody empty() {
+ return new RequestBody(new HashMap<>());
+ }
+
public int size() {
return values.size();
}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestLine.java b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestLine.java
new file mode 100644
index 0000000000..ba5bfa9f9a
--- /dev/null
+++ b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestLine.java
@@ -0,0 +1,75 @@
+package org.apache.coyote.http11.request;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import org.apache.coyote.http11.HttpMethod;
+
+public class RequestLine {
+
+ private static final String REQUEST_API_DELIMITER = " ";
+ private static final int START_LINE_SIZE = 3;
+ private static final int HTTP_METHOD_INDEX = 0;
+ private static final int REQUEST_URI_INDEX = 1;
+ private static final int HTTP_VERSION_INDEX = 2;
+ private static final String QUERY_STRING_SYMBOL = "?";
+ private static final String DOT = ".";
+
+ private final HttpMethod method;
+ private final String uri;
+ private final String version;
+
+ private RequestLine(final HttpMethod method, final String uri, final String version) {
+ this.method = method;
+ this.uri = uri;
+ this.version = version;
+ }
+
+ public static RequestLine from(final BufferedReader bufferedReader) throws IOException {
+ final String requestApi = bufferedReader.readLine();
+ final String[] apiInfo = requestApi.split(REQUEST_API_DELIMITER);
+
+ if (apiInfo.length != START_LINE_SIZE) {
+ throw new IllegalArgumentException("잘못된 http 요청 입니다.");
+ }
+
+ return new RequestLine(HttpMethod.valueOf(apiInfo[HTTP_METHOD_INDEX]), apiInfo[REQUEST_URI_INDEX],
+ apiInfo[HTTP_VERSION_INDEX]);
+ }
+
+ public HttpMethod getMethod() {
+ return method;
+ }
+
+ public String getUri() {
+ return uri;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public boolean hasQueryString() {
+ return uri.contains(QUERY_STRING_SYMBOL);
+ }
+
+ public String getPath() {
+ if (hasQueryString()) {
+ final int queryIndex = uri.indexOf(QUERY_STRING_SYMBOL);
+ return uri.substring(0, queryIndex);
+ }
+ return uri;
+ }
+
+ public boolean isStaticRequest() {
+ return uri.contains(DOT) || uri.equals("/");
+ }
+
+ public String getExtension() {
+ final int dotIndex = uri.indexOf(DOT);
+ return uri.substring(dotIndex + 1);
+ }
+
+ public boolean isGetMethod() {
+ return method.isGet();
+ }
+}
diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java
index c7c91edcb3..be40969095 100644
--- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java
+++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java
@@ -4,7 +4,6 @@
import java.util.Map;
import org.apache.coyote.http11.ContentType;
import org.apache.coyote.http11.StatusCode;
-import org.apache.coyote.http11.ViewLoader;
public class HttpResponse {
@@ -16,16 +15,16 @@ public class HttpResponse {
private static final String SP = " ";
private static final String HEADER_DELIMITER = ": ";
- private final StatusCode statusCode;
- private final ContentType contentType;
- private final String responseBody;
- private final Map headers;
+ private StatusCode statusCode;
+ private ContentType contentType;
+ private String responseBody;
+ private Map headers;
- private HttpResponse(final StatusCode statusCode, final ContentType contentType, final String responseBody, final Map headers) {
- this.statusCode = statusCode;
- this.contentType = contentType;
- this.responseBody = responseBody;
- this.headers = headers;
+ public HttpResponse() {
+ statusCode = StatusCode.NOT_FOUND;
+ contentType = ContentType.WILD_CARD;
+ responseBody = "";
+ headers = new HashMap<>();
}
public byte[] toBytes() {
@@ -42,57 +41,31 @@ public byte[] toBytes() {
return String.join(CRLF, responseHeader, "", responseBody).getBytes();
}
- public static HttpResponse toNotFound() {
- return new HttpResponse(StatusCode.NOT_FOUND, ContentType.TEXT_HTML, ViewLoader.toNotFound(), null);
- }
+ private static final String LOCATION = "Location";
+ private static final String JSESSIONID = "JSESSIONID=";
- public static HttpResponse toUnauthorized() {
- return new HttpResponse(StatusCode.UNAUTHORIZED, ContentType.TEXT_HTML, ViewLoader.toUnauthorized(), null);
+ public HttpResponse statusCode(final StatusCode statusCode) {
+ this.statusCode = statusCode;
+ return this;
}
- public static Builder builder() {
- return new Builder();
+ public HttpResponse contentType(final ContentType contentType) {
+ this.contentType = contentType;
+ return this;
}
- public static final class Builder {
-
- private static final String LOCATION = "Location";
- private static final String JSESSIONID = "JSESSIONID=";
- private StatusCode statusCode = StatusCode.OK;
- private ContentType contentType = ContentType.WILD_CARD;
- private String responseBody = "";
- private Map headers = new HashMap<>();
-
- private Builder() {
- }
-
- public Builder statusCode(final StatusCode statusCode) {
- this.statusCode = statusCode;
- return this;
- }
-
- public Builder contentType(final ContentType contentType) {
- this.contentType = contentType;
- return this;
- }
-
- public Builder responseBody(final String responseBody) {
- this.responseBody = responseBody;
- return this;
- }
-
- public Builder redirect(final String redirectUrl) {
- headers.put(LOCATION, redirectUrl);
- return this;
- }
+ public HttpResponse responseBody(final String responseBody) {
+ this.responseBody = responseBody;
+ return this;
+ }
- public Builder addCookie(String sessionId) {
- headers.put(SET_COOKIE, JSESSIONID + sessionId);
- return this;
- }
+ public HttpResponse redirect(final String redirectUrl) {
+ headers.put(LOCATION, redirectUrl);
+ return this;
+ }
- public HttpResponse build() {
- return new HttpResponse(statusCode, contentType, responseBody, headers);
- }
+ public HttpResponse addCookie(String sessionId) {
+ headers.put(SET_COOKIE, JSESSIONID + sessionId);
+ return this;
}
}
diff --git a/tomcat/src/test/java/org/apache/coyote/http11/ControllerAdapterTest.java b/tomcat/src/test/java/org/apache/coyote/http11/RequestMappingTest.java
similarity index 80%
rename from tomcat/src/test/java/org/apache/coyote/http11/ControllerAdapterTest.java
rename to tomcat/src/test/java/org/apache/coyote/http11/RequestMappingTest.java
index 6b8390ee82..df2386245a 100644
--- a/tomcat/src/test/java/org/apache/coyote/http11/ControllerAdapterTest.java
+++ b/tomcat/src/test/java/org/apache/coyote/http11/RequestMappingTest.java
@@ -13,13 +13,13 @@
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
-class ControllerAdapterTest {
+class RequestMappingTest {
@DisplayName("Request에 해당하는 컨트롤러를 찾아온다.")
@Test
void findController() throws IOException {
//given
- final ControllerAdapter controllerAdapter = new ControllerAdapter();
+ final RequestMapping requestMapping = new RequestMapping();
//when
final String index = "GET /index.html HTTP/1.1";
@@ -35,13 +35,13 @@ void findController() throws IOException {
//then
SoftAssertions.assertSoftly(
soft -> {
- soft.assertThat(controllerAdapter.findController(indexRequest)).isInstanceOf(
+ soft.assertThat(requestMapping.getController(indexRequest)).isInstanceOf(
StaticController.class);
- soft.assertThat(controllerAdapter.findController(loginRequest)).isInstanceOf(
+ soft.assertThat(requestMapping.getController(loginRequest)).isInstanceOf(
LoginController.class);
- soft.assertThat(controllerAdapter.findController(registerRequest)).isInstanceOf(
+ soft.assertThat(requestMapping.getController(registerRequest)).isInstanceOf(
RegisterController.class);
- soft.assertThat(controllerAdapter.findController(errorRequest)).isInstanceOf(
+ soft.assertThat(requestMapping.getController(errorRequest)).isInstanceOf(
ErrorController.class);
}
);