Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 13 additions & 28 deletions src/main/java/webserver/RequestHandler.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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());
Expand All @@ -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);
}
}
}
5 changes: 4 additions & 1 deletion src/main/java/webserver/http/HttpHeader.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> header = new HashMap<>();

public void add(String key, String value) {
Expand Down
31 changes: 31 additions & 0 deletions src/main/java/webserver/http/HttpRequestLine.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
28 changes: 17 additions & 11 deletions src/main/java/webserver/http/HttpRequestMessage.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package webserver.http;

import static webserver.http.HttpHeader.CONTENT_LENGTH;

import util.HttpRequestUtils;
import util.IOUtils;

Expand All @@ -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));

Expand All @@ -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 {
Expand All @@ -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();
}
}
43 changes: 43 additions & 0 deletions src/main/java/webserver/http/HttpResponseMessage.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
44 changes: 44 additions & 0 deletions src/test/java/webserver/http/HttpRequestLineTest.java
Original file line number Diff line number Diff line change
@@ -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);
}
}

}
34 changes: 34 additions & 0 deletions src/test/java/webserver/http/HttpResponseMessageTest.java
Original file line number Diff line number Diff line change
@@ -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 = "<html><h1>안녕하세요</h1></html>";
String expectedResponse = "HTTP/1.1 200 OK\r\n\r\n<html><h1>안녕하세요</h1></html>";
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);
}
}
}