From 148767392a685b7518e801580e62a28af726dbe1 Mon Sep 17 00:00:00 2001 From: emes-g Date: Tue, 26 Mar 2024 22:12:54 +0900 Subject: [PATCH 01/26] Feat: return index.html MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit localhost 혹은 localhost/index.html로 접속하는 경우, index.html로 라우팅 --- src/main/java/webserver/RequestHandler.java | 21 ++++++++++++++++----- src/main/java/webserver/WebServer.java | 4 ++-- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index 614a755..9817e22 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -1,11 +1,14 @@ package webserver; +import http.util.IOUtils; + import java.io.*; import java.net.Socket; +import java.nio.file.Files; import java.util.logging.Level; import java.util.logging.Logger; -public class RequestHandler implements Runnable{ +public class RequestHandler implements Runnable { Socket connection; private static final Logger log = Logger.getLogger(RequestHandler.class.getName()); @@ -16,16 +19,24 @@ public RequestHandler(Socket connection) { @Override public void run() { log.log(Level.INFO, "New Client Connect! Connected IP : " + connection.getInetAddress() + ", Port : " + connection.getPort()); - try (InputStream in = connection.getInputStream(); OutputStream out = connection.getOutputStream()){ + try (InputStream in = connection.getInputStream(); OutputStream out = connection.getOutputStream()) { BufferedReader br = new BufferedReader(new InputStreamReader(in)); DataOutputStream dos = new DataOutputStream(out); - byte[] body = "Hello World".getBytes(); + // request message start-line 검증 + String startLine = br.readLine(); + String[] startLines = startLine.split(" "); + String method = startLines[0]; + String url = startLines[1]; + if(!url.equals("/") && !url.equals("/index.html")){ + return; + } + // index.html 반환 + byte[] body = Files.readAllBytes(new File("webapp/index.html").toPath()); response200Header(dos, body.length); responseBody(dos, body); - } catch (IOException e) { - log.log(Level.SEVERE,e.getMessage()); + log.log(Level.SEVERE, e.getMessage()); } } diff --git a/src/main/java/webserver/WebServer.java b/src/main/java/webserver/WebServer.java index 85caad0..7aa4c68 100644 --- a/src/main/java/webserver/WebServer.java +++ b/src/main/java/webserver/WebServer.java @@ -21,10 +21,10 @@ public static void main(String[] args) throws IOException { port = Integer.parseInt(args[0]); } - // TCP 환영 소켓 + // 1. TCP 환영 소켓 (서버 소켓) try (ServerSocket welcomeSocket = new ServerSocket(port)){ - // 연결 소켓 + // 2. 연결 소켓 (클라이언트 접속 대기) Socket connection; while ((connection = welcomeSocket.accept()) != null) { // 스레드에 작업 전달 From 43982a09b1c873aca66836efbcf0c0a470870846 Mon Sep 17 00:00:00 2001 From: emes-g Date: Tue, 26 Mar 2024 23:35:27 +0900 Subject: [PATCH 02/26] Feat: sign up in 'GET' method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GET 방식으로 회원 가입하고 /index.html로 리다이렉트 --- src/main/java/webserver/RequestHandler.java | 39 ++++++++++++++++++--- webapp/user/form.html | 2 +- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index 9817e22..0162cdc 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -1,10 +1,15 @@ package webserver; +import db.MemoryUserRepository; +import http.util.HttpRequestUtils; import http.util.IOUtils; +import model.User; import java.io.*; import java.net.Socket; import java.nio.file.Files; +import java.util.HashMap; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -28,13 +33,29 @@ public void run() { String[] startLines = startLine.split(" "); String method = startLines[0]; String url = startLines[1]; - if(!url.equals("/") && !url.equals("/index.html")){ + if (url.equals("/") || url.equals("/index.html")) { + // index.html 반환 + byte[] body = Files.readAllBytes(new File("webapp/index.html").toPath()); + response200Header(dos, body.length); + responseBody(dos, body); return; } - // index.html 반환 - byte[] body = Files.readAllBytes(new File("webapp/index.html").toPath()); - response200Header(dos, body.length); - responseBody(dos, body); + if (url.equals("/user/form.html")) { + // user/form.html 반환 + byte[] body = Files.readAllBytes(new File("webapp/user/form.html").toPath()); + response200Header(dos, body.length); + responseBody(dos, body); + return; + } + if (url.startsWith("/user/signup") || method.equals("GET")) { + String queryString = url.substring("/user/signup?".length()); + Map elements = HttpRequestUtils.parseQueryParameter(queryString); + MemoryUserRepository userRepository = MemoryUserRepository.getInstance(); + userRepository.addUser(new User(elements.get("userId"), elements.get("password"), elements.get("name"), elements.get("email"))); + // for redirect + response302Header(dos, "/index.html"); + } + } catch (IOException e) { log.log(Level.SEVERE, e.getMessage()); } @@ -60,4 +81,12 @@ private void responseBody(DataOutputStream dos, byte[] body) { } } + private void response302Header(DataOutputStream dos, String path) { + try { + dos.writeBytes("HTTP/1.1 302 Found \r\n"); + dos.writeBytes(String.format("Location: %s\r\n", path)); + } catch (IOException e) { + log.log(Level.SEVERE, e.getMessage()); + } + } } \ No newline at end of file diff --git a/webapp/user/form.html b/webapp/user/form.html index 168cca2..77a96ae 100644 --- a/webapp/user/form.html +++ b/webapp/user/form.html @@ -55,7 +55,7 @@

Sign up

-
+
From 39068989453cae22ff184080515fb973677fc5e8 Mon Sep 17 00:00:00 2001 From: emes-g Date: Wed, 27 Mar 2024 00:39:13 +0900 Subject: [PATCH 03/26] Feat: sign up in 'POST' method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit POST 방식으로 회원 가입하고 /index.html로 리다이렉트 --- src/main/java/webserver/RequestHandler.java | 19 ++++++++++++++++++- webapp/user/form.html | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index 0162cdc..9051b14 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -47,13 +47,30 @@ public void run() { responseBody(dos, body); return; } - if (url.startsWith("/user/signup") || method.equals("GET")) { + if (url.startsWith("/user/signup") && method.equals("GET")) { String queryString = url.substring("/user/signup?".length()); Map elements = HttpRequestUtils.parseQueryParameter(queryString); MemoryUserRepository userRepository = MemoryUserRepository.getInstance(); userRepository.addUser(new User(elements.get("userId"), elements.get("password"), elements.get("name"), elements.get("email"))); // for redirect response302Header(dos, "/index.html"); + return; + } + if (url.startsWith("/user/signup") && method.equals("POST")) { + // Content-Length 가져오고, BufferedReader offset 을 request message (http) body 입구에 위치 + int requestContentLength = 0; + String line = ""; + while (!(line = br.readLine()).isEmpty()) { + if (line.startsWith("Content-Length")) { + requestContentLength = Integer.parseInt(line.split(": ")[1]); + } + } + + Map elements = HttpRequestUtils.parseQueryParameter(IOUtils.readData(br, requestContentLength)); + MemoryUserRepository userRepository = MemoryUserRepository.getInstance(); + userRepository.addUser(new User(elements.get("userId"), elements.get("password"), elements.get("name"), elements.get("email"))); + // for redirect + response302Header(dos, "/index.html"); } } catch (IOException e) { diff --git a/webapp/user/form.html b/webapp/user/form.html index 77a96ae..168cca2 100644 --- a/webapp/user/form.html +++ b/webapp/user/form.html @@ -55,7 +55,7 @@

Sign up

- +
From 5ce9940a367660c2da2b36e135ad4e5b01ba45ee Mon Sep 17 00:00:00 2001 From: emes-g Date: Wed, 27 Mar 2024 00:47:45 +0900 Subject: [PATCH 04/26] Refactor: declare 'index' that is used in queryString --- src/main/java/webserver/RequestHandler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index 9051b14..426fa9b 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -48,7 +48,8 @@ public void run() { return; } if (url.startsWith("/user/signup") && method.equals("GET")) { - String queryString = url.substring("/user/signup?".length()); + int index = url.indexOf("?"); + String queryString = url.substring(index + 1); Map elements = HttpRequestUtils.parseQueryParameter(queryString); MemoryUserRepository userRepository = MemoryUserRepository.getInstance(); userRepository.addUser(new User(elements.get("userId"), elements.get("password"), elements.get("name"), elements.get("email"))); From 9a8173e332ecdd362f73f55854c40b5027cbe7df Mon Sep 17 00:00:00 2001 From: emes-g Date: Wed, 27 Mar 2024 02:51:43 +0900 Subject: [PATCH 05/26] Feat: login and save cookie MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로그인 기능과 쿠키 저장 기능 구현 --- src/main/java/webserver/RequestHandler.java | 54 ++++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index 426fa9b..f1b0d66 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -8,7 +8,6 @@ import java.io.*; import java.net.Socket; import java.nio.file.Files; -import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -72,6 +71,46 @@ public void run() { userRepository.addUser(new User(elements.get("userId"), elements.get("password"), elements.get("name"), elements.get("email"))); // for redirect response302Header(dos, "/index.html"); + return; + } + if (url.equals("/user/login.html")) { + // user/login.html 반환 + byte[] body = Files.readAllBytes(new File("webapp/user/login.html").toPath()); + response200Header(dos, body.length); + responseBody(dos, body); + return; + } + if (url.equals("/user/login_failed.html")) { + // user/login_failed.html 반환 + byte[] body = Files.readAllBytes(new File("webapp/user/login_failed.html").toPath()); + response200Header(dos, body.length); + responseBody(dos, body); + return; + } + if (url.startsWith("/user/login") && method.equals("POST")) { + // Content-Length 가져오고, BufferedReader offset 을 request message (http) body 입구에 위치 + int requestContentLength = 0; + String line = ""; + while (!(line = br.readLine()).isEmpty()) { + if (line.startsWith("Content-Length")) { + requestContentLength = Integer.parseInt(line.split(": ")[1]); + } + } + + Map elements = HttpRequestUtils.parseQueryParameter(IOUtils.readData(br, requestContentLength)); + MemoryUserRepository userRepository = MemoryUserRepository.getInstance(); + try { + User user = userRepository.findUserById(elements.get("userId")); + // 비밀번호가 틀린 경우 + if (!user.getPassword().equals(elements.get("password"))) { + response302Header(dos, "/user/login_failed.html"); + return; + } + response302HeaderWithCookie(dos, "/index.html"); + } catch (NullPointerException e) { + // 해당 아이디가 없는 경우 + response302Header(dos, "/user/login_failed.html"); + } } } catch (IOException e) { @@ -99,10 +138,21 @@ private void responseBody(DataOutputStream dos, byte[] body) { } } + // for redirect private void response302Header(DataOutputStream dos, String path) { try { dos.writeBytes("HTTP/1.1 302 Found \r\n"); - dos.writeBytes(String.format("Location: %s\r\n", path)); + dos.writeBytes("Location: " + path + "\r\n"); + } catch (IOException e) { + log.log(Level.SEVERE, e.getMessage()); + } + } + + private void response302HeaderWithCookie(DataOutputStream dos, String path) { + try { + dos.writeBytes("HTTP/1.1 302 Found \r\n"); + dos.writeBytes("Location: " + path + "\r\n"); + dos.writeBytes("Set-Cookie: logined=true\r\n"); } catch (IOException e) { log.log(Level.SEVERE, e.getMessage()); } From 894d3d1a5643582eb7179cfd2b3f87b4ba73d162 Mon Sep 17 00:00:00 2001 From: emes-g Date: Wed, 27 Mar 2024 03:35:07 +0900 Subject: [PATCH 06/26] Feat: apply css --- src/main/java/webserver/RequestHandler.java | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index f1b0d66..d6869f7 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -112,6 +112,11 @@ public void run() { response302Header(dos, "/user/login_failed.html"); } } + if (url.endsWith(".css")) { + byte[] body = Files.readAllBytes(new File("webapp/css/styles.css").toPath()); + response200HeaderWithCss(dos, body.length); + responseBody(dos, body); + } } catch (IOException e) { log.log(Level.SEVERE, e.getMessage()); @@ -129,6 +134,17 @@ private void response200Header(DataOutputStream dos, int lengthOfBodyContent) { } } + private void response200HeaderWithCss(DataOutputStream dos, 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.log(Level.SEVERE, e.getMessage()); + } + } + private void responseBody(DataOutputStream dos, byte[] body) { try { dos.write(body, 0, body.length); @@ -143,16 +159,21 @@ private void response302Header(DataOutputStream dos, String path) { try { dos.writeBytes("HTTP/1.1 302 Found \r\n"); dos.writeBytes("Location: " + path + "\r\n"); + dos.writeBytes("\r\n"); } catch (IOException e) { log.log(Level.SEVERE, e.getMessage()); } } + // (서버) 응답 헤더 (/user/login)에는 Set-Cookie 잘 설정되어 있음 (브라우저를 통해서 index.html 로 재요청을 보냈다?) + // 내 생각에는 index.html 의 요청 헤더에 설정된 Cookie 가 보여져야 할 것 같은데, (logined = true) + // 왜 없는지 모르겠다. -> 6번 진행 안됨 private void response302HeaderWithCookie(DataOutputStream dos, String path) { try { dos.writeBytes("HTTP/1.1 302 Found \r\n"); dos.writeBytes("Location: " + path + "\r\n"); dos.writeBytes("Set-Cookie: logined=true\r\n"); + dos.writeBytes("\r\n"); } catch (IOException e) { log.log(Level.SEVERE, e.getMessage()); } From 3e95242d7cbc49e887d4a176bc22a93eec7d061f Mon Sep 17 00:00:00 2001 From: emes-g Date: Wed, 27 Mar 2024 14:25:05 +0900 Subject: [PATCH 07/26] Feat: set cookie path and print userList MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 기존 Cookie Path가 /user에 한정되어서, 홈 경로(/)로 수정 2. 로그인 상태일 경우, 유저 리스트 출력 3. 비로그인 상태일 경우, 로그인 화면으로 이동 --- src/main/java/webserver/RequestHandler.java | 50 ++++++++++++--------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index d6869f7..6784db8 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -32,6 +32,32 @@ public void run() { String[] startLines = startLine.split(" "); String method = startLines[0]; String url = startLines[1]; + + // Content-Length 랑 Cookie 가져오고, BufferedReader offset 을 request message (http) body 입구에 위치 + int requestContentLength = 0; + String cookie = ""; + String line = ""; + while (!(line = br.readLine()).isEmpty()) { + if (line.startsWith("Content-Length")) { + requestContentLength = Integer.parseInt(line.split(": ")[1]); + } + if (line.startsWith("Cookie")) { + cookie = line.split(": ")[1]; + } + } + + if (url.equals("/user/userList")) { + if (cookie.equals("logined=true")) { + // user/list.html 반환 + byte[] body = Files.readAllBytes(new File("webapp/user/list.html").toPath()); + response200Header(dos, body.length); + responseBody(dos, body); + return; + } + // 비로그인 상태 + response302Header(dos, "/user/login.html"); + return; + } if (url.equals("/") || url.equals("/index.html")) { // index.html 반환 byte[] body = Files.readAllBytes(new File("webapp/index.html").toPath()); @@ -57,15 +83,6 @@ public void run() { return; } if (url.startsWith("/user/signup") && method.equals("POST")) { - // Content-Length 가져오고, BufferedReader offset 을 request message (http) body 입구에 위치 - int requestContentLength = 0; - String line = ""; - while (!(line = br.readLine()).isEmpty()) { - if (line.startsWith("Content-Length")) { - requestContentLength = Integer.parseInt(line.split(": ")[1]); - } - } - Map elements = HttpRequestUtils.parseQueryParameter(IOUtils.readData(br, requestContentLength)); MemoryUserRepository userRepository = MemoryUserRepository.getInstance(); userRepository.addUser(new User(elements.get("userId"), elements.get("password"), elements.get("name"), elements.get("email"))); @@ -88,15 +105,6 @@ public void run() { return; } if (url.startsWith("/user/login") && method.equals("POST")) { - // Content-Length 가져오고, BufferedReader offset 을 request message (http) body 입구에 위치 - int requestContentLength = 0; - String line = ""; - while (!(line = br.readLine()).isEmpty()) { - if (line.startsWith("Content-Length")) { - requestContentLength = Integer.parseInt(line.split(": ")[1]); - } - } - Map elements = HttpRequestUtils.parseQueryParameter(IOUtils.readData(br, requestContentLength)); MemoryUserRepository userRepository = MemoryUserRepository.getInstance(); try { @@ -165,14 +173,12 @@ private void response302Header(DataOutputStream dos, String path) { } } - // (서버) 응답 헤더 (/user/login)에는 Set-Cookie 잘 설정되어 있음 (브라우저를 통해서 index.html 로 재요청을 보냈다?) - // 내 생각에는 index.html 의 요청 헤더에 설정된 Cookie 가 보여져야 할 것 같은데, (logined = true) - // 왜 없는지 모르겠다. -> 6번 진행 안됨 + // 트러블 슈팅 (Cookie Path) private void response302HeaderWithCookie(DataOutputStream dos, String path) { try { dos.writeBytes("HTTP/1.1 302 Found \r\n"); dos.writeBytes("Location: " + path + "\r\n"); - dos.writeBytes("Set-Cookie: logined=true\r\n"); + dos.writeBytes("Set-Cookie: logined=true; Path=/\r\n"); dos.writeBytes("\r\n"); } catch (IOException e) { log.log(Level.SEVERE, e.getMessage()); From a89f06f53f4ee57a1c452020e6a115b3cf36a8c1 Mon Sep 17 00:00:00 2001 From: emes-g Date: Wed, 27 Mar 2024 14:33:45 +0900 Subject: [PATCH 08/26] Refactor: early return with respect to userList --- src/main/java/webserver/RequestHandler.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index 6784db8..cedd1af 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -47,15 +47,15 @@ public void run() { } if (url.equals("/user/userList")) { - if (cookie.equals("logined=true")) { - // user/list.html 반환 - byte[] body = Files.readAllBytes(new File("webapp/user/list.html").toPath()); - response200Header(dos, body.length); - responseBody(dos, body); + // 비로그인 상태 : redirect to /user/login.html + if (!cookie.equals("logined=true")) { + response302Header(dos, "/user/login.html"); return; } - // 비로그인 상태 - response302Header(dos, "/user/login.html"); + // 로그인 상태 : user/list.html 반환 + byte[] body = Files.readAllBytes(new File("webapp/user/list.html").toPath()); + response200Header(dos, body.length); + responseBody(dos, body); return; } if (url.equals("/") || url.equals("/index.html")) { From 41e541d0625f94c1c4aaf1766dc6f3d8f9406c0d Mon Sep 17 00:00:00 2001 From: emes-g Date: Thu, 28 Mar 2024 13:00:08 +0900 Subject: [PATCH 09/26] Refactor: remove duplicate code and make url constant --- src/main/java/webserver/RequestHandler.java | 121 ++++++++++---------- 1 file changed, 61 insertions(+), 60 deletions(-) diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index cedd1af..b5340c9 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -1,6 +1,7 @@ package webserver; import db.MemoryUserRepository; +import db.Repository; import http.util.HttpRequestUtils; import http.util.IOUtils; import model.User; @@ -8,6 +9,7 @@ import java.io.*; import java.net.Socket; import java.nio.file.Files; +import java.nio.file.Paths; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -15,9 +17,17 @@ public class RequestHandler implements Runnable { Socket connection; private static final Logger log = Logger.getLogger(RequestHandler.class.getName()); + private static final String ROOT_URL = "./webapp"; + private static final String HOME_URL = "/index.html"; + private static final String LOGIN_FAILED_URL = "/user/login_failed.html"; + private static final String LOGIN_URL = "/user/login.html"; + private static final String LIST_URL = "/user/list.html"; + + private final Repository repository; public RequestHandler(Socket connection) { this.connection = connection; + repository = MemoryUserRepository.getInstance(); } @Override @@ -27,6 +37,8 @@ public void run() { BufferedReader br = new BufferedReader(new InputStreamReader(in)); DataOutputStream dos = new DataOutputStream(out); + byte[] body = new byte[0]; + // request message start-line 검증 String startLine = br.readLine(); String[] startLines = startLine.split(" "); @@ -36,7 +48,7 @@ public void run() { // Content-Length 랑 Cookie 가져오고, BufferedReader offset 을 request message (http) body 입구에 위치 int requestContentLength = 0; String cookie = ""; - String line = ""; + String line; while (!(line = br.readLine()).isEmpty()) { if (line.startsWith("Content-Length")) { requestContentLength = Integer.parseInt(line.split(": ")[1]); @@ -46,91 +58,80 @@ public void run() { } } - if (url.equals("/user/userList")) { - // 비로그인 상태 : redirect to /user/login.html - if (!cookie.equals("logined=true")) { - response302Header(dos, "/user/login.html"); - return; - } - // 로그인 상태 : user/list.html 반환 - byte[] body = Files.readAllBytes(new File("webapp/user/list.html").toPath()); - response200Header(dos, body.length); - responseBody(dos, body); - return; - } - if (url.equals("/") || url.equals("/index.html")) { - // index.html 반환 - byte[] body = Files.readAllBytes(new File("webapp/index.html").toPath()); - response200Header(dos, body.length); - responseBody(dos, body); - return; + // 요구사항 1번 + if (url.equals("/")) { + body = Files.readAllBytes(Paths.get(ROOT_URL + HOME_URL)); } - if (url.equals("/user/form.html")) { - // user/form.html 반환 - byte[] body = Files.readAllBytes(new File("webapp/user/form.html").toPath()); - response200Header(dos, body.length); - responseBody(dos, body); - return; + // url 에 맞는 웹페이지 반환 + if (method.equals("GET") && url.endsWith(".html")) { + body = Files.readAllBytes(Paths.get(ROOT_URL + url)); } - if (url.startsWith("/user/signup") && method.equals("GET")) { - int index = url.indexOf("?"); - String queryString = url.substring(index + 1); + + // 요구사항 2,3,4번 + if (url.startsWith("/user/signup")) { + String queryString = makeQueryStringByMethod(method, br, requestContentLength, url); Map elements = HttpRequestUtils.parseQueryParameter(queryString); - MemoryUserRepository userRepository = MemoryUserRepository.getInstance(); - userRepository.addUser(new User(elements.get("userId"), elements.get("password"), elements.get("name"), elements.get("email"))); + repository.addUser(new User(elements.get("userId"), elements.get("password"), elements.get("name"), elements.get("email"))); // for redirect - response302Header(dos, "/index.html"); - return; - } - if (url.startsWith("/user/signup") && method.equals("POST")) { - Map elements = HttpRequestUtils.parseQueryParameter(IOUtils.readData(br, requestContentLength)); - MemoryUserRepository userRepository = MemoryUserRepository.getInstance(); - userRepository.addUser(new User(elements.get("userId"), elements.get("password"), elements.get("name"), elements.get("email"))); - // for redirect - response302Header(dos, "/index.html"); - return; - } - if (url.equals("/user/login.html")) { - // user/login.html 반환 - byte[] body = Files.readAllBytes(new File("webapp/user/login.html").toPath()); - response200Header(dos, body.length); - responseBody(dos, body); - return; - } - if (url.equals("/user/login_failed.html")) { - // user/login_failed.html 반환 - byte[] body = Files.readAllBytes(new File("webapp/user/login_failed.html").toPath()); - response200Header(dos, body.length); - responseBody(dos, body); + response302Header(dos, HOME_URL); return; } + + // 요구사항 5번 if (url.startsWith("/user/login") && method.equals("POST")) { Map elements = HttpRequestUtils.parseQueryParameter(IOUtils.readData(br, requestContentLength)); - MemoryUserRepository userRepository = MemoryUserRepository.getInstance(); try { - User user = userRepository.findUserById(elements.get("userId")); + User user = repository.findUserById(elements.get("userId")); // 비밀번호가 틀린 경우 if (!user.getPassword().equals(elements.get("password"))) { - response302Header(dos, "/user/login_failed.html"); + response302Header(dos, LOGIN_FAILED_URL); return; } - response302HeaderWithCookie(dos, "/index.html"); + response302HeaderWithCookie(dos, HOME_URL); + return; } catch (NullPointerException e) { // 해당 아이디가 없는 경우 - response302Header(dos, "/user/login_failed.html"); + response302Header(dos, LOGIN_FAILED_URL); + return; } } - if (url.endsWith(".css")) { - byte[] body = Files.readAllBytes(new File("webapp/css/styles.css").toPath()); + + // 요구사항 6번 + if (url.equals("/user/userList")) { + // 비로그인 상태 : redirect to /user/login.html + if (!cookie.equals("logined=true")) { + response302Header(dos, LOGIN_URL); + return; + } + // 로그인 상태 : user/list.html 반환 + body = Files.readAllBytes(Paths.get(ROOT_URL + LIST_URL)); + } + + // 요구사항 7번 + if (method.equals("GET") && url.endsWith(".css")) { + body = Files.readAllBytes(Paths.get(ROOT_URL + url)); response200HeaderWithCss(dos, body.length); responseBody(dos, body); + return; } + response200Header(dos, body.length); + responseBody(dos, body); + } catch (IOException e) { log.log(Level.SEVERE, e.getMessage()); } } + private String makeQueryStringByMethod(String method, BufferedReader br, int requestContentLength, String url) throws IOException { + if (method.equals("POST")) { + return IOUtils.readData(br, requestContentLength); + } + // GET 방식 + int index = url.indexOf("?"); + return url.substring(index + 1); + } + private void response200Header(DataOutputStream dos, int lengthOfBodyContent) { try { dos.writeBytes("HTTP/1.1 200 OK \r\n"); From 5b6c6dac6f01b34604bf07fee6551368137acb5b Mon Sep 17 00:00:00 2001 From: emes-g Date: Thu, 28 Mar 2024 13:07:28 +0900 Subject: [PATCH 10/26] Refactor: remove duplicate code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit null을 처리하는 방식을 try-catch에서 !=로 변경 --- src/main/java/webserver/RequestHandler.java | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index b5340c9..4cb6f1b 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -79,21 +79,18 @@ public void run() { // 요구사항 5번 if (url.startsWith("/user/login") && method.equals("POST")) { - Map elements = HttpRequestUtils.parseQueryParameter(IOUtils.readData(br, requestContentLength)); - try { - User user = repository.findUserById(elements.get("userId")); - // 비밀번호가 틀린 경우 - if (!user.getPassword().equals(elements.get("password"))) { - response302Header(dos, LOGIN_FAILED_URL); - return; - } + String queryString = IOUtils.readData(br, requestContentLength); + Map elements = HttpRequestUtils.parseQueryParameter(queryString); + User user = repository.findUserById(elements.get("userId")); + + // 로그인 성공 + if(user != null && user.getPassword().equals(elements.get("password"))){ response302HeaderWithCookie(dos, HOME_URL); return; - } catch (NullPointerException e) { - // 해당 아이디가 없는 경우 - response302Header(dos, LOGIN_FAILED_URL); - return; } + // 로그인 실패 + response302Header(dos, LOGIN_FAILED_URL); + return; } // 요구사항 6번 From d1eb93c005c9d8b8f6a6adf55d0e19346db1923d Mon Sep 17 00:00:00 2001 From: emes-g Date: Thu, 28 Mar 2024 13:18:27 +0900 Subject: [PATCH 11/26] Fix: fix bug that user can enter non-existent page --- src/main/java/webserver/RequestHandler.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index 4cb6f1b..8711c51 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -68,7 +68,7 @@ public void run() { } // 요구사항 2,3,4번 - if (url.startsWith("/user/signup")) { + if (url.equals("/user/signup")) { String queryString = makeQueryStringByMethod(method, br, requestContentLength, url); Map elements = HttpRequestUtils.parseQueryParameter(queryString); repository.addUser(new User(elements.get("userId"), elements.get("password"), elements.get("name"), elements.get("email"))); @@ -78,13 +78,13 @@ public void run() { } // 요구사항 5번 - if (url.startsWith("/user/login") && method.equals("POST")) { + if (method.equals("POST") && url.equals("/user/login")) { String queryString = IOUtils.readData(br, requestContentLength); Map elements = HttpRequestUtils.parseQueryParameter(queryString); User user = repository.findUserById(elements.get("userId")); // 로그인 성공 - if(user != null && user.getPassword().equals(elements.get("password"))){ + if (user != null && user.getPassword().equals(elements.get("password"))) { response302HeaderWithCookie(dos, HOME_URL); return; } @@ -112,6 +112,9 @@ public void run() { return; } + if (body.length == 0) { + body = "Sorry, This page doesn't exist.".getBytes(); + } response200Header(dos, body.length); responseBody(dos, body); From 0d73452767ffb37d83864b314f1d18fabea8fda2 Mon Sep 17 00:00:00 2001 From: emes-g Date: Thu, 28 Mar 2024 13:23:04 +0900 Subject: [PATCH 12/26] Fix: add flush() after writing HTTP response headers --- src/main/java/webserver/RequestHandler.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index 8711c51..060483c 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -138,6 +138,7 @@ private void response200Header(DataOutputStream dos, int lengthOfBodyContent) { dos.writeBytes("Content-Type: text/html;charset=utf-8\r\n"); dos.writeBytes("Content-Length: " + lengthOfBodyContent + "\r\n"); dos.writeBytes("\r\n"); + dos.flush(); } catch (IOException e) { log.log(Level.SEVERE, e.getMessage()); } @@ -149,6 +150,7 @@ private void response200HeaderWithCss(DataOutputStream dos, int lengthOfBodyCont dos.writeBytes("Content-Type: text/css;charset=utf-8\r\n"); dos.writeBytes("Content-Length: " + lengthOfBodyContent + "\r\n"); dos.writeBytes("\r\n"); + dos.flush(); } catch (IOException e) { log.log(Level.SEVERE, e.getMessage()); } @@ -169,6 +171,7 @@ private void response302Header(DataOutputStream dos, String path) { dos.writeBytes("HTTP/1.1 302 Found \r\n"); dos.writeBytes("Location: " + path + "\r\n"); dos.writeBytes("\r\n"); + dos.flush(); } catch (IOException e) { log.log(Level.SEVERE, e.getMessage()); } @@ -181,6 +184,7 @@ private void response302HeaderWithCookie(DataOutputStream dos, String path) { dos.writeBytes("Location: " + path + "\r\n"); dos.writeBytes("Set-Cookie: logined=true; Path=/\r\n"); dos.writeBytes("\r\n"); + dos.flush(); } catch (IOException e) { log.log(Level.SEVERE, e.getMessage()); } From 8e3c97e4b19f9e465e2ad6aae0e62baf1b5e81d1 Mon Sep 17 00:00:00 2001 From: emes-g Date: Thu, 28 Mar 2024 14:34:39 +0900 Subject: [PATCH 13/26] Refactor: add enum class(HttpMethod, URL) --- src/main/java/util/HttpMethod.java | 16 +++++++++++ src/main/java/util/URL.java | 19 ++++++++++++ src/main/java/webserver/RequestHandler.java | 32 +++++++++------------ 3 files changed, 49 insertions(+), 18 deletions(-) create mode 100644 src/main/java/util/HttpMethod.java create mode 100644 src/main/java/util/URL.java diff --git a/src/main/java/util/HttpMethod.java b/src/main/java/util/HttpMethod.java new file mode 100644 index 0000000..7604a9d --- /dev/null +++ b/src/main/java/util/HttpMethod.java @@ -0,0 +1,16 @@ +package util; + +public enum HttpMethod { + GET("GET"), + POST("POST"); + + private final String method; + + HttpMethod(String method) { + this.method = method; + } + + public String value() { + return method; + } +} diff --git a/src/main/java/util/URL.java b/src/main/java/util/URL.java new file mode 100644 index 0000000..69a809a --- /dev/null +++ b/src/main/java/util/URL.java @@ -0,0 +1,19 @@ +package util; + +public enum URL { + ROOT_URL("./webapp"), + HOME_URL("/index.html"), + LOGIN_FAILED_URL("/user/login_failed.html"), + LOGIN_URL("/user/login.html"), + LIST_URL("/user/list.html"); + + private final String url; + + URL(String url) { + this.url = url; + } + + public String value() { + return url; + } +} diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index 060483c..b6a0d51 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -5,6 +5,8 @@ import http.util.HttpRequestUtils; import http.util.IOUtils; import model.User; +import util.HttpMethod; +import util.URL; import java.io.*; import java.net.Socket; @@ -17,12 +19,6 @@ public class RequestHandler implements Runnable { Socket connection; private static final Logger log = Logger.getLogger(RequestHandler.class.getName()); - private static final String ROOT_URL = "./webapp"; - private static final String HOME_URL = "/index.html"; - private static final String LOGIN_FAILED_URL = "/user/login_failed.html"; - private static final String LOGIN_URL = "/user/login.html"; - private static final String LIST_URL = "/user/list.html"; - private final Repository repository; public RequestHandler(Socket connection) { @@ -60,11 +56,11 @@ public void run() { // 요구사항 1번 if (url.equals("/")) { - body = Files.readAllBytes(Paths.get(ROOT_URL + HOME_URL)); + body = Files.readAllBytes(Paths.get(URL.ROOT_URL.value() + URL.HOME_URL.value())); } // url 에 맞는 웹페이지 반환 - if (method.equals("GET") && url.endsWith(".html")) { - body = Files.readAllBytes(Paths.get(ROOT_URL + url)); + if (method.equals(HttpMethod.GET.value()) && url.endsWith(".html")) { + body = Files.readAllBytes(Paths.get(URL.ROOT_URL.value() + url)); } // 요구사항 2,3,4번 @@ -73,23 +69,23 @@ public void run() { Map elements = HttpRequestUtils.parseQueryParameter(queryString); repository.addUser(new User(elements.get("userId"), elements.get("password"), elements.get("name"), elements.get("email"))); // for redirect - response302Header(dos, HOME_URL); + response302Header(dos, URL.HOME_URL.value()); return; } // 요구사항 5번 - if (method.equals("POST") && url.equals("/user/login")) { + if (method.equals(HttpMethod.POST.value()) && url.equals("/user/login")) { String queryString = IOUtils.readData(br, requestContentLength); Map elements = HttpRequestUtils.parseQueryParameter(queryString); User user = repository.findUserById(elements.get("userId")); // 로그인 성공 if (user != null && user.getPassword().equals(elements.get("password"))) { - response302HeaderWithCookie(dos, HOME_URL); + response302HeaderWithCookie(dos, URL.HOME_URL.value()); return; } // 로그인 실패 - response302Header(dos, LOGIN_FAILED_URL); + response302Header(dos, URL.LOGIN_FAILED_URL.value()); return; } @@ -97,16 +93,16 @@ public void run() { if (url.equals("/user/userList")) { // 비로그인 상태 : redirect to /user/login.html if (!cookie.equals("logined=true")) { - response302Header(dos, LOGIN_URL); + response302Header(dos, URL.LOGIN_URL.value()); return; } // 로그인 상태 : user/list.html 반환 - body = Files.readAllBytes(Paths.get(ROOT_URL + LIST_URL)); + body = Files.readAllBytes(Paths.get(URL.ROOT_URL.value() + URL.LIST_URL.value())); } // 요구사항 7번 - if (method.equals("GET") && url.endsWith(".css")) { - body = Files.readAllBytes(Paths.get(ROOT_URL + url)); + if (method.equals(HttpMethod.GET.value()) && url.endsWith(".css")) { + body = Files.readAllBytes(Paths.get(URL.ROOT_URL.value() + url)); response200HeaderWithCss(dos, body.length); responseBody(dos, body); return; @@ -124,7 +120,7 @@ public void run() { } private String makeQueryStringByMethod(String method, BufferedReader br, int requestContentLength, String url) throws IOException { - if (method.equals("POST")) { + if (method.equals(HttpMethod.POST.value())) { return IOUtils.readData(br, requestContentLength); } // GET 방식 From 262aaebf71cf88abe5fc8101cb39c07beff887de Mon Sep 17 00:00:00 2001 From: emes-g Date: Fri, 29 Mar 2024 05:04:18 +0900 Subject: [PATCH 14/26] Test: test that request message is separated well MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit request message를 RequestHandler에서 분리 가능한가를 테스트하는 코드 작성 --- src/main/java/http/request/HttpHeader.java | 20 ++++ src/main/java/http/request/HttpRequest.java | 96 +++++++++++++++++++ src/main/java/http/request/HttpStartLine.java | 42 ++++++++ src/main/java/http/request/RequestBody.java | 23 +++++ .../java/http/request/HttpRequestTest.java | 67 +++++++++++++ src/test/resource/Http_GET.txt | 6 ++ src/test/resource/Http_POST.txt | 7 ++ 7 files changed, 261 insertions(+) create mode 100644 src/main/java/http/request/HttpHeader.java create mode 100644 src/main/java/http/request/HttpRequest.java create mode 100644 src/main/java/http/request/HttpStartLine.java create mode 100644 src/main/java/http/request/RequestBody.java create mode 100644 src/test/java/http/request/HttpRequestTest.java create mode 100644 src/test/resource/Http_GET.txt create mode 100644 src/test/resource/Http_POST.txt diff --git a/src/main/java/http/request/HttpHeader.java b/src/main/java/http/request/HttpHeader.java new file mode 100644 index 0000000..5bf242c --- /dev/null +++ b/src/main/java/http/request/HttpHeader.java @@ -0,0 +1,20 @@ +package http.request; + +import java.util.Map; + +// 일급 컬렉션 +public class HttpHeader { + private final Map lines; + + private HttpHeader(Map lines) { + this.lines = lines; + } + + public static HttpHeader of(Map lines) { + return new HttpHeader(lines); + } + + public String getHeaderLine(String key) { + return lines.get(key); + } +} diff --git a/src/main/java/http/request/HttpRequest.java b/src/main/java/http/request/HttpRequest.java new file mode 100644 index 0000000..08956b0 --- /dev/null +++ b/src/main/java/http/request/HttpRequest.java @@ -0,0 +1,96 @@ +package http.request; + +import http.util.IOUtils; +import util.HttpMethod; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; + +public class HttpRequest { + private final BufferedReader br; + private HttpStartLine httpStartLine; + private HttpHeader httpHeader; + private RequestBody requestBody; + + private HttpRequest(InputStream in) { + this.br = new BufferedReader(new InputStreamReader(in)); + init(); + } + + private void init() { + try { + createHttpStartLine(); + createHttpHeader(); + createRequestBody(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void createRequestBody() throws IOException { + String body = ""; + if (getMethod().equals(HttpMethod.POST.value())) { + int requestContentLength = Integer.parseInt(getHeaderLine("Content-Length")); + body = IOUtils.readData(br, requestContentLength); + } + requestBody = RequestBody.of(body); + } + + private void createHttpHeader() throws IOException { + Map lines = new HashMap<>(); + String line; + while (!(line = br.readLine()).isEmpty()) { + String[] elements = line.split(": "); + String key = elements[0]; + String value = elements[1]; + lines.put(key, value); + } + + httpHeader = HttpHeader.of(lines); + } + + private void createHttpStartLine() throws IOException { + String startLine = br.readLine(); + String[] elements = startLine.split(" "); + String method = elements[0]; + String url = elements[1]; + String version = elements[2]; + httpStartLine = HttpStartLine.of(method, url, version); + + if (getMethod().equals(HttpMethod.GET.value())) { + int index = getPath().indexOf("?"); + String queryString = getPath().substring(index + 1); + httpStartLine.initParameters(queryString); + } + } + + public static HttpRequest from(InputStream in) { + return new HttpRequest(in); + } + + public String getMethod() { + return httpStartLine.getMethod(); + } + + public String getPath() { + return httpStartLine.getPath(); + } + + public String getVersion() { + return httpStartLine.getVersion(); + } + + public String getHeaderLine(String key) { + return httpHeader.getHeaderLine(key); + } + + public String getParameter(String key) throws IOException { + if (getMethod().equals(HttpMethod.GET.value())) + return httpStartLine.getParameter(key); + return requestBody.getParameter(key); + } +} diff --git a/src/main/java/http/request/HttpStartLine.java b/src/main/java/http/request/HttpStartLine.java new file mode 100644 index 0000000..0b09b31 --- /dev/null +++ b/src/main/java/http/request/HttpStartLine.java @@ -0,0 +1,42 @@ +package http.request; + +import http.util.HttpRequestUtils; + +import java.util.Map; + +public class HttpStartLine { + private final String method; + private final String path; + private final String version; + private Map parameters; + + private HttpStartLine(String method, String path, String version) { + this.method = method; + this.path = path; + this.version = version; + } + + public static HttpStartLine of(String method, String path, String version) { + return new HttpStartLine(method, path, version); + } + + public String getMethod() { + return method; + } + + public String getPath() { + return path; + } + + public String getVersion() { + return version; + } + + public void initParameters(String queryString) { + parameters = HttpRequestUtils.parseQueryParameter(queryString); + } + + public String getParameter(String key) { + return parameters.get(key); + } +} diff --git a/src/main/java/http/request/RequestBody.java b/src/main/java/http/request/RequestBody.java new file mode 100644 index 0000000..5cce000 --- /dev/null +++ b/src/main/java/http/request/RequestBody.java @@ -0,0 +1,23 @@ +package http.request; + +import http.util.HttpRequestUtils; + +import java.util.Map; + +public class RequestBody { + private Map parameters; + + private RequestBody(String body) { + if (!body.isEmpty()) { + parameters = HttpRequestUtils.parseQueryParameter(body); + } + } + + public static RequestBody of(String body) { + return new RequestBody(body); + } + + public String getParameter(String key) { + return parameters.get(key); + } +} diff --git a/src/test/java/http/request/HttpRequestTest.java b/src/test/java/http/request/HttpRequestTest.java new file mode 100644 index 0000000..c07f538 --- /dev/null +++ b/src/test/java/http/request/HttpRequestTest.java @@ -0,0 +1,67 @@ +package http.request; + +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class HttpRequestTest { + private final File GET_FILE = new File("src/test/resource/Http_GET.txt"); + private final File POST_FILE = new File("src/test/resource/Http_POST.txt"); + + @Test + void HttpRequest_GET_방식_테스트() { + // given + try (InputStream in = new FileInputStream(GET_FILE)) { + + //when + HttpRequest httpRequest = HttpRequest.from(in); + + //then + assertEquals("GET", httpRequest.getMethod()); + assertEquals("/user/create?userId=ms&password=0000&name=minseok", httpRequest.getPath()); + assertEquals("HTTP/1.1", httpRequest.getVersion()); + + assertEquals("localhost:8080", httpRequest.getHeaderLine("Host")); + assertEquals("keep-alive", httpRequest.getHeaderLine("Connection")); + assertEquals("40", httpRequest.getHeaderLine("Content-Length")); + assertEquals("*/*", httpRequest.getHeaderLine("Accept")); + + assertEquals("ms", httpRequest.getParameter("userId")); + assertEquals("0000", httpRequest.getParameter("password")); + assertEquals("minseok", httpRequest.getParameter("name")); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Test + void HttpRequest_POST_방식_테스트() { + // given + try (InputStream in = new FileInputStream(POST_FILE)) { + + //when + HttpRequest httpRequest = HttpRequest.from(in); + + //then + assertEquals("POST", httpRequest.getMethod()); + assertEquals("/user/create", httpRequest.getPath()); + assertEquals("HTTP/1.1", httpRequest.getVersion()); + + assertEquals("localhost:8080", httpRequest.getHeaderLine("Host")); + assertEquals("keep-alive", httpRequest.getHeaderLine("Connection")); + assertEquals("40", httpRequest.getHeaderLine("Content-Length")); + assertEquals("*/*", httpRequest.getHeaderLine("Accept")); + + assertEquals("jw", httpRequest.getParameter("userId")); + assertEquals("password", httpRequest.getParameter("password")); + assertEquals("jungwoo", httpRequest.getParameter("name")); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/src/test/resource/Http_GET.txt b/src/test/resource/Http_GET.txt new file mode 100644 index 0000000..6914a32 --- /dev/null +++ b/src/test/resource/Http_GET.txt @@ -0,0 +1,6 @@ +GET /user/create?userId=ms&password=0000&name=minseok HTTP/1.1 +Host: localhost:8080 +Connection: keep-alive +Content-Length: 40 +Accept: */* + diff --git a/src/test/resource/Http_POST.txt b/src/test/resource/Http_POST.txt new file mode 100644 index 0000000..a3f2abd --- /dev/null +++ b/src/test/resource/Http_POST.txt @@ -0,0 +1,7 @@ +POST /user/create HTTP/1.1 +Host: localhost:8080 +Connection: keep-alive +Content-Length: 40 +Accept: */* + +userId=jw&password=password&name=jungwoo From 60c9f181067a0eee9711bb01ba697e4c42b66b9c Mon Sep 17 00:00:00 2001 From: emes-g Date: Mon, 1 Apr 2024 00:38:48 +0900 Subject: [PATCH 15/26] Revert "Test: test that request message is separated well" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 정답 코드와 비교하면서 리팩토링을 해보기 위함 --- src/main/java/http/request/HttpHeader.java | 20 ---- src/main/java/http/request/HttpRequest.java | 96 ------------------- src/main/java/http/request/HttpStartLine.java | 42 -------- src/main/java/http/request/RequestBody.java | 23 ----- .../java/http/request/HttpRequestTest.java | 67 ------------- src/test/resource/Http_GET.txt | 6 -- src/test/resource/Http_POST.txt | 7 -- 7 files changed, 261 deletions(-) delete mode 100644 src/main/java/http/request/HttpHeader.java delete mode 100644 src/main/java/http/request/HttpRequest.java delete mode 100644 src/main/java/http/request/HttpStartLine.java delete mode 100644 src/main/java/http/request/RequestBody.java delete mode 100644 src/test/java/http/request/HttpRequestTest.java delete mode 100644 src/test/resource/Http_GET.txt delete mode 100644 src/test/resource/Http_POST.txt diff --git a/src/main/java/http/request/HttpHeader.java b/src/main/java/http/request/HttpHeader.java deleted file mode 100644 index 5bf242c..0000000 --- a/src/main/java/http/request/HttpHeader.java +++ /dev/null @@ -1,20 +0,0 @@ -package http.request; - -import java.util.Map; - -// 일급 컬렉션 -public class HttpHeader { - private final Map lines; - - private HttpHeader(Map lines) { - this.lines = lines; - } - - public static HttpHeader of(Map lines) { - return new HttpHeader(lines); - } - - public String getHeaderLine(String key) { - return lines.get(key); - } -} diff --git a/src/main/java/http/request/HttpRequest.java b/src/main/java/http/request/HttpRequest.java deleted file mode 100644 index 08956b0..0000000 --- a/src/main/java/http/request/HttpRequest.java +++ /dev/null @@ -1,96 +0,0 @@ -package http.request; - -import http.util.IOUtils; -import util.HttpMethod; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.HashMap; -import java.util.Map; - -public class HttpRequest { - private final BufferedReader br; - private HttpStartLine httpStartLine; - private HttpHeader httpHeader; - private RequestBody requestBody; - - private HttpRequest(InputStream in) { - this.br = new BufferedReader(new InputStreamReader(in)); - init(); - } - - private void init() { - try { - createHttpStartLine(); - createHttpHeader(); - createRequestBody(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private void createRequestBody() throws IOException { - String body = ""; - if (getMethod().equals(HttpMethod.POST.value())) { - int requestContentLength = Integer.parseInt(getHeaderLine("Content-Length")); - body = IOUtils.readData(br, requestContentLength); - } - requestBody = RequestBody.of(body); - } - - private void createHttpHeader() throws IOException { - Map lines = new HashMap<>(); - String line; - while (!(line = br.readLine()).isEmpty()) { - String[] elements = line.split(": "); - String key = elements[0]; - String value = elements[1]; - lines.put(key, value); - } - - httpHeader = HttpHeader.of(lines); - } - - private void createHttpStartLine() throws IOException { - String startLine = br.readLine(); - String[] elements = startLine.split(" "); - String method = elements[0]; - String url = elements[1]; - String version = elements[2]; - httpStartLine = HttpStartLine.of(method, url, version); - - if (getMethod().equals(HttpMethod.GET.value())) { - int index = getPath().indexOf("?"); - String queryString = getPath().substring(index + 1); - httpStartLine.initParameters(queryString); - } - } - - public static HttpRequest from(InputStream in) { - return new HttpRequest(in); - } - - public String getMethod() { - return httpStartLine.getMethod(); - } - - public String getPath() { - return httpStartLine.getPath(); - } - - public String getVersion() { - return httpStartLine.getVersion(); - } - - public String getHeaderLine(String key) { - return httpHeader.getHeaderLine(key); - } - - public String getParameter(String key) throws IOException { - if (getMethod().equals(HttpMethod.GET.value())) - return httpStartLine.getParameter(key); - return requestBody.getParameter(key); - } -} diff --git a/src/main/java/http/request/HttpStartLine.java b/src/main/java/http/request/HttpStartLine.java deleted file mode 100644 index 0b09b31..0000000 --- a/src/main/java/http/request/HttpStartLine.java +++ /dev/null @@ -1,42 +0,0 @@ -package http.request; - -import http.util.HttpRequestUtils; - -import java.util.Map; - -public class HttpStartLine { - private final String method; - private final String path; - private final String version; - private Map parameters; - - private HttpStartLine(String method, String path, String version) { - this.method = method; - this.path = path; - this.version = version; - } - - public static HttpStartLine of(String method, String path, String version) { - return new HttpStartLine(method, path, version); - } - - public String getMethod() { - return method; - } - - public String getPath() { - return path; - } - - public String getVersion() { - return version; - } - - public void initParameters(String queryString) { - parameters = HttpRequestUtils.parseQueryParameter(queryString); - } - - public String getParameter(String key) { - return parameters.get(key); - } -} diff --git a/src/main/java/http/request/RequestBody.java b/src/main/java/http/request/RequestBody.java deleted file mode 100644 index 5cce000..0000000 --- a/src/main/java/http/request/RequestBody.java +++ /dev/null @@ -1,23 +0,0 @@ -package http.request; - -import http.util.HttpRequestUtils; - -import java.util.Map; - -public class RequestBody { - private Map parameters; - - private RequestBody(String body) { - if (!body.isEmpty()) { - parameters = HttpRequestUtils.parseQueryParameter(body); - } - } - - public static RequestBody of(String body) { - return new RequestBody(body); - } - - public String getParameter(String key) { - return parameters.get(key); - } -} diff --git a/src/test/java/http/request/HttpRequestTest.java b/src/test/java/http/request/HttpRequestTest.java deleted file mode 100644 index c07f538..0000000 --- a/src/test/java/http/request/HttpRequestTest.java +++ /dev/null @@ -1,67 +0,0 @@ -package http.request; - -import org.junit.jupiter.api.Test; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -class HttpRequestTest { - private final File GET_FILE = new File("src/test/resource/Http_GET.txt"); - private final File POST_FILE = new File("src/test/resource/Http_POST.txt"); - - @Test - void HttpRequest_GET_방식_테스트() { - // given - try (InputStream in = new FileInputStream(GET_FILE)) { - - //when - HttpRequest httpRequest = HttpRequest.from(in); - - //then - assertEquals("GET", httpRequest.getMethod()); - assertEquals("/user/create?userId=ms&password=0000&name=minseok", httpRequest.getPath()); - assertEquals("HTTP/1.1", httpRequest.getVersion()); - - assertEquals("localhost:8080", httpRequest.getHeaderLine("Host")); - assertEquals("keep-alive", httpRequest.getHeaderLine("Connection")); - assertEquals("40", httpRequest.getHeaderLine("Content-Length")); - assertEquals("*/*", httpRequest.getHeaderLine("Accept")); - - assertEquals("ms", httpRequest.getParameter("userId")); - assertEquals("0000", httpRequest.getParameter("password")); - assertEquals("minseok", httpRequest.getParameter("name")); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Test - void HttpRequest_POST_방식_테스트() { - // given - try (InputStream in = new FileInputStream(POST_FILE)) { - - //when - HttpRequest httpRequest = HttpRequest.from(in); - - //then - assertEquals("POST", httpRequest.getMethod()); - assertEquals("/user/create", httpRequest.getPath()); - assertEquals("HTTP/1.1", httpRequest.getVersion()); - - assertEquals("localhost:8080", httpRequest.getHeaderLine("Host")); - assertEquals("keep-alive", httpRequest.getHeaderLine("Connection")); - assertEquals("40", httpRequest.getHeaderLine("Content-Length")); - assertEquals("*/*", httpRequest.getHeaderLine("Accept")); - - assertEquals("jw", httpRequest.getParameter("userId")); - assertEquals("password", httpRequest.getParameter("password")); - assertEquals("jungwoo", httpRequest.getParameter("name")); - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} \ No newline at end of file diff --git a/src/test/resource/Http_GET.txt b/src/test/resource/Http_GET.txt deleted file mode 100644 index 6914a32..0000000 --- a/src/test/resource/Http_GET.txt +++ /dev/null @@ -1,6 +0,0 @@ -GET /user/create?userId=ms&password=0000&name=minseok HTTP/1.1 -Host: localhost:8080 -Connection: keep-alive -Content-Length: 40 -Accept: */* - diff --git a/src/test/resource/Http_POST.txt b/src/test/resource/Http_POST.txt deleted file mode 100644 index a3f2abd..0000000 --- a/src/test/resource/Http_POST.txt +++ /dev/null @@ -1,7 +0,0 @@ -POST /user/create HTTP/1.1 -Host: localhost:8080 -Connection: keep-alive -Content-Length: 40 -Accept: */* - -userId=jw&password=password&name=jungwoo From 913ec5b4986e62e653b7b5d0311ff7f1bb22fd54 Mon Sep 17 00:00:00 2001 From: emes-g Date: Mon, 1 Apr 2024 00:38:48 +0900 Subject: [PATCH 16/26] Revert "Refactor: add enum class(HttpMethod, URL)" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 정답 코드와 비교하면서 리팩토링을 해보기 위함 --- src/main/java/util/HttpMethod.java | 16 ----------- src/main/java/util/URL.java | 19 ------------ src/main/java/webserver/RequestHandler.java | 32 ++++++++++++--------- 3 files changed, 18 insertions(+), 49 deletions(-) delete mode 100644 src/main/java/util/HttpMethod.java delete mode 100644 src/main/java/util/URL.java diff --git a/src/main/java/util/HttpMethod.java b/src/main/java/util/HttpMethod.java deleted file mode 100644 index 7604a9d..0000000 --- a/src/main/java/util/HttpMethod.java +++ /dev/null @@ -1,16 +0,0 @@ -package util; - -public enum HttpMethod { - GET("GET"), - POST("POST"); - - private final String method; - - HttpMethod(String method) { - this.method = method; - } - - public String value() { - return method; - } -} diff --git a/src/main/java/util/URL.java b/src/main/java/util/URL.java deleted file mode 100644 index 69a809a..0000000 --- a/src/main/java/util/URL.java +++ /dev/null @@ -1,19 +0,0 @@ -package util; - -public enum URL { - ROOT_URL("./webapp"), - HOME_URL("/index.html"), - LOGIN_FAILED_URL("/user/login_failed.html"), - LOGIN_URL("/user/login.html"), - LIST_URL("/user/list.html"); - - private final String url; - - URL(String url) { - this.url = url; - } - - public String value() { - return url; - } -} diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index b6a0d51..060483c 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -5,8 +5,6 @@ import http.util.HttpRequestUtils; import http.util.IOUtils; import model.User; -import util.HttpMethod; -import util.URL; import java.io.*; import java.net.Socket; @@ -19,6 +17,12 @@ public class RequestHandler implements Runnable { Socket connection; private static final Logger log = Logger.getLogger(RequestHandler.class.getName()); + private static final String ROOT_URL = "./webapp"; + private static final String HOME_URL = "/index.html"; + private static final String LOGIN_FAILED_URL = "/user/login_failed.html"; + private static final String LOGIN_URL = "/user/login.html"; + private static final String LIST_URL = "/user/list.html"; + private final Repository repository; public RequestHandler(Socket connection) { @@ -56,11 +60,11 @@ public void run() { // 요구사항 1번 if (url.equals("/")) { - body = Files.readAllBytes(Paths.get(URL.ROOT_URL.value() + URL.HOME_URL.value())); + body = Files.readAllBytes(Paths.get(ROOT_URL + HOME_URL)); } // url 에 맞는 웹페이지 반환 - if (method.equals(HttpMethod.GET.value()) && url.endsWith(".html")) { - body = Files.readAllBytes(Paths.get(URL.ROOT_URL.value() + url)); + if (method.equals("GET") && url.endsWith(".html")) { + body = Files.readAllBytes(Paths.get(ROOT_URL + url)); } // 요구사항 2,3,4번 @@ -69,23 +73,23 @@ public void run() { Map elements = HttpRequestUtils.parseQueryParameter(queryString); repository.addUser(new User(elements.get("userId"), elements.get("password"), elements.get("name"), elements.get("email"))); // for redirect - response302Header(dos, URL.HOME_URL.value()); + response302Header(dos, HOME_URL); return; } // 요구사항 5번 - if (method.equals(HttpMethod.POST.value()) && url.equals("/user/login")) { + if (method.equals("POST") && url.equals("/user/login")) { String queryString = IOUtils.readData(br, requestContentLength); Map elements = HttpRequestUtils.parseQueryParameter(queryString); User user = repository.findUserById(elements.get("userId")); // 로그인 성공 if (user != null && user.getPassword().equals(elements.get("password"))) { - response302HeaderWithCookie(dos, URL.HOME_URL.value()); + response302HeaderWithCookie(dos, HOME_URL); return; } // 로그인 실패 - response302Header(dos, URL.LOGIN_FAILED_URL.value()); + response302Header(dos, LOGIN_FAILED_URL); return; } @@ -93,16 +97,16 @@ public void run() { if (url.equals("/user/userList")) { // 비로그인 상태 : redirect to /user/login.html if (!cookie.equals("logined=true")) { - response302Header(dos, URL.LOGIN_URL.value()); + response302Header(dos, LOGIN_URL); return; } // 로그인 상태 : user/list.html 반환 - body = Files.readAllBytes(Paths.get(URL.ROOT_URL.value() + URL.LIST_URL.value())); + body = Files.readAllBytes(Paths.get(ROOT_URL + LIST_URL)); } // 요구사항 7번 - if (method.equals(HttpMethod.GET.value()) && url.endsWith(".css")) { - body = Files.readAllBytes(Paths.get(URL.ROOT_URL.value() + url)); + if (method.equals("GET") && url.endsWith(".css")) { + body = Files.readAllBytes(Paths.get(ROOT_URL + url)); response200HeaderWithCss(dos, body.length); responseBody(dos, body); return; @@ -120,7 +124,7 @@ public void run() { } private String makeQueryStringByMethod(String method, BufferedReader br, int requestContentLength, String url) throws IOException { - if (method.equals(HttpMethod.POST.value())) { + if (method.equals("POST")) { return IOUtils.readData(br, requestContentLength); } // GET 방식 From fd822f5b6168d01ffa4a2ac2e8673306c20ae886 Mon Sep 17 00:00:00 2001 From: emes-g Date: Mon, 1 Apr 2024 03:12:43 +0900 Subject: [PATCH 17/26] =?UTF-8?q?Refactor:=20TDD=20=EB=B0=A9=EC=8B=9D?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20HttpRequest=20=EB=A9=94=EC=8B=9C=EC=A7=80?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/http/constants/HttpMethod.java | 16 ++++ src/main/java/http/request/HttpHeader.java | 40 ++++++++++ src/main/java/http/request/HttpRequest.java | 79 +++++++++++++++++++ .../http/request/HttpRequestStartLine.java | 59 ++++++++++++++ .../java/http/request/HttpRequestTest.java | 50 ++++++++++++ src/test/resources/HttpGetWithQuery.txt | 5 ++ src/test/resources/HttpPostWithQuery.txt | 7 ++ 7 files changed, 256 insertions(+) create mode 100644 src/main/java/http/constants/HttpMethod.java create mode 100644 src/main/java/http/request/HttpHeader.java create mode 100644 src/main/java/http/request/HttpRequest.java create mode 100644 src/main/java/http/request/HttpRequestStartLine.java create mode 100644 src/test/java/http/request/HttpRequestTest.java create mode 100644 src/test/resources/HttpGetWithQuery.txt create mode 100644 src/test/resources/HttpPostWithQuery.txt diff --git a/src/main/java/http/constants/HttpMethod.java b/src/main/java/http/constants/HttpMethod.java new file mode 100644 index 0000000..3ec5251 --- /dev/null +++ b/src/main/java/http/constants/HttpMethod.java @@ -0,0 +1,16 @@ +package http.constants; + +public enum HttpMethod { + GET("GET"), + POST("POST"); + + private final String method; + + HttpMethod(String method) { + this.method = method; + } + + public String get() { + return method; + } +} diff --git a/src/main/java/http/request/HttpHeader.java b/src/main/java/http/request/HttpHeader.java new file mode 100644 index 0000000..b091b0a --- /dev/null +++ b/src/main/java/http/request/HttpHeader.java @@ -0,0 +1,40 @@ +package http.request; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class HttpHeader { + private final Map header; // header fields + + private HttpHeader(Map header) { + this.header = header; + } + + public static HttpHeader from(BufferedReader br) throws IOException { + return new HttpHeader(readHeader(br)); + } + + private static Map readHeader(BufferedReader br) throws IOException { + Map header = new HashMap<>(); + String field = ""; + + while (!(field = br.readLine()).isEmpty()) { + String[] pair = field.split(": "); + String name = pair[0]; + String value = pair[1]; + header.put(name, value); + } + + return header; + } + + public boolean contains(String fieldName) { + return header.containsKey(fieldName); + } + + public String get(String fieldName) { + return header.get(fieldName); + } +} diff --git a/src/main/java/http/request/HttpRequest.java b/src/main/java/http/request/HttpRequest.java new file mode 100644 index 0000000..c7a2acc --- /dev/null +++ b/src/main/java/http/request/HttpRequest.java @@ -0,0 +1,79 @@ +package http.request; + +import http.constants.HttpMethod; +import http.util.HttpRequestUtils; +import http.util.IOUtils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; + +public class HttpRequest { + private final HttpRequestStartLine startLine; // request start-line + private final HttpHeader header; // request header (fields) + private final String body; // request body + private final Map query; + + private HttpRequest(HttpRequestStartLine startLine, HttpHeader header, String body, Map query) { + this.startLine = startLine; + this.header = header; + this.body = body; + this.query = query; + } + + public static HttpRequest from(InputStream in) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + String startLine = br.readLine(); + if (startLine == null) { + throw new IllegalArgumentException("request is empty."); + } + + HttpRequestStartLine httpRequestStartLine = HttpRequestStartLine.from(startLine); + HttpHeader header = HttpHeader.from(br); + String body = readBody(br, header); + Map query = createQuery(body); + + return new HttpRequest(httpRequestStartLine, header, body, query); + } + + private static String readBody(BufferedReader br, HttpHeader header) throws IOException { + if (!header.contains("Content-Length")) { + return ""; + } + + int contentLength = Integer.parseInt(header.get("Content-Length")); + return IOUtils.readData(br, contentLength); + } + + private static Map createQuery(String body) { + // GET 방식인 경우 + if (body.isEmpty()) { + return new HashMap<>(); + } + + // POST 방식인 경우 + System.out.print(body); + return HttpRequestUtils.parseQueryParameter(body); + } + + public String getPath() { + return startLine.getPath(); + } + + public String getMethod() { + return startLine.getMethod(); + } + + public String getQueryParameter(String fieldName) { + if (getMethod().equals(HttpMethod.GET.get())) + return startLine.getQueryParameter(fieldName); + return query.get(fieldName); + } + + public String getField(String fieldName) { + return header.get(fieldName); + } +} diff --git a/src/main/java/http/request/HttpRequestStartLine.java b/src/main/java/http/request/HttpRequestStartLine.java new file mode 100644 index 0000000..07ea9f1 --- /dev/null +++ b/src/main/java/http/request/HttpRequestStartLine.java @@ -0,0 +1,59 @@ +package http.request; + +import http.util.HttpRequestUtils; + +import java.util.HashMap; +import java.util.Map; + +public class HttpRequestStartLine { + private final String method; + private final String path; + private final String version; + private final Map query; + + public HttpRequestStartLine(String method, String path, String version, Map query) { + this.method = method; + this.path = path; + this.version = version; + this.query = query; + } + + public static HttpRequestStartLine from(String startLine) { + String[] elements = startLine.split(" "); + + String method = elements[0]; + String path = elements[1]; + String version = elements[2]; + Map query = createQuery(path); + + return new HttpRequestStartLine(method, path, version, query); + } + + private static Map createQuery(String path) { + // POST 방식인 경우 + int index = path.indexOf("?"); + if (index == -1) { + return new HashMap<>(); + } + + // GET 방식인 경우 + String queryString = path.substring(index + 1); + return HttpRequestUtils.parseQueryParameter(queryString); + } + + public String getMethod() { + return method; + } + + public String getPath() { + return path; + } + + public String getVersion() { + return version; + } + + public String getQueryParameter(String queryParameter) { + return query.get(queryParameter); + } +} diff --git a/src/test/java/http/request/HttpRequestTest.java b/src/test/java/http/request/HttpRequestTest.java new file mode 100644 index 0000000..59816e0 --- /dev/null +++ b/src/test/java/http/request/HttpRequestTest.java @@ -0,0 +1,50 @@ +package http.request; + +import http.constants.HttpMethod; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class HttpRequestTest { + private final String testDirectory = "./src/test/resources/"; + private final String getPath = "HttpGetWithQuery.txt"; + private final String postPath = "HttpPostWithQuery.txt"; + + @Test + void HTTP_GET_Query() throws IOException { + InputStream in = Files.newInputStream(Paths.get(testDirectory + getPath)); + HttpRequest httpRequest = HttpRequest.from(in); + + assertEquals("/user/create?userId=encoreJeong&password=password&name=jaeyeon", httpRequest.getPath()); + assertEquals(HttpMethod.GET.get(), httpRequest.getMethod()); + assertEquals("encoreJeong", httpRequest.getQueryParameter("userId")); + assertEquals("password", httpRequest.getQueryParameter("password")); + assertEquals("jaeyeon", httpRequest.getQueryParameter("name")); + // assert fields + assertEquals("localhost:8080", httpRequest.getField("Host")); + assertEquals("keep-alive", httpRequest.getField("Connection")); + assertEquals("*/*", httpRequest.getField("Accept")); + } + + @Test + void HTTP_POST_Query() throws IOException { + InputStream in = Files.newInputStream(Paths.get(testDirectory + postPath)); + HttpRequest httpRequest = HttpRequest.from(in); + + assertEquals("/user/create", httpRequest.getPath()); + assertEquals(HttpMethod.POST.get(), httpRequest.getMethod()); + assertEquals("encoreJeong", httpRequest.getQueryParameter("userId")); + assertEquals("password", httpRequest.getQueryParameter("password")); + assertEquals("jaeyeon", httpRequest.getQueryParameter("name")); + // assert fields + assertEquals("localhost:8080", httpRequest.getField("Host")); + assertEquals("keep-alive", httpRequest.getField("Connection")); + assertEquals("49", httpRequest.getField("Content-Length")); + assertEquals("*/*", httpRequest.getField("Accept")); + } +} \ No newline at end of file diff --git a/src/test/resources/HttpGetWithQuery.txt b/src/test/resources/HttpGetWithQuery.txt new file mode 100644 index 0000000..e7b49b2 --- /dev/null +++ b/src/test/resources/HttpGetWithQuery.txt @@ -0,0 +1,5 @@ +GET /user/create?userId=encoreJeong&password=password&name=jaeyeon HTTP/1.1 +Host: localhost:8080 +Connection: keep-alive +Accept: */* + diff --git a/src/test/resources/HttpPostWithQuery.txt b/src/test/resources/HttpPostWithQuery.txt new file mode 100644 index 0000000..e7ce687 --- /dev/null +++ b/src/test/resources/HttpPostWithQuery.txt @@ -0,0 +1,7 @@ +POST /user/create HTTP/1.1 +Host: localhost:8080 +Connection: keep-alive +Content-Length: 49 +Accept: */* + +userId=encoreJeong&password=password&name=jaeyeon From 8d5fb47a1ff28e5bd556956e073cfb3d9e96f835 Mon Sep 17 00:00:00 2001 From: emes-g Date: Mon, 1 Apr 2024 12:56:31 +0900 Subject: [PATCH 18/26] =?UTF-8?q?Refactor:=20RequestURL=20enum=20class=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/http/request/RequestURL.java | 19 +++++++++++++++++ src/main/java/webserver/RequestHandler.java | 23 ++++++++------------- 2 files changed, 28 insertions(+), 14 deletions(-) create mode 100644 src/main/java/http/request/RequestURL.java diff --git a/src/main/java/http/request/RequestURL.java b/src/main/java/http/request/RequestURL.java new file mode 100644 index 0000000..7e87c06 --- /dev/null +++ b/src/main/java/http/request/RequestURL.java @@ -0,0 +1,19 @@ +package http.request; + +public enum RequestURL { + ROOT_URL("./webapp"), + HOME_URL("/index.html"), + LOGIN_FAILED_URL("/user/login_failed.html"), + LOGIN_URL("/user/login.html"), + LIST_URL("/user/list.html"); + + private final String url; + + RequestURL(String url) { + this.url = url; + } + + public String get() { + return url; + } +} diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index 060483c..f630310 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -2,6 +2,7 @@ import db.MemoryUserRepository; import db.Repository; +import http.request.RequestURL; import http.util.HttpRequestUtils; import http.util.IOUtils; import model.User; @@ -17,12 +18,6 @@ public class RequestHandler implements Runnable { Socket connection; private static final Logger log = Logger.getLogger(RequestHandler.class.getName()); - private static final String ROOT_URL = "./webapp"; - private static final String HOME_URL = "/index.html"; - private static final String LOGIN_FAILED_URL = "/user/login_failed.html"; - private static final String LOGIN_URL = "/user/login.html"; - private static final String LIST_URL = "/user/list.html"; - private final Repository repository; public RequestHandler(Socket connection) { @@ -60,11 +55,11 @@ public void run() { // 요구사항 1번 if (url.equals("/")) { - body = Files.readAllBytes(Paths.get(ROOT_URL + HOME_URL)); + body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + RequestURL.HOME_URL.get())); } // url 에 맞는 웹페이지 반환 if (method.equals("GET") && url.endsWith(".html")) { - body = Files.readAllBytes(Paths.get(ROOT_URL + url)); + body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + url)); } // 요구사항 2,3,4번 @@ -73,7 +68,7 @@ public void run() { Map elements = HttpRequestUtils.parseQueryParameter(queryString); repository.addUser(new User(elements.get("userId"), elements.get("password"), elements.get("name"), elements.get("email"))); // for redirect - response302Header(dos, HOME_URL); + response302Header(dos, RequestURL.HOME_URL.get()); return; } @@ -85,11 +80,11 @@ public void run() { // 로그인 성공 if (user != null && user.getPassword().equals(elements.get("password"))) { - response302HeaderWithCookie(dos, HOME_URL); + response302HeaderWithCookie(dos, RequestURL.HOME_URL.get()); return; } // 로그인 실패 - response302Header(dos, LOGIN_FAILED_URL); + response302Header(dos, RequestURL.LOGIN_FAILED_URL.get()); return; } @@ -97,16 +92,16 @@ public void run() { if (url.equals("/user/userList")) { // 비로그인 상태 : redirect to /user/login.html if (!cookie.equals("logined=true")) { - response302Header(dos, LOGIN_URL); + response302Header(dos, RequestURL.LOGIN_URL.get()); return; } // 로그인 상태 : user/list.html 반환 - body = Files.readAllBytes(Paths.get(ROOT_URL + LIST_URL)); + body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + RequestURL.LIST_URL.get())); } // 요구사항 7번 if (method.equals("GET") && url.endsWith(".css")) { - body = Files.readAllBytes(Paths.get(ROOT_URL + url)); + body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + url)); response200HeaderWithCss(dos, body.length); responseBody(dos, body); return; From bb9458098a289d69fdbad786a9c18bfdc6498a0a Mon Sep 17 00:00:00 2001 From: emes-g Date: Mon, 1 Apr 2024 13:04:25 +0900 Subject: [PATCH 19/26] =?UTF-8?q?Feat:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/webserver/RequestHandler.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index f630310..493390a 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -107,6 +107,14 @@ public void run() { return; } + // 이미지 + if (method.equals("GET") && url.endsWith(".jpeg")) { + body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + url)); + response200Header(dos, body.length); + responseBody(dos, body); + return; + } + if (body.length == 0) { body = "Sorry, This page doesn't exist.".getBytes(); } From 1f4264d359210137baad0e571d794768431b611c Mon Sep 17 00:00:00 2001 From: emes-g Date: Mon, 1 Apr 2024 13:40:22 +0900 Subject: [PATCH 20/26] =?UTF-8?q?Refactor:=20RequestHandler=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=9A=94=EC=B2=AD=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EB=A1=9C=EC=A7=81=EC=9D=84=20HttpRequest?= =?UTF-8?q?=EC=97=90=20=EC=9C=84=EC=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/webserver/RequestHandler.java | 78 +++++++-------------- 1 file changed, 27 insertions(+), 51 deletions(-) diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index 493390a..a3f8cf0 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -2,16 +2,17 @@ import db.MemoryUserRepository; import db.Repository; +import http.request.HttpRequest; import http.request.RequestURL; -import http.util.HttpRequestUtils; -import http.util.IOUtils; import model.User; -import java.io.*; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.net.Socket; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -29,57 +30,39 @@ public RequestHandler(Socket connection) { public void run() { log.log(Level.INFO, "New Client Connect! Connected IP : " + connection.getInetAddress() + ", Port : " + connection.getPort()); try (InputStream in = connection.getInputStream(); OutputStream out = connection.getOutputStream()) { - BufferedReader br = new BufferedReader(new InputStreamReader(in)); DataOutputStream dos = new DataOutputStream(out); - byte[] body = new byte[0]; + HttpRequest httpRequest = HttpRequest.from(in); - // request message start-line 검증 - String startLine = br.readLine(); - String[] startLines = startLine.split(" "); - String method = startLines[0]; - String url = startLines[1]; - - // Content-Length 랑 Cookie 가져오고, BufferedReader offset 을 request message (http) body 입구에 위치 - int requestContentLength = 0; - String cookie = ""; - String line; - while (!(line = br.readLine()).isEmpty()) { - if (line.startsWith("Content-Length")) { - requestContentLength = Integer.parseInt(line.split(": ")[1]); - } - if (line.startsWith("Cookie")) { - cookie = line.split(": ")[1]; - } - } + byte[] body = new byte[0]; // 요구사항 1번 - if (url.equals("/")) { + if (httpRequest.getPath().equals("/")) { body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + RequestURL.HOME_URL.get())); } // url 에 맞는 웹페이지 반환 - if (method.equals("GET") && url.endsWith(".html")) { - body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + url)); + if (httpRequest.getMethod().equals("GET") && httpRequest.getPath().endsWith(".html")) { + body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + httpRequest.getPath())); } // 요구사항 2,3,4번 - if (url.equals("/user/signup")) { - String queryString = makeQueryStringByMethod(method, br, requestContentLength, url); - Map elements = HttpRequestUtils.parseQueryParameter(queryString); - repository.addUser(new User(elements.get("userId"), elements.get("password"), elements.get("name"), elements.get("email"))); + if (httpRequest.getPath().equals("/user/signup")) { + User user = new User(httpRequest.getQueryParameter("userId"), + httpRequest.getQueryParameter("password"), + httpRequest.getQueryParameter("name"), + httpRequest.getQueryParameter("email")); + repository.addUser(user); // for redirect response302Header(dos, RequestURL.HOME_URL.get()); return; } // 요구사항 5번 - if (method.equals("POST") && url.equals("/user/login")) { - String queryString = IOUtils.readData(br, requestContentLength); - Map elements = HttpRequestUtils.parseQueryParameter(queryString); - User user = repository.findUserById(elements.get("userId")); + if (httpRequest.getMethod().equals("POST") && httpRequest.getPath().equals("/user/login")) { + User user = repository.findUserById(httpRequest.getQueryParameter("userId")); // 로그인 성공 - if (user != null && user.getPassword().equals(elements.get("password"))) { + if (user != null && user.getPassword().equals(httpRequest.getQueryParameter("password"))) { response302HeaderWithCookie(dos, RequestURL.HOME_URL.get()); return; } @@ -89,9 +72,11 @@ public void run() { } // 요구사항 6번 - if (url.equals("/user/userList")) { + if (httpRequest.getPath().equals("/user/userList")) { // 비로그인 상태 : redirect to /user/login.html - if (!cookie.equals("logined=true")) { + String cookie = httpRequest.getField("Cookie"); + log.info(cookie); + if (cookie == null || !cookie.equals("logined=true")) { response302Header(dos, RequestURL.LOGIN_URL.get()); return; } @@ -100,16 +85,16 @@ public void run() { } // 요구사항 7번 - if (method.equals("GET") && url.endsWith(".css")) { - body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + url)); + if (httpRequest.getMethod().equals("GET") && httpRequest.getPath().endsWith(".css")) { + body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + httpRequest.getPath())); response200HeaderWithCss(dos, body.length); responseBody(dos, body); return; } // 이미지 - if (method.equals("GET") && url.endsWith(".jpeg")) { - body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + url)); + if (httpRequest.getMethod().equals("GET") && httpRequest.getPath().endsWith(".jpeg")) { + body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + httpRequest.getPath())); response200Header(dos, body.length); responseBody(dos, body); return; @@ -126,15 +111,6 @@ public void run() { } } - private String makeQueryStringByMethod(String method, BufferedReader br, int requestContentLength, String url) throws IOException { - if (method.equals("POST")) { - return IOUtils.readData(br, requestContentLength); - } - // GET 방식 - int index = url.indexOf("?"); - return url.substring(index + 1); - } - private void response200Header(DataOutputStream dos, int lengthOfBodyContent) { try { dos.writeBytes("HTTP/1.1 200 OK \r\n"); From 9ca1732e5b16b903b57e86dc82a279a9871771e5 Mon Sep 17 00:00:00 2001 From: emes-g Date: Mon, 1 Apr 2024 16:02:34 +0900 Subject: [PATCH 21/26] =?UTF-8?q?Refactor:=20HttpResponse=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/http/request/HttpHeader.java | 20 ++- src/main/java/http/response/HttpResponse.java | 67 +++++++++ .../http/response/HttpResponseStatusLine.java | 25 ++++ .../java/http/response/HttpResponseTest.java | 32 +++++ src/test/resources/Http_Forward.txt | 127 ++++++++++++++++++ src/test/resources/Http_Response_Redirect.txt | 4 + 6 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 src/main/java/http/response/HttpResponse.java create mode 100644 src/main/java/http/response/HttpResponseStatusLine.java create mode 100644 src/test/java/http/response/HttpResponseTest.java create mode 100644 src/test/resources/Http_Forward.txt create mode 100644 src/test/resources/Http_Response_Redirect.txt diff --git a/src/main/java/http/request/HttpHeader.java b/src/main/java/http/request/HttpHeader.java index b091b0a..a46f2ca 100644 --- a/src/main/java/http/request/HttpHeader.java +++ b/src/main/java/http/request/HttpHeader.java @@ -8,7 +8,7 @@ public class HttpHeader { private final Map header; // header fields - private HttpHeader(Map header) { + public HttpHeader(Map header) { this.header = header; } @@ -37,4 +37,22 @@ public boolean contains(String fieldName) { public String get(String fieldName) { return header.get(fieldName); } + + public void put(String name, String value) { + header.put(name, value); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + for (String name : header.keySet()) { + sb.append(name) + .append(": ") + .append(header.get(name)) + .append("\r\n"); + } + + return sb.append("\r\n").toString(); + } } diff --git a/src/main/java/http/response/HttpResponse.java b/src/main/java/http/response/HttpResponse.java new file mode 100644 index 0000000..4d495da --- /dev/null +++ b/src/main/java/http/response/HttpResponse.java @@ -0,0 +1,67 @@ +package http.response; + + +import http.request.HttpHeader; +import http.request.RequestURL; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.HashMap; + +public class HttpResponse { + private final DataOutputStream dos; + private HttpResponseStatusLine statusLine; + private HttpHeader httpHeader; + private byte[] body; + + public HttpResponse(OutputStream out) { + this.dos = new DataOutputStream(out); + this.httpHeader = new HttpHeader(new HashMap<>()); + this.body = new byte[0]; + httpHeader.put("Content-Type", "text/html;charset=utf-8"); + } + + public void forward(String path) throws IOException { + statusLine = new HttpResponseStatusLine("HTTP/1.1", "200", "OK"); + if (path.endsWith(".css")) { + httpHeader.put("Content-Type", "text/css"); + } + if (path.endsWith(".jpeg")) { + httpHeader.put("Content-Type", "image/jpeg"); + } + setBody(path); + write(); + } + + private void setBody(String path) throws IOException { + if (path.equals("/")) { + body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + RequestURL.HOME_URL.get())); + } + if (path.endsWith(".html") || path.endsWith(".css") || path.endsWith(".jpeg")) { + body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + path)); + } + if (path.equals("/user/userList")) { + body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + RequestURL.LIST_URL)); + } + if (body.length == 0) { + body = "Sorry, This page doesn't exist.".getBytes(); + } + httpHeader.put("Content-Length", String.valueOf(body.length)); + } + + public void redirect(String path) throws IOException { + statusLine = new HttpResponseStatusLine("HTTP/1.1", "302", "Found"); + httpHeader.put("Location", path); + write(); + } + + private void write() throws IOException { + dos.writeBytes(statusLine.getVersion() + " " + statusLine.getStatusCode() + " " + statusLine.getStatusMessage() + " \r\n"); + dos.writeBytes(httpHeader.toString()); + dos.write(body); + dos.flush(); + } +} diff --git a/src/main/java/http/response/HttpResponseStatusLine.java b/src/main/java/http/response/HttpResponseStatusLine.java new file mode 100644 index 0000000..d30c0f3 --- /dev/null +++ b/src/main/java/http/response/HttpResponseStatusLine.java @@ -0,0 +1,25 @@ +package http.response; + +public class HttpResponseStatusLine { + private String version; + private String statusCode; + private String statusMessage; + + public HttpResponseStatusLine(String version, String statusMessage, String statusCode) { + this.version = version; + this.statusMessage = statusMessage; + this.statusCode = statusCode; + } + + public String getVersion() { + return version; + } + + public String getStatusMessage() { + return statusMessage; + } + + public String getStatusCode() { + return statusCode; + } +} diff --git a/src/test/java/http/response/HttpResponseTest.java b/src/test/java/http/response/HttpResponseTest.java new file mode 100644 index 0000000..72a3050 --- /dev/null +++ b/src/test/java/http/response/HttpResponseTest.java @@ -0,0 +1,32 @@ +package http.response; + +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; + +import static org.junit.jupiter.api.Assertions.*; + +class HttpResponseTest { + private final String testDirectory = "./src/test/resources/"; + private final String forwardPath = "Http_Forward.txt"; + private final String redirectPath = "Http_Response_Redirect.txt"; + + @Test + void forward() throws IOException { + OutputStream out = Files.newOutputStream(Paths.get(testDirectory + forwardPath)); + HttpResponse httpResponse = new HttpResponse(out); + + httpResponse.forward("/index.html"); + } + + @Test + void redirect() throws IOException { + OutputStream out = Files.newOutputStream(Paths.get(testDirectory + redirectPath)); + HttpResponse httpResponse = new HttpResponse(out); + + httpResponse.redirect("/index.html"); + } +} \ No newline at end of file diff --git a/src/test/resources/Http_Forward.txt b/src/test/resources/Http_Forward.txt new file mode 100644 index 0000000..f450bd8 --- /dev/null +++ b/src/test/resources/Http_Forward.txt @@ -0,0 +1,127 @@ +HTTP/1.1 OK 200 +Content-Length: 5984 +Content-Type: text/html;charset=utf-8 + + + + + + + KUIT + + + + + + +
+

Q&A

+
+ +
+
+
+ +
+ +
+
+
+ + + + + \ No newline at end of file diff --git a/src/test/resources/Http_Response_Redirect.txt b/src/test/resources/Http_Response_Redirect.txt new file mode 100644 index 0000000..f800689 --- /dev/null +++ b/src/test/resources/Http_Response_Redirect.txt @@ -0,0 +1,4 @@ +HTTP/1.1 Found 302 +Content-Type: text/html;charset=utf-8 +Location: /index.html + From 0d074408c934804f3f58c4be0ac8fe0dab1bbb8e Mon Sep 17 00:00:00 2001 From: emes-g Date: Mon, 1 Apr 2024 16:58:22 +0900 Subject: [PATCH 22/26] =?UTF-8?q?Fix:=20=EC=83=9D=EC=84=B1=EC=9E=90=20?= =?UTF-8?q?=EB=A7=A4=EA=B0=9C=EB=B3=80=EC=88=98=20=EC=88=9C=EC=84=9C?= =?UTF-8?q?=EB=A5=BC=20=EC=98=AC=EB=B0=94=EB=A5=B4=EA=B2=8C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/http/response/HttpResponseStatusLine.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/http/response/HttpResponseStatusLine.java b/src/main/java/http/response/HttpResponseStatusLine.java index d30c0f3..6b82141 100644 --- a/src/main/java/http/response/HttpResponseStatusLine.java +++ b/src/main/java/http/response/HttpResponseStatusLine.java @@ -5,21 +5,21 @@ public class HttpResponseStatusLine { private String statusCode; private String statusMessage; - public HttpResponseStatusLine(String version, String statusMessage, String statusCode) { + public HttpResponseStatusLine(String version, String statusCode, String statusMessage) { this.version = version; - this.statusMessage = statusMessage; this.statusCode = statusCode; + this.statusMessage = statusMessage; } public String getVersion() { return version; } - public String getStatusMessage() { - return statusMessage; - } - public String getStatusCode() { return statusCode; } + + public String getStatusMessage() { + return statusMessage; + } } From 18f3d592e4d275f2f09eee6575ff6d0e4c88ffb6 Mon Sep 17 00:00:00 2001 From: emes-g Date: Mon, 1 Apr 2024 16:59:24 +0900 Subject: [PATCH 23/26] =?UTF-8?q?Refactor:=20RequestHandler=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=9D=91=EB=8B=B5=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EB=A1=9C=EC=A7=81=EC=9D=84=20HttpResponse?= =?UTF-8?q?=EC=97=90=20=EC=9C=84=EC=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/http/response/HttpResponse.java | 11 +- src/main/java/webserver/RequestHandler.java | 101 +++--------------- 2 files changed, 24 insertions(+), 88 deletions(-) diff --git a/src/main/java/http/response/HttpResponse.java b/src/main/java/http/response/HttpResponse.java index 4d495da..57ead4a 100644 --- a/src/main/java/http/response/HttpResponse.java +++ b/src/main/java/http/response/HttpResponse.java @@ -21,11 +21,13 @@ public HttpResponse(OutputStream out) { this.dos = new DataOutputStream(out); this.httpHeader = new HttpHeader(new HashMap<>()); this.body = new byte[0]; - httpHeader.put("Content-Type", "text/html;charset=utf-8"); } public void forward(String path) throws IOException { statusLine = new HttpResponseStatusLine("HTTP/1.1", "200", "OK"); + if (path.endsWith(".html")) { + httpHeader.put("Content-Type", "text/html;charset=utf-8"); + } if (path.endsWith(".css")) { httpHeader.put("Content-Type", "text/css"); } @@ -58,6 +60,13 @@ public void redirect(String path) throws IOException { write(); } + public void redirectWithCookie(String path) throws IOException { + statusLine = new HttpResponseStatusLine("HTTP/1.1", "302", "Found"); + httpHeader.put("Location", path); + httpHeader.put("Set-Cookie", "logined=true; Path=/"); + write(); + } + private void write() throws IOException { dos.writeBytes(statusLine.getVersion() + " " + statusLine.getStatusCode() + " " + statusLine.getStatusMessage() + " \r\n"); dos.writeBytes(httpHeader.toString()); diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index a3f8cf0..07433a6 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -4,15 +4,13 @@ import db.Repository; import http.request.HttpRequest; import http.request.RequestURL; +import http.response.HttpResponse; import model.User; -import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.logging.Level; import java.util.logging.Logger; @@ -30,19 +28,18 @@ public RequestHandler(Socket connection) { public void run() { log.log(Level.INFO, "New Client Connect! Connected IP : " + connection.getInetAddress() + ", Port : " + connection.getPort()); try (InputStream in = connection.getInputStream(); OutputStream out = connection.getOutputStream()) { - DataOutputStream dos = new DataOutputStream(out); - HttpRequest httpRequest = HttpRequest.from(in); - - byte[] body = new byte[0]; + HttpResponse httpResponse = new HttpResponse(out); // 요구사항 1번 if (httpRequest.getPath().equals("/")) { - body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + RequestURL.HOME_URL.get())); + httpResponse.forward(RequestURL.HOME_URL.get()); + return; } // url 에 맞는 웹페이지 반환 if (httpRequest.getMethod().equals("GET") && httpRequest.getPath().endsWith(".html")) { - body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + httpRequest.getPath())); + httpResponse.forward(httpRequest.getPath()); + return; } // 요구사항 2,3,4번 @@ -52,8 +49,7 @@ public void run() { httpRequest.getQueryParameter("name"), httpRequest.getQueryParameter("email")); repository.addUser(user); - // for redirect - response302Header(dos, RequestURL.HOME_URL.get()); + httpResponse.redirect(RequestURL.HOME_URL.get()); return; } @@ -63,11 +59,11 @@ public void run() { // 로그인 성공 if (user != null && user.getPassword().equals(httpRequest.getQueryParameter("password"))) { - response302HeaderWithCookie(dos, RequestURL.HOME_URL.get()); + httpResponse.redirectWithCookie(RequestURL.HOME_URL.get()); return; } // 로그인 실패 - response302Header(dos, RequestURL.LOGIN_FAILED_URL.get()); + httpResponse.redirect(RequestURL.LOGIN_FAILED_URL.get()); return; } @@ -75,95 +71,26 @@ public void run() { if (httpRequest.getPath().equals("/user/userList")) { // 비로그인 상태 : redirect to /user/login.html String cookie = httpRequest.getField("Cookie"); - log.info(cookie); if (cookie == null || !cookie.equals("logined=true")) { - response302Header(dos, RequestURL.LOGIN_URL.get()); + httpResponse.redirect(RequestURL.LOGIN_URL.get()); return; } // 로그인 상태 : user/list.html 반환 - body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + RequestURL.LIST_URL.get())); + httpResponse.forward(RequestURL.LIST_URL.get()); + return; } // 요구사항 7번 if (httpRequest.getMethod().equals("GET") && httpRequest.getPath().endsWith(".css")) { - body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + httpRequest.getPath())); - response200HeaderWithCss(dos, body.length); - responseBody(dos, body); + httpResponse.forward(httpRequest.getPath()); return; } // 이미지 if (httpRequest.getMethod().equals("GET") && httpRequest.getPath().endsWith(".jpeg")) { - body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + httpRequest.getPath())); - response200Header(dos, body.length); - responseBody(dos, body); - return; - } - - if (body.length == 0) { - body = "Sorry, This page doesn't exist.".getBytes(); + httpResponse.forward(httpRequest.getPath()); } - response200Header(dos, body.length); - responseBody(dos, body); - - } catch (IOException e) { - log.log(Level.SEVERE, e.getMessage()); - } - } - - private void response200Header(DataOutputStream dos, int lengthOfBodyContent) { - try { - dos.writeBytes("HTTP/1.1 200 OK \r\n"); - dos.writeBytes("Content-Type: text/html;charset=utf-8\r\n"); - dos.writeBytes("Content-Length: " + lengthOfBodyContent + "\r\n"); - dos.writeBytes("\r\n"); - dos.flush(); - } catch (IOException e) { - log.log(Level.SEVERE, e.getMessage()); - } - } - - private void response200HeaderWithCss(DataOutputStream dos, 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"); - dos.flush(); - } catch (IOException e) { - log.log(Level.SEVERE, e.getMessage()); - } - } - - private void responseBody(DataOutputStream dos, byte[] body) { - try { - dos.write(body, 0, body.length); - dos.flush(); - } catch (IOException e) { - log.log(Level.SEVERE, e.getMessage()); - } - } - - // for redirect - private void response302Header(DataOutputStream dos, String path) { - try { - dos.writeBytes("HTTP/1.1 302 Found \r\n"); - dos.writeBytes("Location: " + path + "\r\n"); - dos.writeBytes("\r\n"); - dos.flush(); - } catch (IOException e) { - log.log(Level.SEVERE, e.getMessage()); - } - } - // 트러블 슈팅 (Cookie Path) - private void response302HeaderWithCookie(DataOutputStream dos, String path) { - try { - dos.writeBytes("HTTP/1.1 302 Found \r\n"); - dos.writeBytes("Location: " + path + "\r\n"); - dos.writeBytes("Set-Cookie: logined=true; Path=/\r\n"); - dos.writeBytes("\r\n"); - dos.flush(); } catch (IOException e) { log.log(Level.SEVERE, e.getMessage()); } From 07342cf4328d1ca3b419ab488136b7751bdb68de Mon Sep 17 00:00:00 2001 From: emes-g Date: Mon, 1 Apr 2024 19:34:10 +0900 Subject: [PATCH 24/26] =?UTF-8?q?Refactor:=20Controller=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=EB=A5=BC=20=ED=86=B5?= =?UTF-8?q?=ED=95=B4=20URL=20=EB=A7=88=EB=8B=A4=20=EC=9E=91=EC=97=85=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/controller/AbstractController.java | 21 ++++++++ src/main/java/controller/Controller.java | 10 ++++ .../java/controller/ForwardController.java | 13 +++++ src/main/java/controller/HomeController.java | 14 +++++ src/main/java/controller/ListController.java | 22 ++++++++ src/main/java/controller/LoginController.java | 26 +++++++++ .../java/controller/SignUpController.java | 24 +++++++++ src/main/java/webserver/RequestHandler.java | 54 +++---------------- 8 files changed, 138 insertions(+), 46 deletions(-) create mode 100644 src/main/java/controller/AbstractController.java create mode 100644 src/main/java/controller/Controller.java create mode 100644 src/main/java/controller/ForwardController.java create mode 100644 src/main/java/controller/HomeController.java create mode 100644 src/main/java/controller/ListController.java create mode 100644 src/main/java/controller/LoginController.java create mode 100644 src/main/java/controller/SignUpController.java diff --git a/src/main/java/controller/AbstractController.java b/src/main/java/controller/AbstractController.java new file mode 100644 index 0000000..1224f44 --- /dev/null +++ b/src/main/java/controller/AbstractController.java @@ -0,0 +1,21 @@ +package controller; + +import http.constants.HttpMethod; +import http.request.HttpRequest; +import http.response.HttpResponse; + +import java.io.IOException; + +public abstract class AbstractController implements Controller { + @Override + public void execute(HttpRequest request, HttpResponse response) throws IOException { + if(request.getMethod().equals(HttpMethod.GET.get())){ + doGet(request, response); + return; + } + doPost(request, response); + } + + public abstract void doGet(HttpRequest request, HttpResponse response) throws IOException; + public abstract void doPost(HttpRequest request, HttpResponse response) throws IOException; +} diff --git a/src/main/java/controller/Controller.java b/src/main/java/controller/Controller.java new file mode 100644 index 0000000..0495148 --- /dev/null +++ b/src/main/java/controller/Controller.java @@ -0,0 +1,10 @@ +package controller; + +import http.request.HttpRequest; +import http.response.HttpResponse; + +import java.io.IOException; + +public interface Controller { + void execute(HttpRequest request, HttpResponse response) throws IOException; +} diff --git a/src/main/java/controller/ForwardController.java b/src/main/java/controller/ForwardController.java new file mode 100644 index 0000000..0f78a19 --- /dev/null +++ b/src/main/java/controller/ForwardController.java @@ -0,0 +1,13 @@ +package controller; + +import http.request.HttpRequest; +import http.response.HttpResponse; + +import java.io.IOException; + +public class ForwardController implements Controller { + @Override + public void execute(HttpRequest request, HttpResponse response) throws IOException { + response.forward(request.getPath()); + } +} diff --git a/src/main/java/controller/HomeController.java b/src/main/java/controller/HomeController.java new file mode 100644 index 0000000..e31704e --- /dev/null +++ b/src/main/java/controller/HomeController.java @@ -0,0 +1,14 @@ +package controller; + +import http.request.HttpRequest; +import http.request.RequestURL; +import http.response.HttpResponse; + +import java.io.IOException; + +public class HomeController implements Controller { + @Override + public void execute(HttpRequest request, HttpResponse response) throws IOException { + response.forward(RequestURL.HOME_URL.get()); + } +} diff --git a/src/main/java/controller/ListController.java b/src/main/java/controller/ListController.java new file mode 100644 index 0000000..a98523d --- /dev/null +++ b/src/main/java/controller/ListController.java @@ -0,0 +1,22 @@ +package controller; + +import http.request.HttpRequest; +import http.request.RequestURL; +import http.response.HttpResponse; + +import java.io.IOException; + +public class ListController implements Controller { + + @Override + public void execute(HttpRequest request, HttpResponse response) throws IOException { + // 비로그인 상태 : redirect to /user/login.html + String cookie = request.getField("Cookie"); + if (cookie == null || !cookie.equals("logined=true")) { + response.redirect(RequestURL.LOGIN_URL.get()); + return; + } + // 로그인 상태 : user/list.html 반환 + response.forward(RequestURL.LIST_URL.get()); + } +} diff --git a/src/main/java/controller/LoginController.java b/src/main/java/controller/LoginController.java new file mode 100644 index 0000000..e448411 --- /dev/null +++ b/src/main/java/controller/LoginController.java @@ -0,0 +1,26 @@ +package controller; + +import db.MemoryUserRepository; +import db.Repository; +import http.request.HttpRequest; +import http.request.RequestURL; +import http.response.HttpResponse; +import model.User; + +import java.io.IOException; + +public class LoginController implements Controller { + private final Repository repository = MemoryUserRepository.getInstance(); + @Override + public void execute(HttpRequest request, HttpResponse response) throws IOException { + User user = repository.findUserById(request.getQueryParameter("userId")); + + // 로그인 성공 + if (user != null && user.getPassword().equals(request.getQueryParameter("password"))) { + response.redirectWithCookie(RequestURL.HOME_URL.get()); + return; + } + // 로그인 실패 + response.redirect(RequestURL.LOGIN_FAILED_URL.get()); + } +} diff --git a/src/main/java/controller/SignUpController.java b/src/main/java/controller/SignUpController.java new file mode 100644 index 0000000..8d9c7a7 --- /dev/null +++ b/src/main/java/controller/SignUpController.java @@ -0,0 +1,24 @@ +package controller; + +import db.MemoryUserRepository; +import db.Repository; +import http.request.HttpRequest; +import http.request.RequestURL; +import http.response.HttpResponse; +import model.User; + +import java.io.IOException; + +public class SignUpController implements Controller { + private final Repository repository = MemoryUserRepository.getInstance(); + + @Override + public void execute(HttpRequest request, HttpResponse response) throws IOException { + User user = new User(request.getQueryParameter("userId"), + request.getQueryParameter("password"), + request.getQueryParameter("name"), + request.getQueryParameter("email")); + repository.addUser(user); + response.redirect(RequestURL.HOME_URL.get()); + } +} diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index 07433a6..b0ff5e9 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -1,11 +1,10 @@ package webserver; +import controller.*; import db.MemoryUserRepository; import db.Repository; import http.request.HttpRequest; -import http.request.RequestURL; import http.response.HttpResponse; -import model.User; import java.io.IOException; import java.io.InputStream; @@ -18,6 +17,7 @@ public class RequestHandler implements Runnable { Socket connection; private static final Logger log = Logger.getLogger(RequestHandler.class.getName()); private final Repository repository; + private Controller controller = new ForwardController(); // 요구사항 1,7번 및 이미지 public RequestHandler(Socket connection) { this.connection = connection; @@ -28,68 +28,30 @@ public RequestHandler(Socket connection) { public void run() { log.log(Level.INFO, "New Client Connect! Connected IP : " + connection.getInetAddress() + ", Port : " + connection.getPort()); try (InputStream in = connection.getInputStream(); OutputStream out = connection.getOutputStream()) { + // Header 분석 HttpRequest httpRequest = HttpRequest.from(in); HttpResponse httpResponse = new HttpResponse(out); // 요구사항 1번 if (httpRequest.getPath().equals("/")) { - httpResponse.forward(RequestURL.HOME_URL.get()); - return; - } - // url 에 맞는 웹페이지 반환 - if (httpRequest.getMethod().equals("GET") && httpRequest.getPath().endsWith(".html")) { - httpResponse.forward(httpRequest.getPath()); - return; + controller = new HomeController(); } // 요구사항 2,3,4번 if (httpRequest.getPath().equals("/user/signup")) { - User user = new User(httpRequest.getQueryParameter("userId"), - httpRequest.getQueryParameter("password"), - httpRequest.getQueryParameter("name"), - httpRequest.getQueryParameter("email")); - repository.addUser(user); - httpResponse.redirect(RequestURL.HOME_URL.get()); - return; + controller = new SignUpController(); } // 요구사항 5번 if (httpRequest.getMethod().equals("POST") && httpRequest.getPath().equals("/user/login")) { - User user = repository.findUserById(httpRequest.getQueryParameter("userId")); - - // 로그인 성공 - if (user != null && user.getPassword().equals(httpRequest.getQueryParameter("password"))) { - httpResponse.redirectWithCookie(RequestURL.HOME_URL.get()); - return; - } - // 로그인 실패 - httpResponse.redirect(RequestURL.LOGIN_FAILED_URL.get()); - return; + controller = new LoginController(); } // 요구사항 6번 if (httpRequest.getPath().equals("/user/userList")) { - // 비로그인 상태 : redirect to /user/login.html - String cookie = httpRequest.getField("Cookie"); - if (cookie == null || !cookie.equals("logined=true")) { - httpResponse.redirect(RequestURL.LOGIN_URL.get()); - return; - } - // 로그인 상태 : user/list.html 반환 - httpResponse.forward(RequestURL.LIST_URL.get()); - return; - } - - // 요구사항 7번 - if (httpRequest.getMethod().equals("GET") && httpRequest.getPath().endsWith(".css")) { - httpResponse.forward(httpRequest.getPath()); - return; - } - - // 이미지 - if (httpRequest.getMethod().equals("GET") && httpRequest.getPath().endsWith(".jpeg")) { - httpResponse.forward(httpRequest.getPath()); + controller = new ListController(); } + controller.execute(httpRequest, httpResponse); } catch (IOException e) { log.log(Level.SEVERE, e.getMessage()); From c9d6d11eeeb17d6276b6d455bc582e790cd2077d Mon Sep 17 00:00:00 2001 From: emes-g Date: Mon, 1 Apr 2024 20:09:51 +0900 Subject: [PATCH 25/26] =?UTF-8?q?Refactor:=20RequestMapper=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=EB=A5=BC=20=ED=86=B5=ED=95=B4=20request=20UR?= =?UTF-8?q?L=EC=97=90=20=EB=8C=80=ED=95=9C=20Controller=EB=A5=BC=20?= =?UTF-8?q?=EB=B0=98=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/http/request/RequestURL.java | 7 +++- src/main/java/http/response/HttpResponse.java | 6 +-- src/main/java/webserver/RequestHandler.java | 23 ++--------- src/main/java/webserver/RequestMapper.java | 41 +++++++++++++++++++ 4 files changed, 52 insertions(+), 25 deletions(-) create mode 100644 src/main/java/webserver/RequestMapper.java diff --git a/src/main/java/http/request/RequestURL.java b/src/main/java/http/request/RequestURL.java index 7e87c06..7b999bc 100644 --- a/src/main/java/http/request/RequestURL.java +++ b/src/main/java/http/request/RequestURL.java @@ -1,11 +1,14 @@ package http.request; public enum RequestURL { - ROOT_URL("./webapp"), + ROOT("./webapp"), HOME_URL("/index.html"), LOGIN_FAILED_URL("/user/login_failed.html"), LOGIN_URL("/user/login.html"), - LIST_URL("/user/list.html"); + LIST_URL("/user/list.html"), + SIGN_UP("/user/signup"), + LOGIN("/user/login"), + USER_LIST("/user/userList"); private final String url; diff --git a/src/main/java/http/response/HttpResponse.java b/src/main/java/http/response/HttpResponse.java index 57ead4a..c9def44 100644 --- a/src/main/java/http/response/HttpResponse.java +++ b/src/main/java/http/response/HttpResponse.java @@ -40,13 +40,13 @@ public void forward(String path) throws IOException { private void setBody(String path) throws IOException { if (path.equals("/")) { - body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + RequestURL.HOME_URL.get())); + body = Files.readAllBytes(Paths.get(RequestURL.ROOT.get() + RequestURL.HOME_URL.get())); } if (path.endsWith(".html") || path.endsWith(".css") || path.endsWith(".jpeg")) { - body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + path)); + body = Files.readAllBytes(Paths.get(RequestURL.ROOT.get() + path)); } if (path.equals("/user/userList")) { - body = Files.readAllBytes(Paths.get(RequestURL.ROOT_URL.get() + RequestURL.LIST_URL)); + body = Files.readAllBytes(Paths.get(RequestURL.ROOT.get() + RequestURL.LIST_URL)); } if (body.length == 0) { body = "Sorry, This page doesn't exist.".getBytes(); diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index b0ff5e9..671b9cf 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -32,26 +32,9 @@ public void run() { HttpRequest httpRequest = HttpRequest.from(in); HttpResponse httpResponse = new HttpResponse(out); - // 요구사항 1번 - if (httpRequest.getPath().equals("/")) { - controller = new HomeController(); - } - - // 요구사항 2,3,4번 - if (httpRequest.getPath().equals("/user/signup")) { - controller = new SignUpController(); - } - - // 요구사항 5번 - if (httpRequest.getMethod().equals("POST") && httpRequest.getPath().equals("/user/login")) { - controller = new LoginController(); - } - - // 요구사항 6번 - if (httpRequest.getPath().equals("/user/userList")) { - controller = new ListController(); - } - controller.execute(httpRequest, httpResponse); + // 요청에 맞는 응답을 처리 + RequestMapper requestMapper = new RequestMapper(httpRequest, httpResponse); + requestMapper.proceed(); } catch (IOException e) { log.log(Level.SEVERE, e.getMessage()); diff --git a/src/main/java/webserver/RequestMapper.java b/src/main/java/webserver/RequestMapper.java new file mode 100644 index 0000000..2cc5a5c --- /dev/null +++ b/src/main/java/webserver/RequestMapper.java @@ -0,0 +1,41 @@ +package webserver; + +import controller.*; +import http.request.HttpRequest; +import http.request.RequestURL; +import http.response.HttpResponse; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class RequestMapper { + private final HttpRequest request; + private final HttpResponse response; + private Map controllers = new HashMap<>(); + private Controller controller; + + public RequestMapper(HttpRequest request, HttpResponse response) { + this.request = request; + this.response = response; + initControllers(); + controller = controllers.get(request.getPath()); + } + + private void initControllers() { + controllers.put("/", new HomeController()); + controllers.put(RequestURL.HOME_URL.get(), new ForwardController()); + controllers.put(RequestURL.SIGN_UP.get(), new SignUpController()); + controllers.put(RequestURL.LOGIN.get(), new LoginController()); + controllers.put(RequestURL.USER_LIST.get(), new ListController()); + } + + public void proceed() throws IOException { + if (controller != null) { + controller.execute(request, response); + return; + } + // controllers 에 해당 경로가 없어서 매핑이 안 된 경우 + response.forward(request.getPath()); + } +} From c39a067c02fce298d78bdac38038f2a32cc40090 Mon Sep 17 00:00:00 2001 From: emes-g Date: Mon, 1 Apr 2024 20:13:03 +0900 Subject: [PATCH 26/26] =?UTF-8?q?Style:=20=EC=B6=9C=EB=A0=A5=EB=AC=B8=20?= =?UTF-8?q?=EB=B0=8F=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8A=94=20=EB=B3=80=EC=88=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/http/request/HttpRequest.java | 1 - src/main/java/webserver/RequestHandler.java | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/main/java/http/request/HttpRequest.java b/src/main/java/http/request/HttpRequest.java index c7a2acc..4c20963 100644 --- a/src/main/java/http/request/HttpRequest.java +++ b/src/main/java/http/request/HttpRequest.java @@ -55,7 +55,6 @@ private static Map createQuery(String body) { } // POST 방식인 경우 - System.out.print(body); return HttpRequestUtils.parseQueryParameter(body); } diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index 671b9cf..2105c56 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -1,6 +1,5 @@ package webserver; -import controller.*; import db.MemoryUserRepository; import db.Repository; import http.request.HttpRequest; @@ -17,7 +16,6 @@ public class RequestHandler implements Runnable { Socket connection; private static final Logger log = Logger.getLogger(RequestHandler.class.getName()); private final Repository repository; - private Controller controller = new ForwardController(); // 요구사항 1,7번 및 이미지 public RequestHandler(Socket connection) { this.connection = connection;