-
Notifications
You must be signed in to change notification settings - Fork 24
3주차 미션 / 서버 2조 박서연 #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b1d49ac
4f9def3
2806ba8
8e15eb7
89930f2
314626e
1cd9d4b
4797deb
5c107ee
d2bf0fe
e1aa2fb
64c54ca
b9c92dc
1d181b0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package http; | ||
|
|
||
| import java.io.BufferedReader; | ||
| import java.io.IOException; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| public class HttpHeader { | ||
| private final List<String> lines; | ||
| private final int lengthOfContent; | ||
|
|
||
| public HttpHeader(List<String> lines, int lengthOfContent) { | ||
| this.lines = lines; | ||
| this.lengthOfContent = lengthOfContent; | ||
| } | ||
|
|
||
| public static HttpHeader from(BufferedReader br) throws IOException { | ||
| int requestContentLength = 0; | ||
| List<String> lines = new ArrayList<>(); | ||
| while (true) { | ||
| final String line = br.readLine(); | ||
| if (line.equals("")) { | ||
| break; | ||
| } | ||
| lines.add(line); | ||
| // header info | ||
| if (line.startsWith("Content-Length")) { | ||
| requestContentLength = Integer.parseInt(line.split(": ")[1]); | ||
| } | ||
| } | ||
| return new HttpHeader(lines, requestContentLength); | ||
| } | ||
|
|
||
| public int getLengthOfContent() { | ||
| return lengthOfContent; | ||
| } | ||
|
|
||
| public List<String> getLines() { | ||
| return lines; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,16 @@ | ||||||||||||
| package http; | ||||||||||||
|
|
||||||||||||
| public enum HttpMethod { | ||||||||||||
| GET("get"), | ||||||||||||
| POST("post"); | ||||||||||||
|
Comment on lines
+4
to
+5
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. HTTP 메서드는 대문자여야 합니다. HTTP 명세(RFC 7231)에 따르면 메서드 이름은 대소문자를 구분하며, 표준 메서드는 모두 대문자로 정의됩니다( 다음과 같이 수정하세요: public enum HttpMethod {
- GET("get"),
- POST("post");
+ GET("GET"),
+ POST("POST");📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||
|
|
||||||||||||
| private final String method; | ||||||||||||
|
|
||||||||||||
| public String getMethod() { | ||||||||||||
| return method; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| HttpMethod(String method) { | ||||||||||||
| this.method = method; | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,44 @@ | ||||||||||||||||||||||||
| package http; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import http.util.IOUtils; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import java.io.BufferedReader; | ||||||||||||||||||||||||
| import java.io.IOException; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| public class HttpRequest { | ||||||||||||||||||||||||
| private final HttpStartLine startLine; | ||||||||||||||||||||||||
| private final HttpHeader header; | ||||||||||||||||||||||||
| private final String body; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| private HttpRequest(HttpStartLine startLine, HttpHeader header, String body) { | ||||||||||||||||||||||||
| this.startLine = startLine; | ||||||||||||||||||||||||
| this.header = header; | ||||||||||||||||||||||||
| this.body = body; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| public static HttpRequest from(BufferedReader br) throws IOException { | ||||||||||||||||||||||||
| String requestLine = br.readLine(); | ||||||||||||||||||||||||
| if (requestLine == null || requestLine.isEmpty()) return null; | ||||||||||||||||||||||||
|
Comment on lines
+19
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Null 반환 대신 예외 처리를 고려하세요. 요청 라인이 null이거나 비어있을 때 null을 반환하면 호출자에서 NPE가 발생할 수 있습니다. 빈 요청은 일반적으로 클라이언트가 연결을 조기에 닫은 경우이므로, 명시적인 예외를 던지거나 Optional을 반환하여 호출자가 이를 명확히 처리하도록 하는 것이 안전합니다. 다음과 같이 수정을 고려하세요: - if (requestLine == null || requestLine.isEmpty()) return null;
+ if (requestLine == null || requestLine.isEmpty()) {
+ throw new IllegalArgumentException("요청 라인이 비어있습니다");
+ }또는 Optional 반환: - public static HttpRequest from(BufferedReader br) throws IOException {
+ public static Optional<HttpRequest> from(BufferedReader br) throws IOException {
String requestLine = br.readLine();
- if (requestLine == null || requestLine.isEmpty()) return null;
+ if (requestLine == null || requestLine.isEmpty()) return Optional.empty();
HttpHeader httpHeader = HttpHeader.from(br);
String body = IOUtils.readData(br, httpHeader.getLengthOfContent());
- return new HttpRequest(HttpStartLine.from(requestLine), httpHeader, body);
+ return Optional.of(new HttpRequest(HttpStartLine.from(requestLine), httpHeader, body));
}
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| HttpHeader httpHeader = HttpHeader.from(br); | ||||||||||||||||||||||||
| String body = IOUtils.readData(br, httpHeader.getLengthOfContent()); | ||||||||||||||||||||||||
|
Comment on lines
+23
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Content-Length 값 검증이 필요합니다.
다음과 같이 검증 로직을 추가하세요: HttpHeader httpHeader = HttpHeader.from(br);
+ int contentLength = httpHeader.getLengthOfContent();
+ if (contentLength < 0) {
+ throw new IllegalArgumentException("Content-Length가 음수입니다: " + contentLength);
+ }
+ if (contentLength > 10_485_760) { // 10MB 제한 예시
+ throw new IllegalArgumentException("Content-Length가 너무 큽니다: " + contentLength);
+ }
- String body = IOUtils.readData(br, httpHeader.getLengthOfContent());
+ String body = contentLength > 0 ? IOUtils.readData(br, contentLength) : "";📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| return new HttpRequest(HttpStartLine.from(requestLine), httpHeader, body); | ||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chainHttpStartLine.from에서 발생할 수 있는 예외를 처리하세요.
다음 스크립트로 예외 처리를 추가하는 것을 권장합니다: + try {
- return new HttpRequest(HttpStartLine.from(requestLine), httpHeader, body);
+ return new HttpRequest(HttpStartLine.from(requestLine), httpHeader, body);
+ } catch (IllegalArgumentException | ArrayIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException("잘못된 HTTP 요청 라인: " + requestLine, e);
+ }🏁 Script executed: #!/bin/bash
# HttpStartLine.from의 구현을 확인하여 예외 처리 확인
rg -A 10 'public static HttpStartLine from' --type javaLength of output: 766 HttpStartLine.from(requestLine) 호출부에 예외 처리 추가 + try {
return new HttpRequest(HttpStartLine.from(requestLine), httpHeader, body);
+ } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException e) {
+ throw new IllegalArgumentException("잘못된 HTTP 요청 라인: " + requestLine, e);
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| public String getMethod(){ | ||||||||||||||||||||||||
| return startLine.getMethod(); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| public String getUrl(){ | ||||||||||||||||||||||||
| return startLine.getTarget(); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| public String getBody() { | ||||||||||||||||||||||||
| return body; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| public HttpHeader getHeader(){ | ||||||||||||||||||||||||
| return header; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,83 @@ | ||||||||||||||||||||||||||||||||||||||
| package http; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| import java.io.DataOutputStream; | ||||||||||||||||||||||||||||||||||||||
| import java.io.File; | ||||||||||||||||||||||||||||||||||||||
| import java.io.IOException; | ||||||||||||||||||||||||||||||||||||||
| import java.nio.file.Files; | ||||||||||||||||||||||||||||||||||||||
| import java.nio.file.Paths; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| public class HttpResponse { | ||||||||||||||||||||||||||||||||||||||
| private final DataOutputStream dos; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| public HttpResponse(DataOutputStream dos) { | ||||||||||||||||||||||||||||||||||||||
| this.dos = dos; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| public void forward(String path) throws IOException { | ||||||||||||||||||||||||||||||||||||||
| File file = new File("webapp" + path); | ||||||||||||||||||||||||||||||||||||||
| int fileLength = (int) file.length(); | ||||||||||||||||||||||||||||||||||||||
| if(path.equals("/user/login_failed.html")){ | ||||||||||||||||||||||||||||||||||||||
| responseLoginFailedHeader(dos, fileLength); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| else response200Header(dos, fileLength); | ||||||||||||||||||||||||||||||||||||||
| responseBody(dos, Files.readAllBytes(Paths.get(file.getPath()))); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+16
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 경로 검증이 없어 보안 취약점이 있습니다.
다음과 같이 경로를 정규화하고 검증하세요: public void forward(String path) throws IOException {
+ // 경로 정규화 및 검증
+ Path normalizedPath = Paths.get("webapp" + path).normalize();
+ if (!normalizedPath.startsWith(Paths.get("webapp").toAbsolutePath())) {
+ throw new SecurityException("Invalid path: " + path);
+ }
- File file = new File("webapp" + path);
+ File file = normalizedPath.toFile();
+ if (!file.exists() || !file.isFile()) {
+ throw new IOException("File not found: " + path);
+ }
int fileLength = (int) file.length();🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| public void redirectLogin(String path) { | ||||||||||||||||||||||||||||||||||||||
| responseLoginHeader(dos, "/index.html"); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| public void redirectLoginFail(String path) { | ||||||||||||||||||||||||||||||||||||||
| response302Header(dos, "/user/login_failed.html"); } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| 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"); | ||||||||||||||||||||||||||||||||||||||
| } catch (IOException e) { | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+34
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 예외를 무시하지 마세요. IOException을 빈 catch 블록으로 잡아서 무시하고 있습니다. 이는 응답 전송 실패를 감지할 수 없게 만들어 디버깅을 어렵게 하고, 클라이언트는 불완전한 응답을 받을 수 있습니다. 최소한 로깅을 추가하거나 예외를 다시 던지세요: } catch (IOException e) {
-
+ throw new RuntimeException("Failed to write response header", e);
}같은 문제가 lines 50-52, 60-61, 71-72, 79-81에도 있습니다. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| private void responseLoginHeader(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=/; HttpOnly; SameSite=Lax\r\n"); | ||||||||||||||||||||||||||||||||||||||
| dos.writeBytes("\r\n"); | ||||||||||||||||||||||||||||||||||||||
| } catch (IOException e) { | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| private void response302Header(DataOutputStream dos, String path) { | ||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| dos.writeBytes("HTTP/1.1 302 Found \r\n"); | ||||||||||||||||||||||||||||||||||||||
| dos.writeBytes("Location: " + path); | ||||||||||||||||||||||||||||||||||||||
| dos.writeBytes("\r\n"); | ||||||||||||||||||||||||||||||||||||||
| } catch (IOException e) { | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+55
to
+62
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Location 헤더 뒤에 \r\n이 누락되었습니다. Line 58에서 Location 헤더 뒤에 dos.writeBytes("HTTP/1.1 302 Found \r\n");
- dos.writeBytes("Location: " + path);
+ dos.writeBytes("Location: " + path + "\r\n");
dos.writeBytes("\r\n");📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| private void responseLoginFailedHeader(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("Set-Cookie: logined=; Path=/; Max-Age=0; HttpOnly; SameSite=Lax\r\n"); | ||||||||||||||||||||||||||||||||||||||
| dos.writeBytes("Content-Length: " + lengthOfBodyContent + "\r\n"); | ||||||||||||||||||||||||||||||||||||||
| dos.writeBytes("\r\n"); | ||||||||||||||||||||||||||||||||||||||
| } catch (IOException e) { | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| private void responseBody(DataOutputStream dos, byte[] body) { | ||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| dos.write(body, 0, body.length); | ||||||||||||||||||||||||||||||||||||||
| dos.flush(); | ||||||||||||||||||||||||||||||||||||||
| } catch (IOException e) { | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,30 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||
| package http; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| import webserver.URL; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Optional; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| public class HttpStartLine { | ||||||||||||||||||||||||||||||||||||||||||||||||
| private final HttpMethod method; | ||||||||||||||||||||||||||||||||||||||||||||||||
| private final URL target; | ||||||||||||||||||||||||||||||||||||||||||||||||
| private final String version; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| public HttpStartLine(HttpMethod method, Optional<URL> target, String version) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| this.method = method; | ||||||||||||||||||||||||||||||||||||||||||||||||
| this.target = target.orElse(null); | ||||||||||||||||||||||||||||||||||||||||||||||||
| this.version = version; | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+12
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Optional 파라미터를 받고 null을 저장하는 것은 안티패턴입니다. 생성자가 방안 1 (권장): 필드를 Optional로 유지 private final HttpMethod method;
- private final URL target;
+ private final Optional<URL> target;
private final String version;
public HttpStartLine(HttpMethod method, Optional<URL> target, String version) {
this.method = method;
- this.target = target.orElse(null);
+ this.target = target;
this.version = version;
}그리고 getter도 수정: public String getTarget(){
- return target.getUrl();
+ return target.map(URL::getUrl).orElse("");
}방안 2: 파라미터를 nullable로 변경 - public HttpStartLine(HttpMethod method, Optional<URL> target, String version) {
+ public HttpStartLine(HttpMethod method, URL target, String version) {
this.method = method;
- this.target = target.orElse(null);
+ this.target = target;
this.version = version;
}🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| public static HttpStartLine from(String startLine){ | ||||||||||||||||||||||||||||||||||||||||||||||||
| String[] tokens = startLine.split(" "); | ||||||||||||||||||||||||||||||||||||||||||||||||
| return new HttpStartLine(HttpMethod.valueOf(tokens[0]), URL.fromPath(tokens[1]), tokens[2]); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+18
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. split 결과 배열의 길이를 검증하세요.
다음과 같이 검증 및 예외 처리를 추가하세요: public static HttpStartLine from(String startLine){
String[] tokens = startLine.split(" ");
+ if (tokens.length != 3) {
+ throw new IllegalArgumentException(
+ "잘못된 HTTP 요청 라인 형식: " + startLine + " (예상: METHOD PATH VERSION)"
+ );
+ }
+ try {
- return new HttpStartLine(HttpMethod.valueOf(tokens[0]), URL.fromPath(tokens[1]), tokens[2]);
+ return new HttpStartLine(
+ HttpMethod.valueOf(tokens[0]),
+ URL.fromPath(tokens[1]),
+ tokens[2]
+ );
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException(
+ "HTTP 요청 라인 파싱 실패: " + startLine, e
+ );
+ }
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| public String getMethod() { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return method.getMethod(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| public String getTarget(){ | ||||||||||||||||||||||||||||||||||||||||||||||||
| return target.getUrl(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+27
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NPE 위험: target이 null일 수 있습니다. Line 14에서 위의 Line 12-16에 대한 리뷰 코멘트에서 제안한 수정사항을 적용하면 이 문제도 함께 해결됩니다. Optional을 유지하는 방안을 권장합니다. 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,37 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package http; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import http.controller.*; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import webserver.URL; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.io.IOException; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.HashMap; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Map; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public class RequestMapper { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static final Map<String, Controller> controllerMap = new HashMap<>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final Controller controller; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final HttpRequest request; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final HttpResponse response; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public RequestMapper(HttpRequest request, HttpResponse response) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initializeMap(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.request = request; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.response = response; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if(request.getMethod().equals("get") && request.getUrl().endsWith(".html")) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. HTTP 메서드 비교가 대소문자를 구분합니다.
- if(request.getMethod().equals("get") && request.getUrl().endsWith(".html")) {
+ if(request.getMethod().equalsIgnoreCase("GET") && request.getUrl().endsWith(".html")) {
this.controller = controllerMap.get("forward");
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.controller = controllerMap.get("forward"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }else this.controller = controllerMap.get(request.getUrl()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+16
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 매핑되지 않은 경로에 대한 null 체크가 누락되었습니다. Line 22에서 다음과 같이 null 체크를 추가하거나 기본 컨트롤러를 설정하세요: public RequestMapper(HttpRequest request, HttpResponse response) {
initializeMap();
this.request = request;
this.response = response;
if(request.getMethod().equals("get") && request.getUrl().endsWith(".html")) {
this.controller = controllerMap.get("forward");
- }else this.controller = controllerMap.get(request.getUrl());
+ } else {
+ this.controller = controllerMap.get(request.getUrl());
+ if (this.controller == null) {
+ throw new IllegalArgumentException("매핑되지 않은 경로: " + request.getUrl());
+ }
+ }
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void initializeMap(){ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| controllerMap.put("forward",new ForwardController()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| controllerMap.put(URL.DEFAULT.getUrl(),new HomeController()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| controllerMap.put(URL.INDEX.getUrl(),new HomeController()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| controllerMap.put(URL.USER_SIGNUP.getUrl(),new SignUpController()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| controllerMap.put(URL.USER_LOGIN.getUrl(),new LoginController()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| controllerMap.put(URL.USER_USERLIST.getUrl(),new ListController()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+25
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. static map을 매 요청마다 초기화하는 것은 비효율적이고 위험합니다.
static 초기화 블록으로 한 번만 초기화하세요: public class RequestMapper {
- private static final Map<String, Controller> controllerMap = new HashMap<>();
+ private static final Map<String, Controller> controllerMap;
+
+ static {
+ controllerMap = new HashMap<>();
+ controllerMap.put("forward", new ForwardController());
+ controllerMap.put(URL.DEFAULT.getUrl(), new HomeController());
+ controllerMap.put(URL.INDEX.getUrl(), new HomeController());
+ controllerMap.put(URL.USER_SIGNUP.getUrl(), new SignUpController());
+ controllerMap.put(URL.USER_LOGIN.getUrl(), new LoginController());
+ controllerMap.put(URL.USER_USERLIST.getUrl(), new ListController());
+ }
+
private final Controller controller;
private final HttpRequest request;
private final HttpResponse response;
public RequestMapper(HttpRequest request, HttpResponse response) {
- initializeMap();
this.request = request;
this.response = response;
if(request.getMethod().equals("get") && request.getUrl().endsWith(".html")) {
this.controller = controllerMap.get("forward");
}else this.controller = controllerMap.get(request.getUrl());
}
- private void initializeMap(){
- controllerMap.put("forward",new ForwardController());
- controllerMap.put(URL.DEFAULT.getUrl(),new HomeController());
- controllerMap.put(URL.INDEX.getUrl(),new HomeController());
- controllerMap.put(URL.USER_SIGNUP.getUrl(),new SignUpController());
- controllerMap.put(URL.USER_LOGIN.getUrl(),new LoginController());
- controllerMap.put(URL.USER_USERLIST.getUrl(),new ListController());
- }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void proceed() throws IOException { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| controller.execute(request, response); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package http.controller; | ||
|
|
||
| import http.HttpRequest; | ||
| import http.HttpResponse; | ||
|
|
||
| import java.io.IOException; | ||
|
|
||
| public interface Controller { | ||
| public void execute(HttpRequest request, HttpResponse response) throws IOException; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package http.controller; | ||
|
|
||
| import http.HttpRequest; | ||
| import http.HttpResponse; | ||
|
|
||
| import java.io.IOException; | ||
|
|
||
| public class ForwardController implements Controller{ | ||
| @Override | ||
| public void execute(HttpRequest request, HttpResponse response) throws IOException { | ||
| response.forward(request.getUrl()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package http.controller; | ||
|
|
||
| import http.HttpRequest; | ||
| import http.HttpResponse; | ||
|
|
||
| import java.io.IOException; | ||
|
|
||
| public class HomeController implements Controller{ | ||
| @Override | ||
| public void execute(HttpRequest request, HttpResponse response) throws IOException { | ||
| response.forward("/index.html"); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,46 @@ | ||||||||||||||||||
| package http.controller; | ||||||||||||||||||
|
|
||||||||||||||||||
| import http.HttpRequest; | ||||||||||||||||||
| import http.HttpResponse; | ||||||||||||||||||
| import webserver.URL; | ||||||||||||||||||
|
|
||||||||||||||||||
| import java.io.File; | ||||||||||||||||||
| import java.io.IOException; | ||||||||||||||||||
| import java.nio.file.Files; | ||||||||||||||||||
| import java.nio.file.Paths; | ||||||||||||||||||
| import java.util.List; | ||||||||||||||||||
|
|
||||||||||||||||||
| public class ListController implements Controller{ | ||||||||||||||||||
| @Override | ||||||||||||||||||
| public void execute(HttpRequest request, HttpResponse response) throws IOException { | ||||||||||||||||||
| String logined = ""; | ||||||||||||||||||
| int i = 0; | ||||||||||||||||||
| List<String> lines = request.getHeader().getLines(); | ||||||||||||||||||
| while (true) { | ||||||||||||||||||
| if( i== lines.size()) { | ||||||||||||||||||
| break; | ||||||||||||||||||
| } | ||||||||||||||||||
| final String line = lines.get(i++); | ||||||||||||||||||
| if (line.isEmpty()) { | ||||||||||||||||||
| break; | ||||||||||||||||||
| } | ||||||||||||||||||
| if (line.startsWith("Cookie: ")) { | ||||||||||||||||||
| String cookieHeader = line.substring("Cookie:".length()).trim(); | ||||||||||||||||||
| String[] pairs = cookieHeader.split(";"); | ||||||||||||||||||
| for (String pair : pairs) { | ||||||||||||||||||
| String[] kv = pair.trim().split("=", 2); // 최대 2개만 split | ||||||||||||||||||
| if (kv.length == 2) { | ||||||||||||||||||
| String name = kv[0].trim(); | ||||||||||||||||||
| String value = kv[1].trim(); | ||||||||||||||||||
| if (name.equals("logined")) { | ||||||||||||||||||
| logined = value; | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| if(logined.equals("true")){ | ||||||||||||||||||
| response.forward( "/user/list.html"); | ||||||||||||||||||
| } | ||||||||||||||||||
|
Comment on lines
+42
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 로그인되지 않은 경우 응답이 비어 있습니다. 다음과 같이 보완해 주세요: - if(logined.equals("true")){
- response.forward( "/user/list.html");
- }
+ if ("true".equals(logined)) {
+ response.forward(URL.USER_USERLIST_HTML.getUrl());
+ return;
+ }
+ response.forward(URL.USER_LOGIN_HTML.getUrl());📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| package http.controller; | ||
|
|
||
| import db.MemoryUserRepository; | ||
| import http.HttpRequest; | ||
| import http.HttpResponse; | ||
| import http.util.HttpRequestUtils; | ||
| import http.util.IOUtils; | ||
| import model.User; | ||
|
|
||
| import java.io.IOException; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| public class LoginController implements Controller { | ||
| @Override | ||
| public void execute(HttpRequest request, HttpResponse response) throws IOException { | ||
| MemoryUserRepository db = MemoryUserRepository.getInstance(); | ||
| Map<String, String> userInfos = HttpRequestUtils | ||
| .parseQueryParameter(request.getBody()); | ||
| User user = db.findUserById(userInfos.get("userId")); | ||
| if (user == null) { | ||
| response.redirectLoginFail("/user/login_failed.html"); | ||
| return; | ||
| } | ||
| if (user.getPassword().equals(userInfos.get("password"))) { | ||
| response.redirectLogin("/index.html"); | ||
| } else response.redirectLoginFail("/user/login_failed.html"); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
널 체크 없이 빈 줄을 판별하고 있습니다.
Line 22에서
BufferedReader.readLine()이null을 반환할 수 있는데, 곧바로line.equals("")를 호출하면 NPE가 발생합니다. 클라이언트가 연결을 끊거나 잘못된 요청을 보낼 때 서버가 바로 죽습니다.다음과 같이 보정해 주세요:
📝 Committable suggestion
🤖 Prompt for AI Agents