diff --git a/study/src/main/resources/application.yml b/study/src/main/resources/application.yml index 083381f6a8..557751f206 100644 --- a/study/src/main/resources/application.yml +++ b/study/src/main/resources/application.yml @@ -2,11 +2,11 @@ handlebars: suffix: .html server: - # tomcat: - # accept-count: 1 - # max-connections: 1 - # threads: - # max: 2 + tomcat: + accept-count: 10 + max-connections: 2 + threads: + max: 2 compression: enabled: true min-response-size: 10 # 기본 설정은 2KB diff --git a/study/src/test/java/thread/stage2/AppTest.java b/study/src/test/java/thread/stage2/AppTest.java index e253c4a249..9de5825678 100644 --- a/study/src/test/java/thread/stage2/AppTest.java +++ b/study/src/test/java/thread/stage2/AppTest.java @@ -24,7 +24,7 @@ class AppTest { @Test void test() throws Exception { final var NUMBER_OF_THREAD = 10; - var threads = new Thread[NUMBER_OF_THREAD]; + Thread[] threads = new Thread[NUMBER_OF_THREAD]; for (int i = 0; i < NUMBER_OF_THREAD; i++) { threads[i] = new Thread(() -> incrementIfOk(TestHttpUtils.send("/test"))); 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..f7ef7fc45a 100644 --- a/tomcat/src/main/java/org/apache/catalina/connector/Connector.java +++ b/tomcat/src/main/java/org/apache/catalina/connector/Connector.java @@ -8,6 +8,10 @@ import java.io.UncheckedIOException; import java.net.ServerSocket; import java.net.Socket; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; public class Connector implements Runnable { @@ -15,12 +19,14 @@ public class Connector implements Runnable { private static final int DEFAULT_PORT = 8080; private static final int DEFAULT_ACCEPT_COUNT = 100; + private static final int DEFAULT_MAX_THREAD = 250; private final ServerSocket serverSocket; private boolean stopped; + private ExecutorService executorService; public Connector() { - this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT); + this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, DEFAULT_MAX_THREAD); } public Connector(final int port, final int acceptCount) { @@ -28,6 +34,18 @@ public Connector(final int port, final int acceptCount) { this.stopped = false; } + public Connector(final int port, final int acceptCount, final int maxThreads) { + this.serverSocket = createServerSocket(port, acceptCount); + this.executorService = new ThreadPoolExecutor( + maxThreads, // 쓰레드풀의 최대 크기 + maxThreads, // 쓰레드풀의 핵심 크기 + 0L, // 쓰레드의 비활성화 시간 + TimeUnit.MILLISECONDS, // 비활성화 시간의 단위 + new ArrayBlockingQueue<>(acceptCount) // 작업 큐의 크기 + ); + this.stopped = false; + } + private ServerSocket createServerSocket(final int port, final int acceptCount) { try { final int checkedPort = checkPort(port); @@ -67,13 +85,14 @@ private void process(final Socket connection) { return; } var processor = new Http11Processor(connection); - new Thread(processor).start(); + executorService.execute(processor); } public void stop() { stopped = true; try { serverSocket.close(); + executorService.shutdown(); } catch (IOException e) { log.error(e.getMessage(), e); } 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 ec4915df17..2d59d17110 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -6,7 +6,6 @@ import org.apache.coyote.http11.controller.RequestMapping; import org.apache.coyote.http11.request.HttpRequest; import org.apache.coyote.http11.response.HttpResponse; -import org.apache.coyote.http11.response.ResponseEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,10 +36,11 @@ public void process(final Socket connection) { final var outputStream = connection.getOutputStream()) { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); HttpRequest httpRequest = HttpRequest.from(bufferedReader); + HttpResponse httpResponse = new HttpResponse(); Controller controller = controllers.getController(httpRequest); - ResponseEntity responseEntity = controller.service(httpRequest); - String response = HttpResponse.from(responseEntity).getResponse(); + controller.service(httpRequest, httpResponse); + String response = httpResponse.getResponse(); outputStream.write(response.getBytes()); outputStream.flush(); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/Controller.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/Controller.java index 7f989c1674..95bd2d2db8 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/controller/Controller.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/Controller.java @@ -1,11 +1,11 @@ package org.apache.coyote.http11.controller; import org.apache.coyote.http11.request.HttpRequest; -import org.apache.coyote.http11.response.ResponseEntity; +import org.apache.coyote.http11.response.HttpResponse; import java.io.IOException; public interface Controller { - ResponseEntity service(HttpRequest request) throws IOException; + void service(HttpRequest request, HttpResponse response) throws IOException; } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/IndexPageController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/IndexPageController.java index 219c6a5887..1a53617806 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/controller/IndexPageController.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/IndexPageController.java @@ -1,16 +1,20 @@ package org.apache.coyote.http11.controller; import org.apache.coyote.http11.request.HttpRequest; -import org.apache.coyote.http11.response.ContentType; -import org.apache.coyote.http11.response.HttpResponseBody; -import org.apache.coyote.http11.response.HttpStatus; -import org.apache.coyote.http11.response.ResponseEntity; +import org.apache.coyote.http11.response.*; + +import java.io.IOException; public class IndexPageController implements Controller { @Override - public ResponseEntity service(HttpRequest request) { + public void service(HttpRequest request, HttpResponse response) throws IOException { final var responseBody = "Hello world!"; + ResponseEntity responseEntity = generateResponseEntity(responseBody); + response.modifyResponse(responseEntity); + } + + private static ResponseEntity generateResponseEntity(String responseBody) { return ResponseEntity .builder() .httpStatus(HttpStatus.OK) 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 0bd9fe581d..1e9db516ca 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 @@ -4,6 +4,7 @@ import nextstep.jwp.model.User; import org.apache.coyote.http11.request.*; import org.apache.coyote.http11.response.ContentType; +import org.apache.coyote.http11.response.HttpResponse; import org.apache.coyote.http11.response.HttpStatus; import org.apache.coyote.http11.response.ResponseEntity; import org.apache.coyote.http11.session.HttpCookie; @@ -13,6 +14,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.NoSuchElementException; import java.util.Optional; @@ -26,24 +28,29 @@ public class LoginController implements Controller { private final SessionManager sessionManager = new SessionManager(); @Override - public ResponseEntity service(HttpRequest request) { + public void service(HttpRequest request, HttpResponse response) throws IOException { + response.modifyResponse(generateResponseEntity(request)); + } + + private ResponseEntity generateResponseEntity(HttpRequest request) { HttpRequestStartLine httpRequestStartLine = request.getHttpRequestStartLine(); HttpRequestHeader httpRequestHeader = request.getHttpRequestHeader(); HttpRequestBody httpRequestBody = request.getHttpRequestBody(); HttpMethod httpMethod = httpRequestStartLine.getHttpMethod(); - String account = httpRequestBody.find("account"); - - if (httpMethod == HttpMethod.GET && account == null) { + if (httpMethod == HttpMethod.GET) { HttpCookie httpCookie = httpRequestHeader.getCookie(); - Session session = sessionManager.findSession(httpCookie.findJSessionId()); - if (session != null) { - return ResponseEntity.builder() - .httpStatus(HttpStatus.FOUND) - .contentType(ContentType.HTML) - .location(INDEX_PAGE_URI) - .build(); + Optional jSessionId = httpCookie.findJSessionId(); + if (jSessionId.isPresent()) { + Optional session = sessionManager.findSession(jSessionId.get()); + if (session.isPresent()) { + return ResponseEntity.builder() + .httpStatus(HttpStatus.FOUND) + .contentType(ContentType.HTML) + .location(INDEX_PAGE_URI) + .build(); + } } return ResponseEntity.builder() @@ -53,6 +60,7 @@ public ResponseEntity service(HttpRequest request) { .build(); } + String account = httpRequestBody.find("account"); String password = httpRequestBody.find("password"); User findAccount = findAccount(account); 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 2e86bbd378..3e3864eb5c 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 @@ -7,11 +7,14 @@ import org.apache.coyote.http11.request.HttpRequestBody; import org.apache.coyote.http11.request.HttpRequestStartLine; import org.apache.coyote.http11.response.ContentType; +import org.apache.coyote.http11.response.HttpResponse; import org.apache.coyote.http11.response.HttpStatus; import org.apache.coyote.http11.response.ResponseEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; + public class RegisterController implements Controller { private static final Logger log = LoggerFactory.getLogger(RegisterController.class); @@ -19,7 +22,11 @@ public class RegisterController implements Controller { private static final String INDEX_PAGE_URI = "/index.html"; @Override - public ResponseEntity service(HttpRequest request) { + public void service(HttpRequest request, HttpResponse response) throws IOException { + response.modifyResponse(generateResponseEntity(request)); + } + + private static ResponseEntity generateResponseEntity(HttpRequest request) { HttpRequestStartLine httpRequestStartLine = request.getHttpRequestStartLine(); HttpRequestBody httpRequestBody = request.getHttpRequestBody(); HttpMethod httpMethod = httpRequestStartLine.getHttpMethod(); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/RequestMapping.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/RequestMapping.java index 1e1f96143e..d520b856b5 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/controller/RequestMapping.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/RequestMapping.java @@ -2,22 +2,22 @@ import org.apache.coyote.http11.request.HttpRequest; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public class RequestMapping { - private static final Map controllers = new HashMap<>(); + private final Map controllers = new ConcurrentHashMap<>(); private RequestMapping() { + registerController(); } public static RequestMapping init() { - registerController(); return new RequestMapping(); } - private static void registerController() { + private void registerController() { controllers.put("/", new IndexPageController()); controllers.put("/login", new LoginController()); controllers.put("/register", new RegisterController()); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java index 32a43c5130..a0af473a16 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/StaticResourceController.java @@ -1,10 +1,7 @@ package org.apache.coyote.http11.controller; import org.apache.coyote.http11.request.HttpRequest; -import org.apache.coyote.http11.response.ContentType; -import org.apache.coyote.http11.response.HttpResponseBody; -import org.apache.coyote.http11.response.HttpStatus; -import org.apache.coyote.http11.response.ResponseEntity; +import org.apache.coyote.http11.response.*; import java.io.File; import java.io.IOException; @@ -13,8 +10,7 @@ public class StaticResourceController implements Controller { - @Override - public ResponseEntity service(HttpRequest request) throws IOException { + private ResponseEntity generateResponseEntity(HttpRequest request) throws IOException { String requestURI = request.getHttpRequestStartLine().getPath(); URL resource = getClass() @@ -29,4 +25,9 @@ public ResponseEntity service(HttpRequest request) throws IOException { .responseBody(HttpResponseBody.from(responseBody)) .build(); } + + @Override + public void service(HttpRequest request, HttpResponse response) throws IOException { + response.modifyResponse(generateResponseEntity(request)); + } } 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 1705f8302c..4cdf0d3c47 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java @@ -19,9 +19,13 @@ public class HttpResponse { private static final String BLANK_LINE = ""; private static final String BLANK_SPACE = " "; - private final HttpResponseStatusLine httpResponseStatusLine; - private final HttpResponseHeaders httpResponseHeaders; - private final HttpResponseBody httpResponseBody; + private HttpResponseStatusLine httpResponseStatusLine; + private HttpResponseHeaders httpResponseHeaders; + private HttpResponseBody httpResponseBody; + + public HttpResponse() { + this(null, null, null); + } @Builder public HttpResponse(HttpResponseStatusLine httpResponseStatusLine, HttpResponseHeaders httpResponseHeaders, HttpResponseBody httpResponseBody) { @@ -30,6 +34,12 @@ public HttpResponse(HttpResponseStatusLine httpResponseStatusLine, HttpResponseH this.httpResponseBody = httpResponseBody; } + public void modifyResponse(ResponseEntity responseEntity) throws IOException { + this.httpResponseStatusLine = HttpResponseStatusLine.of(HTTP11, responseEntity.getHttpStatus()); + this.httpResponseHeaders = generateHttpResponseHeaders(responseEntity, getOrGenerateResponseBody(responseEntity)); + this.httpResponseBody = getOrGenerateResponseBody(responseEntity); + } + public static HttpResponse from(ResponseEntity responseEntity) throws IOException { HttpStatus httpStatus = responseEntity.getHttpStatus(); HttpResponseBody responseBody = getOrGenerateResponseBody(responseEntity); diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java index bf630ee402..9f3ab007a5 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponseHeaders.java @@ -5,6 +5,7 @@ import java.util.LinkedHashMap; import java.util.Map; +import java.util.Optional; @Getter public class HttpResponseHeaders { @@ -44,11 +45,11 @@ public HttpResponseHeaders setCookie(HttpCookie httpCookie) { if (httpCookie == null) { return this; } - String jSessionId = httpCookie.findJSessionId(); - if (jSessionId == null) { + Optional jSessionId = httpCookie.findJSessionId(); + if (jSessionId.isEmpty()) { return this; } - setHeader("Set-Cookie", String.format("JSESSIONID=%s", jSessionId)); + setHeader("Set-Cookie", String.format("JSESSIONID=%s", jSessionId.get())); return this; } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/session/HttpCookie.java b/tomcat/src/main/java/org/apache/coyote/http11/session/HttpCookie.java index c114c1b801..6f88a8da7f 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/session/HttpCookie.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/session/HttpCookie.java @@ -5,6 +5,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import static java.util.stream.Collectors.collectingAndThen; import static java.util.stream.Collectors.toMap; @@ -18,7 +19,7 @@ public class HttpCookie { private final Map cookies = new HashMap<>(); public HttpCookie(Map cookies) { - cookies.putAll(cookies); + this.cookies.putAll(cookies); } private HttpCookie() { @@ -44,11 +45,11 @@ public void put(String key, String value) { cookies.put(key, value); } - public String find(String key) { - return cookies.get(key); + public Optional find(String key) { + return Optional.ofNullable(cookies.get(key)); } - public String findJSessionId() { - return cookies.get("JSESSIONID"); + public Optional findJSessionId() { + return find("JSESSIONID"); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/session/SessionManager.java b/tomcat/src/main/java/org/apache/coyote/http11/session/SessionManager.java index 44baa1e47c..1c6e14324f 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/session/SessionManager.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/session/SessionManager.java @@ -1,18 +1,19 @@ package org.apache.coyote.http11.session; -import java.util.HashMap; import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; public class SessionManager { - private static final Map SESSIONS = new HashMap<>(); + private static final Map SESSIONS = new ConcurrentHashMap<>(); public void add(Session session) { SESSIONS.put(session.getId(), session); } - public Session findSession(String id) { - return SESSIONS.get(id); + public Optional findSession(String id) { + return Optional.ofNullable(SESSIONS.get(id)); } public void remove(Session session) { diff --git a/tomcat/src/test/java/org/apache/coyote/http11/session/HttpCookieTest.java b/tomcat/src/test/java/org/apache/coyote/http11/session/HttpCookieTest.java new file mode 100644 index 0000000000..681b2cb974 --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/session/HttpCookieTest.java @@ -0,0 +1,24 @@ +package org.apache.coyote.http11.session; + +import org.junit.jupiter.api.Test; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +class HttpCookieTest { + + @Test + void 쿠키_파싱_테스트() { + String cookies = "JSESSIONID=f51889e2-275a-471e-b5ed-9c794180c85f"; + HttpCookie httpCookie = HttpCookie.from(cookies); + + Optional jSessionId = httpCookie.findJSessionId(); + + assertAll( + () -> assertThat(jSessionId).isNotEmpty(), + () -> assertThat(jSessionId.get()).isEqualTo("f51889e2-275a-471e-b5ed-9c794180c85f") + ); + } +}