diff --git a/study/src/main/resources/application.yml b/study/src/main/resources/application.yml index 385c11d5f1..708e368729 100644 --- a/study/src/main/resources/application.yml +++ b/study/src/main/resources/application.yml @@ -4,9 +4,9 @@ handlebars: server: tomcat: accept-count: 1 - max-connections: 1 + max-connections: 10 threads: - max: 2 + max: 10 compression: enabled: true min-response-size: 10 diff --git a/study/src/test/java/thread/stage0/SynchronizationTest.java b/study/src/test/java/thread/stage0/SynchronizationTest.java index 0333c18e3b..b463c2b984 100644 --- a/study/src/test/java/thread/stage0/SynchronizationTest.java +++ b/study/src/test/java/thread/stage0/SynchronizationTest.java @@ -41,7 +41,7 @@ private static final class SynchronizedMethods { private int sum = 0; - public void calculate() { + public synchronized void calculate() { setSum(getSum() + 1); } diff --git a/study/src/test/java/thread/stage0/ThreadPoolsTest.java b/study/src/test/java/thread/stage0/ThreadPoolsTest.java index 238611ebfe..03efdabc8d 100644 --- a/study/src/test/java/thread/stage0/ThreadPoolsTest.java +++ b/study/src/test/java/thread/stage0/ThreadPoolsTest.java @@ -31,8 +31,8 @@ void testNewFixedThreadPool() { executor.submit(logWithSleep("hello fixed thread pools")); // 올바른 값으로 바꿔서 테스트를 통과시키자. - final int expectedPoolSize = 0; - final int expectedQueueSize = 0; + final int expectedPoolSize = 2; + final int expectedQueueSize = 1; assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize()); assertThat(expectedQueueSize).isEqualTo(executor.getQueue().size()); @@ -46,7 +46,7 @@ void testNewCachedThreadPool() { executor.submit(logWithSleep("hello cached thread pools")); // 올바른 값으로 바꿔서 테스트를 통과시키자. - final int expectedPoolSize = 0; + final int expectedPoolSize = 3; final int expectedQueueSize = 0; assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize()); diff --git a/study/src/test/java/thread/stage1/ConcurrencyTest.java b/study/src/test/java/thread/stage1/ConcurrencyTest.java index f5e8ee070a..bc721f2db9 100644 --- a/study/src/test/java/thread/stage1/ConcurrencyTest.java +++ b/study/src/test/java/thread/stage1/ConcurrencyTest.java @@ -1,9 +1,9 @@ package thread.stage1; -import org.junit.jupiter.api.Test; - import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; + /** * 스레드를 다룰 때 어떤 상황을 조심해야 할까? * - 상태를 가진 한 객체를 여러 스레드에서 동시에 접근할 경우 diff --git a/study/src/test/java/thread/stage1/UserServlet.java b/study/src/test/java/thread/stage1/UserServlet.java index b180a84c32..a78e7b4ad4 100644 --- a/study/src/test/java/thread/stage1/UserServlet.java +++ b/study/src/test/java/thread/stage1/UserServlet.java @@ -11,7 +11,7 @@ public void service(final User user) { join(user); } - private void join(final User user) { + private synchronized void join(final User user) { if (!users.contains(user)) { users.add(user); } diff --git a/study/src/test/java/thread/stage2/AppTest.java b/study/src/test/java/thread/stage2/AppTest.java index e253c4a249..9e85357ac0 100644 --- a/study/src/test/java/thread/stage2/AppTest.java +++ b/study/src/test/java/thread/stage2/AppTest.java @@ -39,7 +39,7 @@ void test() throws Exception { thread.join(); } - assertThat(count.intValue()).isEqualTo(2); + assertThat(count.intValue()).isEqualTo(10); } private static void incrementIfOk(final HttpResponse response) { diff --git a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java deleted file mode 100644 index 04c979af46..0000000000 --- a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java +++ /dev/null @@ -1,28 +0,0 @@ -package nextstep.jwp.controller; - -import java.util.NoSuchElementException; -import nextstep.jwp.dto.LoginResponseDto; -import nextstep.jwp.exception.InvalidLoginInfoException; -import nextstep.jwp.service.LoginService; -import nextstep.org.apache.coyote.http11.HttpCookie; -import nextstep.org.apache.coyote.http11.Session; - -public class LoginController { - - private final LoginService loginService = new LoginService(); - - public LoginResponseDto login(HttpCookie httpCookie, String account, String password) { - try { - Session loginSession = loginService.login(account, password); - httpCookie.set("JSESSIONID", loginSession.getId()); - return new LoginResponseDto("/index.html"); - } catch (NoSuchElementException | InvalidLoginInfoException | NullPointerException e) { - return new LoginResponseDto("/401.html"); - } - } - - public LoginResponseDto register(String account, String password, String email) { - loginService.register(account, password, email); - return new LoginResponseDto("/index.html"); - } -} diff --git a/tomcat/src/main/java/nextstep/jwp/dto/LoginResponseDto.java b/tomcat/src/main/java/nextstep/jwp/dto/LoginResponseDto.java deleted file mode 100644 index 25e0b9e82b..0000000000 --- a/tomcat/src/main/java/nextstep/jwp/dto/LoginResponseDto.java +++ /dev/null @@ -1,14 +0,0 @@ -package nextstep.jwp.dto; - -public class LoginResponseDto { - - private final String redirectUrl; - - public LoginResponseDto(String redirectUrl) { - this.redirectUrl = redirectUrl; - } - - public String getRedirectUrl() { - return redirectUrl; - } -} diff --git a/tomcat/src/main/java/nextstep/jwp/service/LoginService.java b/tomcat/src/main/java/nextstep/jwp/service/LoginService.java deleted file mode 100644 index f2f1c0a678..0000000000 --- a/tomcat/src/main/java/nextstep/jwp/service/LoginService.java +++ /dev/null @@ -1,37 +0,0 @@ -package nextstep.jwp.service; - -import java.util.UUID; -import nextstep.jwp.db.InMemoryUserRepository; -import nextstep.jwp.exception.InvalidLoginInfoException; -import nextstep.jwp.model.User; -import nextstep.org.apache.coyote.http11.Session; -import nextstep.org.apache.coyote.http11.SessionManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class LoginService { - - private final Logger log = LoggerFactory.getLogger(LoginService.class); - - public Session login(String account, String password) { - User user = InMemoryUserRepository.findByAccount(account) - .orElseThrow(); - if (!user.checkPassword(password)) { - throw new InvalidLoginInfoException(); - } - log.info(user.toString()); - return createSession(user); - } - - private Session createSession(User user) { - SessionManager sessionManager = new SessionManager(); - Session session = new Session(UUID.randomUUID().toString()); - session.setAttribute(user.getAccount(), user); - sessionManager.add(session); - return session; - } - - public void register(String account, String password, String email) { - InMemoryUserRepository.save(new User(account, password, email)); - } -} diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/Context.java b/tomcat/src/main/java/nextstep/org/apache/catalina/Context.java new file mode 100644 index 0000000000..27ff402373 --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/Context.java @@ -0,0 +1,36 @@ +package nextstep.org.apache.catalina; + +import java.util.HashMap; +import java.util.Map; +import nextstep.org.apache.catalina.servlet.DefaultServlet; +import nextstep.org.apache.catalina.servlet.LoginServlet; +import nextstep.org.apache.catalina.servlet.RegisterServlet; +import nextstep.org.apache.catalina.servlet.Servlet; + +public class Context { + + private static final Map servletMappings; + private static final String EXTENSION_DELIMITER = "."; + + static { + servletMappings = new HashMap<>(); + servletMappings.put("/login", new LoginServlet()); + servletMappings.put("/register", new RegisterServlet()); + servletMappings.put("default", new DefaultServlet()); + } + + public Servlet getServlet(String pathInfo) { + if (servletMappings.containsKey(removeExtension(pathInfo))) { + return servletMappings.get(removeExtension(pathInfo)); + } + return servletMappings.get("default"); + } + + private String removeExtension(String pathInfo) { + int idx = pathInfo.indexOf(EXTENSION_DELIMITER); + if (idx == -1) { + return pathInfo; + } + return pathInfo.substring(0,idx); + } +} diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/Manager.java b/tomcat/src/main/java/nextstep/org/apache/catalina/Manager.java index c3dd6088d5..678695694c 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/Manager.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/Manager.java @@ -1,7 +1,7 @@ package nextstep.org.apache.catalina; import java.io.IOException; -import nextstep.org.apache.coyote.http11.Session; +import nextstep.org.apache.catalina.session.Session; /** * A Manager manages the pool of Sessions that are associated with a diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java b/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java index 6df168a492..9d89f9979e 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/connector/Connector.java @@ -1,13 +1,15 @@ package nextstep.org.apache.catalina.connector; -import nextstep.org.apache.coyote.http11.Http11Processor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.io.UncheckedIOException; import java.net.ServerSocket; import java.net.Socket; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; +import nextstep.org.apache.catalina.Context; +import nextstep.org.apache.coyote.http11.Http11Processor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Connector implements Runnable { @@ -15,15 +17,20 @@ public class Connector implements Runnable { private static final int DEFAULT_PORT = 8080; private static final int DEFAULT_ACCEPT_COUNT = 100; + private static final int MAX_THREADS_COUNT = 250; + private final Context context; + private final ThreadPoolExecutor threadPoolExecutor; private final ServerSocket serverSocket; private boolean stopped; - public Connector() { - this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT); + public Connector(Context context) { + this(context, DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, MAX_THREADS_COUNT); } - public Connector(final int port, final int acceptCount) { + public Connector(Context context, final int port, final int acceptCount, final int maxThreads) { + this.context = context; + this.threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(maxThreads); this.serverSocket = createServerSocket(port, acceptCount); this.stopped = false; } @@ -66,8 +73,8 @@ private void process(final Socket connection) { if (connection == null) { return; } - var processor = new Http11Processor(connection); - new Thread(processor).start(); + var processor = new Http11Processor(context, connection); + this.threadPoolExecutor.submit(processor); } public void stop() { diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpCookie.java b/tomcat/src/main/java/nextstep/org/apache/catalina/cookie/Cookies.java similarity index 63% rename from tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpCookie.java rename to tomcat/src/main/java/nextstep/org/apache/catalina/cookie/Cookies.java index 48bfa7fb91..314c63f2f9 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpCookie.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/cookie/Cookies.java @@ -1,14 +1,15 @@ -package nextstep.org.apache.coyote.http11; +package nextstep.org.apache.catalina.cookie; import static nextstep.org.apache.coyote.http11.HttpUtil.parseMultipleValues; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; +import nextstep.org.apache.coyote.http11.HttpHeader; -public class HttpCookie { +public class Cookies { - private static final String SET_COOKIE_HEADER = "Set-Cookie: %s \r\n"; + private static final String SET_COOKIE_HEADER_FORMAT = "%s: %s \r\n"; private static final String COOKIE_VALUES_DELIMITER = "; "; private static final String COOKIE_KEY_VALUE_DELIMITER = "="; @@ -36,9 +37,17 @@ public boolean hasCookie(String key) { } public String createSetCookieHeader() { - String cookies = cookie.entrySet().stream() + return cookie.entrySet().stream() .map(entry -> entry.getKey() + COOKIE_KEY_VALUE_DELIMITER + entry.getValue()) - .collect(Collectors.joining(COOKIE_VALUES_DELIMITER)); - return String.format(SET_COOKIE_HEADER, cookies); + .map(value -> String.format( + SET_COOKIE_HEADER_FORMAT, HttpHeader.SET_COOKIE.getValue(), value) + ) + .collect(Collectors.joining()); + } + + public Cookies defensiveCopy() { + Cookies newCookies = new Cookies(); + cookie.forEach(newCookies::set); + return newCookies; } } diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/AbstractServlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/AbstractServlet.java new file mode 100644 index 0000000000..1eed878187 --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/AbstractServlet.java @@ -0,0 +1,88 @@ +package nextstep.org.apache.catalina.servlet; + +import static nextstep.org.apache.coyote.http11.HttpUtil.selectFirstContentTypeOrDefault; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Objects; +import java.util.Optional; +import nextstep.org.apache.coyote.http11.HttpHeader; +import nextstep.org.apache.coyote.http11.request.Http11Request; +import nextstep.org.apache.coyote.http11.response.Http11Response; +import nextstep.org.apache.coyote.http11.Status; + +public abstract class AbstractServlet implements Servlet{ + + private static final String RESOURCES_PATH_PREFIX = "static"; + protected static final String NOT_FOUND_DEFAULT_MESSAGE = "404 Not Found"; + private static final String CHARSET_UTF_8 = ";charset=utf-8"; + private static final String DEFAULT_EXTENSION = ".html"; + private static final String NOT_FOUND_PAGE = "/404.html"; + private static final String HTTP_GET_METHOD = "GET"; + private static final String HTTP_POST_METHOD = "POST"; + + @Override + public void service(Http11Request request, Http11Response response) throws Exception { + String method = request.getMethod(); + + if (method.equals(HTTP_GET_METHOD)) { + doGet(request, response); + } else if (method.equals(HTTP_POST_METHOD)) { + doPost(request, response); + } else { + response.setStatus(Status.NOT_IMPLEMENTED); + } + } + + protected abstract void doGet(Http11Request request, Http11Response response) throws Exception; + + protected abstract void doPost(Http11Request request, Http11Response response) throws Exception; + + protected Optional createResponseBody(String requestPath) throws IOException { + if (requestPath.equals("/")) { + return Optional.of("Hello world!"); + } + + String resourceName = RESOURCES_PATH_PREFIX + requestPath; + if (!resourceName.contains(".")) { + resourceName += DEFAULT_EXTENSION; + } + URL resource = getClass().getClassLoader().getResource(resourceName); + + if (Objects.isNull(resource)) { + return Optional.empty(); + } + return Optional.of(new String(Files.readAllBytes(new File(resource.getFile()).toPath()))); + } + + protected void responseWithBody(Http11Request request, Http11Response response) throws IOException { + Optional responseBody = createResponseBody(request.getPathInfo()); + String contentType = selectFirstContentTypeOrDefault(request.getHeader(HttpHeader.ACCEPT)); + + if (responseBody.isEmpty()) { + responseWithNotFound(request, response); + return; + } + + response.setStatus(Status.OK) + .setHeader(HttpHeader.CONTENT_TYPE, contentType + CHARSET_UTF_8) + .setHeader(HttpHeader.CONTENT_LENGTH, String.valueOf( + responseBody.get().getBytes(StandardCharsets.UTF_8).length)) + .setBody(responseBody.get()); + } + + private void responseWithNotFound(Http11Request request, Http11Response response) throws IOException { + String notFoundPageBody = createResponseBody(NOT_FOUND_PAGE) + .orElse(NOT_FOUND_DEFAULT_MESSAGE); + String contentType = selectFirstContentTypeOrDefault(request.getHeader(HttpHeader.ACCEPT)); + + response.setStatus(Status.NOT_FOUND) + .setHeader(HttpHeader.CONTENT_TYPE, contentType + CHARSET_UTF_8) + .setHeader(HttpHeader.CONTENT_LENGTH, String.valueOf( + notFoundPageBody.getBytes(StandardCharsets.UTF_8).length)) + .setBody(notFoundPageBody); + } +} diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/DefaultServlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/DefaultServlet.java new file mode 100644 index 0000000000..c874b2149e --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/DefaultServlet.java @@ -0,0 +1,18 @@ +package nextstep.org.apache.catalina.servlet; + +import nextstep.org.apache.coyote.http11.request.Http11Request; +import nextstep.org.apache.coyote.http11.response.Http11Response; +import nextstep.org.apache.coyote.http11.Status; + +public class DefaultServlet extends AbstractServlet { + + @Override + protected void doGet(Http11Request request, Http11Response response) throws Exception { + responseWithBody(request, response); + } + + @Override + protected void doPost(Http11Request request, Http11Response response) { + response.setStatus(Status.NOT_IMPLEMENTED); + } +} diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java new file mode 100644 index 0000000000..7f39612fa9 --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/LoginServlet.java @@ -0,0 +1,70 @@ +package nextstep.org.apache.catalina.servlet; + +import java.util.NoSuchElementException; +import java.util.UUID; +import nextstep.jwp.db.InMemoryUserRepository; +import nextstep.jwp.exception.InvalidLoginInfoException; +import nextstep.jwp.model.User; +import nextstep.org.apache.catalina.cookie.Cookies; +import nextstep.org.apache.coyote.http11.HttpHeader; +import nextstep.org.apache.coyote.http11.request.Http11Request; +import nextstep.org.apache.coyote.http11.response.Http11Response; +import nextstep.org.apache.catalina.session.Session; +import nextstep.org.apache.catalina.session.SessionManager; +import nextstep.org.apache.coyote.http11.Status; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LoginServlet extends AbstractServlet { + + private static final Logger log = LoggerFactory.getLogger(LoginServlet.class); + private static final SessionManager sessionManager = new SessionManager(); + + @Override + protected void doGet(Http11Request request, Http11Response response) throws Exception { + Cookies cookies = request.getCookies(); + if (cookies.hasCookie("JSESSIONID")) { + response.setStatus(Status.FOUND) + .setHeader(HttpHeader.LOCATION, "/index.html"); + return; + } + responseWithBody(request, response); + } + + @Override + protected void doPost(Http11Request request, Http11Response response) { + Cookies cookies = request.getCookies(); + try { + Session loginSession = login( + request.getParsedBodyValue("account"), + request.getParsedBodyValue("password") + ); + cookies.set("JSESSIONID", loginSession.getId()); + + response.setStatus(Status.FOUND) + .setHeader(HttpHeader.LOCATION, "/index.html") + .setCookies(cookies); + } catch (NoSuchElementException | InvalidLoginInfoException | NullPointerException e) { + response.setStatus(Status.FOUND) + .setHeader(HttpHeader.LOCATION, "/401.html") + .setCookies(cookies); + } + } + + private Session login(String account, String password) { + User user = InMemoryUserRepository.findByAccount(account) + .orElseThrow(); + if (!user.checkPassword(password)) { + throw new InvalidLoginInfoException(); + } + log.info(user.toString()); + return createSession(user); + } + + private Session createSession(User user) { + Session session = new Session(UUID.randomUUID().toString()); + session.setAttribute(user.getAccount(), user); + sessionManager.add(session); + return session; + } +} diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/RegisterServlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/RegisterServlet.java new file mode 100644 index 0000000000..064b2b9c20 --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/RegisterServlet.java @@ -0,0 +1,31 @@ +package nextstep.org.apache.catalina.servlet; + +import nextstep.jwp.db.InMemoryUserRepository; +import nextstep.jwp.model.User; +import nextstep.org.apache.coyote.http11.HttpHeader; +import nextstep.org.apache.coyote.http11.request.Http11Request; +import nextstep.org.apache.coyote.http11.response.Http11Response; +import nextstep.org.apache.coyote.http11.Status; + +public class RegisterServlet extends AbstractServlet{ + + @Override + protected void doGet(Http11Request request, Http11Response response) throws Exception { + responseWithBody(request, response); + } + + @Override + protected void doPost(Http11Request request, Http11Response response) { + + InMemoryUserRepository.save( + new User( + request.getParsedBodyValue("account"), + request.getParsedBodyValue("password"), + request.getParsedBodyValue("email") + ) + ); + + response.setStatus(Status.FOUND) + .setHeader(HttpHeader.LOCATION, "/index.html"); + } +} diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/Servlet.java b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/Servlet.java new file mode 100644 index 0000000000..8bbaa4464b --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/servlet/Servlet.java @@ -0,0 +1,9 @@ +package nextstep.org.apache.catalina.servlet; + +import nextstep.org.apache.coyote.http11.request.Http11Request; +import nextstep.org.apache.coyote.http11.response.Http11Response; + +public interface Servlet { + + void service(Http11Request request, Http11Response response) throws Exception; +} diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Session.java b/tomcat/src/main/java/nextstep/org/apache/catalina/session/Session.java similarity index 91% rename from tomcat/src/main/java/nextstep/org/apache/coyote/http11/Session.java rename to tomcat/src/main/java/nextstep/org/apache/catalina/session/Session.java index 0622a2ec32..afdb360c65 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Session.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/session/Session.java @@ -1,4 +1,4 @@ -package nextstep.org.apache.coyote.http11; +package nextstep.org.apache.catalina.session; import java.util.HashMap; import java.util.Map; diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/SessionManager.java b/tomcat/src/main/java/nextstep/org/apache/catalina/session/SessionManager.java similarity index 70% rename from tomcat/src/main/java/nextstep/org/apache/coyote/http11/SessionManager.java rename to tomcat/src/main/java/nextstep/org/apache/catalina/session/SessionManager.java index cbafda4e68..1ac8dab6f9 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/SessionManager.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/session/SessionManager.java @@ -1,14 +1,12 @@ -package nextstep.org.apache.coyote.http11; +package nextstep.org.apache.catalina.session; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import nextstep.org.apache.catalina.Manager; public class SessionManager implements Manager { - private static final Map SESSIONS = new HashMap<>(); - - public SessionManager() {} + private static final Map SESSIONS = new ConcurrentHashMap<>(); @Override public void add(Session session) { diff --git a/tomcat/src/main/java/nextstep/org/apache/catalina/startup/Tomcat.java b/tomcat/src/main/java/nextstep/org/apache/catalina/startup/Tomcat.java index c56485fd2b..1ccedd83c1 100644 --- a/tomcat/src/main/java/nextstep/org/apache/catalina/startup/Tomcat.java +++ b/tomcat/src/main/java/nextstep/org/apache/catalina/startup/Tomcat.java @@ -1,5 +1,6 @@ package nextstep.org.apache.catalina.startup; +import nextstep.org.apache.catalina.Context; import nextstep.org.apache.catalina.connector.Connector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,8 +11,10 @@ public class Tomcat { private static final Logger log = LoggerFactory.getLogger(Tomcat.class); + private final Context context = new Context(); + public void start() { - var connector = new Connector(); + var connector = new Connector(context); connector.start(); try { diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HandlerMapper.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HandlerMapper.java deleted file mode 100644 index 15bdd9cac9..0000000000 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HandlerMapper.java +++ /dev/null @@ -1,20 +0,0 @@ -package nextstep.org.apache.coyote.http11; - -import java.util.HashMap; -import java.util.Map; -import nextstep.jwp.controller.LoginController; - -public class HandlerMapper { - - private static final Map handlerMapper = new HashMap<>(); - - static { - LoginController loginController = new LoginController(); - handlerMapper.put("/login", loginController); - handlerMapper.put("/register", loginController); - } - - public Object mapHandler(String requestedUrl) { - return handlerMapper.get(requestedUrl); - } -} diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java index c14153beb1..07a009773a 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Http11Processor.java @@ -1,27 +1,19 @@ package nextstep.org.apache.coyote.http11; -import static nextstep.org.apache.coyote.http11.HttpUtil.parseMultipleValues; +import static nextstep.org.apache.coyote.http11.HttpUtil.createResponseBody; +import static nextstep.org.apache.coyote.http11.HttpUtil.selectFirstContentTypeOrDefault; -import java.io.BufferedReader; -import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; -import java.net.URL; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import nextstep.jwp.controller.LoginController; -import nextstep.jwp.dto.LoginResponseDto; import nextstep.jwp.exception.UncheckedServletException; import nextstep.org.apache.coyote.Processor; +import nextstep.org.apache.catalina.Context; +import nextstep.org.apache.catalina.servlet.Servlet; +import nextstep.org.apache.coyote.http11.request.Http11Request; +import nextstep.org.apache.coyote.http11.response.Http11Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,21 +21,12 @@ public class Http11Processor implements Runnable, Processor { private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); - private static final int KEY_INDEX = 0; - private static final int VALUE_INDEX = 1; - private static final String EMPTY_LINE = ""; - private static final String RESOURCES_PATH_PREFIX = "static"; - private static final int ACCEPT_HEADER_BEST_CONTENT_TYPE_INDEX = 0; - private static final String FORM_VALUES_DELIMITER = "&"; - private static final String FORM_KEY_VALUE_DELIMITER = "="; - private static final String NOT_FOUND_DEFALULT_MESSAGE = "404 Not Found"; - + private final Context context; private final Socket connection; - private final HandlerMapper handlerMapper; - public Http11Processor(final Socket connection) { + public Http11Processor(final Context context, final Socket connection) { + this.context = context; this.connection = connection; - this.handlerMapper = new HandlerMapper(); } @Override @@ -56,102 +39,16 @@ public void run() { public void process(final Socket connection) { try ( InputStream inputStream = connection.getInputStream(); - OutputStream outputStream = connection.getOutputStream(); - InputStreamReader inputStreamReader = new InputStreamReader(inputStream); - BufferedReader bufferedReader = new BufferedReader(inputStreamReader) + OutputStream outputStream = connection.getOutputStream() ) { - StartLine startLine = new StartLine(bufferedReader.readLine()); - Map requestHeaders = extractHeaders(bufferedReader); - - Map parsedBody = new HashMap<>(); - if (requestHeaders.containsKey("Content-Length")) { - String requestBody = extractRequestBody(bufferedReader, requestHeaders); - parseMultipleValues(parsedBody, - requestBody, FORM_VALUES_DELIMITER, FORM_KEY_VALUE_DELIMITER); - } - - HttpCookie httpCookie = new HttpCookie(); - if (requestHeaders.containsKey("Cookie")) { - httpCookie.parseCookieHeaders(requestHeaders.get("Cookie")); - } - - Map queryParams = new HashMap<>(); - if (startLine.hasQueryString()) { - parseMultipleValues(queryParams, - startLine.getQueryString(), "&", "="); - } - - String response = null; - String requestPath = startLine.getPath(); - - Object handler = handlerMapper.mapHandler(requestPath); - if (Objects.nonNull(handler) && startLine.getHttpMethod().equals("POST") - && requestPath.equals("/login")) { - LoginController loginController = (LoginController) handler; - LoginResponseDto loginDto = loginController.login(httpCookie, - parsedBody.get("account"), - parsedBody.get("password")); - - response = String.join("\r\n", - "HTTP/1.1 302 Found ", - String.format("Location: %s \r\n", loginDto.getRedirectUrl())); - - if (!httpCookie.isEmpty()) { - response += httpCookie.createSetCookieHeader(); - } - response += ""; - } - - if (Objects.nonNull(handler) && startLine.getHttpMethod().equals("POST") - && requestPath.equals("/register")) { - LoginController loginController = (LoginController) handler; - LoginResponseDto loginDto = loginController.register(parsedBody.get("account"), - parsedBody.get("password"), parsedBody.get("email")); - - response = String.join("\r\n", - "HTTP/1.1 302 Found ", - String.format("Location: %s ", loginDto.getRedirectUrl()), - ""); - } - - if (startLine.getHttpMethod().equals("GET") && Objects.isNull(response)) { - String contentType = selectFirstContentTypeOrDefault(requestHeaders.get("Accept")); - - // Todo: createResponseBody() pageController로 위임해보기 - // Todo: 헤더에 담긴 sessionId 유효성 검증 - if (requestPath.contains("/login") && httpCookie.hasCookie("JSESSIONID")) { - response = String.join("\r\n", - "HTTP/1.1 302 Found ", - "Location: /index.html ", - ""); - - writeResponse(outputStream, response); - return; - } - - Optional responseBody = createResponseBody(requestPath); - if (responseBody.isEmpty()) { - String notFoundPageBody = createResponseBody("/404.html") - .orElse(NOT_FOUND_DEFALULT_MESSAGE); - - response = String.join("\r\n", - "HTTP/1.1 404 Not Found ", - String.format("Content-Type: %s;charset=utf-8 ", contentType), - String.format("Content-Length: %s ", - notFoundPageBody.getBytes(StandardCharsets.UTF_8).length), - "", - notFoundPageBody); - writeResponse(outputStream, response); - return; - } - - response = String.join("\r\n", - "HTTP/1.1 200 OK ", - String.format("Content-Type: %s;charset=utf-8 ", contentType), - String.format("Content-Length: %s ", - responseBody.get().getBytes(StandardCharsets.UTF_8).length), - "", - responseBody.get()); + Http11Request request = new Http11Request(inputStream); + Http11Response response = new Http11Response(Status.OK); + + Servlet servlet = context.getServlet(request.getPathInfo()); + try { + servlet.service(request, response); + } catch (Exception e) { + responseInternalServerError(request, response); } writeResponse(outputStream, response); @@ -160,53 +57,22 @@ public void process(final Socket connection) { } } - private String extractRequestBody( - BufferedReader bufferedReader, - Map requestHeaders - ) throws IOException { - int contentLength = Integer.parseInt(requestHeaders.get("Content-Length")); - char[] buffer = new char[contentLength]; - bufferedReader.read(buffer, 0, contentLength); - return new String(buffer); + private void responseInternalServerError(Http11Request request, Http11Response response) + throws IOException { + String internalServerErrorPageBody = createResponseBody("/500.html") + .orElse(Status.INTERNAL_SERVER_ERROR.getCodeMessage()); + String contentType = selectFirstContentTypeOrDefault(request.getHeader(HttpHeader.ACCEPT)); + response.setStatus(Status.INTERNAL_SERVER_ERROR) + .setHeader(HttpHeader.CONTENT_TYPE, contentType + ";charset=utf-8") + .setHeader(HttpHeader.CONTENT_LENGTH, String.valueOf( + internalServerErrorPageBody.getBytes( + StandardCharsets.UTF_8).length)) + .setBody(internalServerErrorPageBody); } - private void writeResponse(OutputStream outputStream, String response) throws IOException { - outputStream.write(response.getBytes()); + private void writeResponse(OutputStream outputStream, Http11Response response) + throws IOException { + outputStream.write(response.createResponseAsByteArray()); outputStream.flush(); } - - private String selectFirstContentTypeOrDefault(String acceptHeader) { - if (Objects.isNull(acceptHeader)) { - return "text/html"; - } - List acceptHeaderValues = Arrays.asList(acceptHeader.split(",")); - return acceptHeaderValues.get(ACCEPT_HEADER_BEST_CONTENT_TYPE_INDEX); - } - - private Map extractHeaders(BufferedReader bufferedReader) throws IOException { - Map requestHeaders = new HashMap<>(); - String line; - while (!EMPTY_LINE.equals(line = bufferedReader.readLine())) { - String[] splited = line.split(": "); - requestHeaders.put(splited[KEY_INDEX], splited[VALUE_INDEX].strip()); - } - return requestHeaders; - } - - private Optional createResponseBody(String requestPath) throws IOException { - if (requestPath.equals("/")) { - return Optional.of("Hello world!"); - } - - String resourceName = RESOURCES_PATH_PREFIX + requestPath; - if (!resourceName.contains(".")) { - resourceName += ".html"; - } - URL resource = getClass().getClassLoader().getResource(resourceName); - - if (Objects.isNull(resource)) { - return Optional.empty(); - } - return Optional.of(new String(Files.readAllBytes(new File(resource.getFile()).toPath()))); - } } diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpHeader.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpHeader.java new file mode 100644 index 0000000000..062d282743 --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpHeader.java @@ -0,0 +1,31 @@ +package nextstep.org.apache.coyote.http11; + +import java.util.Arrays; + +public enum HttpHeader { + ACCEPT("Accept"), + COOKIE("Cookie"), + CONTENT_LENGTH("Content-Length"), + CONTENT_TYPE("Content-Type"), + LOCATION("Location"), + SET_COOKIE("Set-Cookie"); + + + + private final String value; + + HttpHeader(String value) { + this.value = value; + } + + public static HttpHeader getHttpHeader(String value) { + return Arrays.stream(HttpHeader.values()) + .filter(header -> header.value.equals(value)) + .findFirst() + .orElseThrow(IllegalArgumentException::new); + } + + public String getValue() { + return value; + } +} diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpUtil.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpUtil.java index ae8314dbb6..c9141498d7 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpUtil.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/HttpUtil.java @@ -1,12 +1,21 @@ package nextstep.org.apache.coyote.http11; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; import java.util.Arrays; +import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Optional; public class HttpUtil { private static final int KEY_INDEX = 0; private static final int VALUE_INDEX = 1; + private static final int ACCEPT_HEADER_BEST_CONTENT_TYPE_INDEX = 0; + private static final String RESOURCES_PATH_PREFIX = "static"; private HttpUtil() { } @@ -21,4 +30,33 @@ public static void parseMultipleValues( parsedValues.put(splited[KEY_INDEX], splited[VALUE_INDEX]); }); } + + public static String selectFirstContentTypeOrDefault(String acceptHeader) { + if (Objects.isNull(acceptHeader)) { + return "text/html"; + } + List acceptHeaderValues = Arrays.asList(acceptHeader.split(",")); + return acceptHeaderValues.get(ACCEPT_HEADER_BEST_CONTENT_TYPE_INDEX); + } + + public static Optional createResponseBody(String requestPath) throws IOException { + if (requestPath.equals("/")) { + return Optional.of("Hello world!"); + } + + String resourceName = preprocessRequestPath(requestPath); + URL resource = HttpUtil.class.getClassLoader().getResource(resourceName); + if (Objects.isNull(resource)) { + return Optional.empty(); + } + return Optional.of(new String(Files.readAllBytes(new File(resource.getFile()).toPath()))); + } + + private static String preprocessRequestPath(String requestPath) { + String resourceName = RESOURCES_PATH_PREFIX + requestPath; + if (!resourceName.contains(".")) { + resourceName += ".html"; + } + return resourceName; + } } diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Status.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Status.java new file mode 100644 index 0000000000..2b9cda9913 --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/Status.java @@ -0,0 +1,22 @@ +package nextstep.org.apache.coyote.http11; + +public enum Status { + + OK("200", "OK"), + FOUND("302", "Found"), + NOT_FOUND("404", "Not Found"), + NOT_IMPLEMENTED("501", "Not Implemented"), + INTERNAL_SERVER_ERROR("500", "Internal Server Error"); + + private final String code; + private final String message; + + Status(String code, String message) { + this.code = code; + this.message = message; + } + + public String getCodeMessage() { + return this.code + " " + this.message; + } +} diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/Http11Request.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/Http11Request.java new file mode 100644 index 0000000000..dcd505cf45 --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/Http11Request.java @@ -0,0 +1,89 @@ +package nextstep.org.apache.coyote.http11.request; + +import static nextstep.org.apache.coyote.http11.HttpUtil.parseMultipleValues; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; +import nextstep.org.apache.catalina.cookie.Cookies; +import nextstep.org.apache.coyote.http11.HttpHeader; + +public class Http11Request { + + private static final int KEY_INDEX = 0; + private static final int VALUE_INDEX = 1; + private static final String EMPTY_LINE = ""; + private static final String FORM_VALUES_DELIMITER = "&"; + private static final String FORM_KEY_VALUE_DELIMITER = "="; + + private StartLine startLine; + private Map headers = new HashMap<>(); + private final Cookies cookies = new Cookies(); + private Map parsedBody = null; + + public Http11Request(InputStream inputStream) throws IOException { + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + + this.startLine = new StartLine(bufferedReader.readLine()); + extractHeaders(bufferedReader); + + if (headers.containsKey(HttpHeader.COOKIE)) { + cookies.parseCookieHeaders(headers.get(HttpHeader.COOKIE)); + } + if (headers.containsKey(HttpHeader.CONTENT_LENGTH)) { + parseBody(bufferedReader); + } + } + + private void extractHeaders(BufferedReader bufferedReader) throws IOException { + String line; + while (!EMPTY_LINE.equals(line = bufferedReader.readLine())) { + extractHeader(line); + } + } + + private void extractHeader(String line) { + try { + String[] splited = line.split(": "); + headers.put(HttpHeader.getHttpHeader(splited[KEY_INDEX]), splited[VALUE_INDEX].strip()); + } catch (IllegalArgumentException ignored) { + } + } + + private void parseBody(BufferedReader bufferedReader) throws IOException { + parsedBody = new HashMap<>(); + String requestBody = extractRequestBody(bufferedReader); + parseMultipleValues(parsedBody, + requestBody, FORM_VALUES_DELIMITER, FORM_KEY_VALUE_DELIMITER); + } + + private String extractRequestBody(BufferedReader bufferedReader) throws IOException { + int contentLength = Integer.parseInt(headers.get(HttpHeader.CONTENT_LENGTH)); + char[] buffer = new char[contentLength]; + bufferedReader.read(buffer, 0, contentLength); + return new String(buffer); + } + + public String getMethod() { + return startLine.getHttpMethod(); + } + + public String getPathInfo() { + return startLine.getPath(); + } + + public String getHeader(HttpHeader httpHeader) { + return headers.get(httpHeader); + } + + public Cookies getCookies() { + return cookies.defensiveCopy(); + } + + public String getParsedBodyValue(String name) { + return parsedBody.get(name); + } +} diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/StartLine.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/StartLine.java similarity index 52% rename from tomcat/src/main/java/nextstep/org/apache/coyote/http11/StartLine.java rename to tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/StartLine.java index d2db69776d..aec7a88320 100644 --- a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/StartLine.java +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/request/StartLine.java @@ -1,8 +1,11 @@ -package nextstep.org.apache.coyote.http11; +package nextstep.org.apache.coyote.http11.request; + +import static nextstep.org.apache.coyote.http11.HttpUtil.parseMultipleValues; import java.util.Arrays; +import java.util.HashMap; import java.util.List; -import java.util.Objects; +import java.util.Map; public class StartLine { @@ -11,17 +14,19 @@ public class StartLine { private static final int REQUEST_TARGET_INDEX = 1; private static final int HTTP_VERSION_INDEX = 2; private static final String QUERY_PARAM_DELIMITER = "?"; + private static final String VALUES_DELIMITER = "&"; + private static final String KEY_VALUE_DELIMITER = "="; private String httpMethod; private String path; + private Map queryParams = new HashMap<>(); private String httpVersion; - private String queryString = null; public StartLine(String startLine) { - List parsed = Arrays.asList(startLine.split(START_LINE_DELIMITER)); - httpMethod = parsed.get(HTTP_METHOD_INDEX); - parsePath(parsed); - httpVersion = parsed.get(HTTP_VERSION_INDEX); + List parsedStartLine = Arrays.asList(startLine.split(START_LINE_DELIMITER)); + httpMethod = parsedStartLine.get(HTTP_METHOD_INDEX); + parsePath(parsedStartLine); + httpVersion = parsedStartLine.get(HTTP_VERSION_INDEX); } private void parsePath(List parsed) { @@ -29,14 +34,11 @@ private void parsePath(List parsed) { if (path.contains(QUERY_PARAM_DELIMITER)) { int queryParamIndex = path.indexOf(QUERY_PARAM_DELIMITER); path = parsed.get(REQUEST_TARGET_INDEX).substring(0, queryParamIndex); - queryString = parsed.get(queryParamIndex + 1); + String queryString = parsed.get(queryParamIndex + 1); + parseMultipleValues(queryParams, queryString, VALUES_DELIMITER, KEY_VALUE_DELIMITER); } } - public boolean hasQueryString() { - return Objects.nonNull(queryString); - } - public String getHttpMethod() { return httpMethod; } @@ -45,11 +47,7 @@ public String getPath() { return path; } - public String getQueryString() { - return queryString; - } - - public String getHttpVersion() { - return httpVersion; + public Map getQueryParams() { + return new HashMap<>(queryParams); } } diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/response/Http11Response.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/response/Http11Response.java new file mode 100644 index 0000000000..aed87d6404 --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/response/Http11Response.java @@ -0,0 +1,72 @@ +package nextstep.org.apache.coyote.http11.response; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import nextstep.org.apache.catalina.cookie.Cookies; +import nextstep.org.apache.coyote.http11.HttpHeader; +import nextstep.org.apache.coyote.http11.Status; + +public class Http11Response { + + private static final String HTTP_VERSION = "HTTP/1.1"; + private static final String HEADER_FORMAT = "%s: %s "; + private static final String EMPTY_LINE = ""; + private static final String LINEBREAK_DELIMITER = "\r\n"; + + private StatusLine statusLine; + private Map headers = new LinkedHashMap<>(); + private Cookies cookies = null; + private String body = null; + + public Http11Response(Status status) { + this.statusLine = new StatusLine(HTTP_VERSION, status); + } + + public byte[] createResponseAsByteArray() { + return createResponse().getBytes(); + } + + private String createResponse() { + String response = String.join(LINEBREAK_DELIMITER, + statusLine.toString(), + createHeadersResponse(), + EMPTY_LINE + ); + if (Objects.nonNull(body)) { + response += LINEBREAK_DELIMITER + body; + } + return response; + } + + private String createHeadersResponse() { + String headersResponse = this.headers.entrySet().stream() + .map(entry -> String.format(HEADER_FORMAT, entry.getKey().getValue(), entry.getValue())) + .collect(Collectors.joining(LINEBREAK_DELIMITER)); + if (Objects.nonNull(cookies) && !cookies.isEmpty()) { + headersResponse += LINEBREAK_DELIMITER + cookies.createSetCookieHeader(); + } + return headersResponse; + } + + public Http11Response setStatus(Status status) { + this.statusLine = new StatusLine(HTTP_VERSION, status); + return this; + } + + public Http11Response setHeader(HttpHeader httpHeader, String value) { + this.headers.put(httpHeader, value); + return this; + } + + public Http11Response setCookies(Cookies cookies) { + this.cookies = cookies; + return this; + } + + public Http11Response setBody(String body) { + this.body = body; + return this; + } +} diff --git a/tomcat/src/main/java/nextstep/org/apache/coyote/http11/response/StatusLine.java b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/response/StatusLine.java new file mode 100644 index 0000000000..096d09aade --- /dev/null +++ b/tomcat/src/main/java/nextstep/org/apache/coyote/http11/response/StatusLine.java @@ -0,0 +1,18 @@ +package nextstep.org.apache.coyote.http11.response; + +import nextstep.org.apache.coyote.http11.Status; + +public class StatusLine { + + private final String httpVersion; + private final Status status; + + public StatusLine(String httpVersion, Status status) { + this.httpVersion = httpVersion; + this.status = status; + } + + public String toString() { + return httpVersion + " " + status.getCodeMessage() + " "; + } +} diff --git a/tomcat/src/test/java/nextstep/org/apache/coyote/http11/HandlerMapperTest.java b/tomcat/src/test/java/nextstep/org/apache/coyote/http11/HandlerMapperTest.java deleted file mode 100644 index dfda50e0af..0000000000 --- a/tomcat/src/test/java/nextstep/org/apache/coyote/http11/HandlerMapperTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package nextstep.org.apache.coyote.http11; - -import static org.junit.jupiter.api.Assertions.*; - -import nextstep.jwp.controller.LoginController; -import org.junit.jupiter.api.Test; - -class HandlerMapperTest { - - private HandlerMapper handlerMapper = new HandlerMapper(); - - @Test - void mapHandlerByUrlTest() { - // given - String requestedUrl = "/login"; - - // when - LoginController loginController = (LoginController) handlerMapper.mapHandler(requestedUrl); - - // then - assertNotNull(loginController); - } -} diff --git a/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java b/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java index 05dda79a10..75226c6600 100644 --- a/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java +++ b/tomcat/src/test/java/nextstep/org/apache/coyote/http11/Http11ProcessorTest.java @@ -11,6 +11,9 @@ import java.util.List; import java.util.Map; import nextstep.jwp.model.User; +import nextstep.org.apache.catalina.Context; +import nextstep.org.apache.catalina.cookie.Cookies; +import nextstep.org.apache.catalina.session.SessionManager; import org.junit.jupiter.api.Test; import support.StubSocket; @@ -20,7 +23,8 @@ class Http11ProcessorTest { void process() { // given final var socket = new StubSocket(); - final var processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -47,7 +51,8 @@ void index() throws IOException { ""); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -77,7 +82,8 @@ void css() throws IOException { ""); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -105,7 +111,8 @@ void login() throws IOException { ""); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -133,7 +140,8 @@ void register() throws IOException { ""); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -163,7 +171,8 @@ void loginSuccessTest() { requestBody); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -189,7 +198,8 @@ void loginFailTest() { requestBody); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -217,7 +227,8 @@ void registerSuccessTest() { requestBody); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -243,7 +254,8 @@ void sessionTest() { requestBody); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -261,9 +273,9 @@ void sessionTest() { requestHeaders.put(splitedLine[0], splitedLine[1].strip()); } - HttpCookie httpCookie = new HttpCookie(); - httpCookie.parseCookieHeaders(requestHeaders.get("Set-Cookie")); - String jSessionId = httpCookie.get("JSESSIONID"); + Cookies cookies = new Cookies(); + cookies.parseCookieHeaders(requestHeaders.get("Set-Cookie")); + String jSessionId = cookies.get("JSESSIONID"); User user = (User) sessionManager.findSession(jSessionId).getAttribute("gugu"); assertThat(user).isNotNull(); @@ -284,7 +296,8 @@ void loginRedirectionWithSessionTest() { // when final var socket = new StubSocket(httpGetRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); processor.process(socket); @@ -307,7 +320,8 @@ void wrongPathReturnNotFoundPageTest() throws IOException { ""); final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var context = new Context(); + final var processor = new Http11Processor(context, socket); // when processor.process(socket); @@ -335,7 +349,8 @@ private String loginAndGetSessionId() { requestBody); var preSocket = new StubSocket(httpRequest); - Http11Processor preProcessor = new Http11Processor(preSocket); + final var context = new Context(); + final var preProcessor = new Http11Processor(context, preSocket); preProcessor.process(preSocket); String preOutput = preSocket.output(); @@ -349,8 +364,8 @@ private String loginAndGetSessionId() { requestHeaders.put(splitedLine[0], splitedLine[1].strip()); } - HttpCookie httpCookie = new HttpCookie(); - httpCookie.parseCookieHeaders(requestHeaders.get("Set-Cookie")); - return httpCookie.get("JSESSIONID"); + Cookies cookies = new Cookies(); + cookies.parseCookieHeaders(requestHeaders.get("Set-Cookie")); + return cookies.get("JSESSIONID"); } }