diff --git a/README.md b/README.md index 3f7ef2e..4d234ad 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,9 @@ ### 요구사항 7 - 지금까지 구현한 소스 코드는 stylesheet 파일을 지원하지 못하고 있다. Stylesheet 파일을 지원하도록 구현하도록 한다. +## 2단계 - 리팩토링 - - - +- 리팩토링 접근 방법 - 메소드 분리 및 클래스 분리 +- 클라이언트 요청 데이터를 처리하는 로직을 별도의 클래스로 분리한다.(HttpRequest) +- 클라이언트 응답 데이터를 처리하는 로직을 별도의 클래스로 분리한다.(HttpResponse) +- 다형성을 활용해 클라이언트 요청 URL에 대한 분기 처리를 제거한다. diff --git a/build.gradle b/build.gradle index 039b2bc..3c7e5e8 100644 --- a/build.gradle +++ b/build.gradle @@ -16,11 +16,11 @@ dependencies { implementation 'org.apache.commons:commons-dbcp2:2.5.0' implementation 'org.reflections:reflections:0.9.11' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0' - testImplementation 'org.assertj:assertj-core:3.11.1' + testImplementation 'org.assertj:assertj-core:3.18.0' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' runtimeOnly 'com.h2database:h2:1.4.200' } test { useJUnitPlatform() -} \ No newline at end of file +} diff --git a/src/main/java/controller/BaseController.java b/src/main/java/controller/BaseController.java new file mode 100644 index 0000000..5a6a160 --- /dev/null +++ b/src/main/java/controller/BaseController.java @@ -0,0 +1,27 @@ +package controller; + +import http.HttpMethod; +import http.HttpRequest; +import http.HttpResponse; + +public class BaseController implements Controller { + + @Override + public void service(HttpRequest request, HttpResponse response) { + HttpMethod method = request.getMethod(); + + if (method.isPost()) { + doPost(request, response); + } else { + doGet(request, response); + } + } + + public void doPost(HttpRequest request, HttpResponse response) { + + } + + public void doGet(HttpRequest request, HttpResponse response) { + + } +} diff --git a/src/main/java/controller/Controller.java b/src/main/java/controller/Controller.java new file mode 100644 index 0000000..3d23ddc --- /dev/null +++ b/src/main/java/controller/Controller.java @@ -0,0 +1,8 @@ +package controller; + +import http.HttpRequest; +import http.HttpResponse; + +public interface Controller { + void service(HttpRequest request, HttpResponse response); +} diff --git a/src/main/java/controller/CreateUserController.java b/src/main/java/controller/CreateUserController.java new file mode 100644 index 0000000..906d282 --- /dev/null +++ b/src/main/java/controller/CreateUserController.java @@ -0,0 +1,28 @@ +package controller; + +import db.DataBase; +import model.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import http.HttpRequest; +import http.HttpResponse; + +public class CreateUserController extends BaseController { + private static final Logger log = LoggerFactory.getLogger(CreateUserController.class); + + @Override + public void doPost(HttpRequest request, HttpResponse response) { + User user = getUserFrom(request); + DataBase.addUser(user); + response.sendRedirect("/index.html"); + log.debug("{}님이 회원가입에 성공하셨습니다.", user.getUserId()); + } + + private User getUserFrom(HttpRequest request) { + String userId = request.getParameter("userId"); + String password = request.getParameter("password"); + String name = request.getParameter("name"); + String email = request.getParameter("email"); + return new User(userId, password, name, email); + } +} diff --git a/src/main/java/controller/ListUserController.java b/src/main/java/controller/ListUserController.java new file mode 100644 index 0000000..2b2fad3 --- /dev/null +++ b/src/main/java/controller/ListUserController.java @@ -0,0 +1,74 @@ +package controller; + +import db.DataBase; +import db.SessionDataBase; +import model.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import util.HttpRequestUtils; +import http.HttpRequest; +import http.HttpResponse; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Collection; +import java.util.Map; + +public class ListUserController extends BaseController { + private static final Logger log = LoggerFactory.getLogger(ListUserController.class); + + @Override + public void doGet(HttpRequest request, HttpResponse response) { + String sessionId = request.getHeader("Cookie"); + if (!isLogin(sessionId)) { + response.sendRedirect("/user/login.html"); + } else { + String body = getBody("/user/list.html"); + int tbodyIndex = body.indexOf(""); + String result = addUserList(body, tbodyIndex).toString(); + response.forwardBody(result); + } + } + + private boolean isLogin(String id) { + Map cookieStringMap = HttpRequestUtils.parseCookies(id); + log.info("Cookie: {}", cookieStringMap.toString()); + return SessionDataBase.isLoginUser(cookieStringMap.get("JSESSIONID")); + } + + private String getBody(String url) { + byte[] body = new byte[0]; + try { + body = Files.readAllBytes(new File("./webapp" + url).toPath()); + } catch (IOException e) { + e.printStackTrace(); + } + return new String(body); + } + + private StringBuilder processUserList(String body, int tbodyIndex) { + StringBuilder result = new StringBuilder(body.substring(0, tbodyIndex + 7)); + Collection users = DataBase.findAll(); + int id = 0; + for (User user : users) { + id++; + result.append("") + .append(id) + .append(" ") + .append(user.getUserId()) + .append(" ") + .append(user.getName()) + .append(" ") + .append(user.getEmail()) + .append(" ") + .append("수정"); + } + return result; + } + + private StringBuilder addUserList(String body, int tbodyIndex) { + StringBuilder result = processUserList(body, tbodyIndex); + return result.append(body.substring(tbodyIndex + 7)); + } +} diff --git a/src/main/java/controller/LoginController.java b/src/main/java/controller/LoginController.java new file mode 100644 index 0000000..5f865d6 --- /dev/null +++ b/src/main/java/controller/LoginController.java @@ -0,0 +1,32 @@ +package controller; + +import db.DataBase; +import db.SessionDataBase; +import model.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import http.HttpRequest; +import http.HttpResponse; + +import java.util.UUID; + +public class LoginController extends BaseController { + private static final Logger log = LoggerFactory.getLogger(LoginController.class); + + @Override + public void doPost(HttpRequest request, HttpResponse response) { + String userId = request.getParameter("userId"); + String password = request.getParameter("password"); + User targetUser = DataBase.findUserById(userId); + if (targetUser == null || !targetUser.isValidPassword(password)) { + response.addHeader("Set-Cookie", "loggedIn=false"); + response.sendRedirect("/user/login_failed.html"); + return; + } + UUID uuid = UUID.randomUUID(); + SessionDataBase.sessions.put(uuid.toString(), targetUser); + response.addHeader("Set-Cookie", SessionDataBase.JSESSIONID + "=" + uuid); + response.sendRedirect("/index.html"); + log.info("{}님이 로그인하셨습니다.", userId); + } +} diff --git a/src/main/java/db/SessionDataBase.java b/src/main/java/db/SessionDataBase.java new file mode 100644 index 0000000..3311983 --- /dev/null +++ b/src/main/java/db/SessionDataBase.java @@ -0,0 +1,20 @@ +package db; + +import com.google.common.collect.Maps; +import model.User; + +import java.util.Map; + +public class SessionDataBase { + public static Map sessions = Maps.newHashMap(); + + public static final String JSESSIONID = "JSESSIONID"; + + public static boolean isLoginUser(String sessionId) { + return getSessionUser(sessionId) != null; + } + + public static User getSessionUser(String sessionId) { + return sessions.get(sessionId); + } +} diff --git a/src/main/java/http/HttpMethod.java b/src/main/java/http/HttpMethod.java new file mode 100644 index 0000000..d344cc1 --- /dev/null +++ b/src/main/java/http/HttpMethod.java @@ -0,0 +1,9 @@ +package http; + +public enum HttpMethod { + GET, POST; + + public boolean isPost() { + return this == POST; + } +} diff --git a/src/main/java/http/HttpRequest.java b/src/main/java/http/HttpRequest.java new file mode 100644 index 0000000..0ecb9f5 --- /dev/null +++ b/src/main/java/http/HttpRequest.java @@ -0,0 +1,85 @@ +package http; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import util.HttpRequestUtils; +import util.IOUtils; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +public class HttpRequest { + private static final Logger log = LoggerFactory.getLogger(HttpRequest.class); + + private String requestLine; + private final Map headers = new HashMap<>(); + private Map parameters; + + public HttpRequest(InputStream in) { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); + requestLine = br.readLine(); + processHeaders(br); + processParameters(br); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void processHeaders(BufferedReader br) throws IOException { + String line = br.readLine(); + while (!line.isEmpty()) { + log.debug("headers : {}", line); + line = br.readLine(); + if (line == null) { + return; + } + parseHeader(line); + } + } + + private void parseHeader(String line) { + String[] headerTokens = line.split(": "); + if (headerTokens.length == 2) { + headers.put(headerTokens[0], headerTokens[1]); + } + } + + private void processParameters(BufferedReader br) throws IOException { + String[] tokens = requestLine.split(" "); + String queryString = ""; + String requestBody = ""; + if (tokens[1].contains("?")) { + queryString = tokens[1].substring(tokens[1].indexOf("?") + 1); + } + if (headers.get("Content-Length") != null) { + requestBody = IOUtils.readData(br, Integer.parseInt(headers.get("Content-Length"))); + } + queryString = queryString + "&" + requestBody; + parameters = HttpRequestUtils.parseQueryString(queryString); + } + + public HttpMethod getMethod() { + String[] tokens = requestLine.split(" "); + String method = tokens[0]; + return HttpMethod.valueOf(method); + } + + public String getPath() { + String[] tokens = requestLine.split(" "); + if (tokens[1].contains("?")) { + return tokens[1].substring(0, tokens[1].indexOf("?")); + } + return tokens[1]; + } + + public String getHeader(String fieldName) { + return headers.get(fieldName); + } + + public String getParameter(String fieldName) { + return parameters.get(fieldName); + } +} diff --git a/src/main/java/webserver/HttpResponse.java b/src/main/java/http/HttpResponse.java similarity index 65% rename from src/main/java/webserver/HttpResponse.java rename to src/main/java/http/HttpResponse.java index fa2c929..b1b9e3a 100644 --- a/src/main/java/webserver/HttpResponse.java +++ b/src/main/java/http/HttpResponse.java @@ -1,18 +1,27 @@ -package webserver; +package http; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.DataOutputStream; import java.io.*; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.HashMap; import java.util.Map; public class HttpResponse { private static final Logger log = LoggerFactory.getLogger(HttpResponse.class); - private Map headers = new HashMap<>(); - private DataOutputStream dos; + + private static final Map EXTENSIONS = new HashMap<>(); + private final Map headers = new HashMap<>(); + private final DataOutputStream dos; + + static { + EXTENSIONS.put(".css", "text/css"); + EXTENSIONS.put(".js", "application/javascript"); + EXTENSIONS.put(".html", "text/html"); + } public HttpResponse(OutputStream out) { this.dos = new DataOutputStream(out); @@ -24,33 +33,21 @@ public void addHeader(String fieldName, String fieldValue) { public void forward(String url) throws IOException { byte[] body = Files.readAllBytes(new File("./webapp" + url).toPath()); - if (url.endsWith(".css")) { - response200HeaderWithCss(body.length); - } else { - response200Header(body.length); - } + String extension = url.substring(url.lastIndexOf(".")); + response200Header(body.length, EXTENSIONS.get(extension)); responseBody(body); } - public void forwardBody(String string) { - - } - - public void response200HeaderWithCss(int lengthOfBodyContent) { - try { - dos.writeBytes("HTTP/1.1 200 OK \r\n"); - dos.writeBytes("Content-Type: text/css;charset=utf-8\r\n"); - dos.writeBytes("Content-Length: " + lengthOfBodyContent + "\r\n"); - dos.writeBytes("\r\n"); - } catch (IOException e) { - log.error(e.getMessage()); - } + public void forwardBody(String result) { + byte[] body = result.getBytes(StandardCharsets.UTF_8); + response200Header(body.length, "text/html"); + responseBody(body); } - public void response200Header(int lengthOfBodyContent) { + public void response200Header(int lengthOfBodyContent, String contentType) { try { dos.writeBytes("HTTP/1.1 200 OK \r\n"); - dos.writeBytes("Content-Type: text/html;charset=utf-8\r\n"); + dos.writeBytes("Content-Type:" + contentType + ";charset=utf-8\r\n"); dos.writeBytes("Content-Length: " + lengthOfBodyContent + "\r\n"); dos.writeBytes("\r\n"); } catch (IOException e) { @@ -73,7 +70,7 @@ public void sendRedirect(String url) { processHeaders(); dos.writeBytes("Location: " + url + "\r\n"); } catch (IOException e) { - + e.printStackTrace(); } } diff --git a/src/main/java/model/User.java b/src/main/java/model/User.java index b7abb73..b3a3127 100644 --- a/src/main/java/model/User.java +++ b/src/main/java/model/User.java @@ -13,6 +13,10 @@ public User(String userId, String password, String name, String email) { this.email = email; } + public boolean isValidPassword(String password) { + return this.password.equals(password); + } + public String getUserId() { return userId; } diff --git a/src/main/java/util/HttpRequestUtils.java b/src/main/java/util/HttpRequestUtils.java index 6386da6..a679d7e 100644 --- a/src/main/java/util/HttpRequestUtils.java +++ b/src/main/java/util/HttpRequestUtils.java @@ -3,6 +3,7 @@ import java.net.URLDecoder; import java.util.Arrays; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import com.google.common.base.Strings; @@ -10,8 +11,8 @@ public class HttpRequestUtils { /** - * @param queryString은 - * URL에서 ? 이후에 전달되는 field1=value1&field2=value2 형식임 + * @param queryString + * 은 URL에서 ? 이후에 전달되는 field1=value1&field2=value2 형식임 * @return */ public static Map parseQueryString(String queryString) { @@ -19,7 +20,7 @@ public static Map parseQueryString(String queryString) { } /** - * @param 쿠키 + * @param cookies * 값은 name1=value1; name2=value2 형식임 * @return */ @@ -32,14 +33,14 @@ private static Map parseValues(String values, String separator) return Maps.newHashMap(); } String[] tokens = values.split(separator); - return Arrays.stream(tokens).map(t -> getKeyValue(t, "=")).filter(p -> p != null) - .collect(Collectors.toMap(p -> p.getKey(), p -> { + return Arrays.stream(tokens).map(t -> getKeyValue(t, "=")).filter(Objects::nonNull) + .collect(Collectors.toMap(Pair::getKey, p -> { try { return URLDecoder.decode(p.getValue(), "UTF-8"); } catch (Exception e) { e.printStackTrace(); } - return null; + return p.getValue(); })); } diff --git a/src/main/java/util/IOUtils.java b/src/main/java/util/IOUtils.java index d88efe7..56186c6 100644 --- a/src/main/java/util/IOUtils.java +++ b/src/main/java/util/IOUtils.java @@ -3,18 +3,18 @@ import java.io.BufferedReader; import java.io.IOException; -public class IOUtils { +public class IOUtils { /** - * @param BufferedReader는 + * @param bufferedReader * Request Body를 시작하는 시점이어야 - * @param contentLength는 + * @param contentLength * Request Header의 Content-Length 값이다. * @return * @throws IOException */ - public static String readData(BufferedReader br, int contentLength) throws IOException { + public static String readData(BufferedReader bufferedReader, int contentLength) throws IOException { char[] body = new char[contentLength]; - br.read(body, 0, contentLength); + bufferedReader.read(body, 0, contentLength); return String.copyValueOf(body); } } diff --git a/src/main/java/webserver/HttpMethod.java b/src/main/java/webserver/HttpMethod.java deleted file mode 100644 index 3db4524..0000000 --- a/src/main/java/webserver/HttpMethod.java +++ /dev/null @@ -1,5 +0,0 @@ -package webserver; - -public enum HttpMethod { - GET, POST; -} diff --git a/src/main/java/webserver/HttpRequest.java b/src/main/java/webserver/HttpRequest.java deleted file mode 100644 index 9be415f..0000000 --- a/src/main/java/webserver/HttpRequest.java +++ /dev/null @@ -1,43 +0,0 @@ -package webserver; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import util.HttpRequestUtils; -import util.IOUtils; - -import java.io.BufferedReader; -import java.util.Map; - -public class HttpRequest { - private static final Logger log = LoggerFactory.getLogger(HttpRequest.class); - - private String startLine; - private Map headers; - - public HttpRequest(String startLine, Map headers) { - this.startLine = startLine; - this.headers = headers; - } - - public HttpMethod getMethod() { - String[] tokens = startLine.split(" "); - String method = tokens[0]; - return HttpMethod.valueOf(method); - } - - public String getPath() { - String[] tokens = startLine.split(" "); - return tokens[1]; - } - - public String getHeader(String fieldName) { - return headers.get(fieldName); - } - - public String getParameter(String fieldName, BufferedReader br) throws Exception { - String RequestBody = IOUtils.readData(br, Integer.parseInt(headers.get("Content-Length"))); - Map parameters = HttpRequestUtils.parseQueryString(RequestBody); - log.info("parameters : {}", parameters.toString()); - return parameters.get(fieldName); - } -} diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index 75f3e21..ec69f02 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -2,20 +2,29 @@ import java.io.*; import java.net.Socket; -import java.nio.file.Files; -import java.util.*; - -import db.DataBase; -import model.User; +import java.util.HashMap; +import java.util.Map; + +import controller.Controller; +import controller.CreateUserController; +import controller.ListUserController; +import controller.LoginController; +import http.HttpRequest; +import http.HttpResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import util.HttpRequestUtils; -import util.IOUtils; public class RequestHandler extends Thread { private static final Logger log = LoggerFactory.getLogger(RequestHandler.class); - private Socket connection; + private static final Map CONTROLLER_MAP = new HashMap<>(); + private final Socket connection; + + static { + CONTROLLER_MAP.put("/create", new CreateUserController()); + CONTROLLER_MAP.put("/login", new LoginController()); + CONTROLLER_MAP.put("/list", new ListUserController()); + } public RequestHandler(Socket connectionSocket) { this.connection = connectionSocket; @@ -26,91 +35,19 @@ public void run() { connection.getPort()); try (InputStream in = connection.getInputStream(); OutputStream out = connection.getOutputStream()) { - // TODO 사용자 요청에 대한 처리는 이 곳에 구현하면 된다. - BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8")); - - String requestLine = br.readLine(); - String line = requestLine; - Map headers = new HashMap<>(); - while (!"".equals(line)) { - System.out.println(line); - line = br.readLine(); - String[] headerTokens = line.split(": "); - if (headerTokens.length == 2) { - headers.put(headerTokens[0], headerTokens[1]); - } - } - - HttpRequest httpRequest = new HttpRequest(requestLine, headers); + HttpRequest httpRequest = new HttpRequest(in); HttpResponse httpResponse = new HttpResponse(out); - HttpMethod method = httpRequest.getMethod(); - log.info("Http Method: {}", method.name()); String url = httpRequest.getPath(); - log.info("path : {}", url); - DataOutputStream dos = new DataOutputStream(out); - - if (url.startsWith("/create")) { - String RequestBody = IOUtils.readData(br, Integer.parseInt(headers.get("Content-Length"))); - Map parameters = HttpRequestUtils.parseQueryString(RequestBody); - log.info("user : {}", parameters.toString()); - DataBase.addUser(new User(parameters.get("userId"), parameters.get("password"), parameters.get("name"), parameters.get("email"))); - httpResponse.sendRedirect("/index.html"); - } else if (url.startsWith("/login")) { - String contentLength = headers.get("Content-Length"); - log.info("contentLength : {}", contentLength); - String RequestBody = IOUtils.readData(br, Integer.parseInt(headers.get("Content-Length"))); - - Map info = HttpRequestUtils.parseQueryString(RequestBody); - String userId = info.get("userId"); - String password = info.get("password"); - User targetUser = DataBase.findUserById(userId); - log.info("userId : {}", userId); - log.info("password : {}", password); - if (targetUser == null || !password.equals(targetUser.getPassword())) { - httpResponse.addHeader("Set-Cookie", "logined=false"); - httpResponse.sendRedirect("/user/login_failed.html"); - } else if (password.equals(targetUser.getPassword())) { - log.info("로그인 성공"); - httpResponse.addHeader("Set-Cookie", "logined=true"); - httpResponse.sendRedirect("/index.html"); - } - } else if (url.startsWith("/list")) { - String cookies = headers.get("Cookie"); - Map cookieStringMap = HttpRequestUtils.parseCookies(cookies); - log.info("Cookie: {}", cookieStringMap.toString()); - if (cookieStringMap.get("logined").equals("false")) { - httpResponse.sendRedirect("/user/login.html"); - } else { - url = "/user/list.html"; - byte[] body = Files.readAllBytes(new File("./webapp" + url).toPath()); - String bodyStr = new String(body); - int tbodyIndex = bodyStr.indexOf(""); - log.info("bodyStr : {}", bodyStr.substring(0, tbodyIndex + 7)); - - StringBuilder result = new StringBuilder(bodyStr.substring(0, tbodyIndex + 7)); - Collection users = DataBase.findAll(); - int id = 0; - for (User user : users) { - id++; - result.append("") - .append(id).append(" ") - .append(user.getUserId()).append(" ") - .append(user.getName()).append(" ") - .append(user.getEmail()).append(" ") - .append("수정"); - } - result.append(bodyStr.substring(tbodyIndex + 7)); - log.info("result: {}", result.toString()); - httpResponse.response200Header(result.toString().getBytes().length); - httpResponse.responseBody(result.toString().getBytes()); - } - } else { + if (!CONTROLLER_MAP.containsKey(url)) { httpResponse.forward(url); + return; } + + Controller controller = CONTROLLER_MAP.get(url); + controller.service(httpRequest, httpResponse); } catch (IOException e) { log.error(e.getMessage()); } - } } diff --git a/src/main/java/webserver/WebServer.java b/src/main/java/webserver/WebServer.java index 91f4a0f..bac543e 100644 --- a/src/main/java/webserver/WebServer.java +++ b/src/main/java/webserver/WebServer.java @@ -2,6 +2,8 @@ import java.net.ServerSocket; import java.net.Socket; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,9 +27,9 @@ public static void main(String args[]) throws Exception { // 클라이언트가 연결될때까지 대기한다. Socket connection; + ExecutorService executorService = Executors.newFixedThreadPool(10); while ((connection = listenSocket.accept()) != null) { - RequestHandler requestHandler = new RequestHandler(connection); - requestHandler.start(); + executorService.execute(new RequestHandler(connection)); } } } diff --git a/src/test/java/util/HttpRequestUtilsTest.java b/src/test/java/util/HttpRequestUtilsTest.java index a4265f5..d2918c6 100644 --- a/src/test/java/util/HttpRequestUtilsTest.java +++ b/src/test/java/util/HttpRequestUtilsTest.java @@ -1,73 +1,78 @@ package util; -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; - import java.util.Map; -import org.junit.Test; - +import org.assertj.core.api.SoftAssertions; +import org.assertj.core.api.junit.jupiter.InjectSoftAssertions; +import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import util.HttpRequestUtils.Pair; -public class HttpRequestUtilsTest { +@ExtendWith(SoftAssertionsExtension.class) +class HttpRequestUtilsTest { + + @InjectSoftAssertions + SoftAssertions softly; + @Test - public void parseQueryString() { + void parseQueryString() { String queryString = "userId=javajigi"; Map parameters = HttpRequestUtils.parseQueryString(queryString); - assertThat(parameters.get("userId"), is("javajigi")); - assertThat(parameters.get("password"), is(nullValue())); + softly.assertThat(parameters.get("userId")).isEqualTo("javajigi"); + softly.assertThat(parameters.get("password")).isNull(); queryString = "userId=javajigi&password=password2"; parameters = HttpRequestUtils.parseQueryString(queryString); - assertThat(parameters.get("userId"), is("javajigi")); - assertThat(parameters.get("password"), is("password2")); + softly.assertThat(parameters.get("userId")).isEqualTo("javajigi"); + softly.assertThat(parameters.get("password")).isEqualTo("password2"); } @Test - public void parseQueryString_null() { + void parseQueryString_null() { Map parameters = HttpRequestUtils.parseQueryString(null); - assertThat(parameters.isEmpty(), is(true)); + softly.assertThat(parameters.isEmpty()).isTrue(); parameters = HttpRequestUtils.parseQueryString(""); - assertThat(parameters.isEmpty(), is(true)); + softly.assertThat(parameters.isEmpty()).isTrue(); parameters = HttpRequestUtils.parseQueryString(" "); - assertThat(parameters.isEmpty(), is(true)); + softly.assertThat(parameters.isEmpty()).isTrue(); } @Test - public void parseQueryString_invalid() { + void parseQueryString_invalid() { String queryString = "userId=javajigi&password"; Map parameters = HttpRequestUtils.parseQueryString(queryString); - assertThat(parameters.get("userId"), is("javajigi")); - assertThat(parameters.get("password"), is(nullValue())); + softly.assertThat(parameters.get("userId")).isEqualTo("javajigi"); + softly.assertThat(parameters.get("password")).isNull(); } @Test - public void parseCookies() { + void parseCookies() { String cookies = "logined=true; JSessionId=1234"; Map parameters = HttpRequestUtils.parseCookies(cookies); - assertThat(parameters.get("logined"), is("true")); - assertThat(parameters.get("JSessionId"), is("1234")); - assertThat(parameters.get("session"), is(nullValue())); + softly.assertThat(parameters.get("logined")).isEqualTo("true"); + softly.assertThat(parameters.get("JSessionId")).isEqualTo("1234"); + softly.assertThat(parameters.get("session")).isNull(); } @Test - public void getKeyValue() throws Exception { + void getKeyValue() { Pair pair = HttpRequestUtils.getKeyValue("userId=javajigi", "="); - assertThat(pair, is(new Pair("userId", "javajigi"))); + softly.assertThat(pair).isEqualTo(new Pair("userId", "javajigi")); } @Test - public void getKeyValue_invalid() throws Exception { + void getKeyValue_invalid() { Pair pair = HttpRequestUtils.getKeyValue("userId", "="); - assertThat(pair, is(nullValue())); + softly.assertThat(pair).isNull(); } @Test - public void parseHeader() throws Exception { + void parseHeader() { String header = "Content-Length: 59"; Pair pair = HttpRequestUtils.parseHeader(header); - assertThat(pair, is(new Pair("Content-Length", "59"))); + softly.assertThat(pair).isEqualTo(new Pair("Content-Length", "59")); } } diff --git a/src/test/java/util/IOUtilsTest.java b/src/test/java/util/IOUtilsTest.java index 3c00cc4..eb8812b 100644 --- a/src/test/java/util/IOUtilsTest.java +++ b/src/test/java/util/IOUtilsTest.java @@ -3,7 +3,7 @@ import java.io.BufferedReader; import java.io.StringReader; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/test/java/webserver/HttpRequestTest.java b/src/test/java/webserver/HttpRequestTest.java new file mode 100644 index 0000000..d1d4104 --- /dev/null +++ b/src/test/java/webserver/HttpRequestTest.java @@ -0,0 +1,53 @@ +package webserver; + +import http.HttpRequest; +import org.assertj.core.api.SoftAssertions; +import org.assertj.core.api.junit.jupiter.InjectSoftAssertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension; + +import java.io.FileInputStream; +import java.io.InputStream; + +@ExtendWith(SoftAssertionsExtension.class) +class HttpRequestTest { + private final String testDirectory = "./src/test/resources/"; + + @InjectSoftAssertions + SoftAssertions softly; + + @Test + public void request_GET() throws Exception { + InputStream in = new FileInputStream(testDirectory + "Http_GET.txt"); + HttpRequest request = new HttpRequest(in); + + softly.assertThat(request.getMethod().name()).isEqualTo("GET"); + softly.assertThat(request.getPath()).isEqualTo("/user/create"); + softly.assertThat(request.getHeader("Connection")).isEqualTo("keep-alive"); + softly.assertThat(request.getParameter("userId")).isEqualTo("javajigi"); + } + + @Test + public void request_POST() throws Exception { + InputStream in = new FileInputStream(testDirectory + "Http_POST.txt"); + HttpRequest request = new HttpRequest(in); + + softly.assertThat(request.getMethod().name()).isEqualTo("POST"); + softly.assertThat(request.getPath()).isEqualTo("/user/create"); + softly.assertThat(request.getHeader("Connection")).isEqualTo("keep-alive"); + softly.assertThat(request.getParameter("userId")).isEqualTo("javajigi"); + } + + @Test + public void request_POST2() throws Exception { + InputStream in = new FileInputStream(testDirectory + "Http_POST2.txt"); + HttpRequest request = new HttpRequest(in); + + softly.assertThat(request.getMethod().name()).isEqualTo("POST"); + softly.assertThat(request.getPath()).isEqualTo("/user/create"); + softly.assertThat(request.getHeader("Connection")).isEqualTo("keep-alive"); + softly.assertThat(request.getParameter("id")).isEqualTo("1"); + softly.assertThat(request.getParameter("userId")).isEqualTo("javajigi"); + } +} diff --git a/src/test/java/webserver/HttpResponseTest.java b/src/test/java/webserver/HttpResponseTest.java new file mode 100644 index 0000000..13bbc1a --- /dev/null +++ b/src/test/java/webserver/HttpResponseTest.java @@ -0,0 +1,38 @@ +package webserver; + +import http.HttpResponse; +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 responseForward() throws Exception { + // Http_Forward.txt 결과는 응답 body에 index.html이 포함되어 있어야 한다. + HttpResponse response = new HttpResponse(createOutputStream("Http_Forward.txt")); + response.forward("/index.html"); + } + + @Test + public void responseRedirect() throws Exception { + // Http_Redirect.txt 결과는 응답 header에 Location 정보가 /index.html로 포함되어 있어야 한다. + HttpResponse response = new HttpResponse(createOutputStream("Http_Redirect.txt")); + response.sendRedirect("/index.html"); + } + + @Test + public void responseCookies() 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.sendRedirect("/index.html"); + } + + private OutputStream createOutputStream(String filename) throws FileNotFoundException { + return new FileOutputStream(testDirectory + filename); + } +} diff --git a/src/test/resources/Http_Cookie.txt b/src/test/resources/Http_Cookie.txt new file mode 100644 index 0000000..c0a4480 --- /dev/null +++ b/src/test/resources/Http_Cookie.txt @@ -0,0 +1,3 @@ +HTTP/1.1 302 Found +Set-Cookie: logined=true +Location: /index.html diff --git a/src/test/resources/Http_Forward.txt b/src/test/resources/Http_Forward.txt new file mode 100644 index 0000000..abfead4 --- /dev/null +++ b/src/test/resources/Http_Forward.txt @@ -0,0 +1,152 @@ +HTTP/1.1 200 OK +Content-Type:text/html;charset=utf-8 +Content-Length: 6892 + + + + + + + SLiPP Java Web Programming + + + + + + + + + + +
+
+
+ +
+
+
+ +
+ +
+
+
+
+ + + + + + + diff --git a/src/test/resources/Http_GET.txt b/src/test/resources/Http_GET.txt new file mode 100644 index 0000000..bd2324b --- /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_POST.txt b/src/test/resources/Http_POST.txt new file mode 100644 index 0000000..2b62495 --- /dev/null +++ b/src/test/resources/Http_POST.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 diff --git a/src/test/resources/Http_POST2.txt b/src/test/resources/Http_POST2.txt new file mode 100644 index 0000000..d8151a6 --- /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 diff --git a/src/test/resources/Http_Redirect.txt b/src/test/resources/Http_Redirect.txt new file mode 100644 index 0000000..7f8efa9 --- /dev/null +++ b/src/test/resources/Http_Redirect.txt @@ -0,0 +1,2 @@ +HTTP/1.1 302 Found +Location: /index.html diff --git a/webapp/index.html b/webapp/index.html index 1675898..92f6b0b 100644 --- a/webapp/index.html +++ b/webapp/index.html @@ -40,7 +40,7 @@
  • Facebook
  • -
  • +
  • @@ -145,4 +145,4 @@ - \ No newline at end of file +