diff --git a/src/main/java/webserver/RequestHandler.java b/src/main/java/webserver/RequestHandler.java index e57c854..c4fc213 100644 --- a/src/main/java/webserver/RequestHandler.java +++ b/src/main/java/webserver/RequestHandler.java @@ -1,5 +1,9 @@ package webserver; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import util.FileUtils; @@ -9,15 +13,10 @@ import webserver.http.HttpRequest; import webserver.http.HttpRequestMessage; import webserver.http.HttpResponse; +import webserver.http.HttpResponseMessage; import webserver.http.HttpStatus; import webserver.was.Dispatcher; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; - public class RequestHandler extends Thread { private static final Logger log = LoggerFactory.getLogger(RequestHandler.class); @@ -40,6 +39,8 @@ public void run() { HttpRequestMessage requestMessage = HttpRequestMessage.parse(in); String path = requestMessage.uri().getPath(); + HttpResponseMessage httpResponseMessage = null; + // 정적 요청 처리 if (isStaticResourceRequest(path)) { String extension = HttpRequestUtils.parseExtension(path); @@ -49,16 +50,18 @@ public void run() { header.add("Content-Type", ContentType.findByExtension(extension).getHeader()); header.add("Content-Length", String.valueOf(file.length)); - flush(out, new HttpResponse(HttpStatus.OK, header, file)); - return; + httpResponseMessage = new HttpResponseMessage(out, new HttpResponse(HttpStatus.OK, header, file)); } // 동적 요청 처리 if (dispatcher.isMapped(requestMessage.method(), requestMessage.uri())) { HttpRequest request = HttpRequest.from(requestMessage); - HttpResponse httpResponse = this.dispatcher.handlerRequest(request); - flush(out, httpResponse); + httpResponseMessage = new HttpResponseMessage(out, this.dispatcher.handlerRequest(request)); + } + + if (httpResponseMessage != null) { + httpResponseMessage.flush(); } } catch (IOException e) { log.error(e.getMessage()); @@ -68,22 +71,4 @@ public void run() { private boolean isStaticResourceRequest(String path) { return ContentType.isExistsByExtension(HttpRequestUtils.parseExtension(path)); } - - private void flush(OutputStream out, HttpResponse response) { - try (DataOutputStream dos = new DataOutputStream(out)) { - HttpStatus status = response.getStatus(); - HttpHeader header = response.getHeader(); - byte[] body = response.getBody(); - - dos.writeBytes(String.format("HTTP/1.1 %d %s \r\n", status.getCode(), status.getStatus())); - for (String key : header.keySet()) { - dos.writeBytes(String.format("%s: %s\r\n", key, header.get(key))); - } - dos.writeBytes("\r\n"); - dos.write(body, 0, body.length); - dos.flush(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } } diff --git a/src/main/java/webserver/http/HttpHeader.java b/src/main/java/webserver/http/HttpHeader.java index f292c7a..6ca4580 100644 --- a/src/main/java/webserver/http/HttpHeader.java +++ b/src/main/java/webserver/http/HttpHeader.java @@ -4,7 +4,10 @@ import java.util.Map; import java.util.Set; -public class HttpHeader{ +public class HttpHeader { + + public static final String CONTENT_LENGTH = "Content-Length"; + private final Map header = new HashMap<>(); public void add(String key, String value) { diff --git a/src/main/java/webserver/http/HttpRequestLine.java b/src/main/java/webserver/http/HttpRequestLine.java new file mode 100644 index 0000000..e839fc5 --- /dev/null +++ b/src/main/java/webserver/http/HttpRequestLine.java @@ -0,0 +1,31 @@ +package webserver.http; + +import java.net.URI; +import java.util.Objects; + +public class HttpRequestLine { + + private static final String SP = " "; + private static final int METHOD_INDEX = 0; + private static final int URI_INDEX = 1; + + private final HttpMethod method; + private final URI uri; + + public HttpRequestLine(String requestLine) { + Objects.requireNonNull(requestLine); + + String[] splittedRequestLine = requestLine.split(SP); + + this.method = HttpMethod.valueOf(splittedRequestLine[METHOD_INDEX]); + this.uri = URI.create(splittedRequestLine[URI_INDEX]); + } + + public HttpMethod getMethod() { + return method; + } + + public URI getUri() { + return uri; + } +} diff --git a/src/main/java/webserver/http/HttpRequestMessage.java b/src/main/java/webserver/http/HttpRequestMessage.java index 391b27a..0016c4a 100644 --- a/src/main/java/webserver/http/HttpRequestMessage.java +++ b/src/main/java/webserver/http/HttpRequestMessage.java @@ -1,5 +1,7 @@ package webserver.http; +import static webserver.http.HttpHeader.CONTENT_LENGTH; + import util.HttpRequestUtils; import util.IOUtils; @@ -9,24 +11,19 @@ import java.io.InputStreamReader; import java.net.URI; -public record HttpRequestMessage(HttpMethod method, URI uri, HttpHeader header, HttpRequestBody body) { - - private static final String CONTENT_LENGTH = "Content-Length"; - private static final String SP = " "; - private static final int METHOD_INDEX = 0; - private static final int URI_INDEX = 1; +public record HttpRequestMessage(HttpRequestLine requestLine, HttpHeader header, HttpRequestBody body) { public static HttpRequestMessage parse(InputStream in) throws IOException, IllegalArgumentException, IndexOutOfBoundsException { BufferedReader br = new BufferedReader(new InputStreamReader(in)); - String[] requestLine = br.readLine().split(SP); + // Http Request Line + HttpRequestLine httpRequestLine = new HttpRequestLine(br.readLine()); - HttpMethod httpMethod = HttpMethod.valueOf(requestLine[METHOD_INDEX]); - String path = requestLine[URI_INDEX]; + // Http Header HttpHeader httpHeader = parseHttpHeader(br); + // Http Request Body HttpRequestBody requestBody = new HttpRequestBody(); - if (hasContent(httpHeader)) { int contentLength = Integer.parseInt(httpHeader.get(CONTENT_LENGTH)); @@ -35,7 +32,7 @@ public static HttpRequestMessage parse(InputStream in) throws IOException, Illeg requestBody.addAll(HttpRequestUtils.parseQueryString(data)); } - return new HttpRequestMessage(httpMethod, URI.create(path), httpHeader, requestBody); + return new HttpRequestMessage(httpRequestLine, httpHeader, requestBody); } private static HttpHeader parseHttpHeader(BufferedReader br) throws IOException { @@ -52,4 +49,13 @@ private static HttpHeader parseHttpHeader(BufferedReader br) throws IOException private static boolean hasContent(HttpHeader httpHeader) { return httpHeader.keySet().contains(CONTENT_LENGTH); } + + + public HttpMethod method() { + return this.requestLine.getMethod(); + } + + public URI uri() { + return this.requestLine.getUri(); + } } diff --git a/src/main/java/webserver/http/HttpResponseMessage.java b/src/main/java/webserver/http/HttpResponseMessage.java new file mode 100644 index 0000000..ba96c24 --- /dev/null +++ b/src/main/java/webserver/http/HttpResponseMessage.java @@ -0,0 +1,43 @@ +package webserver.http; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class HttpResponseMessage { + + private final OutputStream out; + private final HttpResponse httpResponse; + + public HttpResponseMessage(OutputStream out, HttpResponse httpResponse) { + this.out = out; + this.httpResponse = httpResponse; + } + + public void flush() { + try (DataOutputStream dos = new DataOutputStream(out)) { + writeResponseLine(dos, httpResponse.getStatus()); + writeResponseHeader(dos, httpResponse.getHeader()); + dos.writeBytes("\r\n"); + writeResponseBody(dos, httpResponse.getBody()); + + dos.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void writeResponseLine(DataOutputStream dos, HttpStatus status) throws IOException { + dos.writeBytes(String.format("HTTP/1.1 %d %s\r\n", status.getCode(), status.getStatus())); + } + + private void writeResponseHeader(DataOutputStream dos, HttpHeader header) throws IOException { + for (String key : header.keySet()) { + dos.writeBytes(String.format("%s: %s\r\n", key, header.get(key))); + } + } + + private static void writeResponseBody(DataOutputStream dos, byte[] body) throws IOException { + dos.write(body, 0, body.length); + } +} diff --git a/src/test/java/webserver/http/HttpRequestLineTest.java b/src/test/java/webserver/http/HttpRequestLineTest.java new file mode 100644 index 0000000..0af7d13 --- /dev/null +++ b/src/test/java/webserver/http/HttpRequestLineTest.java @@ -0,0 +1,44 @@ +package webserver.http; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.net.URI; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +class HttpRequestLineTest { + + @Nested + @DisplayName("HttpRequestLine 를 생성할 때") + class Create { + + @Test + void 정상적인_RequestLine_이_들어오면_객체가_생성된다() { + // given + String requestLine = "GET /index.html HTTP/1.1"; + HttpMethod expectedMethod = HttpMethod.GET; + URI expectedURI = URI.create("/index.html"); + + // when + HttpRequestLine httpRequestLine = new HttpRequestLine(requestLine); + + // then + assertThat(httpRequestLine).isNotNull(); + assertThat(httpRequestLine.getMethod()).isEqualTo(expectedMethod); + assertThat(httpRequestLine.getUri()).isEqualTo(expectedURI); + } + + @Test + void RequestLine_이_null_이면_예외가_발생한다() { + // given + String requestLine = null; + + // then + assertThatThrownBy(() -> new HttpRequestLine(requestLine)) + .isInstanceOf(NullPointerException.class); + } + } + +} \ No newline at end of file diff --git a/src/test/java/webserver/http/HttpResponseMessageTest.java b/src/test/java/webserver/http/HttpResponseMessageTest.java new file mode 100644 index 0000000..ffe6c46 --- /dev/null +++ b/src/test/java/webserver/http/HttpResponseMessageTest.java @@ -0,0 +1,34 @@ +package webserver.http; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +class HttpResponseMessageTest { + + @Nested + @DisplayName("HttpResponseMessage 를 생성할 때") + class Create{ + + @Test + void 정적_파일_응답에는_응답출력을_성공한다() { + // given + ByteArrayOutputStream out = new ByteArrayOutputStream(); + HttpStatus status = HttpStatus.OK; + String html = "

안녕하세요

"; + String expectedResponse = "HTTP/1.1 200 OK\r\n\r\n

안녕하세요

"; + HttpResponse httpResponse = new HttpResponse(status, new HttpHeader(), html.getBytes()); + + // when + HttpResponseMessage httpResponseMessage = new HttpResponseMessage(out, httpResponse); + httpResponseMessage.flush(); + + // then + String flushedResponseMessage = out.toString(); + assertThat(flushedResponseMessage).isEqualTo(expectedResponse); + } + } +} \ No newline at end of file