diff --git a/study/src/main/resources/application.yml b/study/src/main/resources/application.yml index 4e8655a962..8ec8db7af2 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: 100 + max-connections: 250 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..b5310d1a59 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() { + synchronized public 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/stage2/AppTest.java b/study/src/test/java/thread/stage2/AppTest.java index e253c4a249..5920d7e02e 100644 --- a/study/src/test/java/thread/stage2/AppTest.java +++ b/study/src/test/java/thread/stage2/AppTest.java @@ -23,7 +23,7 @@ class AppTest { */ @Test void test() throws Exception { - final var NUMBER_OF_THREAD = 10; + final var NUMBER_OF_THREAD = 250; var threads = new Thread[NUMBER_OF_THREAD]; for (int i = 0; i < NUMBER_OF_THREAD; i++) { diff --git a/tomcat/src/main/java/nextstep/jwp/config/LoginFilter.java b/tomcat/src/main/java/nextstep/jwp/config/LoginFilter.java new file mode 100644 index 0000000000..bb1d1e8ec9 --- /dev/null +++ b/tomcat/src/main/java/nextstep/jwp/config/LoginFilter.java @@ -0,0 +1,43 @@ +package nextstep.jwp.config; + +import java.util.Map; +import nextstep.jwp.db.InMemorySession; +import org.apache.coyote.http11.filter.Filter; +import org.apache.coyote.http11.filter.FilterChain; +import org.apache.coyote.http11.request.Request; +import org.apache.coyote.http11.response.HttpStatus; +import org.apache.coyote.http11.response.Response; +import org.apache.coyote.http11.util.Resource; + +public class LoginFilter implements Filter { + + @Override + public void doFilter(Request request, Response response, FilterChain filterChain) { + + final String uri = request.getPath(); + final Map cookie = request.getCookie(); + + if (uri.equals("/login") && cookie.containsKey("JSESSIONID")) { + validKey(cookie.get("JSESSIONID"), response); + } + filterChain.doFilter(request, response); + } + + private void validKey(String jSessionId, Response response) { + if (InMemorySession.isLogin(jSessionId)) { + response + .setStatus(HttpStatus.FOUND) + .setContentType("html") + .setLocation("/index.html") + .setResponseBody(Resource.getFile("index.html")) + .setFiltered(true); + return; + } + response + .setStatus(HttpStatus.UNAUTHORIZED) + .setContentType("html") + .setResponseBody(Resource.getFile("401.html")) + .setLocation("/401.html") + .setFiltered(true); + } +} diff --git a/tomcat/src/main/java/nextstep/jwp/config/RegisterFilter.java b/tomcat/src/main/java/nextstep/jwp/config/RegisterFilter.java new file mode 100644 index 0000000000..082b743f86 --- /dev/null +++ b/tomcat/src/main/java/nextstep/jwp/config/RegisterFilter.java @@ -0,0 +1,43 @@ +package nextstep.jwp.config; + +import java.util.Map; +import nextstep.jwp.db.InMemorySession; +import org.apache.coyote.http11.filter.Filter; +import org.apache.coyote.http11.filter.FilterChain; +import org.apache.coyote.http11.request.Request; +import org.apache.coyote.http11.response.HttpStatus; +import org.apache.coyote.http11.response.Response; +import org.apache.coyote.http11.util.Resource; + +public class RegisterFilter implements Filter { + + @Override + public void doFilter(Request request, Response response, FilterChain filterChain) { + + final String uri = request.getPath(); + final Map cookie = request.getCookie(); + + if ((uri.equals("/register")) && cookie.containsKey("JSESSIONID")) { + validKey(cookie.get("JSESSIONID"), response); + } + filterChain.doFilter(request, response); + } + + private void validKey(String jSessionId, Response response) { + if (InMemorySession.isLogin(jSessionId)) { + response + .setStatus(HttpStatus.FOUND) + .setContentType("html") + .setLocation("/index.html") + .setResponseBody(Resource.getFile("index.html")) + .setFiltered(true); + return; + } + response + .setStatus(HttpStatus.UNAUTHORIZED) + .setContentType("html") + .setResponseBody(Resource.getFile("401.html")) + .setLocation("/401.html") + .setFiltered(true); + } +} diff --git a/tomcat/src/main/java/nextstep/jwp/controller/HelloWorldController.java b/tomcat/src/main/java/nextstep/jwp/controller/HelloWorldController.java new file mode 100644 index 0000000000..0869d2e154 --- /dev/null +++ b/tomcat/src/main/java/nextstep/jwp/controller/HelloWorldController.java @@ -0,0 +1,16 @@ +package nextstep.jwp.controller; + +import org.apache.coyote.http11.controller.AbstractController; +import org.apache.coyote.http11.request.Request; +import org.apache.coyote.http11.response.HttpStatus; +import org.apache.coyote.http11.response.Response; + +public class HelloWorldController extends AbstractController { + + @Override + protected void doGet(Request request, Response response) { + response.setStatus(HttpStatus.OK) + .setContentType("html") + .setResponseBody("Hello world!"); + } +} diff --git a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java index 9d85b50561..542f6f55e5 100644 --- a/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java +++ b/tomcat/src/main/java/nextstep/jwp/controller/LoginController.java @@ -1,68 +1,43 @@ package nextstep.jwp.controller; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; import java.util.HashMap; import java.util.Map; import nextstep.jwp.db.InMemorySession; import nextstep.jwp.db.InMemoryUserRepository; import nextstep.jwp.exception.UnauthorizedException; -import nextstep.jwp.model.AuthUser; import nextstep.jwp.model.User; +import org.apache.coyote.http11.controller.AbstractController; import org.apache.coyote.http11.request.Request; import org.apache.coyote.http11.response.HttpStatus; import org.apache.coyote.http11.response.Response; -import org.apache.coyote.http11.servlet.Servlet; +import org.apache.coyote.http11.util.Resource; -public class LoginController { +public class LoginController extends AbstractController { - public static Response login(Request request){ + @Override + protected void doPost(Request request, Response response) { Map body = request.getBody(); - AuthUser authUser = AuthUser.from(body); - User user = InMemoryUserRepository.findByAccount(authUser.getAccount()).orElseThrow(()->new UnauthorizedException("해당 유저가 없습니다.")); - if(!user.checkPassword(authUser.getPassword())){ + User user = InMemoryUserRepository.findByAccount(body.get("account")) + .orElseThrow(() -> new UnauthorizedException("해당 유저가 없습니다.")); + if (!user.checkPassword(body.get("password"))) { throw new UnauthorizedException("아이디 및 패스워드가 틀렸습니다."); } String jSessionId = InMemorySession.login(user); - Map cookie = new HashMap<>(); - if(!request.getCookie().containsKey("JSESSIONID")){ - cookie.put("JSESSIONID",jSessionId); + Map cookie = new HashMap<>(); + if (!request.getCookie().containsKey("JSESSIONID")) { + cookie.put("JSESSIONID", jSessionId); } - return Response.builder() - .status(HttpStatus.FOUND) - .contentType("html") - .cookie(cookie) - .location("index.html") - .responseBody(getFile("index.html")) - .build(); + response.setStatus(HttpStatus.FOUND) + .setContentType("html") + .setCookie(cookie) + .setLocation("index.html") + .setResponseBody(Resource.getFile("index.html")); } - public static Response signUp(Request request){ - Map requestBody = request.getBody(); - final String account = requestBody.get("account"); - final String password = requestBody.get("password"); - final String email = requestBody.get("email"); - User user = new User(account,password,email); - InMemoryUserRepository.save(user); - return Response.builder() - .status(HttpStatus.FOUND) - .contentType("html") - .location("index.html") - .responseBody(getFile("index.html")) - .build(); - } - - - private static String getFile(String fileName){ - try { - final var fileUrl = Servlet.class.getClassLoader().getResource("static/" + fileName); - final var fileBytes = Files.readAllBytes(new File(fileUrl.getFile()).toPath()); - return new String(fileBytes); - } catch (IOException e) { - throw new RuntimeException(e); - } catch (NullPointerException e){ - return ""; - } + @Override + protected void doGet(Request request, Response response) { + response.setStatus(HttpStatus.OK) + .setContentType("html") + .setResponseBody(Resource.getFile("login.html")); } } diff --git a/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java b/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java new file mode 100644 index 0000000000..9088b3811c --- /dev/null +++ b/tomcat/src/main/java/nextstep/jwp/controller/RegisterController.java @@ -0,0 +1,34 @@ +package nextstep.jwp.controller; + +import java.util.Map; +import nextstep.jwp.db.InMemoryUserRepository; +import nextstep.jwp.model.User; +import org.apache.coyote.http11.controller.AbstractController; +import org.apache.coyote.http11.request.Request; +import org.apache.coyote.http11.response.HttpStatus; +import org.apache.coyote.http11.response.Response; +import org.apache.coyote.http11.util.Resource; + +public class RegisterController extends AbstractController { + + @Override + public void doPost(Request request, Response response) { + Map requestBody = request.getBody(); + final String account = requestBody.get("account"); + final String password = requestBody.get("password"); + final String email = requestBody.get("email"); + User user = new User(account, password, email); + InMemoryUserRepository.save(user); + response.setStatus(HttpStatus.FOUND) + .setContentType("html") + .setLocation("index.html") + .setResponseBody(Resource.getFile("index.html")); + } + + @Override + protected void doGet(Request request, Response response) { + response.setStatus(HttpStatus.OK) + .setContentType("html") + .setResponseBody(Resource.getFile("register.html")); + } +} diff --git a/tomcat/src/main/java/nextstep/jwp/controller/ViewController.java b/tomcat/src/main/java/nextstep/jwp/controller/ViewController.java index 71a8b62ee2..7554d1e4fd 100644 --- a/tomcat/src/main/java/nextstep/jwp/controller/ViewController.java +++ b/tomcat/src/main/java/nextstep/jwp/controller/ViewController.java @@ -1,48 +1,20 @@ package nextstep.jwp.controller; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; +import org.apache.coyote.http11.controller.AbstractController; import org.apache.coyote.http11.request.Request; import org.apache.coyote.http11.response.HttpStatus; import org.apache.coyote.http11.response.Response; -import org.apache.coyote.http11.servlet.Servlet; +import org.apache.coyote.http11.util.Resource; -public class ViewController { - public static Response getLogin(Request request){ - return Response.builder() - .status(HttpStatus.OK) - .contentType("html") - .responseBody(getFile("login.html")) - .build(); - } - - public static Response getRegister(Request request){ - return Response.builder() - .status(HttpStatus.OK) - .contentType("html") - .responseBody(getFile("register.html")) - .build(); - } +public class ViewController extends AbstractController { - public static Response getVoid(Request request){ - return Response.builder() - .status(HttpStatus.OK) - .responseBody("Hello world!") - .contentType("html") - .build(); - } + @Override + protected void doGet(Request request, Response response) { + String fileName = request.getPath(); - private static String getFile(String fileName){ - try { - final var fileUrl = Servlet.class.getClassLoader().getResource("static/" + fileName); - final var fileBytes = Files.readAllBytes(new File(fileUrl.getFile()).toPath()); - return new String(fileBytes); - } catch (IOException e) { - throw new RuntimeException(e); - } catch (NullPointerException e){ - return ""; - } + response.setStatus(HttpStatus.OK) + .setContentType(fileName.split("\\.")[1]) + .setResponseBody(Resource.getFile(fileName)); } } diff --git a/tomcat/src/main/java/nextstep/jwp/db/InMemorySession.java b/tomcat/src/main/java/nextstep/jwp/db/InMemorySession.java index bdc123ee4b..c28d71c880 100644 --- a/tomcat/src/main/java/nextstep/jwp/db/InMemorySession.java +++ b/tomcat/src/main/java/nextstep/jwp/db/InMemorySession.java @@ -3,20 +3,23 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; -import nextstep.jwp.exception.UnauthorizedException; import nextstep.jwp.model.User; public class InMemorySession { - private static final Map session = new HashMap<>(); - public static String login(User user){ + private static final Map session = new HashMap<>(); + + private InMemorySession(){} + + public static String login(User user) { UUID uuid = UUID.randomUUID(); - session.put(user,uuid); + session.put(uuid, user); return uuid.toString(); } - public static boolean isLogin(String id){ - for(UUID uuid: session.values()){ - if(uuid.toString().equals(id)){ + + public static boolean isLogin(String id) { + for (UUID uuid : session.keySet()) { + if (uuid.toString().equals(id)) { return true; } } diff --git a/tomcat/src/main/java/nextstep/jwp/db/InMemoryUserRepository.java b/tomcat/src/main/java/nextstep/jwp/db/InMemoryUserRepository.java index ab74f1b7f3..6df4794d79 100644 --- a/tomcat/src/main/java/nextstep/jwp/db/InMemoryUserRepository.java +++ b/tomcat/src/main/java/nextstep/jwp/db/InMemoryUserRepository.java @@ -1,21 +1,24 @@ package nextstep.jwp.db; -import java.util.concurrent.atomic.AtomicLong; -import nextstep.jwp.model.User; - import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; +import nextstep.jwp.model.User; public class InMemoryUserRepository { private static final Map database = new ConcurrentHashMap<>(); private static final AtomicLong autoIncrementId = new AtomicLong(1L); + static { final User user = new User(autoIncrementId.get(), "gugu", "password", "hkkang@woowahan.com"); database.put(user.getAccount(), user); } + private InMemoryUserRepository() { + } + public static void save(User user) { database.put(user.getAccount(), user.putId(autoIncrementId.addAndGet(1L))); } @@ -23,6 +26,4 @@ public static void save(User user) { public static Optional findByAccount(String account) { return Optional.ofNullable(database.get(account)); } - - private InMemoryUserRepository() {} } diff --git a/tomcat/src/main/java/nextstep/jwp/exception/UnauthorizedException.java b/tomcat/src/main/java/nextstep/jwp/exception/UnauthorizedException.java index 0d5de46eaf..dc77d38a2b 100644 --- a/tomcat/src/main/java/nextstep/jwp/exception/UnauthorizedException.java +++ b/tomcat/src/main/java/nextstep/jwp/exception/UnauthorizedException.java @@ -1,6 +1,7 @@ package nextstep.jwp.exception; -public class UnauthorizedException extends RuntimeException{ +public class UnauthorizedException extends RuntimeException { + public UnauthorizedException(String message) { super(message); } diff --git a/tomcat/src/main/java/nextstep/jwp/model/AuthUser.java b/tomcat/src/main/java/nextstep/jwp/model/AuthUser.java deleted file mode 100644 index bc9ec9a0c5..0000000000 --- a/tomcat/src/main/java/nextstep/jwp/model/AuthUser.java +++ /dev/null @@ -1,25 +0,0 @@ -package nextstep.jwp.model; - -import java.util.Map; - -public class AuthUser { - private final String account; - private final String password; - - public AuthUser(String account, String password) { - this.account = account; - this.password = password; - } - - public static AuthUser from(Map query){ - return new AuthUser(query.get("account"),query.get("password")); - } - - public String getAccount() { - return account; - } - - public String getPassword() { - return password; - } -} diff --git a/tomcat/src/main/java/nextstep/jwp/model/User.java b/tomcat/src/main/java/nextstep/jwp/model/User.java index 5319b41ebd..df12abd852 100644 --- a/tomcat/src/main/java/nextstep/jwp/model/User.java +++ b/tomcat/src/main/java/nextstep/jwp/model/User.java @@ -28,8 +28,8 @@ public String getAccount() { return account; } - public User putId(Long id){ - return new User(id,account,password,email); + public User putId(Long id) { + return new User(id, account, password, email); } 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..8e0398a8c1 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,14 @@ 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.Executors; +import org.apache.coyote.http11.Http11Processor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Connector implements Runnable { @@ -17,14 +18,16 @@ public class Connector implements Runnable { private static final int DEFAULT_ACCEPT_COUNT = 100; private final ServerSocket serverSocket; + private final ExecutorService executor; private boolean stopped; - public Connector() { - this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT); + public Connector(final int maxThreads) { + this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, maxThreads); } - public Connector(final int port, final int acceptCount) { + private Connector(final int port, final int acceptCount, final int maxThreads) { this.serverSocket = createServerSocket(port, acceptCount); + this.executor = Executors.newFixedThreadPool(maxThreads); this.stopped = false; } @@ -67,7 +70,7 @@ private void process(final Socket connection) { return; } var processor = new Http11Processor(connection); - new Thread(processor).start(); + executor.execute(processor); } public void stop() { diff --git a/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java b/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java index 205159e95b..65b145d414 100644 --- a/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java +++ b/tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java @@ -1,17 +1,16 @@ package org.apache.catalina.startup; +import java.io.IOException; import org.apache.catalina.connector.Connector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; - public class Tomcat { private static final Logger log = LoggerFactory.getLogger(Tomcat.class); public void start() { - var connector = new Connector(); + var connector = new Connector(250); connector.start(); try { 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 ae6144255f..3ee934dea7 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -1,15 +1,20 @@ package org.apache.coyote.http11; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.Socket; +import nextstep.jwp.config.LoginFilter; +import nextstep.jwp.config.RegisterFilter; import nextstep.jwp.exception.UncheckedServletException; import org.apache.coyote.Processor; +import org.apache.coyote.http11.filter.FilterChainManager; import org.apache.coyote.http11.request.Request; +import org.apache.coyote.http11.response.Response; import org.apache.coyote.http11.servlet.Servlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.net.Socket; - public class Http11Processor implements Runnable, Processor { private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); @@ -30,12 +35,22 @@ public void run() { public void process(final Socket connection) { try (final var inputStream = connection.getInputStream(); final var outputStream = connection.getOutputStream(); + final var bufferedReader = new BufferedReader(new InputStreamReader(inputStream)) ) { - final Request request = Request.from(inputStream); - String response = Servlet.getResponse(request); + final Request request = Request.from(bufferedReader); + Response response = new Response(); + + FilterChainManager filterChainManager = FilterChainManager.getInstance(); + filterChainManager.add(new LoginFilter()); + filterChainManager.add(new RegisterFilter()); + filterChainManager.getInitialChain().doFilter(request, response); + + if (!response.isFiltered()) { + Servlet.getResponse(request, response); + } - outputStream.write(response.getBytes()); + outputStream.write(response.getResponse()); outputStream.flush(); } catch (IOException | UncheckedServletException e) { log.error(e.getMessage(), e); 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..9e77a85f79 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/AbstractController.java @@ -0,0 +1,26 @@ +package org.apache.coyote.http11.controller; + +import org.apache.coyote.http11.exception.NoSuchApiException; +import org.apache.coyote.http11.request.HttpMethod; +import org.apache.coyote.http11.request.Request; +import org.apache.coyote.http11.response.Response; + +public abstract class AbstractController implements Controller { + + @Override + public void service(Request request, Response response) { + if (request.getMethod().equals(HttpMethod.POST)) { + doPost(request, response); + return; + } + doGet(request, response); + } + + protected void doPost(Request request, Response response) { + throw new NoSuchApiException(); + } + + protected void doGet(Request request, Response response) { + throw new NoSuchApiException(); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/controller/Controller.java b/tomcat/src/main/java/org/apache/coyote/http11/controller/Controller.java new file mode 100644 index 0000000000..e485e78939 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/controller/Controller.java @@ -0,0 +1,8 @@ +package org.apache.coyote.http11.controller; + +import org.apache.coyote.http11.request.Request; +import org.apache.coyote.http11.response.Response; + +public interface Controller { + void service(Request request, Response response); +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/exception/NoSuchApiException.java b/tomcat/src/main/java/org/apache/coyote/http11/exception/NoSuchApiException.java index 2b44d881d2..d69a84d99a 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/exception/NoSuchApiException.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/exception/NoSuchApiException.java @@ -1,7 +1,7 @@ package org.apache.coyote.http11.exception; -public class NoSuchApiException extends RuntimeException{ - public NoSuchApiException(){ +public class NoSuchApiException extends RuntimeException { + public NoSuchApiException() { super(); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/filter/DefaultFilter.java b/tomcat/src/main/java/org/apache/coyote/http11/filter/DefaultFilter.java new file mode 100644 index 0000000000..030da5e4fa --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/filter/DefaultFilter.java @@ -0,0 +1,17 @@ +package org.apache.coyote.http11.filter; + +import org.apache.coyote.http11.Http11Processor; +import org.apache.coyote.http11.request.Request; +import org.apache.coyote.http11.response.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DefaultFilter implements Filter { + + private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); + + @Override + public void doFilter(Request request, Response response, FilterChain filterChain) { + log.info("Non filtered by filter chain"); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/filter/Filter.java b/tomcat/src/main/java/org/apache/coyote/http11/filter/Filter.java new file mode 100644 index 0000000000..75d3c1bd08 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/filter/Filter.java @@ -0,0 +1,9 @@ +package org.apache.coyote.http11.filter; + +import org.apache.coyote.http11.request.Request; +import org.apache.coyote.http11.response.Response; + +public interface Filter { + + void doFilter(Request request, Response response, FilterChain filterChain); +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/filter/FilterChain.java b/tomcat/src/main/java/org/apache/coyote/http11/filter/FilterChain.java new file mode 100644 index 0000000000..4cb8adde58 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/filter/FilterChain.java @@ -0,0 +1,9 @@ +package org.apache.coyote.http11.filter; + +import org.apache.coyote.http11.request.Request; +import org.apache.coyote.http11.response.Response; + +public interface FilterChain { + + void doFilter(Request request, Response response); +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/filter/FilterChainManager.java b/tomcat/src/main/java/org/apache/coyote/http11/filter/FilterChainManager.java new file mode 100644 index 0000000000..c8ee133a1a --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/filter/FilterChainManager.java @@ -0,0 +1,66 @@ +package org.apache.coyote.http11.filter; + +import org.apache.coyote.http11.request.Request; +import org.apache.coyote.http11.response.Response; + +public class FilterChainManager { + + private static FilterChainManager instance; + + private final Chain defaultChain; + private Chain initialChain; + private Chain lastChain; + + + public static FilterChainManager getInstance(){ + if(instance == null){ + synchronized (FilterChainManager.class){ + instance = new FilterChainManager(); + } + } + return instance; + } + + private FilterChainManager() { + this.defaultChain = new Chain(new DefaultFilter()); + initialChain = lastChain = defaultChain; + } + + public void add(Filter filter) { + Chain chain = new Chain(filter); + if(initialChain.equals(defaultChain)){ + initialChain = chain; + initialChain.next = defaultChain; + return; + } + if(lastChain.equals(defaultChain)){ + initialChain.next = chain; + lastChain = chain; + lastChain.next = defaultChain; + return; + } + lastChain.next = chain; + lastChain = chain; + lastChain.next = defaultChain; + } + + public FilterChain getInitialChain() { + return initialChain; + } + + private class Chain implements FilterChain { + private final Filter filter; + private Chain next; + + public Chain(Filter filter) { + this.filter = filter; + } + + @Override + public void doFilter(Request request, Response response) { + if(!response.isFiltered()){ + filter.doFilter(request, response, next); + } + } + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/ApiHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/ApiHandler.java deleted file mode 100644 index 9bfdd114c2..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/ApiHandler.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.apache.coyote.http11.handler; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; -import nextstep.jwp.controller.LoginController; -import nextstep.jwp.controller.ViewController; -import org.apache.coyote.http11.request.Request; -import org.apache.coyote.http11.response.Response; - -public class ApiHandler extends Handler{ - private Handler next; - private static final List apiList = new ArrayList<>(); - - static{ - addApi("/", ViewController::getVoid,"Hello world!",false,false); - addApi("/login", ViewController::getLogin,"login.html",false,false); - addApi("/register", ViewController::getRegister,"register.html",false,false); - addApi("/login", LoginController::login,"index.html",false,true); - addApi("/register", LoginController::signUp,"index.html",false, true); - } - - @Override - void setNext(Handler handler) { - this.next = handler; - } - - @Override - String getResponse(Request request) { - for(API api : apiList){ - boolean isHandle = api.isHandle(request); - if(isHandle){ - Response response = api.controller.apply(request); - return response.getResponse(); - } - } - return next.getResponse(request); - } - - public static void addApi( - String api, - Function controller, - String file, - boolean isQueryHandle, - boolean isBodyHandle - ){ - apiList.add(new API(api,controller,file,isQueryHandle,isBodyHandle)); - } - - private static class API{ - private final String uri; - private final Function controller; - private final String file; - private final boolean handleQuery; - private final boolean handleBody; - - public API(String uri, Function controller, String file, boolean handleQuery, - boolean handleBody) { - this.uri = uri; - this.controller = controller; - this.file = file; - this.handleQuery = handleQuery; - this.handleBody = handleBody; - } - - private boolean isHandle(Request request){ - String api = request.getUri().split("\\?")[0]; - boolean isQuery = !request.getQuery().isEmpty(); - boolean isBody = !request.getBody().isEmpty(); - return this.uri.equals(api) && handleBody == isBody && handleQuery == isQuery; - } - } -} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/Configurer.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/Configurer.java deleted file mode 100644 index 851857153e..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/Configurer.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.apache.coyote.http11.handler; - -import org.apache.coyote.http11.request.Request; - -public class Configurer { - private static final Handler loginHandler; - private static final Handler staticHandler; - private static final Handler apiHandler; - private static final Handler failedHandler; - - static{ - loginHandler = new LoginHandler(); - staticHandler = new StaticHandler(); - apiHandler = new ApiHandler(); - failedHandler = new FailedHandler(); - - loginHandler.setNext(staticHandler); - staticHandler.setNext(apiHandler); - apiHandler.setNext(failedHandler); - } - - public static String handle(Request request){ - return loginHandler.getResponse(request); - } -} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/FailedHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/FailedHandler.java deleted file mode 100644 index 75f543d718..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/FailedHandler.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.apache.coyote.http11.handler; - -import org.apache.coyote.http11.exception.NoSuchApiException; -import org.apache.coyote.http11.request.Request; - -public class FailedHandler extends Handler{ - @Override - public void setNext(Handler handler) { - } - - @Override - public String getResponse(Request request) { - throw new NoSuchApiException(); - } -} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/Handler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/Handler.java deleted file mode 100644 index e47844aa80..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/Handler.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.apache.coyote.http11.handler; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import org.apache.coyote.http11.request.Request; - -public abstract class Handler { - abstract void setNext(Handler handler); - abstract String getResponse(Request request); -} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/HandlerAdapter.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/HandlerAdapter.java new file mode 100644 index 0000000000..e42358871a --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/HandlerAdapter.java @@ -0,0 +1,56 @@ +package org.apache.coyote.http11.handler; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import nextstep.jwp.controller.HelloWorldController; +import nextstep.jwp.controller.LoginController; +import nextstep.jwp.controller.RegisterController; +import nextstep.jwp.controller.ViewController; +import org.apache.coyote.http11.controller.Controller; +import org.apache.coyote.http11.exception.NoSuchApiException; +import org.apache.coyote.http11.request.HttpMethod; +import org.apache.coyote.http11.request.Request; + +public class HandlerAdapter { + + private static HandlerAdapter instance; + + private static final Map controllers = new HashMap<>(); + + private HandlerAdapter(){} + + public static HandlerAdapter getInstance(){ + if(instance==null){ + synchronized (HandlerAdapter.class){ + instance = new HandlerAdapter(); + } + } + return instance; + } + + static { + addController(HttpMethod.POST, "/login", new LoginController()); + addController(HttpMethod.POST, "/register", new RegisterController()); + addController(HttpMethod.GET, "/login", new LoginController()); + addController(HttpMethod.GET, "/register", new RegisterController()); + addController(HttpMethod.GET, "/", new HelloWorldController()); + } + + public Controller mapping(Request request) { + MethodPath requestInfo + = new MethodPath(request.getMethod(), request.getPath()); + if (request.getPath().contains(".")) { + return new ViewController(); + } + return controllers.entrySet().stream() + .filter(entry -> entry.getKey().equals(requestInfo)) + .findFirst() + .map(Entry::getValue) + .orElseThrow(NoSuchApiException::new); + } + + private static void addController(HttpMethod httpMethod, String path, Controller controller) { + controllers.put(new MethodPath(httpMethod, path), controller); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/LoginHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/LoginHandler.java deleted file mode 100644 index 877943c557..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/LoginHandler.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.apache.coyote.http11.handler; - -import nextstep.jwp.db.InMemorySession; -import nextstep.jwp.exception.UnauthorizedException; -import org.apache.coyote.http11.request.Request; -import org.apache.coyote.http11.response.HttpStatus; -import org.apache.coyote.http11.response.Response; -import org.apache.coyote.http11.util.Resource; - -public class LoginHandler extends Handler{ - private Handler next; - - @Override - public void setNext(Handler handler) { - this.next = handler; - } - - @Override - public String getResponse(Request request) { - final String uri = request.getUri(); - final var cookie = request.getCookie(); - if(uri.equals("/login") && cookie.containsKey("JSESSIONID")){ - String jSessionId = cookie.get("JSESSIONID"); - return validKey(jSessionId); - } - return next.getResponse(request); - } - - private String validKey(String jSessionId){ - if(InMemorySession.isLogin(jSessionId)){ - return Response.builder() - .status(HttpStatus.OK) - .contentType("html") - .responseBody(Resource.getFile("index.html")) - .build().getResponse(); - } - throw new UnauthorizedException("인가되지 않은 코드입니다."); - } -} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/MethodPath.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/MethodPath.java new file mode 100644 index 0000000000..5917412536 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/handler/MethodPath.java @@ -0,0 +1,32 @@ +package org.apache.coyote.http11.handler; + +import java.util.Objects; +import org.apache.coyote.http11.request.HttpMethod; + +public class MethodPath { + + private final HttpMethod httpMethod; + private final String path; + + public MethodPath(HttpMethod httpMethod, String path) { + this.httpMethod = httpMethod; + this.path = path; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MethodPath that = (MethodPath) o; + return httpMethod == that.httpMethod && Objects.equals(path, that.path); + } + + @Override + public int hashCode() { + return Objects.hash(httpMethod, path); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/handler/StaticHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/handler/StaticHandler.java deleted file mode 100644 index 4e41aeb99b..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/handler/StaticHandler.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.apache.coyote.http11.handler; - -import org.apache.coyote.http11.request.Request; -import org.apache.coyote.http11.response.HttpStatus; -import org.apache.coyote.http11.response.Response; -import org.apache.coyote.http11.util.Resource; - -public class StaticHandler extends Handler{ - private Handler next; - - @Override - public void setNext(Handler handler) { - this.next = handler; - } - - @Override - public String getResponse(Request request) { - String uri = request.getUri(); - if (!uri.contains("?") && uri.contains(".")) { - String contentType = uri.split("\\.")[1]; - return Response.builder() - .status(HttpStatus.OK) - .contentType(contentType) - .responseBody(Resource.getFile(uri)) - .build().getResponse(); - } - return next.getResponse(request); - } -} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpMethod.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpMethod.java new file mode 100644 index 0000000000..b4c77dff8f --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpMethod.java @@ -0,0 +1,30 @@ +package org.apache.coyote.http11.request; + +import java.util.Arrays; +import org.apache.coyote.http11.exception.NoSuchApiException; + +public enum HttpMethod { + + GET("GET"), + POST("POST"), + PATCH("PATCH"), + PUT("PUT"), + DELETE("DELETE"); + private final String method; + + HttpMethod(String method) { + this.method = method; + } + + public static HttpMethod mapping(String method) { + return Arrays.stream(values()) + .filter(value -> value.method.equals(method)) + .findFirst() + .orElseThrow(NoSuchApiException::new); + } + + @Override + public String toString() { + return method; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/Request.java b/tomcat/src/main/java/org/apache/coyote/http11/request/Request.java index 68b12c8212..4d6e0108e9 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/Request.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/Request.java @@ -2,44 +2,33 @@ import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.io.UncheckedIOException; import java.util.HashMap; import java.util.Map; public class Request { - private final String method; - private final String uri; - private final Map query; - private final Map header; + + private final RequestLine requestLine; + private final Map header; private final Map body; private Request( - String method, - String uri, - Map query, + RequestLine requestLine, Map header, Map body ) { - this.method = method; - this.uri = uri; - this.query = query; + this.requestLine = requestLine; this.header = header; this.body = body; } - public static Request from(InputStream inputStream) throws IOException { - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + public static Request from(BufferedReader bufferedReader) throws IOException { String line = bufferedReader.readLine(); valid(line); - final String[] methodUri = line.split(" "); - final String method = methodUri[0]; - final String uri = methodUri[1]; + final RequestLine requestLine = RequestLine.from(line); final Map header = readHeader(bufferedReader); - final Map query = readQueries(uri); - final Map body = readBody(header,bufferedReader); - return new Request(method, uri, query, header, body); + final Map body = readBody(header, bufferedReader); + return new Request(requestLine, header, body); } private static void valid(String line) { @@ -48,19 +37,6 @@ private static void valid(String line) { } } - private static Map readQueries(String uri){ - if(!uri.contains("?")){ - return new HashMap<>(); - } - final Map queriesMap = new HashMap<>(); - String[] queries = uri.split("\\?")[1].split("&"); - for(String query : queries){ - String[] q = query.split("="); - queriesMap.put(q[0],q[1]); - } - return queriesMap; - } - private static Map readHeader(BufferedReader bufferedReader) throws IOException { final Map header = new HashMap<>(); String line = bufferedReader.readLine(); @@ -72,39 +48,40 @@ private static Map readHeader(BufferedReader bufferedReader) thr return header; } - private static Map readBody(Map header,BufferedReader bufferedReader) throws IOException { + private static Map readBody(Map header, BufferedReader bufferedReader) + throws IOException { final Map body = new HashMap<>(); - if(!header.containsKey("Content-Length")){ + if (!header.containsKey("Content-Length")) { return body; } final int contentLength = Integer.parseInt(header.get("Content-Length")); char[] buffer = new char[contentLength]; - bufferedReader.read(buffer,0,contentLength); + bufferedReader.read(buffer, 0, contentLength); String line = new String(buffer); - for(String entry : line.split("&")){ + for (String entry : line.split("&")) { String[] bodyKeyValue = entry.split("="); - body.put(bodyKeyValue[0],bodyKeyValue[1]); + body.put(bodyKeyValue[0], bodyKeyValue[1]); } return body; } - public String getUri() { - return uri; + public HttpMethod getMethod() { + return requestLine.getMethod(); } - public Map getQuery() { - return query; + public String getPath() { + return requestLine.getPath(); } public Map getCookie() { final Map cookie = new HashMap<>(); - if(!header.containsKey("Cookie")){ + if (!header.containsKey("Cookie")) { return cookie; } final String cookieString = header.get("Cookie"); - for(String entryString : cookieString.split("; ")){ - String[] entry =entryString.split("="); - cookie.put(entry[0],entry[1]); + for (String entryString : cookieString.split("; ")) { + String[] entry = entryString.split("="); + cookie.put(entry[0], entry[1]); } return cookie; } 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..4a600f2953 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestLine.java @@ -0,0 +1,61 @@ +package org.apache.coyote.http11.request; + +import java.util.HashMap; +import java.util.Map; + +public class RequestLine { + + private final HttpMethod method; + private final String path; + private final Map query; + private final String version; + + private RequestLine(HttpMethod method, String path, Map query, String version) { + this.method = method; + this.path = path; + this.query = query; + this.version = version; + } + + public static RequestLine from(String line){ + final String[] methodUri = line.split(" "); + final HttpMethod method = HttpMethod.mapping(methodUri[0]); + final String uri = methodUri[1]; + final Map queries = readQueries(uri); + final String path = readPath(uri); + final String version = methodUri[2]; + return new RequestLine(method, path , queries, version); + } + + private static String readPath(String uri){ + if(uri.contains("?")){ + return uri.split("\\?")[0]; + } + return uri; + } + + private static Map readQueries(String uri) { + if (!uri.contains("?")) { + return Map.of(); + } + final Map queriesMap = new HashMap<>(); + String[] queries = uri.split("\\?")[1].split("&"); + for (String query : queries) { + String[] q = query.split("="); + queriesMap.put(q[0], q[1]); + } + return queriesMap; + } + + public HttpMethod getMethod() { + return method; + } + + public String getPath() { + return path; + } + + public Map getQuery() { + return query; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatus.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatus.java index 329ab01b5c..79fcf53dec 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatus.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatus.java @@ -1,10 +1,11 @@ package org.apache.coyote.http11.response; public enum HttpStatus { - OK(200,"OK"), - FOUND(302,"Found"), - UNAUTHORIZED(401,"Unauthorized"), - NOTFOUND(404,"Not Found"); + + OK(200, "OK"), + FOUND(302, "Found"), + UNAUTHORIZED(401, "Unauthorized"), + NOTFOUND(404, "Not Found"); private final int value; private final String detail; diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/Response.java b/tomcat/src/main/java/org/apache/coyote/http11/response/Response.java index 22e198e232..4ac3b07ebe 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/Response.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/Response.java @@ -4,60 +4,33 @@ import java.util.stream.Collectors; public class Response { - private final HttpStatus status; - private final String contentType; - private final String responseBody; - private final String location; - private final Map cookie; - private Response(Builder builder){ - this.status = builder.status; - this.contentType = builder.contentType; - this.responseBody = builder.responseBody; - this.location = builder.location; - this.cookie = builder.cookie; - } - - public static Builder builder(){ - return new Builder(); - } + private HttpStatus status; + private String contentType; + private String responseBody; + private String location; + private Map cookie; + private boolean filtered; - public static Response badResponse(HttpStatus httpStatus){ - return builder() - .status(httpStatus) - .responseBody("") - .contentType("html") - .location(httpStatus.getValue()+".html") - .build(); - } - - public String getResponse(){ + public byte[] getResponse() { return String.join("\r\n", - "HTTP/1.1 " + status + " ", - "Content-Type: text/" + contentType + ";charset=utf-8 ", - "Content-Length: " + responseBody.getBytes().length + " ", - makeLocation() + makeCookie(), - responseBody); + "HTTP/1.1 " + status + " ", + "Content-Type: text/" + contentType + ";charset=utf-8 ", + "Content-Length: " + responseBody.getBytes().length + " ", + makeLocation() + makeCookie(), + responseBody) + .getBytes(); } - public Response redirect(String file, String location){ - return new Builder() - .status(this.status) - .contentType("html") - .location(location) - .responseBody(file) - .build(); - } - - private String makeLocation(){ - if(location==null){ + private String makeLocation() { + if (location == null) { return ""; } return "location : " + location + "\r\n"; } - private String makeCookie(){ - if(cookie == null){ + private String makeCookie() { + if (cookie == null) { return ""; } return "Set-Cookie :" + cookie.entrySet().stream() @@ -65,40 +38,44 @@ private String makeCookie(){ .collect(Collectors.joining("; ")); } - public static class Builder{ - private HttpStatus status; - private String contentType; - private String responseBody; - private String location; - private Map cookie; + public boolean isFiltered() { + return this.filtered; + } - public Builder status(HttpStatus status){ - this.status = status; - return this; - } + public Response setFiltered(boolean filtered) { + this.filtered = filtered; + return this; + } - public Builder contentType(String contentType){ - this.contentType = contentType; - return this; - } + public Response setStatus(HttpStatus status) { + this.status = status; + return this; + } - public Builder responseBody(String responseBody){ - this.responseBody = responseBody; - return this; - } + public Response setContentType(String contentType) { + this.contentType = contentType; + return this; + } - public Builder location(String location){ - this.location = location; - return this; - } + public Response setResponseBody(String responseBody) { + this.responseBody = responseBody; + return this; + } - public Builder cookie(Map cookie){ - this.cookie = cookie; - return this; - } + public Response setLocation(String location) { + this.location = location; + return this; + } - public Response build(){ - return new Response(this); - } + public Response setCookie(Map cookie) { + this.cookie = cookie; + return this; + } + + public void badResponse(HttpStatus httpStatus, String responseBody, String location) { + this.status = httpStatus; + this.contentType = "html"; + this.responseBody = responseBody; + this.location = location; } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/servlet/Servlet.java b/tomcat/src/main/java/org/apache/coyote/http11/servlet/Servlet.java index a05ebc526e..22eb21543d 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/servlet/Servlet.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/servlet/Servlet.java @@ -1,25 +1,28 @@ package org.apache.coyote.http11.servlet; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; import nextstep.jwp.exception.UnauthorizedException; +import org.apache.coyote.http11.controller.Controller; import org.apache.coyote.http11.exception.NoSuchApiException; -import org.apache.coyote.http11.handler.Configurer; +import org.apache.coyote.http11.handler.HandlerAdapter; import org.apache.coyote.http11.request.Request; import org.apache.coyote.http11.response.HttpStatus; import org.apache.coyote.http11.response.Response; import org.apache.coyote.http11.util.Resource; public class Servlet { - public static String getResponse(Request request){ + + private Servlet() { + } + + public static void getResponse(Request request, Response response) { try { - return Configurer.handle(request); + HandlerAdapter handlerAdapter = HandlerAdapter.getInstance(); + Controller controller = handlerAdapter.mapping(request); + controller.service(request, response); } catch (UnauthorizedException unauthorizedException) { - return Response.badResponse(HttpStatus.UNAUTHORIZED).redirect(Resource.getFile("401.html"),"401.html").getResponse(); - } catch (NoSuchApiException e){ - return Response.badResponse(HttpStatus.NOTFOUND).redirect(Resource.getFile("404.html"),"404.html").getResponse(); + response.badResponse(HttpStatus.UNAUTHORIZED, Resource.getFile("401.html"), "401.html"); + } catch (NoSuchApiException e) { + response.badResponse(HttpStatus.NOTFOUND, Resource.getFile("404.html"), "404.html"); } } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/util/Resource.java b/tomcat/src/main/java/org/apache/coyote/http11/util/Resource.java index 56d1c70971..69fd03e9f2 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/util/Resource.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/util/Resource.java @@ -6,14 +6,18 @@ import org.apache.coyote.http11.servlet.Servlet; public class Resource { - public static String getFile(String fileName){ + + private Resource() { + } + + public static String getFile(String fileName) { try { final var fileUrl = Servlet.class.getClassLoader().getResource("static/" + fileName); final var fileBytes = Files.readAllBytes(new File(fileUrl.getFile()).toPath()); return new String(fileBytes); } catch (IOException e) { throw new RuntimeException(e); - } catch (NullPointerException e){ + } catch (NullPointerException e) { return ""; } } 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 e9e6e97f9c..7159cd2ddc 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 @@ -1,34 +1,33 @@ package nextstep.org.apache.coyote.http11; -import nextstep.jwp.db.InMemorySession; -import nextstep.jwp.model.User; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import support.StubSocket; -import org.apache.coyote.http11.Http11Processor; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; import java.io.File; import java.io.IOException; import java.net.URL; import java.nio.file.Files; - -import static org.assertj.core.api.Assertions.assertThat; +import nextstep.jwp.db.InMemorySession; +import nextstep.jwp.model.User; +import org.apache.coyote.http11.Http11Processor; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import support.StubSocket; class Http11ProcessorTest { - private String getRequest(String method , String path, String body){ + private String getRequest(String method, String path, String body) { int length = body.getBytes().length; - if(length == 0){ + if (length == 0) { return String.join("\r\n", - method + " " + path +" "+"HTTP/1.1 ", + method + " " + path + " " + "HTTP/1.1 ", "Host: localhost:8080 ", "Connection: keep-alive ", "", body); } return String.join("\r\n", - method + " " + path +" "+"HTTP/1.1 ", + method + " " + path + " " + "HTTP/1.1 ", "Host: localhost:8080 ", "Connection: keep-alive ", "Content-Length: " + body.getBytes().length, @@ -36,11 +35,12 @@ private String getRequest(String method , String path, String body){ body); } - private String getResponseHeader(int code, String status , String responseBody){ - return "HTTP/1.1 " + code + " "+ status + " \r\n" + + private String getResponseHeader(int code, String status, String responseBody) { + return "HTTP/1.1 " + code + " " + status + " \r\n" + "Content-Type: text/html;charset=utf-8 \r\n" + "Content-Length: " + responseBody.getBytes().length; } + @Test void process() { // given @@ -51,66 +51,67 @@ void process() { processor.process(socket); // then - var expected = getResponseHeader(200,"OK","Hello world!"); + var expected = getResponseHeader(200, "OK", "Hello world!"); assertThat(socket.output()).contains(expected); assertThat(socket.output()).contains("Hello world!"); } + @DisplayName("페이지 테스트") @Nested - class PageTest{ + class PageTest { @DisplayName("index 페이지에 조회할 수 있다") @Test - void indexPageTest () throws IOException { - // given - final String httpRequest = getRequest("GET","/index.html",""); + void indexPageTest() throws IOException { + // given + final String httpRequest = getRequest("GET", "/index.html", ""); - final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var socket = new StubSocket(httpRequest); + final Http11Processor processor = new Http11Processor(socket); - // when - processor.process(socket); + // when + processor.process(socket); - // then - final URL resource = getClass().getClassLoader().getResource("static/index.html"); - final String responseBody = new String(Files.readAllBytes(new File(resource.getFile()).toPath())); - var expectedHeader = getResponseHeader(200,"OK",responseBody); + // then + final URL resource = getClass().getClassLoader().getResource("static/index.html"); + final String responseBody = new String(Files.readAllBytes(new File(resource.getFile()).toPath())); + var expectedHeader = getResponseHeader(200, "OK", responseBody); - assertThat(socket.output()).contains(expectedHeader); - assertThat(socket.output()).contains(responseBody); + assertThat(socket.output()).contains(expectedHeader); + assertThat(socket.output()).contains(responseBody); } @DisplayName("login 페이지에 조회할 수 있다.") @Test - void loginPageTest () throws IOException { - // given - final String httpRequest = getRequest("GET","/login.html",""); + void loginPageTest() throws IOException { + // given + final String httpRequest = getRequest("GET", "/login.html", ""); - final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var socket = new StubSocket(httpRequest); + final Http11Processor processor = new Http11Processor(socket); - // when - processor.process(socket); + // when + processor.process(socket); - // then - final URL resource = getClass().getClassLoader().getResource("static/login.html"); - final String responseBody = new String(Files.readAllBytes(new File(resource.getFile()).toPath())); - var expectedHeader = getResponseHeader(200,"OK",responseBody); + // then + final URL resource = getClass().getClassLoader().getResource("static/login.html"); + final String responseBody = new String(Files.readAllBytes(new File(resource.getFile()).toPath())); + var expectedHeader = getResponseHeader(200, "OK", responseBody); - assertThat(socket.output()).contains(expectedHeader); - assertThat(socket.output()).contains(responseBody); + assertThat(socket.output()).contains(expectedHeader); + assertThat(socket.output()).contains(responseBody); } - @DisplayName("login 상태에서 페이지에 조회시 메인 화면으로 리다이렉트 된다.") + @DisplayName("login 상태에서 로그인 페이지에 조회시 메인 화면으로 리다이렉트 된다.") @Test - void loginPageTest_redirect () throws IOException { + void loginPageTest_loginRedirect() throws IOException { // given - final String jSessionId = InMemorySession.login(new User("","","")).toString(); - final String httpRequest = String.join("\r\n", + final String jSessionId = InMemorySession.login(new User("", "", "")); + final String httpRequest = String.join("\r\n", "GET /login HTTP/1.1 ", "Host: localhost:8080 ", "Connection: keep-alive ", - "Cookie: JSESSIONID="+ jSessionId, + "Cookie: JSESSIONID=" + jSessionId, ""); final var socket = new StubSocket(httpRequest); @@ -122,38 +123,64 @@ void loginPageTest_redirect () throws IOException { // then final URL resource = getClass().getClassLoader().getResource("static/index.html"); final String responseBody = new String(Files.readAllBytes(new File(resource.getFile()).toPath())); - var expectedHeader = getResponseHeader(200,"OK",responseBody); + var expectedHeader = getResponseHeader(302, "Found", responseBody); assertThat(socket.output()).contains(expectedHeader); assertThat(socket.output()).contains(responseBody); } - @DisplayName("register 페이지에 조회할 수 있다.") + @DisplayName("login 상태에서 회원가입 페이지에 조회시 메인 화면으로 리다이렉트 된다.") @Test - void registerPageTest () throws IOException { - // given - final String httpRequest = getRequest("GET","/register.html",""); + void loginPageTest_registerRedirect() throws IOException { + // given + final String jSessionId = InMemorySession.login(new User("", "", "")); + final String httpRequest = String.join("\r\n", + "GET /register HTTP/1.1 ", + "Host: localhost:8080 ", + "Connection: keep-alive ", + "Cookie: JSESSIONID=" + jSessionId, + ""); - final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); + final var socket = new StubSocket(httpRequest); + final Http11Processor processor = new Http11Processor(socket); - // when - processor.process(socket); + // when + processor.process(socket); - // then - final URL resource = getClass().getClassLoader().getResource("static/register.html"); - final String responseBody = new String(Files.readAllBytes(new File(resource.getFile()).toPath())); - var expectedHeader = getResponseHeader(200,"OK",responseBody); + // then + final URL resource = getClass().getClassLoader().getResource("static/index.html"); + final String responseBody = new String(Files.readAllBytes(new File(resource.getFile()).toPath())); + var expectedHeader = getResponseHeader(302, "Found", responseBody); + + assertThat(socket.output()).contains(expectedHeader); + assertThat(socket.output()).contains(responseBody); + } + + @DisplayName("register 페이지에 조회할 수 있다.") + @Test + void registerPageTest() throws IOException { + // given + final String httpRequest = getRequest("GET", "/register.html", ""); + final var socket = new StubSocket(httpRequest); + final Http11Processor processor = new Http11Processor(socket); + + // when + processor.process(socket); - assertThat(socket.output()).contains(expectedHeader); - assertThat(socket.output()).contains(responseBody); + // then + final URL resource = getClass().getClassLoader().getResource("static/register.html"); + final String responseBody = new String(Files.readAllBytes(new File(resource.getFile()).toPath())); + var expectedHeader = getResponseHeader(200, "OK", responseBody); + + assertThat(socket.output()).contains(expectedHeader); + assertThat(socket.output()).contains(responseBody); } } @DisplayName("로그인 테스트") @Nested - class LoginTest{ + class LoginTest { @DisplayName("로그인을 할 수 있다") @Test void login() throws IOException { @@ -170,7 +197,7 @@ void login() throws IOException { // then final URL resource = getClass().getClassLoader().getResource("static/index.html"); final String responseBody = new String(Files.readAllBytes(new File(resource.getFile()).toPath())); - final String expected = getResponseHeader(302,"Found",responseBody); + final String expected = getResponseHeader(302, "Found", responseBody); final String expectedLocation = "location : index.html"; assertThat(socket.output()).contains(expected); assertThat(socket.output()).contains(responseBody); @@ -193,7 +220,7 @@ void login_Fail() throws IOException { // then final URL resource = getClass().getClassLoader().getResource("static/401.html"); final String responseBody = new String(Files.readAllBytes(new File(resource.getFile()).toPath())); - final String expected = getResponseHeader(401,"Unauthorized",responseBody); + final String expected = getResponseHeader(401, "Unauthorized", responseBody); final String expectedLocation = "location : 401.html"; assertThat(socket.output()).contains(expected); assertThat(socket.output()).contains(responseBody); @@ -203,7 +230,7 @@ void login_Fail() throws IOException { @DisplayName("회원가입 테스트") @Nested - class Register{ + class Register { @DisplayName("회원가입을 할 수 있다") @Test void register() throws IOException { @@ -220,7 +247,7 @@ void register() throws IOException { // then final URL resource = getClass().getClassLoader().getResource("static/index.html"); final String responseBody = new String(Files.readAllBytes(new File(resource.getFile()).toPath())); - final String expected = getResponseHeader(302,"Found",responseBody); + final String expected = getResponseHeader(302, "Found", responseBody); final String expectedLocation = "location : index.html"; assertThat(socket.output()).contains(expected); assertThat(socket.output()).contains(responseBody);