From 56a9141ec0acd168e0be5015ce4e581aae67b4d1 Mon Sep 17 00:00:00 2001 From: lookh <103165859+aiaiaiai1@users.noreply.github.com> Date: Fri, 15 Sep 2023 09:56:21 +0900 Subject: [PATCH] =?UTF-8?q?[=ED=86=B0=EC=BA=A3=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=ED=95=98=EA=B8=B0=203,=204=EB=8B=A8=EA=B3=84]=20=EB=A3=A8?= =?UTF-8?q?=EC=BF=A0(=EB=B0=B1=EA=B2=BD=ED=99=98)=20=EB=AF=B8=EC=85=98=20?= =?UTF-8?q?=EC=A0=9C=EC=B6=9C=ED=95=A9=EB=8B=88=EB=8B=A4.=20(#488)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * train : 학습 테스트 학습 * feat : GET /index.html 응답하기 + CSS 지원하기 * feat : QueryString 파싱 * feat : HTTP Status Code 302 구현 * feat : reqeustHeader 파싱 기능 추가 + POST 방식으로 회원가입 기능 추가 * feat : 로그인 페이지도 버튼을 눌렀을 때 GET 방식에서 POST 방식으로 전송하도록 변경 * feat : 세션, 쿠키, 세션매니저 클래스 구현 * feat : HttpResponse 클래스 구현 * feat : 로그인시 쿠키 설정, 로그인 페이지 접속시 쿠키 헤더 확인 후 응답 처리 추가 * refactor: 불필요한 메서드 삭제 * feat: HttpRequest 클래스 구현 * feat: HttpRequest 클래스 적용 * fix: 컨텐트 타입 text/html로 응답 수정 * fix: favicon.ico 응답 추가 * fix: 로그 삭제 * refactor: 접근 제어자 수정 * refactor: 기존 메서드 활용 * fix: 기존 메서드 내용 구현 * refactor: HttpStatus enum 추가 * refactor: HttpMethod enum 추가 * feat: Controller 인터페이스 추가 * refactor: login, register Controller 도입 * refactor: Request url 매핑 컨트롤러 추가 * test: RequestMapping 테스트 코드 추가 * refactor: RootController 분리 및 메서드 삭제 * test: 테스트코드 추가 * feat: 쓰레드 풀 설정 + 세션 동시성 해결 * feat: 쓰레드 풀 설정 + 세션 동시성 해결 * fix: 로그인 페이지 불러오기 버그 수정 * refactor: 새로운 인스턴스 생기지 않도록 수정 * refactor: 메서드명 수정 * refactor: 필드 이름 수정 * refactor: 개행 제거 * refactor: 방어 로직 추가 --- .../apache/catalina/connector/Connector.java | 18 ++- .../coyote/controller/AbstractController.java | 29 ++++ .../apache/coyote/controller/Controller.java | 9 ++ .../coyote/controller/LoginController.java | 65 ++++++++ .../coyote/controller/RegisterController.java | 49 ++++++ .../coyote/controller/RequestMapping.java | 27 ++++ .../coyote/controller/ResourceController.java | 53 +++++++ .../coyote/controller/RootController.java | 16 ++ .../apache/coyote/http11/Http11Processor.java | 142 ++---------------- .../org/apache/coyote/http11/HttpRequest.java | 20 +-- .../apache/coyote/http11/HttpResponse.java | 14 +- .../apache/coyote/http11/SessionManager.java | 13 +- .../java/org/apache/coyote/http11/Utils.java | 35 +++++ .../controller/RegisterControllerTest.java | 39 +++++ .../coyote/controller/RequestMappingTest.java | 31 ++++ 15 files changed, 406 insertions(+), 154 deletions(-) create mode 100644 tomcat/src/main/java/org/apache/coyote/controller/AbstractController.java create mode 100644 tomcat/src/main/java/org/apache/coyote/controller/Controller.java create mode 100644 tomcat/src/main/java/org/apache/coyote/controller/LoginController.java create mode 100644 tomcat/src/main/java/org/apache/coyote/controller/RegisterController.java create mode 100644 tomcat/src/main/java/org/apache/coyote/controller/RequestMapping.java create mode 100644 tomcat/src/main/java/org/apache/coyote/controller/ResourceController.java create mode 100644 tomcat/src/main/java/org/apache/coyote/controller/RootController.java create mode 100644 tomcat/src/main/java/org/apache/coyote/http11/Utils.java create mode 100644 tomcat/src/test/java/org/apache/coyote/controller/RegisterControllerTest.java create mode 100644 tomcat/src/test/java/org/apache/coyote/controller/RequestMappingTest.java 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..52bbb9e81f 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 { @@ -15,16 +16,19 @@ public class Connector implements Runnable { private static final int DEFAULT_PORT = 8080; private static final int DEFAULT_ACCEPT_COUNT = 100; + private static final int DEFAULT_MAX_THREADS = 10; private final ServerSocket serverSocket; + private final ExecutorService executorService; private boolean stopped; public Connector() { - this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT); + this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, DEFAULT_MAX_THREADS); } - public Connector(final int port, final int acceptCount) { + public Connector(final int port, final int acceptCount, final int maxThreads) { this.serverSocket = createServerSocket(port, acceptCount); + this.executorService = Executors.newFixedThreadPool(maxThreads); this.stopped = false; } @@ -67,7 +71,7 @@ private void process(final Socket connection) { return; } var processor = new Http11Processor(connection); - new Thread(processor).start(); + executorService.execute(processor); } public void stop() { diff --git a/tomcat/src/main/java/org/apache/coyote/controller/AbstractController.java b/tomcat/src/main/java/org/apache/coyote/controller/AbstractController.java new file mode 100644 index 0000000000..ffa3fe9b47 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/controller/AbstractController.java @@ -0,0 +1,29 @@ +package org.apache.coyote.controller; + +import static org.apache.coyote.http11.HttpMethod.GET; +import static org.apache.coyote.http11.HttpMethod.POST; + +import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.HttpResponse; + +public abstract class AbstractController implements Controller { + + @Override + public void service(HttpRequest request, HttpResponse response) throws Exception { + if (request.getMethod() == GET) { + doGet(request, response); + return; + } + + if (request.getMethod() == POST) { + doPost(request, response); + return; + } + + throw new IllegalArgumentException("지원하지 않는 메서드 입니다."); + } + + protected void doPost(HttpRequest request, HttpResponse response) throws Exception { /* NOOP */ } + + protected void doGet(HttpRequest request, HttpResponse response) throws Exception { /* NOOP */ } +} diff --git a/tomcat/src/main/java/org/apache/coyote/controller/Controller.java b/tomcat/src/main/java/org/apache/coyote/controller/Controller.java new file mode 100644 index 0000000000..8ac28270a1 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/controller/Controller.java @@ -0,0 +1,9 @@ +package org.apache.coyote.controller; + + +import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.HttpResponse; + +public interface Controller { + void service(HttpRequest request, HttpResponse response) throws Exception; +} diff --git a/tomcat/src/main/java/org/apache/coyote/controller/LoginController.java b/tomcat/src/main/java/org/apache/coyote/controller/LoginController.java new file mode 100644 index 0000000000..9779ac6cc1 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/controller/LoginController.java @@ -0,0 +1,65 @@ +package org.apache.coyote.controller; + +import static org.apache.coyote.http11.HttpStatus.FOUND; +import static org.apache.coyote.http11.HttpStatus.OK; + +import java.util.Map; +import java.util.UUID; +import nextstep.jwp.db.InMemoryUserRepository; +import nextstep.jwp.model.User; +import org.apache.coyote.http11.HttpCookie; +import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.HttpResponse; +import org.apache.coyote.http11.Session; +import org.apache.coyote.http11.SessionManager; +import org.apache.coyote.http11.Utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LoginController extends AbstractController { + private static final Logger log = LoggerFactory.getLogger(LoginController.class); + private static final SessionManager sessionManager = new SessionManager(); + + @Override + protected void doPost(HttpRequest request, HttpResponse response) throws Exception { + Map queryParms = Utils.parseToQueryParms(request.getBody()); + + try { + User user = InMemoryUserRepository.findByAccount(queryParms.get("account")) + .orElseThrow(() -> new IllegalArgumentException("해당 사용자 없음")); + + if (!user.checkPassword(queryParms.get("password"))) { + throw new IllegalArgumentException("비밀번호 불일치"); + } + log.info("user: {}", user); + + Session session = new Session(UUID.randomUUID().toString()); + session.setAttribute("user", user); + sessionManager.add(session); + response.setStatus(FOUND); + response.setRedirectUrl("/index.html"); + response.setCookie("JSESSIONID=" + session.getId()); + + } catch (IllegalArgumentException e) { + log.error("error : {}", e); + response.setStatus(FOUND); + response.setRedirectUrl("/401.html"); + + } + } + + @Override + protected void doGet(HttpRequest request, HttpResponse response) throws Exception { + HttpCookie cookie = new HttpCookie(request.getHeaders().get("Cookie")); + + String sessionId = cookie.findValue("JSESSIONID"); + if (sessionManager.isExist(sessionId)) { + response.setStatus(FOUND); + response.setRedirectUrl("/index.html"); + } else { + response.setStatus(OK); + response.setContentType("text/html"); + response.setBody(Utils.readFile("static", "login.html")); + } + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/controller/RegisterController.java b/tomcat/src/main/java/org/apache/coyote/controller/RegisterController.java new file mode 100644 index 0000000000..587882291e --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/controller/RegisterController.java @@ -0,0 +1,49 @@ +package org.apache.coyote.controller; + +import static org.apache.coyote.http11.HttpStatus.FOUND; +import static org.apache.coyote.http11.HttpStatus.OK; + +import java.util.Map; +import java.util.Objects; +import nextstep.jwp.db.InMemoryUserRepository; +import nextstep.jwp.model.User; +import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.HttpResponse; +import org.apache.coyote.http11.Utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RegisterController extends AbstractController { + private static final Logger log = LoggerFactory.getLogger(RegisterController.class); + public static final int LOGIN_QUERY_PARAMS = 3; + + @Override + protected void doPost(HttpRequest request, HttpResponse response) { + Map queryParms = Utils.parseToQueryParms(request.getBody()); + + validateQueryParms(queryParms); + User user = new User(queryParms.get("account"), queryParms.get("password"), + queryParms.get("email")); + + InMemoryUserRepository.save(user); + response.setStatus(FOUND); + response.setRedirectUrl("/index.html"); + } + + private void validateQueryParms(Map queryParms) { + long count = queryParms.entrySet().stream() + .filter(entry -> Objects.nonNull(entry.getValue())) + .count(); + if (count != LOGIN_QUERY_PARAMS) { + throw new IllegalArgumentException(); + } + } + + @Override + protected void doGet(HttpRequest request, HttpResponse response) throws Exception { + response.setStatus(OK); + response.setContentType("text/html"); + response.setBody(Utils.readFile("static", "register.html")); + } +} + diff --git a/tomcat/src/main/java/org/apache/coyote/controller/RequestMapping.java b/tomcat/src/main/java/org/apache/coyote/controller/RequestMapping.java new file mode 100644 index 0000000000..444e9cbfe6 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/controller/RequestMapping.java @@ -0,0 +1,27 @@ +package org.apache.coyote.controller; + +import java.util.HashMap; +import java.util.Map; +import org.apache.coyote.http11.HttpRequest; + +public class RequestMapping { + + final Map requestMapping = new HashMap<>(); + + public RequestMapping() { + initiateMapping(); + } + + private void initiateMapping() { + requestMapping.put("/login", new LoginController()); + requestMapping.put("/register", new RegisterController()); + requestMapping.put("/", new RootController()); + requestMapping.put("resource", new ResourceController()); + } + + public Controller getController(HttpRequest request) { + Controller resourceController = requestMapping.get("resource"); + return requestMapping.getOrDefault(request.getPath(), resourceController); + } + +} diff --git a/tomcat/src/main/java/org/apache/coyote/controller/ResourceController.java b/tomcat/src/main/java/org/apache/coyote/controller/ResourceController.java new file mode 100644 index 0000000000..b09a05a165 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/controller/ResourceController.java @@ -0,0 +1,53 @@ +package org.apache.coyote.controller; + +import static org.apache.coyote.http11.HttpStatus.FOUND; +import static org.apache.coyote.http11.HttpStatus.OK; + +import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.HttpResponse; +import org.apache.coyote.http11.Utils; + +public class ResourceController extends AbstractController { + + protected void doGet(HttpRequest request, HttpResponse response) throws Exception { + response.setStatus(OK); + + String path = request.getPath(); + String fileName = path.substring(path.lastIndexOf('/') + 1); + + if (fileName.endsWith(".html")) { + response.setContentType("text/html"); + response.setBody(Utils.readFile("static", fileName)); + return; + } + + if (fileName.equals("styles.css")) { + response.setContentType("text/css"); + response.setBody(Utils.readFile("static/css", fileName)); + return; + } + + if (fileName.endsWith(".js") && !fileName.equals("scripts.js")) { + response.setContentType("text/javascript"); + response.setBody(Utils.readFile("static/assets", fileName)); + return; + } + + if (fileName.equals("scripts.js")) { + response.setContentType("text/javascript"); + response.setBody(Utils.readFile("static/js", fileName)); + return; + } + + if (fileName.equals("favicon.ico")) { + response.setContentType("text/javascript"); + response.setBody("Hello world!"); + return; + } + + response.setStatus(FOUND); + response.setRedirectUrl("404.html"); + } + + +} diff --git a/tomcat/src/main/java/org/apache/coyote/controller/RootController.java b/tomcat/src/main/java/org/apache/coyote/controller/RootController.java new file mode 100644 index 0000000000..4b5d9bc607 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/controller/RootController.java @@ -0,0 +1,16 @@ +package org.apache.coyote.controller; + +import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.HttpResponse; +import org.apache.coyote.http11.HttpStatus; + +public class RootController extends AbstractController { + + @Override + protected void doGet(HttpRequest request, HttpResponse response) throws Exception { + response.setStatus(HttpStatus.OK); + response.setContentType("text/html"); + response.setBody("Hello world!"); + } + +} 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 fee7ec7b4d..2121d3d4af 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -4,16 +4,10 @@ import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import nextstep.jwp.db.InMemoryUserRepository; import nextstep.jwp.exception.UncheckedServletException; -import nextstep.jwp.model.User; import org.apache.coyote.Processor; +import org.apache.coyote.controller.Controller; +import org.apache.coyote.controller.RequestMapping; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,7 +16,7 @@ public class Http11Processor implements Runnable, Processor { private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); private final Socket connection; - private final SessionManager sessionManager = new SessionManager(); + private final RequestMapping requestMapping = new RequestMapping(); public Http11Processor(final Socket connection) { this.connection = connection; @@ -41,134 +35,20 @@ public void process(final Socket connection) { InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); - HttpRequest httpRequest = new HttpRequest(bufferedReader); + HttpRequest request = new HttpRequest(bufferedReader); + HttpResponse response = new HttpResponse(); - String requestMethod = httpRequest.getMethod(); - String requestPath = httpRequest.getPath(); - String requestFileName = httpRequest.getFileName(); - String requestBody = httpRequest.getBody(); - Map headers = httpRequest.getHeaders(); + Controller controller = requestMapping.getController(request); + controller.service(request, response); - String response = null; - HttpResponse httpResponse = new HttpResponse(); - - if (requestMethod.equals(HttpMethod.POST.name()) && requestPath.equals("/login")) { - Map queryParms = parseToQueryParms(requestBody); - - try { - User user = InMemoryUserRepository.findByAccount(queryParms.get("account")) - .orElseThrow(() -> new IllegalArgumentException("해당 사용자 없음")); - - if (!user.checkPassword(queryParms.get("password"))) { - throw new IllegalArgumentException("비밀번호 불일치"); - } - log.info("user: {}", user); - - Session session = new Session(UUID.randomUUID().toString()); - session.setAttribute("user", user); - sessionManager.add(session); - httpResponse.setStatus(HttpStatus.FOUND); - httpResponse.setRedirectUrl("/index.html"); - httpResponse.setCookie("JSESSIONID=" + session.getId()); - - response = httpResponse.createResponse(); - } catch (IllegalArgumentException e) { - log.error("error : {}", e); - response = createRedirectResponse("/401.html"); - } - } - - if (requestMethod.equals(HttpMethod.POST.name()) && requestPath.equals("/register")) { - Map queryParms = parseToQueryParms(requestBody); - - User user = new User(queryParms.get("account"), queryParms.get("password"), - queryParms.get("email")); - InMemoryUserRepository.save(user); - - response = createRedirectResponse("/index.html"); - log.info(response); - } - - if (requestMethod.equals(HttpMethod.GET.name()) && requestPath.equals("/login")) { - HttpCookie cookie = new HttpCookie(headers.get("Cookie")); - - String sessionId = cookie.findValue("JSESSIONID"); - if (sessionManager.isExist(sessionId)) { - response = createRedirectResponse("/index.html"); - } else { - response = createResponse("text/html", readFile("static", "login.html")); - } - } - - if (requestMethod.equals(HttpMethod.GET.name()) && requestPath.equals("/register")) { - response = createResponse("text/html", readFile("static", "register.html")); - } - - if (requestMethod.equals(HttpMethod.GET.name()) && requestPath.equals("/")) { - response = createResponse("text/html", "Hello world!"); - } - - if (requestMethod.equals(HttpMethod.GET.name()) && requestFileName.endsWith(".html")) { - response = createResponse("text/html", readFile("static", requestFileName)); - } - - if (requestMethod.equals(HttpMethod.GET.name()) && requestFileName.equals("styles.css")) { - response = createResponse("text/css", readFile("static/css", requestFileName)); - } - - if (requestMethod.equals(HttpMethod.GET.name()) && requestFileName.endsWith(".js") && !requestFileName.equals( - "scripts.js")) { - response = createResponse("text/javascript", readFile("static/assets", requestFileName)); - } - - if (requestMethod.equals(HttpMethod.GET.name()) && requestFileName.equals("scripts.js")) { - response = createResponse("text/javascript", readFile("static/js", requestFileName)); - } - - if (requestMethod.equals(HttpMethod.GET.name()) && requestFileName.equals("favicon.ico")) { - response = createResponse("text/javascript", "Hello world!"); - } - - outputStream.write(response.getBytes()); + outputStream.write(response.createResponseString().getBytes()); outputStream.flush(); } catch (IOException | UncheckedServletException e) { log.error(e.getMessage(), e); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new RuntimeException(e); } } - private Map parseToQueryParms(String queryString) { - String[] keyValues = queryString.split("&"); - - Map queryParms = new HashMap<>(); - - for (String keyValue : keyValues) { - String[] queryParm = keyValue.split("="); - String key = queryParm[0]; - String value = queryParm[1]; - queryParms.put(key, value); - } - return queryParms; - } - - private String createResponse(String contentType, String responseBody) { - return String.join("\r\n", - "HTTP/1.1 200 OK ", - "Content-Type: " + contentType + ";charset=utf-8 ", - "Content-Length: " + responseBody.getBytes().length + " ", - "", - responseBody); - } - - private String createRedirectResponse(String redirectUrl) { - return String.join("\r\n", - "HTTP/1.1 302 FOUND ", - "Location: " + redirectUrl); - } - - private String readFile(String directory, String fileName) throws IOException { - URL resource = getClass().getClassLoader().getResource(directory + "/" + fileName); - Path path = Path.of(resource.getPath()); - return Files.readString(path); - } - } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java index 89a6f63463..d6cb8d8a26 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpRequest.java @@ -7,12 +7,19 @@ import java.util.Objects; public class HttpRequest { - private String method; + + private HttpMethod method; private String path; - private String fileName; private Map headers = new HashMap<>(); private String body; + public HttpRequest(HttpMethod method, String path, Map headers, String body) { + this.method = method; + this.path = path; + this.headers = headers; + this.body = body; + } + public HttpRequest(BufferedReader bufferedReader) throws IOException { parseRequest(bufferedReader); } @@ -23,9 +30,8 @@ private void parseRequest(BufferedReader bufferedReader) throws IOException { return; } String[] requests = requestLine.split(" "); - method = requests[0]; + method = HttpMethod.valueOf(requests[0]); path = requests[1]; - fileName = path.substring(path.lastIndexOf('/') + 1); headers = parseToHeaders(bufferedReader); body = parseToBody(bufferedReader); } @@ -53,7 +59,7 @@ private String parseToBody(BufferedReader bufferedReader) throws IOException { return null; } - public String getMethod() { + public HttpMethod getMethod() { return method; } @@ -61,10 +67,6 @@ public String getPath() { return path; } - public String getFileName() { - return fileName; - } - public Map getHeaders() { return headers; } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java index 3ebc35911e..279f42e5b7 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/HttpResponse.java @@ -8,7 +8,7 @@ public class HttpResponse { private final Map headers = new HashMap<>(); private final StringBuilder body = new StringBuilder(); - public String createResponse() { + public String createResponseString() { StringBuilder responseBuilder = new StringBuilder(); responseBuilder.append("HTTP/1.1 ") .append(status.getCode()).append(" ") @@ -42,4 +42,16 @@ public void setRedirectUrl(String value) { headers.put("Location", value); } + public HttpStatus getStatus() { + return status; + } + + public Map getHeaders() { + return headers; + } + + public StringBuilder getBody() { + return body; + } + } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/SessionManager.java b/tomcat/src/main/java/org/apache/coyote/http11/SessionManager.java index b1929d20b0..bffc822e73 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/SessionManager.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/SessionManager.java @@ -1,30 +1,31 @@ package org.apache.coyote.http11; -import java.util.HashMap; import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; import org.apache.catalina.Manager; public class SessionManager implements Manager { - private static final Map SESSIONS = new HashMap<>(); + private static final Map sessions = new ConcurrentHashMap<>(); @Override public void add(final Session session) { - SESSIONS.put(session.getId(), session); + sessions.put(session.getId(), session); } @Override public Session findSession(final String id) { - return SESSIONS.get(id); + return sessions.get(id); } @Override public void remove(final Session session) { - SESSIONS.remove(session.getId()); + sessions.remove(session.getId()); } public boolean isExist(final String id) { - return SESSIONS.containsKey(id); + return Objects.nonNull(id) && sessions.containsKey(id); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/Utils.java b/tomcat/src/main/java/org/apache/coyote/http11/Utils.java new file mode 100644 index 0000000000..ea3f391db2 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/Utils.java @@ -0,0 +1,35 @@ +package org.apache.coyote.http11; + +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; + +public final class Utils { + + private Utils() { + } + + public static Map parseToQueryParms(String queryString) { + String[] keyValues = queryString.split("&"); + + Map queryParms = new HashMap<>(); + + for (String keyValue : keyValues) { + String[] queryParm = keyValue.split("="); + String key = queryParm[0]; + String value = queryParm[1]; + queryParms.put(key, value); + } + return queryParms; + } + + public static String readFile(String directory, String fileName) throws IOException { + URL resource = Utils.class.getClassLoader().getResource(directory + "/" + fileName); + Path path = Path.of(resource.getPath()); + return Files.readString(path); + } + +} diff --git a/tomcat/src/test/java/org/apache/coyote/controller/RegisterControllerTest.java b/tomcat/src/test/java/org/apache/coyote/controller/RegisterControllerTest.java new file mode 100644 index 0000000000..60d67b2e0e --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/controller/RegisterControllerTest.java @@ -0,0 +1,39 @@ +package org.apache.coyote.controller; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.coyote.http11.HttpMethod; +import org.apache.coyote.http11.HttpRequest; +import org.apache.coyote.http11.HttpResponse; +import org.apache.coyote.http11.HttpStatus; +import org.junit.jupiter.api.Test; + +class RegisterControllerTest { + + @Test + void get() throws Exception { + RegisterController registerController = new RegisterController(); + HttpResponse response = new HttpResponse(); + HttpRequest request = new HttpRequest(HttpMethod.GET, "/register", null, null); + + registerController.service(request, response); + + assertThat(response.getStatus()).isEqualTo(HttpStatus.OK); + assertThat(response.getHeaders().get("Content-Type")).isNotNull(); + assertThat(response.getBody()).isNotNull(); + } + + @Test + void post() throws Exception { + RegisterController registerController = new RegisterController(); + String body = "account=1&password=1&email=abc@abc.com"; + HttpRequest request = new HttpRequest(HttpMethod.POST, "/register", null, body); + HttpResponse response = new HttpResponse(); + + registerController.service(request, response); + + assertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND); + assertThat(response.getHeaders().get("Location")).isEqualTo("/index.html"); + } + +} diff --git a/tomcat/src/test/java/org/apache/coyote/controller/RequestMappingTest.java b/tomcat/src/test/java/org/apache/coyote/controller/RequestMappingTest.java new file mode 100644 index 0000000000..fb19995ca6 --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/controller/RequestMappingTest.java @@ -0,0 +1,31 @@ +package org.apache.coyote.controller; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.coyote.http11.HttpRequest; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class RequestMappingTest { + + @Test + @DisplayName("올바른 매핑인지 확인1") + void getController1() { + RequestMapping requestMapping = new RequestMapping(); + HttpRequest httpRequest = new HttpRequest(null, "/login", null, null); + Controller controller = requestMapping.getController(httpRequest); + + assertThat(controller).isInstanceOf(LoginController.class); + } + + @Test + @DisplayName("올바른 매핑인지 확인2") + void getController2() { + RequestMapping requestMapping = new RequestMapping(); + HttpRequest httpRequest = new HttpRequest(null, "/register", null, null); + Controller controller = requestMapping.getController(httpRequest); + + assertThat(controller).isInstanceOf(RegisterController.class); + } + +}