diff --git a/README.md b/README.md index 50045f90b..1b221db78 100644 --- a/README.md +++ b/README.md @@ -67,4 +67,24 @@ - 동적으로 html을 생성하기 위해 handlebars.java template engine을 활용한다. - [x] 요구사항 7 - - Stylesheet 파일을 지원하도록 구현하도록 한다. \ No newline at end of file + - Stylesheet 파일을 지원하도록 구현하도록 한다. + +### Step3: HTTP 웹 서버 리팩토링 + +- 모든 로직에 단위 테스트를 구현한다. 단, 테스트하기 어려운 UI 로직은 제외 + - 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분한다. + - UI 로직을 InputView, ResultView와 같은 클래스를 추가해 분리한다. +- 자바 코드 컨벤션을 지키면서 프로그래밍한다. + - 이 과정의 Code Style은 intellij idea Code Style. Java을 따른다. + - intellij idea Code Style. Java을 따르려면 code formatting 단축키(Windows : Ctrl + Alt + L. Mac : ⌥ (Option) + ⌘ (Command) + L.)를 사용한다. +- 규칙 1: 한 메서드에 오직 한 단계의 들여쓰기(indent)만 한다. + - indent가 2이상인 메소드의 경우 메소드를 분리하면 indent를 줄일 수 있다. + - else를 사용하지 않아 indent를 줄일 수 있다. +- 규칙 3: 모든 원시값과 문자열을 포장한다. +- 규칙 8: 일급 콜렉션을 쓴다. + +- 리팩토링 힌트 - 1단계 + - 메소드 분리 및 클래스 분리 +- 리팩토링 힌트 - 2단계 + - 클라이언트 요청 데이터를 처리하는 로직을 별도의 클래스로 분리한다.(HttpRequest) + - 클라이언트 응답 데이터를 처리하는 로직을 별도의 클래스로 분리한다.(HttpResponse) \ No newline at end of file diff --git a/src/main/java/controller/CreatUserMappingController.java b/src/main/java/controller/CreatUserMappingController.java index 581300f00..627167e3e 100644 --- a/src/main/java/controller/CreatUserMappingController.java +++ b/src/main/java/controller/CreatUserMappingController.java @@ -1,10 +1,10 @@ package controller; import db.DataBase; +import webserver.http.*; import model.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import webserver.*; public class CreatUserMappingController extends RequestMappingControllerAdapter { private static final Logger logger = LoggerFactory.getLogger(CreatUserMappingController.class); @@ -15,19 +15,19 @@ public boolean checkUrl(String url) { } @Override - public Response doPost(Request request) { - DataBase.addUser(getUserFromRequest(request)); + public void doPost(HttpRequest httpRequest, HttpResponse httpResponse) { + DataBase.addUser(getUserFromRequest(httpRequest)); logger.debug("findAll: {}", DataBase.findAll()); - return new Response(HttpStatus.FOUND, MediaType.TEXT_HTML_UTF8, "/index.html", null); + + httpResponse.redirect("/index.html"); } - private User getUserFromRequest(Request request) { - QueryString queryString = QueryString.parse(request.getRequestBody()); + private User getUserFromRequest(HttpRequest httpRequest) { return new User( - queryString.get("userId"), - queryString.get("password"), - queryString.get("name"), - queryString.get("email") + httpRequest.getParameter("userId"), + httpRequest.getParameter("password"), + httpRequest.getParameter("name"), + httpRequest.getParameter("email") ); } } diff --git a/src/main/java/controller/IndexMappingController.java b/src/main/java/controller/IndexMappingController.java index e87897afb..024a140ae 100644 --- a/src/main/java/controller/IndexMappingController.java +++ b/src/main/java/controller/IndexMappingController.java @@ -1,9 +1,9 @@ package controller; -import webserver.MediaType; -import webserver.Request; -import webserver.RequestLine; -import webserver.Response; +import webserver.http.HttpRequest; +import webserver.http.HttpResponse; +import webserver.http.MediaType; +import webserver.http.RequestMappingControllerAdapter; public class IndexMappingController extends RequestMappingControllerAdapter { @@ -13,8 +13,7 @@ public boolean checkUrl(String url) { } @Override - public Response doGet(Request request) { - RequestLine requestLine = request.getRequestLine(); - return new Response(MediaType.TEXT_HTML_UTF8, requestLine.getPath(), null); + public void doGet(HttpRequest httpRequest, HttpResponse httpResponse) { + httpResponse.redirect("/index.html"); } } diff --git a/src/main/java/controller/LoginMappingController.java b/src/main/java/controller/LoginMappingController.java index 8b2e108a8..af3ada464 100644 --- a/src/main/java/controller/LoginMappingController.java +++ b/src/main/java/controller/LoginMappingController.java @@ -2,7 +2,7 @@ import db.DataBase; import model.User; -import webserver.*; +import webserver.http.*; public class LoginMappingController extends RequestMappingControllerAdapter { @Override @@ -11,39 +11,36 @@ public boolean checkUrl(String url) { } @Override - public Response doGet(Request request) { - return login(request); + public void doGet(HttpRequest httpRequest, HttpResponse httpResponse) { + login(httpRequest, httpResponse); } @Override - public Response doPost(Request request) { - return login(request); + public void doPost(HttpRequest httpRequest, HttpResponse httpResponse) { + login(httpRequest, httpResponse); } - private Response login(Request request) { - User user = getUserFromRequest(request); + private void login(HttpRequest httpRequest, HttpResponse httpResponse) { + User user = getUserFromRequest(httpRequest); User userById = DataBase.findUserById(user.getUserId()); if (userById == null || !userById.checkPassword(user.getPassword())) { - return new Response(HttpStatus.BAD_REQUEST, MediaType.TEXT_HTML_UTF8, "/user/login_failed.html", "logined=false; Path=/"); + httpResponse.addHeader("Set-Cookie", "logined=false; Path=/"); + httpResponse.redirect(HttpStatus.BAD_REQUEST, "/user/login_failed.html"); + return; } - return new Response(HttpStatus.FOUND, MediaType.TEXT_HTML_UTF8, "/index.html", "logined=true; Path=/"); + httpResponse.addHeader("Set-Cookie", "logined=true; Path=/"); + httpResponse.redirect("/index.html"); } - private User getUserFromRequest(Request request) { - QueryString queryString = request.getRequestLine().toQueryString(); - - if (request.getRequestLine().getMethod() == HttpMethod.POST) { - queryString = QueryString.parse(request.getRequestBody()); - } - + private User getUserFromRequest(HttpRequest httpRequest) { return new User( - queryString.get("userId"), - queryString.get("password"), - queryString.get("name"), - queryString.get("email") + httpRequest.getParameter("userId"), + httpRequest.getParameter("password"), + httpRequest.getParameter("name"), + httpRequest.getParameter("email") ); } } diff --git a/src/main/java/controller/RequestController.java b/src/main/java/controller/RequestController.java deleted file mode 100644 index b91da7a62..000000000 --- a/src/main/java/controller/RequestController.java +++ /dev/null @@ -1,13 +0,0 @@ -package controller; - -import webserver.Request; -import webserver.Response; - -import java.io.IOException; -import java.net.URISyntaxException; - -public interface RequestController { - Response doGet(Request request) throws IOException, URISyntaxException; - - Response doPost(Request request) throws IOException, URISyntaxException; -} diff --git a/src/main/java/controller/RequestMappingControllerAdapter.java b/src/main/java/controller/RequestMappingControllerAdapter.java deleted file mode 100644 index 80b34daef..000000000 --- a/src/main/java/controller/RequestMappingControllerAdapter.java +++ /dev/null @@ -1,21 +0,0 @@ -package controller; - -import webserver.HttpStatus; -import webserver.MediaType; -import webserver.Request; -import webserver.Response; - -import java.io.IOException; - -public abstract class RequestMappingControllerAdapter implements RequestController, RequestMapping { - - @Override - public Response doGet(Request request) throws IOException { - return new Response(HttpStatus.NOT_FOUND, MediaType.TEXT_HTML_UTF8, "", null); - } - - @Override - public Response doPost(Request request) { - return new Response(HttpStatus.NOT_FOUND, MediaType.TEXT_HTML_UTF8, "", null); - } -} diff --git a/src/main/java/controller/StaticResourceController.java b/src/main/java/controller/StaticResourceController.java index 2a4a9817c..6ef914496 100644 --- a/src/main/java/controller/StaticResourceController.java +++ b/src/main/java/controller/StaticResourceController.java @@ -1,27 +1,34 @@ package controller; import utils.FileIoUtils; -import webserver.Request; -import webserver.RequestLine; -import webserver.Response; +import webserver.http.HttpRequest; +import webserver.http.HttpResponse; +import webserver.http.RequestAbstractController; +import webserver.http.RequestController; +import java.io.IOException; +import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Paths; -public class StaticResourceController implements RequestController { +public class StaticResourceController extends RequestAbstractController { @Override - public Response doGet(Request request) { - RequestLine requestLine = request.getRequestLine(); - String path = requestLine.getPath(); - String resourcePath = FileIoUtils.getResourcePath(requestLine.getPath()); + public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws Exception { + doGet(httpRequest, httpResponse); + } + + @Override + public void doGet(HttpRequest httpRequest, HttpResponse httpResponse) throws IOException, URISyntaxException { + String resourcePath = FileIoUtils.getResourcePath(httpRequest.getPath()); if (!isExists(resourcePath)) { throw new IllegalArgumentException("NotFound"); } - return new Response(request.getContentType(), path, null); + httpResponse.addHeader("Content-Type", httpRequest.getHeader("Accept")); + httpResponse.ok(FileIoUtils.loadFileFromClasspath(FileIoUtils.getResourcePath(httpRequest.getPath()))); } private boolean isExists(String resourcePath) { @@ -35,9 +42,4 @@ private boolean isExists(String resourcePath) { return Files.exists(Paths.get(path)); } - @Override - public Response doPost(Request request) { - return null; - } - } diff --git a/src/main/java/controller/UserListMappingController.java b/src/main/java/controller/UserListMappingController.java index 869ff44db..7c8ada584 100644 --- a/src/main/java/controller/UserListMappingController.java +++ b/src/main/java/controller/UserListMappingController.java @@ -5,14 +5,12 @@ import com.github.jknack.handlebars.io.ClassPathTemplateLoader; import com.github.jknack.handlebars.io.TemplateLoader; import db.DataBase; -import model.User; -import webserver.HttpStatus; -import webserver.MediaType; -import webserver.Request; -import webserver.Response; +import model.Users; +import webserver.http.HttpRequest; +import webserver.http.HttpResponse; +import webserver.http.RequestMappingControllerAdapter; import java.io.IOException; -import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -23,19 +21,19 @@ public boolean checkUrl(String url) { } @Override - - public Response doGet(Request request) throws IOException { - if (!checkLogin(getCookie(request))) { - return new Response(HttpStatus.FOUND, MediaType.TEXT_HTML_UTF8, "/index.html", null); - + public void doGet(HttpRequest httpRequest, HttpResponse httpResponse) throws IOException { + if (!checkLogin(getCookie(httpRequest))) { + httpResponse.redirect("/index.html"); + return; } - return new Response(HttpStatus.OK, MediaType.TEXT_HTML_UTF8, "/user/list.html", rendering(DataBase.findAll())); + httpResponse.addHeader("Content-Type", httpRequest.getHeader("Accept")); + httpResponse.ok(rendering(DataBase.findAll()).getBytes()); } - private String rendering(Collection users) throws IOException { + private String rendering(Users users) throws IOException { Map parameterMap = new HashMap<>(); - parameterMap.put("users", users); + parameterMap.put("users", users.toList()); TemplateLoader loader = new ClassPathTemplateLoader(); loader.setPrefix("/templates"); @@ -49,13 +47,13 @@ private boolean checkLogin(String cookie) { return cookie.indexOf("logined=true") != -1; } - private String getCookie(Request request) { - String cookie = request.getCookie(); + private String getCookie(HttpRequest httpRequest) { + String cookie = httpRequest.getHeader("Cookie"); if (cookie == null) { return ""; } - return request.getCookie(); + return cookie; } } diff --git a/src/main/java/db/DataBase.java b/src/main/java/db/DataBase.java index d9f85d187..b7a5d3438 100644 --- a/src/main/java/db/DataBase.java +++ b/src/main/java/db/DataBase.java @@ -6,6 +6,7 @@ import com.google.common.collect.Maps; import model.User; +import model.Users; public class DataBase { private static Map users = Maps.newHashMap(); @@ -18,8 +19,8 @@ public static User findUserById(String userId) { return users.get(userId); } - public static Collection findAll() { - return users.values(); + public static Users findAll() { + return Users.from(users.values()); } public static void clear() { diff --git a/src/main/java/model/Users.java b/src/main/java/model/Users.java new file mode 100644 index 000000000..2a472739f --- /dev/null +++ b/src/main/java/model/Users.java @@ -0,0 +1,22 @@ +package model; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class Users { + + private final List users; + + private Users(List users) { + this.users = users; + } + + public static Users from(Collection users) { + return new Users(new ArrayList<>(users)); + } + + public List toList() { + return List.copyOf(users); + } +} diff --git a/src/main/java/webserver/DispatcherServlet.java b/src/main/java/webserver/DispatcherServlet.java deleted file mode 100644 index c19a4d33f..000000000 --- a/src/main/java/webserver/DispatcherServlet.java +++ /dev/null @@ -1,52 +0,0 @@ -package webserver; - -import controller.*; - -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.LinkedList; -import java.util.List; -import java.util.Optional; - -public class DispatcherServlet { - - private DispatcherServlet() { - } - - private static final List container = new LinkedList<>(); - - static { - container.add(new IndexMappingController()); - container.add(new CreatUserMappingController()); - container.add(new LoginMappingController()); - container.add(new UserListMappingController()); - } - - public static Response match(Request request) throws IOException, URISyntaxException { - String url = request.getRequestLine().getPath(); - - Optional optionalController = container.stream() - .filter(controller -> controller.checkUrl(url)) - .findAny(); - - if (optionalController.isPresent()) { - return execute(request, optionalController.get()); - } - - try { - return new StaticResourceController().doGet(request); - } catch (IllegalArgumentException iae) { - return new Response(HttpStatus.NOT_FOUND, MediaType.TEXT_HTML_UTF8, "", null); - } - } - - private static Response execute(Request request, RequestController controller) throws IOException, URISyntaxException { - HttpMethod method = request.getRequestLine().getMethod(); - - if (method == HttpMethod.POST) { - return controller.doPost(request); - } - - return controller.doGet(request); - } -} diff --git a/src/main/java/webserver/Request.java b/src/main/java/webserver/Request.java deleted file mode 100644 index 44b713b96..000000000 --- a/src/main/java/webserver/Request.java +++ /dev/null @@ -1,53 +0,0 @@ -package webserver; - -import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class Request { - - private static final Logger logger = LoggerFactory.getLogger(Request.class); - - private final RequestLine requestLine; - - private final Map headers; - - private final String requestBody; - - public RequestLine getRequestLine() { - return requestLine; - } - - public Request(final RequestLine requestLine, final Map requestHeaders, final String requestBody) { - this.requestLine = requestLine; - this.headers = requestHeaders; - this.requestBody = requestBody; - } - - public MediaType getContentType() { - if (headers == null) { - return MediaType.TEXT_HTML_UTF8; - } - - return new MediaType(getAccept()); - } - - private String getAccept() { - return headers.getOrDefault("Accept", "") - .split(",")[0]; - } - - public String getCookie() { - if (headers == null) { - return ""; - } - return headers.getOrDefault("Cookie", ""); - } - - public String getRequestBody() { - if (headers == null) { - return ""; - } - return requestBody; - } -} diff --git a/src/main/java/webserver/RequestControllerContainer.java b/src/main/java/webserver/RequestControllerContainer.java new file mode 100644 index 000000000..4404fed17 --- /dev/null +++ b/src/main/java/webserver/RequestControllerContainer.java @@ -0,0 +1,41 @@ +package webserver; + +import controller.*; +import webserver.http.HttpRequest; +import webserver.http.RequestController; +import webserver.http.RequestMappingControllerAdapter; + +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; + +public class RequestControllerContainer { + + private RequestControllerContainer() { + } + + private static final List container = new LinkedList<>(); + + private static RequestController staticController = new StaticResourceController(); + + static { + container.add(new IndexMappingController()); + container.add(new CreatUserMappingController()); + container.add(new LoginMappingController()); + container.add(new UserListMappingController()); + } + + public static RequestController match(HttpRequest httpRequest) { + String url = httpRequest.getPath(); + + Optional optionalController = container.stream() + .filter(controller -> controller.checkUrl(url)) + .findAny(); + + if (optionalController.isPresent()) { + return optionalController.get(); + } + + return staticController; + } +} diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index 86aee3d10..d78a2f30a 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -1,15 +1,14 @@ package webserver; -import java.io.BufferedReader; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.Socket; -import java.net.URISyntaxException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import webserver.http.HttpRequest; +import webserver.http.HttpResponse; +import webserver.http.RequestController; +import webserver.http.RequestFactory; + +import java.io.*; +import java.net.Socket; public class RequestHandler implements Runnable { private static final Logger logger = LoggerFactory.getLogger(RequestHandler.class); @@ -21,32 +20,19 @@ public RequestHandler(Socket connectionSocket) { } public void run() { - logger.debug("New Client Connect! Connected IP : {}, Port : {}", connection.getInetAddress(), + logger.debug( + "New Client Connect! Connected IP : {}, Port : {}", + connection.getInetAddress(), connection.getPort()); try (InputStream in = connection.getInputStream(); OutputStream out = connection.getOutputStream()) { - Request request = RequestFactory.create(new BufferedReader(new InputStreamReader(in, "UTF-8"))); - Response response; - try { - response = DispatcherServlet.match(request); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - - DataOutputStream dos = new DataOutputStream(out); - write(dos, response.getBytes()); - } catch (IOException e) { - logger.error(e.getMessage()); - } catch (URISyntaxException e) { - logger.error(e.getMessage()); - } - } + HttpRequest httpRequest = RequestFactory.create(new BufferedReader(new InputStreamReader(in, "UTF-8"))); + HttpResponse httpResponse = new HttpResponse(out); + + RequestController controller = RequestControllerContainer.match(httpRequest); - private void write(DataOutputStream dos, byte[] response) { - try { - dos.write(response, 0, response.length); - dos.flush(); - } catch (IOException e) { + controller.service(httpRequest, httpResponse); + } catch (Exception e) { logger.error(e.getMessage()); } } diff --git a/src/main/java/webserver/Response.java b/src/main/java/webserver/Response.java deleted file mode 100644 index 3376fdfe1..000000000 --- a/src/main/java/webserver/Response.java +++ /dev/null @@ -1,101 +0,0 @@ -package webserver; - -import java.io.IOException; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Paths; -import utils.FileIoUtils; - -public class Response { - - private final HttpStatus httpStatus; - private final MediaType contentType; - private final String path; - private final String cookie; - - private final String body; - - public Response(final MediaType contentType, final String path, final String cookie) { - this(HttpStatus.OK, contentType, path, cookie, null); - } - - public Response(final MediaType contentType, final String path, final String cookie, final String body) { - this(HttpStatus.OK, contentType, path, cookie, body); - } - - public Response(final String contentType, final String path, final String cookie) { - this(HttpStatus.OK, new MediaType(contentType), path, cookie, null); - } - - public Response(final String contentType, final String path, final String cookie, final String body) { - this(HttpStatus.OK, new MediaType(contentType), path, cookie, body); - } - - public Response(final HttpStatus httpStatus, final MediaType contentType, final String path, final String cookie) { - this.httpStatus = httpStatus; - this.contentType = contentType; - this.path = path; - this.cookie = cookie; - this.body = null; - } - - public Response(final HttpStatus httpStatus, final MediaType contentType, final String path, final String cookie, final String body) { - this.httpStatus = httpStatus; - this.contentType = contentType; - this.path = path; - this.cookie = cookie; - this.body = body; - } - - public MediaType getContentType() { - return contentType; - } - - public String getPath() { - return path; - } - - public byte[] getBytes() throws IOException, URISyntaxException { - byte[] body = getBody(); - byte[] header = getHeader(body.length); - return getBytes(header, body); - } - - private byte[] getBody() throws IOException, URISyntaxException { - if (body != null) { - return body.getBytes(); - } - return FileIoUtils.loadFileFromClasspath(FileIoUtils.getResourcePath(path)); - } - - private byte[] getBytes(byte[] header, byte[] body) { - byte[] responseBytes = new byte[header.length + body.length]; - System.arraycopy(header, 0, responseBytes, 0, header.length); - System.arraycopy(body, 0, responseBytes, header.length, body.length); - return responseBytes; - } - - private byte[] getHeader(long bodyLength) { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(String.format("%s %s \r\n", "HTTP/1.1", getStatus().toString())); - stringBuilder.append(String.format("Content-Type: %s \r\n", contentType)); - stringBuilder.append(String.format("Content-Length: %s \r\n", bodyLength)); - stringBuilder.append(String.format("Location: %s \r\n", path)); - - if (cookie != null) { - stringBuilder.append(String.format("Set-Cookie: %s \r\n", cookie)); - } - - stringBuilder.append("\r\n"); - - return stringBuilder.toString().getBytes(); - } - - public HttpStatus getStatus() { - return httpStatus; - } - - public String getCookie() { - return cookie; - } -} diff --git a/src/main/java/webserver/HttpMethod.java b/src/main/java/webserver/http/HttpMethod.java similarity index 64% rename from src/main/java/webserver/HttpMethod.java rename to src/main/java/webserver/http/HttpMethod.java index 508ced7df..1ce76f383 100644 --- a/src/main/java/webserver/HttpMethod.java +++ b/src/main/java/webserver/http/HttpMethod.java @@ -1,4 +1,4 @@ -package webserver; +package webserver.http; public enum HttpMethod { GET, POST diff --git a/src/main/java/webserver/http/HttpRequest.java b/src/main/java/webserver/http/HttpRequest.java new file mode 100644 index 000000000..31e3c6651 --- /dev/null +++ b/src/main/java/webserver/http/HttpRequest.java @@ -0,0 +1,51 @@ +package webserver.http; + +import java.util.HashMap; +import java.util.Map; + +public class HttpRequest { + private final RequestLine requestLine; + + private final Map headers; + + private final Map params; + + public HttpRequest(final RequestLine requestLine, final Map headers, final String requestBody) { + this.requestLine = requestLine; + this.headers = headers; + this.params = getParams(requestLine, requestBody); + } + + private Map getParams(RequestLine requestLine, String requestBody) { + Map params = new HashMap<>(); + + QueryString searchParams = requestLine.toQueryString(); + params.putAll(searchParams.toParams()); + + QueryString bodyParams = QueryString.parse(requestBody); + params.putAll(bodyParams.toParams()); + + return params; + } + + public HttpMethod getMethod() { + return requestLine.getMethod(); + } + + public String getPath() { + return requestLine.getPath(); + } + + public String getHeader(String name) { + if (headers == null) { + return ""; + } + + return headers.getOrDefault(name, "") + .split(",")[0]; + } + + public String getParameter(String name) { + return params.get(name); + } +} diff --git a/src/main/java/webserver/http/HttpResponse.java b/src/main/java/webserver/http/HttpResponse.java new file mode 100644 index 000000000..c455a0f52 --- /dev/null +++ b/src/main/java/webserver/http/HttpResponse.java @@ -0,0 +1,114 @@ +package webserver.http; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; + +public class HttpResponse { + private static final Logger logger = LoggerFactory.getLogger(HttpResponse.class); + private DataOutputStream dos; + + private Map headers = new HashMap<>(); + + private HttpStatus httpStatus; + + private String body; + + public HttpResponse(OutputStream out) { + this.dos = new DataOutputStream(out); + } + + public void addHeader(String name, String value) { + headers.put(name, value); + } + + public void ok() { + ok(null); + } + + public void ok(byte[] body) { + httpStatus = HttpStatus.OK; + + response(dos, body); + } + + public void redirect(String location) { + redirect(HttpStatus.FOUND, location); + } + + public void redirect(HttpStatus httpStatus, String location) { + this.httpStatus = httpStatus; + + addHeader("Content-Type", MediaType.TEXT_HTML_UTF8.toString()); + addHeader("Location", location); + + response(dos, null); + } + + + public void notfound() { + httpStatus = HttpStatus.NOT_FOUND; + + addHeader("Content-Type", MediaType.TEXT_HTML_UTF8.toString()); + + response(dos, null); + } + + private void response(DataOutputStream dos, byte[] body) { + byte[] bytes = null; + try { + bytes = toBytes(body); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + write(dos, bytes); + } + + private byte[] toBytes(byte[] body) throws IOException, URISyntaxException { + if (body == null) { + return getHeader(0); + } + + byte[] header = getHeader(body.length); + return merge(header, body); + } + + private byte[] getHeader(long bodyLength) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(String.format("%s %s \r\n", Protocol.HTTP_1_1, httpStatus.toString())); + + for (String key : headers.keySet()) { + stringBuilder.append(String.format("%s: %s \r\n", key, headers.get(key))); + } + + stringBuilder.append(String.format("Content-Length: %s \r\n", bodyLength)); + + stringBuilder.append("\r\n"); + + return stringBuilder.toString().getBytes(); + } + + private byte[] merge(byte[] header, byte[] body) { + byte[] responseBytes = new byte[header.length + body.length]; + System.arraycopy(header, 0, responseBytes, 0, header.length); + System.arraycopy(body, 0, responseBytes, header.length, body.length); + return responseBytes; + } + + private void write(DataOutputStream dos, byte[] response) { + try { + dos.write(response, 0, response.length); + dos.flush(); + } catch (IOException e) { + logger.error(e.getMessage()); + } + } +} diff --git a/src/main/java/webserver/HttpStatus.java b/src/main/java/webserver/http/HttpStatus.java similarity index 95% rename from src/main/java/webserver/HttpStatus.java rename to src/main/java/webserver/http/HttpStatus.java index 383efa9cf..74cb7a92f 100644 --- a/src/main/java/webserver/HttpStatus.java +++ b/src/main/java/webserver/http/HttpStatus.java @@ -1,4 +1,4 @@ -package webserver; +package webserver.http; public enum HttpStatus { OK(200, "OK"), diff --git a/src/main/java/webserver/MediaType.java b/src/main/java/webserver/http/MediaType.java similarity index 92% rename from src/main/java/webserver/MediaType.java rename to src/main/java/webserver/http/MediaType.java index cc451bb25..c855bcc07 100644 --- a/src/main/java/webserver/MediaType.java +++ b/src/main/java/webserver/http/MediaType.java @@ -1,4 +1,4 @@ -package webserver; +package webserver.http; public class MediaType { diff --git a/src/main/java/webserver/Protocol.java b/src/main/java/webserver/http/Protocol.java similarity index 82% rename from src/main/java/webserver/Protocol.java rename to src/main/java/webserver/http/Protocol.java index e5d3bd572..5cdffe420 100644 --- a/src/main/java/webserver/Protocol.java +++ b/src/main/java/webserver/http/Protocol.java @@ -1,4 +1,4 @@ -package webserver; +package webserver.http; import java.util.Arrays; import java.util.Map; @@ -13,7 +13,7 @@ public enum Protocol { private static final Map mapper = Arrays.stream(values()) .collect(Collectors.toUnmodifiableMap( - protocol -> String.format("%s/%s", protocol.name, protocol.version), + protocol -> protocol.toString(), protocol -> protocol )); @@ -30,9 +30,15 @@ public String getVersion() { return version; } + @Override + public String toString() { + return String.format("%s/%s", name, version); + } + public static Protocol from(String protocol) { return Optional.ofNullable(mapper.get(protocol)) .orElseThrow(() -> new IllegalArgumentException(protocol)); } + } diff --git a/src/main/java/webserver/QueryString.java b/src/main/java/webserver/http/QueryString.java similarity index 95% rename from src/main/java/webserver/QueryString.java rename to src/main/java/webserver/http/QueryString.java index ee1292027..2959fa5ed 100644 --- a/src/main/java/webserver/QueryString.java +++ b/src/main/java/webserver/http/QueryString.java @@ -1,4 +1,4 @@ -package webserver; +package webserver.http; import java.util.Arrays; import java.util.Collections; @@ -83,4 +83,8 @@ public String get(final String key) { public String getPath() { return path; } + + public Map toParams() { + return Map.copyOf(value); + } } diff --git a/src/main/java/webserver/http/RequestAbstractController.java b/src/main/java/webserver/http/RequestAbstractController.java new file mode 100644 index 000000000..2948e9c43 --- /dev/null +++ b/src/main/java/webserver/http/RequestAbstractController.java @@ -0,0 +1,23 @@ +package webserver.http; + +public abstract class RequestAbstractController implements RequestController { + @Override + public void service(HttpRequest httpRequest, HttpResponse response) throws Exception { + if (httpRequest.getMethod() == HttpMethod.POST) { + doPost(httpRequest, response); + return; + } + + doGet(httpRequest, response); + } + + @Override + public void doGet(HttpRequest httpRequest, HttpResponse response) throws Exception { + response.notfound(); + } + + @Override + public void doPost(HttpRequest httpRequest, HttpResponse response) throws Exception { + response.notfound(); + } +} diff --git a/src/main/java/webserver/http/RequestController.java b/src/main/java/webserver/http/RequestController.java new file mode 100644 index 000000000..c7f8c353e --- /dev/null +++ b/src/main/java/webserver/http/RequestController.java @@ -0,0 +1,9 @@ +package webserver.http; + +public interface RequestController { + void service(HttpRequest httpRequest, HttpResponse response) throws Exception; + + void doGet(HttpRequest httpRequest, HttpResponse response) throws Exception; + + void doPost(HttpRequest httpRequest, HttpResponse response) throws Exception; +} diff --git a/src/main/java/webserver/RequestFactory.java b/src/main/java/webserver/http/RequestFactory.java similarity index 77% rename from src/main/java/webserver/RequestFactory.java rename to src/main/java/webserver/http/RequestFactory.java index 4e45aa176..6635bb27c 100644 --- a/src/main/java/webserver/RequestFactory.java +++ b/src/main/java/webserver/http/RequestFactory.java @@ -1,4 +1,4 @@ -package webserver; +package webserver.http; import java.io.BufferedReader; import java.io.IOException; @@ -13,27 +13,27 @@ public class RequestFactory { private RequestFactory() { } - public static Request create(BufferedReader bufferedReader) throws IOException { + public static HttpRequest create(BufferedReader bufferedReader) throws IOException { String requestLine = bufferedReader.readLine(); Map header = createHeader(bufferedReader, requestLine); - return new Request(new RequestLine(requestLine), header, createRequestBody(bufferedReader, header)); + return new HttpRequest(new RequestLine(requestLine), header, createRequestBody(bufferedReader, header)); } private static Map createHeader(BufferedReader bufferedReader, String line) throws IOException { Map headers = new LinkedHashMap<>(); - while (!line.equals("")) { + while (line != null && !line.isBlank()) { line = bufferedReader.readLine(); logger.debug("header: {}", line); - saveHeaderLine(line, headers); + addHeadLine(line, headers); } return headers; } - private static void saveHeaderLine(String line, Map headers) { - if (!line.equals("")) { + private static void addHeadLine(String line, Map headers) { + if (line != null && !line.isBlank()) { String[] keyValue = line.split(": "); setHeader(headers, keyValue); } diff --git a/src/main/java/webserver/RequestLine.java b/src/main/java/webserver/http/RequestLine.java similarity index 87% rename from src/main/java/webserver/RequestLine.java rename to src/main/java/webserver/http/RequestLine.java index 0b8f6d72f..c5b53b1b6 100644 --- a/src/main/java/webserver/RequestLine.java +++ b/src/main/java/webserver/http/RequestLine.java @@ -1,10 +1,10 @@ -package webserver; +package webserver.http; public class RequestLine { private static final int MIN_VALUE_SIZE = 3; private static final String DELIMITER = " "; - private HttpMethod httpMethod; + private HttpMethod requestMethod; private QueryString queryString; private Protocol protocol; @@ -17,7 +17,7 @@ public RequestLine(final String value) { throw new IllegalArgumentException(); } - this.httpMethod = HttpMethod.valueOf(values[0]); + this.requestMethod = HttpMethod.valueOf(values[0]); this.queryString = QueryString.parse(values[1]); this.protocol = Protocol.from(values[2]); } @@ -29,7 +29,7 @@ private void validEmpty(String text) { } public HttpMethod getMethod() { - return httpMethod; + return requestMethod; } public String getPath() { diff --git a/src/main/java/controller/RequestMapping.java b/src/main/java/webserver/http/RequestMapping.java similarity index 74% rename from src/main/java/controller/RequestMapping.java rename to src/main/java/webserver/http/RequestMapping.java index dec5c07c1..3d9183ba9 100644 --- a/src/main/java/controller/RequestMapping.java +++ b/src/main/java/webserver/http/RequestMapping.java @@ -1,4 +1,4 @@ -package controller; +package webserver.http; public interface RequestMapping { boolean checkUrl(String url); diff --git a/src/main/java/webserver/http/RequestMappingControllerAdapter.java b/src/main/java/webserver/http/RequestMappingControllerAdapter.java new file mode 100644 index 000000000..1510c8016 --- /dev/null +++ b/src/main/java/webserver/http/RequestMappingControllerAdapter.java @@ -0,0 +1,4 @@ +package webserver.http; + +public abstract class RequestMappingControllerAdapter extends RequestAbstractController implements RequestMapping { +} diff --git a/src/test/java/controller/CreatUserControllerTest.java b/src/test/java/controller/CreatUserControllerTest.java index 2f7d5799e..56119fdb2 100644 --- a/src/test/java/controller/CreatUserControllerTest.java +++ b/src/test/java/controller/CreatUserControllerTest.java @@ -4,10 +4,14 @@ import model.User; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import webserver.*; - -import java.io.IOException; -import java.net.URISyntaxException; +import webserver.RequestControllerContainer; +import webserver.http.HttpRequest; +import webserver.http.HttpResponse; +import webserver.http.HttpStatus; +import webserver.http.RequestLine; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; import java.util.LinkedHashMap; import java.util.Map; @@ -21,7 +25,7 @@ void setup() { } @Test - void serving_post() throws IOException, URISyntaxException { + void service_post() throws Exception { RequestLine requestLine = new RequestLine("POST /user/create HTTP/1.1"); String body = "userId=javajigi&password=password&name=JaeSung&email=javajigi@slipp.net"; @@ -30,17 +34,19 @@ void serving_post() throws IOException, URISyntaxException { headers.put("Content-Length", String.valueOf(body.getBytes().length)); headers.put("Accept", "*/*"); + OutputStream outputStream = new ByteArrayOutputStream(); + + HttpRequest httpRequest = new HttpRequest(requestLine, headers, body); + HttpResponse httpResponse = new HttpResponse(outputStream); - Request request = new Request( - requestLine, - headers, - body); + RequestControllerContainer.match(httpRequest) + .service(httpRequest, httpResponse); - Response response = DispatcherServlet.match(request); + String response = outputStream.toString(); - assertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND); - assertThat(response.getContentType()).isEqualTo(MediaType.TEXT_HTML_UTF8); - assertThat(response.getPath()).isEqualTo("/index.html"); + assertThat(response).contains("HTTP/1.1 " + HttpStatus.FOUND + " \r\n"); + assertThat(response).contains("Content-Type: text/html;charset=utf-8 \r\n"); + assertThat(response).contains("Location: /index.html \r\n"); User user = DataBase.findUserById("javajigi"); @@ -51,7 +57,7 @@ void serving_post() throws IOException, URISyntaxException { } @Test - void serving_get() throws IOException, URISyntaxException { + void serving_get() throws Exception { RequestLine requestLine = new RequestLine("GET /user/create?" + "userId=javajigi&" + "password=password&" + @@ -59,11 +65,18 @@ void serving_get() throws IOException, URISyntaxException { "email=javajigi@slipp.net" + " HTTP/1.1"); - Request request = new Request(requestLine, null, null); + OutputStream outputStream = new ByteArrayOutputStream(); + + HttpRequest httpRequest = new HttpRequest(requestLine, null, null); + HttpResponse httpResponse = new HttpResponse(outputStream); + + RequestControllerContainer.match(httpRequest) + .service(httpRequest, httpResponse); - Response response = DispatcherServlet.match(request); + String response = outputStream.toString(); - assertThat(response.getStatus()).isEqualTo(HttpStatus.NOT_FOUND); + assertThat(response).contains("HTTP/1.1 " + HttpStatus.NOT_FOUND + " \r\n"); + assertThat(response).contains("Content-Type: text/html;charset=utf-8 \r\n"); User user = DataBase.findUserById("javajigi"); diff --git a/src/test/java/controller/IndexControllerTest.java b/src/test/java/controller/IndexControllerTest.java index 53a4bf031..37fba928e 100644 --- a/src/test/java/controller/IndexControllerTest.java +++ b/src/test/java/controller/IndexControllerTest.java @@ -1,24 +1,34 @@ package controller; import org.junit.jupiter.api.Test; -import webserver.*; +import webserver.RequestControllerContainer; +import webserver.http.HttpRequest; +import webserver.http.HttpResponse; +import webserver.http.HttpStatus; +import webserver.http.RequestLine; -import java.io.IOException; -import java.net.URISyntaxException; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; import static org.assertj.core.api.Assertions.assertThat; class IndexControllerTest { @Test - void serving() throws IOException, URISyntaxException { - Request request = new Request(new RequestLine("GET /index.html HTTP/1.1"), null, null); + void service() throws Exception { + OutputStream outputStream = new ByteArrayOutputStream(); - Response response = DispatcherServlet.match(request); + HttpRequest httpRequest = new HttpRequest(new RequestLine("GET / HTTP/1.1"), null, null); + HttpResponse httpResponse = new HttpResponse(outputStream); - assertThat(response.getStatus()).isEqualTo(HttpStatus.OK); - assertThat(response.getPath()).isEqualTo("/index.html"); - assertThat(response.getContentType()).isEqualTo(MediaType.TEXT_HTML_UTF8); - } + RequestControllerContainer.match(httpRequest) + .service(httpRequest, httpResponse); + + String response = outputStream.toString(); + assertThat(response).contains("HTTP/1.1 " + HttpStatus.FOUND + " \r\n"); + assertThat(response).contains("Content-Type: text/html;charset=utf-8 \r\n"); + assertThat(response).contains("Location: /index.html \r\n"); + assertThat(response).contains("Content-Length: 0 \r\n"); + } } \ No newline at end of file diff --git a/src/test/java/controller/LoginControllerTest.java b/src/test/java/controller/LoginControllerTest.java index abcb9b1fb..9fd9b7e8a 100644 --- a/src/test/java/controller/LoginControllerTest.java +++ b/src/test/java/controller/LoginControllerTest.java @@ -4,24 +4,27 @@ import model.User; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import webserver.*; - -import java.io.IOException; -import java.net.URISyntaxException; +import webserver.RequestControllerContainer; +import webserver.http.HttpRequest; +import webserver.http.HttpResponse; +import webserver.http.HttpStatus; +import webserver.http.RequestLine; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; import java.util.LinkedHashMap; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; class LoginControllerTest { - @BeforeEach void setUp() { DataBase.clear(); } @Test - void match() throws IOException, URISyntaxException { + void service() throws Exception { DataBase.addUser(new User( "javajigi", "P@ssw0rD", @@ -34,46 +37,44 @@ void match() throws IOException, URISyntaxException { Map headers = new LinkedHashMap<>(); headers.put("Accept", "*/*"); + OutputStream outputStream = new ByteArrayOutputStream(); - Request request = new Request( - requestLine, - headers, - null); + HttpRequest httpRequest = new HttpRequest(requestLine, headers, null); + HttpResponse httpResponse = new HttpResponse(outputStream); - Response response = DispatcherServlet.match(request); + RequestControllerContainer.match(httpRequest) + .service(httpRequest, httpResponse); - assertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND); - assertThat(response.getContentType()).isEqualTo(MediaType.TEXT_HTML_UTF8); - assertThat(response.getPath()).isEqualTo("/index.html"); - assertThat(response.getCookie()).isEqualTo("logined=true; Path=/"); + String response = outputStream.toString(); - String result = new String(response.getBytes()); - - assertThat(result.indexOf("Set-Cookie: logined=true; Path=/") != -1).isTrue(); + assertThat(response).contains("HTTP/1.1 " + HttpStatus.FOUND + " \r\n"); + assertThat(response).contains("Content-Type: text/html;charset=utf-8 \r\n"); + assertThat(response).contains("Set-Cookie: logined=true; Path=/ \r\n"); + assertThat(response).contains("Location: /index.html \r\n"); + assertThat(response).contains("Content-Length: 0 \r\n"); } @Test - void match_fail() throws IOException, URISyntaxException { + void service_fail() throws Exception { RequestLine requestLine = new RequestLine("GET /user/login?userId=test&password=111 HTTP/1.1"); Map headers = new LinkedHashMap<>(); headers.put("Accept", "*/*"); + OutputStream outputStream = new ByteArrayOutputStream(); - Request request = new Request( - requestLine, - headers, - null); - - Response response = DispatcherServlet.match(request); + HttpRequest httpRequest = new HttpRequest(requestLine, headers, null); + HttpResponse httpResponse = new HttpResponse(outputStream); - assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST); - assertThat(response.getContentType()).isEqualTo(MediaType.TEXT_HTML_UTF8); - assertThat(response.getPath()).isEqualTo("/user/login_failed.html"); - assertThat(response.getCookie()).isEqualTo("logined=false; Path=/"); + RequestControllerContainer.match(httpRequest) + .service(httpRequest, httpResponse); - String result = new String(response.getBytes()); + String response = outputStream.toString(); - assertThat(result.indexOf("Set-Cookie: logined=false; Path=/") != -1).isTrue(); + assertThat(response).contains("HTTP/1.1 " + HttpStatus.BAD_REQUEST + " \r\n"); + assertThat(response).contains("Content-Type: text/html;charset=utf-8 \r\n"); + assertThat(response).contains("Set-Cookie: logined=false; Path=/ \r\n"); + assertThat(response).contains("Location: /user/login_failed.html \r\n"); + assertThat(response).contains("Content-Length: 0 \r\n"); } } diff --git a/src/test/java/controller/UserListControllerTest.java b/src/test/java/controller/UserListControllerTest.java index 010d4adb5..af4cd668d 100644 --- a/src/test/java/controller/UserListControllerTest.java +++ b/src/test/java/controller/UserListControllerTest.java @@ -1,10 +1,14 @@ package controller; import org.junit.jupiter.api.Test; -import webserver.*; - -import java.io.IOException; -import java.net.URISyntaxException; +import webserver.RequestControllerContainer; +import webserver.http.HttpRequest; +import webserver.http.HttpResponse; +import webserver.http.HttpStatus; +import webserver.http.RequestLine; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; import java.util.LinkedHashMap; import java.util.Map; @@ -13,44 +17,46 @@ class UserListControllerTest { @Test - void serving() throws IOException, URISyntaxException { + void service() throws Exception { RequestLine requestLine = new RequestLine("GET /user/list.html HTTP/1.1"); Map headers = new LinkedHashMap<>(); headers.put("Cookie", "logined=true; Path=/"); - headers.put("Accept", "*/*"); + headers.put("Accept", "text/html;charset=utf-8"); + + OutputStream outputStream = new ByteArrayOutputStream(); + HttpRequest httpRequest = new HttpRequest(requestLine, headers, null); + HttpResponse httpResponse = new HttpResponse(outputStream); - Request request = new Request( - requestLine, - headers, - null); + RequestControllerContainer.match(httpRequest) + .service(httpRequest, httpResponse); - Response response = DispatcherServlet.match(request); + String response = outputStream.toString(); - assertThat(response.getStatus()).isEqualTo(HttpStatus.OK); - assertThat(response.getContentType()).isEqualTo(MediaType.TEXT_HTML_UTF8); - assertThat(response.getPath()).isEqualTo("/user/list.html"); + assertThat(response).contains("HTTP/1.1 " + HttpStatus.OK + " \r\n"); + assertThat(response).contains("Content-Type: text/html;charset=utf-8 \r\n"); } @Test - void serving_unauthorized() throws IOException, URISyntaxException { + void service_unauthorized() throws Exception { RequestLine requestLine = new RequestLine("GET /user/list.html HTTP/1.1"); Map headers = new LinkedHashMap<>(); headers.put("Cookie", "logined=false; Path=/"); - headers.put("Accept", "*/*"); + headers.put("Accept", "text/html;charset=utf-8"); + + OutputStream outputStream = new ByteArrayOutputStream(); + HttpRequest httpRequest = new HttpRequest(requestLine, headers, null); + HttpResponse httpResponse = new HttpResponse(outputStream); - Request request = new Request( - requestLine, - headers, - null); + RequestControllerContainer.match(httpRequest) + .service(httpRequest, httpResponse); - Response response = DispatcherServlet.match(request); + String response = outputStream.toString(); - assertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND); - assertThat(response.getContentType()).isEqualTo(MediaType.TEXT_HTML_UTF8); - assertThat(response.getPath()).isEqualTo("/index.html"); + assertThat(response).contains("HTTP/1.1 " + HttpStatus.FOUND + " \r\n"); + assertThat(response).contains("Content-Type: text/html;charset=utf-8 \r\n"); } } diff --git a/src/test/java/webserver/DispatcherServletTest.java b/src/test/java/webserver/DispatcherServletTest.java deleted file mode 100644 index 64f27d478..000000000 --- a/src/test/java/webserver/DispatcherServletTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package webserver; - -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.net.URISyntaxException; - -import static org.assertj.core.api.Assertions.assertThat; - -class DispatcherServletTest { - - @Test - void match() throws IOException, URISyntaxException { - Request request = new Request(new RequestLine("GET /index.html HTTP/1.1"), null, null); - - Response response = DispatcherServlet.match(request); - - assertThat(response.getPath()).isEqualTo("/index.html"); - assertThat(response.getContentType()).isEqualTo(MediaType.TEXT_HTML_UTF8); - } - -} \ No newline at end of file diff --git a/src/test/java/webserver/HttpRequestTest.java b/src/test/java/webserver/HttpRequestTest.java deleted file mode 100644 index 73bc72794..000000000 --- a/src/test/java/webserver/HttpRequestTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package webserver; - -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.client.RestTemplate; - -import static org.assertj.core.api.Assertions.assertThat; - -public class HttpRequestTest { - @Test - void request_resttemplate() { - RestTemplate restTemplate = new RestTemplate(); - ResponseEntity response = restTemplate.getForEntity("http://localhost:8080", String.class); - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); - } -} diff --git a/src/test/java/webserver/RequestLineTest.java b/src/test/java/webserver/http/HttpRequestLineTest.java similarity index 92% rename from src/test/java/webserver/RequestLineTest.java rename to src/test/java/webserver/http/HttpRequestLineTest.java index 25d9d3eb7..35008195e 100644 --- a/src/test/java/webserver/RequestLineTest.java +++ b/src/test/java/webserver/http/HttpRequestLineTest.java @@ -1,13 +1,16 @@ -package webserver; +package webserver.http; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import webserver.http.HttpMethod; +import webserver.http.QueryString; +import webserver.http.RequestLine; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -class RequestLineTest { +class HttpRequestLineTest { @Test void get() { diff --git a/src/test/java/webserver/HttpMethodTest.java b/src/test/java/webserver/http/HttpRequestMethodTest.java similarity index 87% rename from src/test/java/webserver/HttpMethodTest.java rename to src/test/java/webserver/http/HttpRequestMethodTest.java index 25d04ce04..4602ad620 100644 --- a/src/test/java/webserver/HttpMethodTest.java +++ b/src/test/java/webserver/http/HttpRequestMethodTest.java @@ -1,11 +1,12 @@ -package webserver; +package webserver.http; import org.junit.jupiter.api.Test; +import webserver.http.HttpMethod; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -class HttpMethodTest { +class HttpRequestMethodTest { @Test void GET() { diff --git a/src/test/java/webserver/http/HttpRequestTest.java b/src/test/java/webserver/http/HttpRequestTest.java new file mode 100644 index 000000000..331004fac --- /dev/null +++ b/src/test/java/webserver/http/HttpRequestTest.java @@ -0,0 +1,60 @@ +package webserver.http; + +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.InputStreamReader; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class HttpRequestTest { + + private String testDirectory = "./src/test/resources/"; + + @Test + void request_resttemplate() { + RestTemplate restTemplate = new RestTemplate(); + ResponseEntity response = restTemplate.getForEntity("http://localhost:8080", String.class); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + } + + @Test + public void request_GET() throws Exception { + InputStream in = new FileInputStream(testDirectory + "Http_GET.txt"); + HttpRequest httpRequest = RequestFactory.create(new BufferedReader(new InputStreamReader(in, "UTF-8"))); + + assertEquals("GET", httpRequest.getMethod().toString()); + assertEquals("/user/create", httpRequest.getPath()); + assertEquals("keep-alive", httpRequest.getHeader("Connection")); + assertEquals("javajigi", httpRequest.getParameter("userId")); + } + + @Test + public void request_POST1() throws Exception { + InputStream in = new FileInputStream(testDirectory + "Http_POST1.txt"); + HttpRequest httpRequest = RequestFactory.create(new BufferedReader(new InputStreamReader(in, "UTF-8"))); + + assertEquals("POST", httpRequest.getMethod().toString()); + assertEquals("/user/create", httpRequest.getPath()); + assertEquals("keep-alive", httpRequest.getHeader("Connection")); + assertEquals("javajigi", httpRequest.getParameter("userId")); + } + + @Test + public void request_POST2() throws Exception { + InputStream in = new FileInputStream(testDirectory + "Http_POST2.txt"); + HttpRequest httpRequest = RequestFactory.create(new BufferedReader(new InputStreamReader(in, "UTF-8"))); + + assertEquals("POST", httpRequest.getMethod().toString()); + assertEquals("/user/create", httpRequest.getPath()); + assertEquals("keep-alive", httpRequest.getHeader("Connection")); + assertEquals("1", httpRequest.getParameter("id")); + assertEquals("javajigi", httpRequest.getParameter("userId")); + } +} diff --git a/src/test/java/webserver/http/HttpResponseTest.java b/src/test/java/webserver/http/HttpResponseTest.java new file mode 100644 index 000000000..2c1abf8d3 --- /dev/null +++ b/src/test/java/webserver/http/HttpResponseTest.java @@ -0,0 +1,54 @@ +package webserver.http; + +import com.github.jknack.handlebars.Handlebars; +import com.github.jknack.handlebars.Template; +import com.github.jknack.handlebars.io.ClassPathTemplateLoader; +import com.github.jknack.handlebars.io.TemplateLoader; +import model.User; +import org.junit.jupiter.api.Test; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStream; + +public class HttpResponseTest { + private String testDirectory = "./src/test/resources/"; + + @Test + public void ok() throws Exception { + // Http_Forward.txt 결과는 응답 body에 index.html이 포함되어 있어야 한다. + HttpResponse response = new HttpResponse(createOutputStream("Http_Forward.txt")); + response.ok(rendering().getBytes()); + } + + private String rendering() throws Exception { + TemplateLoader loader = new ClassPathTemplateLoader(); + loader.setPrefix("/templates"); + loader.setSuffix(".html"); + Handlebars handlebars = new Handlebars(loader); + + Template template = handlebars.compile("/user/profile"); + + User user = new User("javajigi", "password", "자바지기", "javajigi@gmail.com"); + return template.apply(user); + } + + @Test + public void redirect() throws Exception { + // Http_Redirect.txt 결과는 응답 headere에 Location 정보가 /index.html로 포함되어 있어야 한다. + HttpResponse response = new HttpResponse(createOutputStream("Http_Redirect.txt")); + response.redirect("/index.html"); + } + + @Test + public void redirectCookies() throws Exception { + // Http_Cookie.txt 결과는 응답 header에 Set-Cookie 값으로 logined=true 값이 포함되어 있어야 한다. + HttpResponse response = new HttpResponse(createOutputStream("Http_Cookie.txt")); + response.addHeader("Set-Cookie", "logined=true"); + response.redirect("/index.html"); + } + + private OutputStream createOutputStream(String filename) throws FileNotFoundException { + return new FileOutputStream(testDirectory + filename); + } +} \ No newline at end of file diff --git a/src/test/java/webserver/ProtocolTest.java b/src/test/java/webserver/http/ProtocolTest.java similarity index 81% rename from src/test/java/webserver/ProtocolTest.java rename to src/test/java/webserver/http/ProtocolTest.java index f22a52d2f..ae4b8cac8 100644 --- a/src/test/java/webserver/ProtocolTest.java +++ b/src/test/java/webserver/http/ProtocolTest.java @@ -1,5 +1,6 @@ -package webserver; +package webserver.http; +import webserver.http.Protocol; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; diff --git a/src/test/java/webserver/QueryStringTest.java b/src/test/java/webserver/http/QueryStringTest.java similarity index 95% rename from src/test/java/webserver/QueryStringTest.java rename to src/test/java/webserver/http/QueryStringTest.java index 50447cb30..694cdcfe8 100644 --- a/src/test/java/webserver/QueryStringTest.java +++ b/src/test/java/webserver/http/QueryStringTest.java @@ -1,5 +1,6 @@ -package webserver; +package webserver.http; +import webserver.http.QueryString; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; diff --git a/src/test/resources/Http_GET.txt b/src/test/resources/Http_GET.txt new file mode 100644 index 000000000..bd2324b9d --- /dev/null +++ b/src/test/resources/Http_GET.txt @@ -0,0 +1,4 @@ +GET /user/create?userId=javajigi&password=password&name=JaeSung HTTP/1.1 +Host: localhost:8080 +Connection: keep-alive +Accept: */* diff --git a/src/test/resources/Http_POST1.txt b/src/test/resources/Http_POST1.txt new file mode 100644 index 000000000..b790f0666 --- /dev/null +++ b/src/test/resources/Http_POST1.txt @@ -0,0 +1,8 @@ +POST /user/create HTTP/1.1 +Host: localhost:8080 +Connection: keep-alive +Content-Length: 46 +Content-Type: application/x-www-form-urlencoded +Accept: */* + +userId=javajigi&password=password&name=JaeSung \ No newline at end of file diff --git a/src/test/resources/Http_POST2.txt b/src/test/resources/Http_POST2.txt new file mode 100644 index 000000000..d8151a6a7 --- /dev/null +++ b/src/test/resources/Http_POST2.txt @@ -0,0 +1,8 @@ +POST /user/create?id=1 HTTP/1.1 +Host: localhost:8080 +Connection: keep-alive +Content-Length: 46 +Content-Type: application/x-www-form-urlencoded +Accept: */* + +userId=javajigi&password=password&name=JaeSung \ No newline at end of file