diff --git a/tomcat/src/main/java/com/techcourse/model/User.java b/tomcat/src/main/java/com/techcourse/model/User.java index e8cf4c8e68..62b7519edb 100644 --- a/tomcat/src/main/java/com/techcourse/model/User.java +++ b/tomcat/src/main/java/com/techcourse/model/User.java @@ -1,5 +1,7 @@ package com.techcourse.model; +import java.util.Objects; + public class User { private final Long id; @@ -27,12 +29,18 @@ public String getAccount() { } @Override - public String toString() { - return "User{" + - "id=" + id + - ", account='" + account + '\'' + - ", email='" + email + '\'' + - ", password='" + password + '\'' + - '}'; + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + User user = (User)o; + return Objects.equals(account, user.account) && Objects.equals(password, user.password) + && Objects.equals(email, user.email); + } + + @Override + public int hashCode() { + return Objects.hash(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..4a0afb46b3 100644 --- a/tomcat/src/main/java/org/apache/catalina/connector/Connector.java +++ b/tomcat/src/main/java/org/apache/catalina/connector/Connector.java @@ -8,24 +8,29 @@ import java.io.UncheckedIOException; import java.net.ServerSocket; import java.net.Socket; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; public class Connector implements Runnable { private static final Logger log = LoggerFactory.getLogger(Connector.class); private static final int DEFAULT_PORT = 8080; - private static final int DEFAULT_ACCEPT_COUNT = 100; + private static final int DEFAULT_ACCEPT_COUNT = 100; + private static final int DEFAULT_THREAD_COUNT = 250; private final ServerSocket serverSocket; private boolean stopped; + private final ExecutorService executorService; public Connector() { - this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT); + this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, DEFAULT_THREAD_COUNT); } - 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.stopped = false; + this.executorService = Executors.newFixedThreadPool(maxThreads); } private ServerSocket createServerSocket(final int port, final int acceptCount) { @@ -67,7 +72,7 @@ private void process(final Socket connection) { return; } var processor = new Http11Processor(connection); - new Thread(processor).start(); + this.executorService.execute(processor); } public void stop() { 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 390db0c886..55de1ef37b 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java @@ -1,62 +1,47 @@ package org.apache.coyote.http11; - -import com.techcourse.db.InMemoryUserRepository; -import com.techcourse.exception.UncheckedServletException; -import com.techcourse.model.User; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.Socket; import org.apache.coyote.Processor; import org.apache.coyote.http11.request.HttpRequest; -import org.apache.coyote.http11.request.RequestHandler; -import org.apache.coyote.http11.session.SessionManager; +import org.apache.coyote.http11.response.HttpResponse; +import org.apache.coyote.http11.response.ResponsePrinter; +import org.apache.coyote.http11.servlet.ServletContainer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.Socket; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - public class Http11Processor implements Runnable, Processor { + private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); - private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); - - private final Socket connection; - private static final Map httpRequestHeader = new HashMap<>(); - private static final String sessionId = "JSESSIONID=sessionId"; + private final Socket connection; - public Http11Processor(final Socket connection) { - this.connection = connection; - } + public Http11Processor(final Socket connection) { + this.connection = connection; + } - @Override - public void run() { - log.info("connect host: {}, port: {}", connection.getInetAddress(), connection.getPort()); - process(connection); - } + @Override + public void run() { + log.info("connect host: {}, port: {}", connection.getInetAddress(), connection.getPort()); + process(connection); + } - @Override - public void process(final Socket connection) { - try (final var inputStream = connection.getInputStream(); - final var outputStream = connection.getOutputStream()) { + @Override + public void process(final Socket connection) { + try (final var inputStream = connection.getInputStream(); + final var outputStream = connection.getOutputStream()) { - BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); - HttpRequest httpRequest = new HttpRequest(reader); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + HttpRequest request = new HttpRequest(reader); + HttpResponse response = new HttpResponse(); - RequestHandler requestHandler = new RequestHandler(httpRequest, outputStream); - requestHandler.handleRequest(); + new ServletContainer().invoke(request, response); - } catch (IOException e) { - log.error(e.getMessage(), e); - } - } + new ResponsePrinter(outputStream).print(response); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/Body.java b/tomcat/src/main/java/org/apache/coyote/http11/common/Body.java new file mode 100644 index 0000000000..290449c6b1 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/Body.java @@ -0,0 +1,42 @@ +package org.apache.coyote.http11.common; + +import static org.apache.coyote.http11.common.HttpDelimiter.*; +import static org.apache.coyote.http11.common.HeaderKey.*; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.Arrays; + +public record Body(String value) { + public static Body parseRequestBody(Headers headers, BufferedReader reader) throws IOException { + return new Body(parseBody(headers, reader)); + } + + private static String parseBody(Headers headers, BufferedReader reader) throws IOException { + String contentLength = headers.getValue(CONTENT_LENGTH); + if (contentLength == null) { + return null; + } + + int length = Integer.parseInt(contentLength); + if (length > 0) { + char[] body = new char[length]; + reader.read(body, 0, length); + return new String(body); + } else if (length < 0) { + throw new IOException("Invalid Content-Length: " + length); + } + return null; + } + + public int getContentLength() { + return this.value.getBytes().length; + } + + public Properties parseProperty() { + Properties properties = new Properties(); + Arrays.asList(value.split(BODY_PROPERTY_DELIMITER.getValue())) + .forEach(properties::add); + return properties; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/ContentType.java b/tomcat/src/main/java/org/apache/coyote/http11/common/ContentType.java new file mode 100644 index 0000000000..5b208eeea9 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/ContentType.java @@ -0,0 +1,32 @@ +package org.apache.coyote.http11.common; + +public enum ContentType { + APPLICATION_JSON("application/json"), + TEXT_CSS("text/css"), + TEXT_HTML("text/html"), + TEXT_JAVASCRIPT("text/javascript"), + ; + + private final String value; + + ContentType(String value) { + this.value = value; + } + + public static ContentType fromPath(String path) { + if (path.endsWith(".css")) { + return ContentType.TEXT_CSS; + } + if (path.endsWith(".js")) { + return ContentType.APPLICATION_JSON; + } + if (path.endsWith(".html")) { + return ContentType.TEXT_HTML; + } + return ContentType.APPLICATION_JSON; + } + + public String getValue() { + return value; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/HeaderKey.java b/tomcat/src/main/java/org/apache/coyote/http11/common/HeaderKey.java new file mode 100644 index 0000000000..14230e0b91 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/HeaderKey.java @@ -0,0 +1,20 @@ +package org.apache.coyote.http11.common; + +public enum HeaderKey { + CONTENT_LENGTH("Content-Length"), + CONTENT_TYPE("Content-Type"), + COOKIE("Cookie"), + SET_COOKIE("Set-Cookie"), + LOCATION("Location") + ; + + private final String value; + + HeaderKey(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/Headers.java b/tomcat/src/main/java/org/apache/coyote/http11/common/Headers.java new file mode 100644 index 0000000000..1b4757c685 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/Headers.java @@ -0,0 +1,40 @@ +package org.apache.coyote.http11.common; + +import static org.apache.coyote.http11.common.HttpDelimiter.*; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public record Headers(Map headers) { + public Headers() { + this(new HashMap<>()); + } + + public static Headers parseRequestHeader(BufferedReader reader) throws IOException { + HashMap headers = new HashMap<>(); + String line; + while ((line = reader.readLine()) != null && !line.isEmpty()) { + String[] header = line.split(HEADER_KEY_VALUE_DELIMITER.getValue()); + headers.put(header[0].trim(), header[1].trim()); + } + return new Headers(headers); + } + + public void add(HeaderKey key, String value) { + headers.put(key.getValue(), value); + } + + public String getValue(HeaderKey headerKey) { + return headers.get(headerKey.getValue()); + } + + public String generatePlainText() { + StringBuilder sb = new StringBuilder(); + headers().forEach((key, value) -> { + sb.append(String.format("%s: %s \r\n", key, value)); + }); + return sb.toString(); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/HttpDelimiter.java b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpDelimiter.java new file mode 100644 index 0000000000..77c0a4fba2 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/HttpDelimiter.java @@ -0,0 +1,20 @@ +package org.apache.coyote.http11.common; + +public enum HttpDelimiter { + REQUEST_LINE_DELIMITER(" "), + BODY_PROPERTY_DELIMITER("&"), + BODY_KEY_VALUE_DELIMITER("="), + SESSION_DELIMITER("; "), + HEADER_KEY_VALUE_DELIMITER(":") + ; + + private final String value; + + HttpDelimiter(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/Properties.java b/tomcat/src/main/java/org/apache/coyote/http11/common/Properties.java new file mode 100644 index 0000000000..157ce936de --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/Properties.java @@ -0,0 +1,27 @@ +package org.apache.coyote.http11.common; + +import static org.apache.coyote.http11.common.HttpDelimiter.*; + +import java.util.HashMap; +import java.util.Map; + +public class Properties { + private static final int KEY_INDEX_OF_PROPERTY = 0; + private static final int VALUE_INDEX_OF_PROPERTY = 1; + + private final Map properties; + + public Properties() { + this.properties = new HashMap<>(); + } + + public void add(String rawProperty) { + String key = rawProperty.split(BODY_KEY_VALUE_DELIMITER.getValue())[KEY_INDEX_OF_PROPERTY]; + String value = rawProperty.split(BODY_KEY_VALUE_DELIMITER.getValue())[VALUE_INDEX_OF_PROPERTY]; + this.properties.put(key, value); + } + + public String get(String key) { + return properties.get(key); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/common/VersionOfProtocol.java b/tomcat/src/main/java/org/apache/coyote/http11/common/VersionOfProtocol.java new file mode 100644 index 0000000000..298e988c18 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/common/VersionOfProtocol.java @@ -0,0 +1,30 @@ +package org.apache.coyote.http11.common; + +import static org.apache.coyote.http11.common.HttpDelimiter.*; + +import java.util.Objects; + +public record VersionOfProtocol(String value) { + private static final int INDEX_OF_VERSION_OF_PROTOCOL = 2; + + public static VersionOfProtocol parseReqeustToVersionOfProtocol(String requestLine) { + return new VersionOfProtocol(requestLine.split(REQUEST_LINE_DELIMITER.getValue())[INDEX_OF_VERSION_OF_PROTOCOL]); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + VersionOfProtocol that = (VersionOfProtocol)o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java index aaf2d5f417..e10fad9081 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/HttpRequest.java @@ -1,57 +1,54 @@ package org.apache.coyote.http11.request; +import static org.apache.coyote.http11.common.HeaderKey.*; + import java.io.BufferedReader; import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -public class HttpRequest { - private static final String CONTENT_LENGTH = "Content-Length"; +import org.apache.coyote.http11.common.Body; +import org.apache.coyote.http11.common.Headers; +import org.apache.coyote.http11.common.Properties; +import org.apache.coyote.http11.common.VersionOfProtocol; +import org.apache.coyote.http11.session.Session; - private final String method; - private final String path; - private final Map headers = new HashMap<>(); - private final String body; +public class HttpRequest { + private final RequestLine requestLine; + private final Headers headers; + private final Body body; public HttpRequest(BufferedReader reader) throws IOException { - String initialLine = reader.readLine(); - this.method = initialLine.split(" ")[0]; - this.path = initialLine.split(" ")[1]; - String line; - while ((line = reader.readLine()) != null && !line.isEmpty()) { - String[] header = line.split(":"); - headers.put(header[0].trim(), header[1].trim()); - } - this.body = parseBody(reader); + String requestLine = reader.readLine(); + this.requestLine = new RequestLine( + Method.parseRequestToMethod(requestLine), + Path.parseRequestToPath(requestLine), + VersionOfProtocol.parseReqeustToVersionOfProtocol(requestLine) + ); + this.headers = Headers.parseRequestHeader(reader); + this.body = Body.parseRequestBody(headers, reader); } - private String parseBody(BufferedReader reader) throws IOException { - if(headers.get(CONTENT_LENGTH) == null) { - return null; - } - int contentLength = Integer.parseInt(headers.get(CONTENT_LENGTH)); - if(contentLength > 0) { - char[] body = new char[contentLength]; - reader.read(body, 0, contentLength); - return new String(body); - } - return null; + public Method getMethod() { + return requestLine.method(); } - public String getMethod() { - return method; + public Path getPath() { + return requestLine.path(); } - public String getPath() { - return path; + public Body getBody() { + return body; } - public String getCookie() { - return headers.get("Cookie"); + public Session getSession() { + String cookie = headers.getValue(COOKIE); + if (cookie == null) { + return null; + } + return new Session(cookie); } - public String getBody() { - return body; + public String getProperty(String key) { + Properties properties = body.parseProperty(); + return properties.get(key); } } diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/Method.java b/tomcat/src/main/java/org/apache/coyote/http11/request/Method.java new file mode 100644 index 0000000000..93949f8745 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/Method.java @@ -0,0 +1,19 @@ +package org.apache.coyote.http11.request; + +import static org.apache.coyote.http11.common.HttpDelimiter.*; + +public record Method(String value) { + private static final int INDEX_OF_METHOD = 0; + + public static Method parseRequestToMethod(String requestLine) { + return new Method(requestLine.split(REQUEST_LINE_DELIMITER.getValue())[INDEX_OF_METHOD]); + } + + public boolean isGet() { + return value.startsWith("GET"); + } + + public boolean isPost() { + return value.startsWith("POST"); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/Path.java b/tomcat/src/main/java/org/apache/coyote/http11/request/Path.java new file mode 100644 index 0000000000..7c688cc678 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/Path.java @@ -0,0 +1,28 @@ +package org.apache.coyote.http11.request; + +import static org.apache.coyote.http11.common.HttpDelimiter.*; + +import java.util.Objects; + +public record Path(String value) { + private static final int INDEX_OF_PATH = 1; + + public static Path parseRequestToPath(String requestLine) { + return new Path(requestLine.split(REQUEST_LINE_DELIMITER.getValue())[INDEX_OF_PATH]); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Path path = (Path)o; + return Objects.equals(value, path.value); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestHandler.java deleted file mode 100644 index 3bd3462836..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/request/RequestHandler.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.apache.coyote.http11.request; - -import java.io.IOException; -import java.io.OutputStream; - -import org.apache.coyote.http11.response.ResponseHandler; -import org.apache.coyote.http11.session.Session; -import org.apache.coyote.http11.session.SessionManager; - -import com.techcourse.db.InMemoryUserRepository; -import com.techcourse.model.User; - -public class RequestHandler { - - private final HttpRequest httpRequest; - private final OutputStream outputStream; - - public RequestHandler(HttpRequest httpRequest, OutputStream outputStream) { - this.httpRequest = httpRequest; - this.outputStream = outputStream; - } - - public void handleRequest() throws IOException { - String httpMethod = httpRequest.getMethod(); - String urlPath = httpRequest.getPath(); - - if(urlPath.endsWith("html") || urlPath.endsWith("css") || urlPath.endsWith("js")) { - ResponseHandler.printFileResource("static" + urlPath, outputStream); - } else if (urlPath.startsWith("/login")) { - handleLoginRequest(httpMethod, urlPath); - } else if (urlPath.startsWith("/register")) { - handleRegisterRequest(httpMethod, urlPath); - } else { - sendHelloWorldResponse(); - } - } - - private void handleLoginRequest(String httpMethod, String urlPath) { - if (urlPath.equals("/login") && httpMethod.equals("GET")) { - Session session = new Session(httpRequest); - SessionManager.findUserBySession(session) - .ifPresentOrElse( - user -> ResponseHandler.redirect("/index.html", outputStream), - () -> ResponseHandler.printFileResource("static" + urlPath + ".html", outputStream)); - } else if (httpMethod.equals("POST")) { - login(); - } - } - - private void login() { - String body = httpRequest.getBody(); - if (body != null) { - String account = body.split("&")[0].split("=")[1]; - String password = body.split("&")[1].split("=")[1]; - InMemoryUserRepository.findByAccount(account) - .ifPresentOrElse( - user -> loginUser(user, password), - () -> ResponseHandler.redirect("/401.html", outputStream) - ); - } - ResponseHandler.redirect("/401.html", outputStream); - } - - private void loginUser(User user, String password) { - if (user.checkPassword(password)) { - Session session = SessionManager.createSession(user); - ResponseHandler.redirectWithSetCookie("/index.html", session.getId(), outputStream); - } - } - - private void handleRegisterRequest(String httpMethod, String urlPath) { - if (httpMethod.equals("GET")) { - ResponseHandler.printFileResource("static" + urlPath + ".html", outputStream); - return; - } - String body = httpRequest.getBody(); - if (body != null) { - String account = body.split("&")[0].split("=")[1]; - String mail = body.split("&")[1].split("=")[1]; - String password = body.split("&")[2].split("=")[1]; - User user = new User(account, mail, password); - InMemoryUserRepository.save(user); - ResponseHandler.redirect("/index.html", outputStream); - } - } - - private void sendHelloWorldResponse() throws IOException { - String responseBody = "Hello world!"; - String response = String.join("\r\n", - "HTTP/1.1 200 OK", - "Content-Type: text/html;charset=utf-8", - "Content-Length: " + responseBody.getBytes().length, - "", - responseBody); - outputStream.write(response.getBytes()); - outputStream.flush(); - } -} 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..173a5eded2 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/request/RequestLine.java @@ -0,0 +1,7 @@ +package org.apache.coyote.http11.request; + +import org.apache.coyote.http11.common.VersionOfProtocol; + +public record RequestLine(Method method, Path path, VersionOfProtocol versionOfProtocol) { + +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java new file mode 100644 index 0000000000..af97f3e9a4 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpResponse.java @@ -0,0 +1,94 @@ +package org.apache.coyote.http11.response; + +import static org.apache.coyote.http11.common.HeaderKey.*; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.AccessDeniedException; +import java.nio.file.Files; +import java.util.Map; + +import org.apache.coyote.http11.common.Body; +import org.apache.coyote.http11.common.ContentType; +import org.apache.coyote.http11.common.Headers; +import org.apache.coyote.http11.common.VersionOfProtocol; + +public class HttpResponse { + private ResponseLine responseLine; + private Headers headers; + private Body body; + + public HttpResponse() { + this.responseLine = new ResponseLine(); + this.headers = new Headers(); + } + + public String toPlainText() { + StringBuilder sb = new StringBuilder(); + sb.append(responseLine.generatePlainText()); + sb.append(headers.generatePlainText()); + sb.append("\r\n"); + if (body != null) { + sb.append(body.value()); + } + return sb.toString(); + } + + public void setRequestLine(String versionOfProtocol, HttpStatusCode httpStatusCode) { + this.responseLine.setVersionOfProtocol(new VersionOfProtocol(versionOfProtocol)); + this.responseLine.setStatusCode(new StatusCode(httpStatusCode.getStatusCode())); + this.responseLine.setStatusMessage(new StatusMessage(httpStatusCode.getStatusMessage())); + } + + public void setStatusCode(int value) { + this.responseLine.setStatusCode(new StatusCode(value)); + } + + public void setStatusMessage(String value) { + this.responseLine.setStatusMessage(new StatusMessage(value)); + } + + public void setHeaders(Map headers) { + this.headers = new Headers(headers); + } + + public void setBodyByPlainText(String body) { + this.body = new Body(body); + } + + public void setBodyByFileName(String filename) throws IOException { + try { + URL url = HttpResponse.class.getClassLoader().getResource(filename); + File file = new File(url.getPath()); + this.body = new Body(new String(Files.readAllBytes(file.toPath()))); + + ContentType contentType = ContentType.fromPath(url.getPath()); + this.headers.add(CONTENT_TYPE, contentType.getValue() + ";charset=utf-8"); + this.headers.add(CONTENT_LENGTH, String.valueOf(body.getContentLength())); + } catch (AccessDeniedException | NullPointerException exception) { + setStatusCode(404); + setStatusMessage("NOT FOUND"); + } + } + + public VersionOfProtocol getVersionOfProtocol() { + return responseLine.getVersionOfProtocol(); + } + + public StatusCode getStatusCode() { + return responseLine.getStatusCode(); + } + + public StatusMessage getStatusMessage() { + return responseLine.getStatusMessage(); + } + + public Headers getHeaders() { + return headers; + } + + public Body getBody() { + return body; + } +} \ No newline at end of file diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatusCode.java b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatusCode.java new file mode 100644 index 0000000000..e8ac8309c3 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/HttpStatusCode.java @@ -0,0 +1,25 @@ +package org.apache.coyote.http11.response; + +public enum HttpStatusCode { + OK(200, "OK"), + REDIRECT(302, "Found"), + NOT_FOUND(404, "Not Found"), + METHOD_NOT_ALLOWED(405, "Method Not Allowed") + ; + + private final int statusCode; + private final String statusMessage; + + HttpStatusCode(int statusCode, String statusMessage) { + this.statusCode = statusCode; + this.statusMessage = statusMessage; + } + + public int getStatusCode() { + return statusCode; + } + + public String getStatusMessage() { + return statusMessage; + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/ResponseHandler.java b/tomcat/src/main/java/org/apache/coyote/http11/response/ResponseHandler.java deleted file mode 100644 index f286e2861e..0000000000 --- a/tomcat/src/main/java/org/apache/coyote/http11/response/ResponseHandler.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.apache.coyote.http11.response; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.net.URL; -import java.nio.file.Files; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.techcourse.exception.UncheckedServletException; - -public class ResponseHandler { - - private static final Logger log = LoggerFactory.getLogger(ResponseHandler.class); - - public static void printFileResource(String fileName, OutputStream outputStream) { - final URL url = ResponseHandler.class.getClassLoader().getResource(fileName); - if (url == null) { - sendNotFoundResponse(outputStream); - return; - } - try { - File file = new File(url.getPath()); - String contentType = determineContentType(fileName); - String responseBody = new String(Files.readAllBytes(file.toPath())); - String response = String.join("\r\n", - "HTTP/1.1 200 OK", - "Content-Type: " + contentType + ";charset=utf-8", - "Content-Length: " + responseBody.getBytes().length, - "", - responseBody); - outputStream.write(response.getBytes()); - outputStream.flush(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - private static String determineContentType(String fileName) { - if (fileName.endsWith("css")) { - return "text/css"; - } - if (fileName.endsWith("js")) { - return "application/javascript"; - } - if(fileName.endsWith("html")) { - return "text/html"; - } - return "application/json"; - } - - private static void sendNotFoundResponse(OutputStream outputStream) { - try { - String response = "HTTP/1.1 404 Not Found \r\n" + - "Content-Length: 0 \r\n" + - "\r\n"; - outputStream.write(response.getBytes()); - outputStream.flush(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public static void redirect(String path, OutputStream outputStream) { - try { - String contentType = "text/html"; - var response = "HTTP/1.1 302 Found \r\n" + - "Location: http://localhost:8080" + path + "\r\n" + - String.format("Content-Type: %s;charset=utf-8 \r\n", contentType) + - "Content-Length: 0"; - - outputStream.write(response.getBytes()); - outputStream.flush(); - } catch (IOException | UncheckedServletException e) { - log.error(e.getMessage(), e); - } - } - - public static void redirectWithSetCookie(String path, String sessionId, OutputStream outputStream) { - try { - String contentType = "text/html"; - var response = "HTTP/1.1 302 Found \r\n" + - "Set-Cookie: JSESSIONID=" + sessionId + " \r\n" + - "Location: http://localhost:8080" + path + " \r\n" + - String.format("Content-Type: %s;charset=utf-8 \r\n", contentType) + - "Content-Length: 0"; - - outputStream.write(response.getBytes()); - outputStream.flush(); - } catch (IOException | UncheckedServletException e) { - log.error(e.getMessage(), e); - } - } -} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/ResponseLine.java b/tomcat/src/main/java/org/apache/coyote/http11/response/ResponseLine.java new file mode 100644 index 0000000000..578c618bbc --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/ResponseLine.java @@ -0,0 +1,41 @@ +package org.apache.coyote.http11.response; + +import org.apache.coyote.http11.common.VersionOfProtocol; + +public class ResponseLine { + private VersionOfProtocol versionOfProtocol; + private StatusCode statusCode; + private StatusMessage statusMessage; + + public ResponseLine() { + } + + public VersionOfProtocol getVersionOfProtocol() { + return versionOfProtocol; + } + + public StatusCode getStatusCode() { + return statusCode; + } + + public StatusMessage getStatusMessage() { + return statusMessage; + } + + + public void setVersionOfProtocol(VersionOfProtocol versionOfProtocol) { + this.versionOfProtocol = versionOfProtocol; + } + + public void setStatusCode(StatusCode statusCode) { + this.statusCode = statusCode; + } + + public void setStatusMessage(StatusMessage statusMessage) { + this.statusMessage = statusMessage; + } + + public String generatePlainText() { + return String.format("%s %d %s \r\n", versionOfProtocol.value(), statusCode.value(), statusMessage.value()); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/ResponsePrinter.java b/tomcat/src/main/java/org/apache/coyote/http11/response/ResponsePrinter.java new file mode 100644 index 0000000000..45e16ba635 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/ResponsePrinter.java @@ -0,0 +1,22 @@ +package org.apache.coyote.http11.response; + +import java.io.IOException; +import java.io.OutputStream; + +public class ResponsePrinter { + private final OutputStream outputStream; + + public ResponsePrinter(OutputStream outputStream) { + this.outputStream = outputStream; + } + + public void print(HttpResponse response) throws IOException { + String plainText = response.toPlainText(); + try { + outputStream.write(plainText.getBytes()); + outputStream.flush(); + } catch (IOException exception) { + exception.printStackTrace(); + } + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/StatusCode.java b/tomcat/src/main/java/org/apache/coyote/http11/response/StatusCode.java new file mode 100644 index 0000000000..32c8b29c9d --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/StatusCode.java @@ -0,0 +1,4 @@ +package org.apache.coyote.http11.response; + +public record StatusCode(int value) { +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/response/StatusMessage.java b/tomcat/src/main/java/org/apache/coyote/http11/response/StatusMessage.java new file mode 100644 index 0000000000..e7b3e30b60 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/response/StatusMessage.java @@ -0,0 +1,4 @@ +package org.apache.coyote.http11.response; + +public record StatusMessage(String value) { +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/servlet/AbstractServlet.java b/tomcat/src/main/java/org/apache/coyote/http11/servlet/AbstractServlet.java new file mode 100644 index 0000000000..7f607c742d --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/servlet/AbstractServlet.java @@ -0,0 +1,28 @@ +package org.apache.coyote.http11.servlet; + +import java.io.IOException; +import java.util.Map; + +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.request.Method; +import org.apache.coyote.http11.response.HttpResponse; +import org.apache.coyote.http11.response.HttpStatusCode; + +public abstract class AbstractServlet implements Servlet { + @Override + public void service(HttpRequest request, HttpResponse response) throws IOException { + Method method = request.getMethod(); + if (method.isGet()) { + doGet(request, response); + } else if (method.isPost()) { + doPost(request, response); + } else { + response.setRequestLine("HTTP/1.1", HttpStatusCode.METHOD_NOT_ALLOWED); + response.setHeaders(Map.of("Allow", "GET, POST")); + } + } + + protected abstract void doPost(HttpRequest request, HttpResponse response); + + protected abstract void doGet(HttpRequest request, HttpResponse response) throws IOException; +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/servlet/HelloWorldServlet.java b/tomcat/src/main/java/org/apache/coyote/http11/servlet/HelloWorldServlet.java new file mode 100644 index 0000000000..0c94883db7 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/servlet/HelloWorldServlet.java @@ -0,0 +1,26 @@ +package org.apache.coyote.http11.servlet; + +import static org.apache.coyote.http11.common.HeaderKey.*; + +import java.io.IOException; +import java.util.Map; + +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.response.HttpResponse; +import org.apache.coyote.http11.response.HttpStatusCode; + +public class HelloWorldServlet extends AbstractServlet { + @Override + protected void doPost(HttpRequest request, HttpResponse response) { + + } + + @Override + protected void doGet(HttpRequest request, HttpResponse response) throws IOException { + response.setRequestLine("HTTP/1.1", HttpStatusCode.OK); + response.setHeaders(Map.of( + "Content-Type", "text/html;charset=utf-8", + "Content-Length", String.valueOf(13))); + response.setBodyByPlainText("Hello, World!"); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/servlet/LoginServlet.java b/tomcat/src/main/java/org/apache/coyote/http11/servlet/LoginServlet.java new file mode 100644 index 0000000000..53a92824de --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/servlet/LoginServlet.java @@ -0,0 +1,54 @@ +package org.apache.coyote.http11.servlet; + +import java.io.IOException; +import java.util.Map; +import java.util.Optional; + +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.response.HttpResponse; +import org.apache.coyote.http11.response.HttpStatusCode; +import org.apache.coyote.http11.session.Session; +import org.apache.coyote.http11.session.SessionManager; + +import com.techcourse.db.InMemoryUserRepository; +import com.techcourse.model.User; + +public class LoginServlet extends AbstractServlet { + @Override + protected void doPost(HttpRequest request, HttpResponse response) { + String account = request.getProperty("account"); + String password = request.getProperty("password"); + + Optional user = InMemoryUserRepository.findByAccount(account); + if (user.isPresent() && user.get().checkPassword(password)) { + Session session = SessionManager.createSession(user.get()); + + response.setRequestLine("HTTP/1.1", HttpStatusCode.REDIRECT); + response.setHeaders(Map.of( + "Set-Cookie", "JSESSIONID=" + session.getId(), + "Location", "http://localhost:8080/index.html" + )); + return; + } + response.setRequestLine("HTTP/1.1", HttpStatusCode.REDIRECT); + response.setHeaders(Map.of( + "Location", "http://localhost:8080/401.html" + )); + } + + @Override + protected void doGet(HttpRequest request, HttpResponse response) throws IOException { + Session session = request.getSession(); + + if (session == null || SessionManager.findUserBySession(session).isEmpty()) { + response.setRequestLine("HTTP/1.1", HttpStatusCode.OK); + response.setBodyByFileName("static" + request.getPath().value() + ".html"); + + } else { + response.setRequestLine("HTTP/1.1", HttpStatusCode.REDIRECT); + response.setHeaders(Map.of( + "Location", "http://localhost:8080/index.html" + )); + } + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/servlet/RegisterServlet.java b/tomcat/src/main/java/org/apache/coyote/http11/servlet/RegisterServlet.java new file mode 100644 index 0000000000..313c40f94e --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/servlet/RegisterServlet.java @@ -0,0 +1,39 @@ +package org.apache.coyote.http11.servlet; + +import java.io.IOException; +import java.util.Map; + +import org.apache.coyote.http11.common.Body; +import org.apache.coyote.http11.common.Properties; +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.response.HttpResponse; +import org.apache.coyote.http11.response.HttpStatusCode; + +import com.techcourse.db.InMemoryUserRepository; +import com.techcourse.model.User; + +public class RegisterServlet extends AbstractServlet { + @Override + protected void doPost(HttpRequest request, HttpResponse response) { + Body body = request.getBody(); + Properties properties = body.parseProperty(); + + String account = properties.get("account"); + String email = properties.get("email"); + String password = properties.get("password"); + + if(InMemoryUserRepository.findByAccount(account).isEmpty()) { + User user = new User(account, password, email); + InMemoryUserRepository.save(user); + } + + response.setRequestLine("HTTP/1.1", HttpStatusCode.REDIRECT); + response.setHeaders(Map.of("Location", "http://localhost:8080/index.html")); + } + + @Override + protected void doGet(HttpRequest request, HttpResponse response) throws IOException { + response.setRequestLine("HTTP/1.1", HttpStatusCode.OK); + response.setBodyByFileName("static" + request.getPath().value() + ".html"); + } +} 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 new file mode 100644 index 0000000000..4b0918f149 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/servlet/Servlet.java @@ -0,0 +1,10 @@ +package org.apache.coyote.http11.servlet; + +import java.io.IOException; + +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.response.HttpResponse; + +public interface Servlet { + void service(HttpRequest request, HttpResponse response) throws IOException; +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/servlet/ServletContainer.java b/tomcat/src/main/java/org/apache/coyote/http11/servlet/ServletContainer.java new file mode 100644 index 0000000000..dd0949b0af --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/servlet/ServletContainer.java @@ -0,0 +1,25 @@ +package org.apache.coyote.http11.servlet; + +import java.io.IOException; +import java.util.Map; + +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.request.Path; +import org.apache.coyote.http11.response.HttpResponse; + +public class ServletContainer { + private final Map servlets; + + public ServletContainer() { + this.servlets = Map.of( + new Path("/"), new HelloWorldServlet(), + new Path("/login"), new LoginServlet(), + new Path("/register"), new RegisterServlet()); + } + + public void invoke(HttpRequest request, HttpResponse response) throws IOException { + Servlet servlet = servlets.getOrDefault(request.getPath(), new StaticServlet()); + + servlet.service(request, response); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/servlet/StaticServlet.java b/tomcat/src/main/java/org/apache/coyote/http11/servlet/StaticServlet.java new file mode 100644 index 0000000000..c2b2092672 --- /dev/null +++ b/tomcat/src/main/java/org/apache/coyote/http11/servlet/StaticServlet.java @@ -0,0 +1,19 @@ +package org.apache.coyote.http11.servlet; + +import java.io.IOException; + +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.response.HttpResponse; +import org.apache.coyote.http11.response.HttpStatusCode; + +public class StaticServlet extends AbstractServlet { + @Override + protected void doPost(HttpRequest request, HttpResponse response) { + } + + @Override + protected void doGet(HttpRequest request, HttpResponse response) throws IOException { + response.setRequestLine("HTTP/1.1", HttpStatusCode.OK); + response.setBodyByFileName("static" + request.getPath().value()); + } +} diff --git a/tomcat/src/main/java/org/apache/coyote/http11/session/Session.java b/tomcat/src/main/java/org/apache/coyote/http11/session/Session.java index ebce4c6da0..e59a403363 100644 --- a/tomcat/src/main/java/org/apache/coyote/http11/session/Session.java +++ b/tomcat/src/main/java/org/apache/coyote/http11/session/Session.java @@ -1,25 +1,36 @@ package org.apache.coyote.http11.session; +import static org.apache.coyote.http11.common.HttpDelimiter.*; + import java.util.Arrays; import java.util.Objects; import java.util.UUID; -import org.apache.coyote.http11.request.HttpRequest; - public class Session { public static final String SESSION_HEADER_KEY = "JSESSIONID"; + private final String id; public Session() { - this.id = UUID.randomUUID().toString(); + this.id = UUID.randomUUID().toString(); + } + + public Session(String cookies) { + String session = getSession(cookies); + if (session != null) { + this.id = session.substring(SESSION_HEADER_KEY.length() + 1); + } else { + this.id = null; + } } - public Session(HttpRequest httpRequest) { - this.id = Arrays.asList(httpRequest.getCookie().split("; ")) + private static String getSession(String cookies) { + String session = Arrays.asList(cookies.split(SESSION_DELIMITER.getValue())) .stream() .filter(cookie -> cookie.startsWith(SESSION_HEADER_KEY)) .findAny() .orElseGet(null); + return session; } public String getId() { diff --git a/tomcat/src/main/resources/static/index.html b/tomcat/src/main/resources/static/index.html index 18ac924d4e..24728ab9cc 100644 --- a/tomcat/src/main/resources/static/index.html +++ b/tomcat/src/main/resources/static/index.html @@ -61,7 +61,7 @@

대시보드

-
+
Bar Chart
@@ -70,7 +70,7 @@

대시보드

-
+
Pie Chart
diff --git a/tomcat/src/main/resources/static/login.html b/tomcat/src/main/resources/static/login.html index bc933357f2..d29c079679 100644 --- a/tomcat/src/main/resources/static/login.html +++ b/tomcat/src/main/resources/static/login.html @@ -18,7 +18,7 @@
-

로그인

+

로그인

diff --git a/tomcat/src/main/resources/static/register.html b/tomcat/src/main/resources/static/register.html index d87de0214c..b05742ef60 100644 --- a/tomcat/src/main/resources/static/register.html +++ b/tomcat/src/main/resources/static/register.html @@ -18,7 +18,7 @@
-

회원 가입

+

회원 가입

diff --git a/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java b/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java index 2aba8c56e0..b40a6a3927 100644 --- a/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java +++ b/tomcat/src/test/java/org/apache/coyote/http11/Http11ProcessorTest.java @@ -1,61 +1,62 @@ package org.apache.coyote.http11; -import org.junit.jupiter.api.Test; -import support.StubSocket; +import static org.assertj.core.api.Assertions.*; 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 org.junit.jupiter.api.Test; + +import support.StubSocket; class Http11ProcessorTest { - @Test - void process() { - // given - final var socket = new StubSocket(); - final var processor = new Http11Processor(socket); - - // when - processor.process(socket); - - // then - var expected = String.join("\r\n", - "HTTP/1.1 200 OK ", - "Content-Type: text/html;charset=utf-8 ", - "Content-Length: 12 ", - "", - "Hello world!"); - - assertThat(socket.output()).isEqualTo(expected); - } - - @Test - void index() throws IOException { - // given - final String httpRequest= String.join("\r\n", - "GET /index.html HTTP/1.1 ", - "Host: localhost:8080 ", - "Connection: keep-alive ", - "", - ""); - - final var socket = new StubSocket(httpRequest); - final Http11Processor processor = new Http11Processor(socket); - - // when - processor.process(socket); - - // then - final URL resource = getClass().getClassLoader().getResource("static/index.html"); - var expected = "HTTP/1.1 200 OK \r\n" + - "Content-Type: text/html;charset=utf-8 \r\n" + - "Content-Length: 5564 \r\n" + - "\r\n"+ - new String(Files.readAllBytes(new File(resource.getFile()).toPath())); - - assertThat(socket.output()).isEqualTo(expected); - } + @Test + void process() { + // given + final var socket = new StubSocket(); + final var processor = new Http11Processor(socket); + + // when + processor.process(socket); + + // then + var expected = String.join("\r\n", + "HTTP/1.1 200 OK ", + "Content-Type: text/html;charset=utf-8 ", + "Content-Length: 13 ", + "", + "Hello, World!"); + + assertThat(socket.output()).isEqualTo(expected); + } + + @Test + void index() throws IOException { + // given + final String httpRequest = String.join("\r\n", + "GET /index.html HTTP/1.1 ", + "Host: localhost:8080 ", + "Connection: keep-alive ", + "", + ""); + + final var socket = new StubSocket(httpRequest); + final Http11Processor processor = new Http11Processor(socket); + + // when + processor.process(socket); + + // then + final URL resource = getClass().getClassLoader().getResource("static/index.html"); + var expected = "HTTP/1.1 200 OK \r\n" + + "Content-Length: 5676 \r\n" + + "Content-Type: text/html;charset=utf-8 \r\n" + + "\r\n" + + new String(Files.readAllBytes(new File(resource.getFile()).toPath())); + + assertThat(socket.output()).isEqualTo(expected); + } } diff --git a/tomcat/src/test/java/org/apache/coyote/http11/servlet/LoginServletTest.java b/tomcat/src/test/java/org/apache/coyote/http11/servlet/LoginServletTest.java new file mode 100644 index 0000000000..35506a9ac8 --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/servlet/LoginServletTest.java @@ -0,0 +1,57 @@ +package org.apache.coyote.http11.servlet; + +import static org.apache.coyote.http11.common.HeaderKey.*; +import static org.assertj.core.api.Assertions.*; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; + +import org.apache.coyote.http11.common.VersionOfProtocol; +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.response.HttpResponse; +import org.apache.coyote.http11.response.StatusCode; +import org.apache.coyote.http11.response.StatusMessage; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class LoginServletTest { + + @DisplayName("로그인 POST 요청에 성공한다.") + @Test + void loginSuccess() throws IOException { + String requestData = "POST /login HTTP/1.1\r\n" + + "Content-Length: 30\r\n\r\n" + + "account=gugu&password=password"; + + BufferedReader reader = new BufferedReader(new StringReader(requestData)); + HttpRequest request = new HttpRequest(reader); + HttpResponse response = new HttpResponse(); + + LoginServlet loginServlet = new LoginServlet(); + loginServlet.doPost(request, response); + + assertThat(new VersionOfProtocol("HTTP/1.1")).isEqualTo(response.getVersionOfProtocol()); + assertThat(new StatusCode(302)).isEqualTo(response.getStatusCode()); + assertThat(new StatusMessage("Found")).isEqualTo(response.getStatusMessage()); + assertThat(response.getHeaders().getValue(SET_COOKIE)).isNotNull(); + assertThat("http://localhost:8080/index.html").isEqualTo(response.getHeaders().getValue(LOCATION)); + } + + @DisplayName("로그인 페이지 조회에 성공한다.") + @Test + void loginPageSuccess() throws IOException { + String requestData = "GET /login HTTP/1.1\r\n"; + + BufferedReader reader = new BufferedReader(new StringReader(requestData)); + HttpRequest request = new HttpRequest(reader); + HttpResponse response = new HttpResponse(); + + LoginServlet loginServlet = new LoginServlet(); + loginServlet.doGet(request, response); + + assertThat(new VersionOfProtocol("HTTP/1.1")).isEqualTo(response.getVersionOfProtocol()); + assertThat(new StatusCode(200)).isEqualTo(response.getStatusCode()); + assertThat(new StatusMessage("OK")).isEqualTo(response.getStatusMessage()); + } +} \ No newline at end of file diff --git a/tomcat/src/test/java/org/apache/coyote/http11/servlet/RegisterServletTest.java b/tomcat/src/test/java/org/apache/coyote/http11/servlet/RegisterServletTest.java new file mode 100644 index 0000000000..5573433ff0 --- /dev/null +++ b/tomcat/src/test/java/org/apache/coyote/http11/servlet/RegisterServletTest.java @@ -0,0 +1,65 @@ +package org.apache.coyote.http11.servlet; + +import static org.apache.coyote.http11.common.HeaderKey.*; +import static org.assertj.core.api.Assertions.*; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; + +import org.apache.coyote.http11.common.VersionOfProtocol; +import org.apache.coyote.http11.request.HttpRequest; +import org.apache.coyote.http11.response.HttpResponse; +import org.apache.coyote.http11.response.StatusCode; +import org.apache.coyote.http11.response.StatusMessage; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.techcourse.db.InMemoryUserRepository; +import com.techcourse.model.User; + +class RegisterServletTest { + + @DisplayName("회원가입에 성공한다.") + @Test + void registerSuccess() throws IOException { + String requestData = "POST /login HTTP/1.1\r\n" + + "Content-Length: 60\r\n\r\n" + + "account=testAccount&mail=test@mail.com&password=testPassword"; + BufferedReader reader = new BufferedReader(new StringReader(requestData)); + + HttpRequest request = new HttpRequest(reader); + HttpResponse response = new HttpResponse(); + + RegisterServlet servlet = new RegisterServlet(); + servlet.doPost(request, response); + + User expectedUser = new User("testAccount", "test@mail.com", "testPassword"); + User savedUser = InMemoryUserRepository.findByAccount("testAccount").orElse(null); + assertThat(savedUser).isNotNull(); + + assertThat(response.getVersionOfProtocol()).isEqualTo(new VersionOfProtocol("HTTP/1.1")); + assertThat(response.getStatusCode()).isEqualTo(new StatusCode(302)); + assertThat(response.getStatusMessage()).isEqualTo(new StatusMessage("Found")); + + assertThat(response.getHeaders().getValue(LOCATION)).isNotNull(); + assertThat(response.getHeaders().getValue(LOCATION)).isEqualTo("http://localhost:8080/index.html"); + } + + @DisplayName("회원가입 페이지를 조회에 성공한다.") + @Test + void registerFailure() throws IOException { + String requestData = "GET /register HTTP/1.1\r\n"; + BufferedReader reader = new BufferedReader(new StringReader(requestData)); + + HttpRequest request = new HttpRequest(reader); + HttpResponse response = new HttpResponse(); + + RegisterServlet servlet = new RegisterServlet(); + servlet.doGet(request, response); + + assertThat(response.getVersionOfProtocol()).isEqualTo(new VersionOfProtocol("HTTP/1.1")); + assertThat(response.getStatusCode()).isEqualTo(new StatusCode(200)); + assertThat(response.getStatusMessage()).isEqualTo(new StatusMessage("OK")); + } +} \ No newline at end of file