Skip to content

Commit

Permalink
Merge pull request #9 from sanhee/header-and-body
Browse files Browse the repository at this point in the history
Header와 Body 구현
  • Loading branch information
sanhee authored Mar 25, 2021
2 parents 51f5476 + 0a184fa commit d20ecb2
Show file tree
Hide file tree
Showing 9 changed files with 420 additions and 4 deletions.
18 changes: 18 additions & 0 deletions src/main/java/webserver/Body.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package webserver;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

public class Body {
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;

private byte[] data;

private Body(byte[] data) {
this.data = data;
}

public static Body create(String bodyText) {
return new Body(bodyText.getBytes(DEFAULT_ENCODING));
}
}
84 changes: 84 additions & 0 deletions src/main/java/webserver/Header.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package webserver;

import util.HttpRequestUtils;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

public abstract class Header {
protected static final String PROTOCOL_VERSION_KEY = "protocolVersion";

protected Map<String, String> statusLineAttributes;
private Map<String, String> attributes;

public Header(Map<String, String> attributes) {
this.attributes = attributes;
this.statusLineAttributes = new HashMap<>();
}

public static Header of(String headerText, String type) {
Map<String, String> attributes = attributeFrom(headerText);

String[] splittedHeaderTexts = headerText.split(System.lineSeparator());
String[] statusLine = splittedHeaderTexts[0].split(" ");

Header header = of(attributes, type);
header.putStatusLine(statusLine);

return header;
}

private static Header of(Map<String, String> attributes, String type) {
switch (type) {
case "request":
return new RequestHeader(attributes);
case "response":
return new ResponseHeader(attributes);
default:
throw new IllegalStateException("Unexpected value: " + type);
}
}

private static Map<String, String> attributeFrom(String headerText) {
Map<String, String> attributes = new LinkedHashMap<>();

String[] splittedHeaderTexts = headerText.split(System.lineSeparator());
for (String splittedHeaderText : splittedHeaderTexts) {
HttpRequestUtils.Pair pair = HttpRequestUtils.parseHeader(splittedHeaderText);

if (pair != null) {
attributes.put(pair.getKey(), pair.getValue());
}
}

return attributes;
}

protected abstract void putStatusLine(String[] statusLine);

public Map<String, String> getAttributes() {
return attributes;
}

public Map<String, String> getStatusLineAttributes() {
return statusLineAttributes;
}

public byte[] toByte() {
StringBuilder sb = new StringBuilder();

sb.append(statusLine()).append(System.lineSeparator());

for (Map.Entry<String, String> entry : getAttributes().entrySet()) {
sb.append(entry.getKey() + ": " + entry.getValue() + System.lineSeparator());
}

sb.append(System.lineSeparator());

return sb.toString().getBytes(StandardCharsets.UTF_8);
}

protected abstract String statusLine();
}
11 changes: 7 additions & 4 deletions src/main/java/webserver/RequestHandler.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package webserver;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.*;
import java.net.Socket;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -23,6 +21,11 @@ public void run() {
connection.getPort());

try (InputStream in = connection.getInputStream(); OutputStream out = connection.getOutputStream()) {

BufferedReader br = new BufferedReader(new InputStreamReader(in));

log.debug("Request Message:"+ br.lines().collect(Collectors.joining(System.lineSeparator())));

// TODO 사용자 요청에 대한 처리는 이 곳에 구현하면 된다.
DataOutputStream dos = new DataOutputStream(out);
byte[] body = "Hello World".getBytes();
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/webserver/RequestHeader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package webserver;

import java.util.Map;

public class RequestHeader extends Header {
private static final String METHOD_KEY = "method";
private static final String PATH_KEY = "path";

public RequestHeader(Map<String, String> attributes) {
super(attributes);
}

@Override
protected void putStatusLine(String[] statusLine) {
statusLineAttributes.put(METHOD_KEY, statusLine[0]);
statusLineAttributes.put(PATH_KEY, statusLine[1]);
statusLineAttributes.put(PROTOCOL_VERSION_KEY, statusLine[2]);
}

@Override
protected String statusLine() {
return statusLineAttributes.get(METHOD_KEY) + " " + statusLineAttributes.get(PATH_KEY) + " " + statusLineAttributes.get(PROTOCOL_VERSION_KEY);
}
}
24 changes: 24 additions & 0 deletions src/main/java/webserver/ResponseHeader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package webserver;

import java.util.Map;

public class ResponseHeader extends Header {
private static final String STATUS_CODE_KEY = "statusCode";
private static final String STATUS_TEXT_KEY = "statusText";

public ResponseHeader(Map<String, String> attributes) {
super(attributes);
}

@Override
protected void putStatusLine(String[] statusLine) {
statusLineAttributes.put(PROTOCOL_VERSION_KEY, statusLine[0]);
statusLineAttributes.put(STATUS_CODE_KEY, statusLine[1]);
statusLineAttributes.put(STATUS_TEXT_KEY, statusLine[2]);
}

@Override
protected String statusLine() {
return statusLineAttributes.get(PROTOCOL_VERSION_KEY) + " " + statusLineAttributes.get(STATUS_CODE_KEY) + " " + statusLineAttributes.get(STATUS_TEXT_KEY);
}
}
14 changes: 14 additions & 0 deletions src/test/java/webserver/BodyTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package webserver;

import org.junit.jupiter.api.Test;

class BodyTest {

@Test
void init() {
String data = "Hello World";
Body expected = Body.create(data);

Body body = Body.create(data);
}
}
4 changes: 4 additions & 0 deletions src/test/java/webserver/HeaderTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package webserver;

class HeaderTest {
}
155 changes: 155 additions & 0 deletions src/test/java/webserver/RequestHeaderTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package webserver;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Stream;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;

class RequestHeaderTest {

@ParameterizedTest
@MethodSource
void getAttributes(String headerText, Map<String, String> expectedAttributes) {
assertThat(Header.of(headerText, "request").getAttributes())
.isEqualTo(expectedAttributes);
}

@SuppressWarnings("unused")
static Stream<Arguments> getAttributes() {
return Stream.of(
Arguments.of("GET / HTTP/1.1" + System.lineSeparator() +
"Host: localhost:8080" + System.lineSeparator() +
"Connection: keep-alive" + System.lineSeparator() +
"Cache-Control: max-age=0" + System.lineSeparator() +
"sec-ch-ua: \"Google Chrome\";v=\"89\", \"Chromium\";v=\"89\", \";Not A Brand\";v=\"99\"" + System.lineSeparator() +
"sec-ch-ua-mobile: ?0" + System.lineSeparator() +
"Upgrade-Insecure-Requests: 1" + System.lineSeparator() +
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36" + System.lineSeparator() +
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" + System.lineSeparator() +
"Sec-Fetch-Site: none" + System.lineSeparator() +
"Sec-Fetch-Mode: navigate" + System.lineSeparator() +
"Sec-Fetch-User: ?1" + System.lineSeparator() +
"Sec-Fetch-Dest: document" + System.lineSeparator() +
"Accept-Encoding: gzip, deflate, br" + System.lineSeparator() +
"Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7" + System.lineSeparator() +
"Cookie: Idea-1c77831=5ced54c8-cabd-4355-ae5a-97b17f9d7443" + System.lineSeparator() +
System.lineSeparator(),
new LinkedHashMap() {{
put("Host", "localhost:8080");
put("Connection", "keep-alive");
put("Cache-Control", "max-age=0");
put("sec-ch-ua", "\"Google Chrome\";v=\"89\", \"Chromium\";v=\"89\", \";Not A Brand\";v=\"99\"");
put("sec-ch-ua-mobile", "?0");
put("Upgrade-Insecure-Requests", "1");
put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36");
put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9");
put("Sec-Fetch-Site", "none");
put("Sec-Fetch-Mode", "navigate");
put("Sec-Fetch-User", "?1");
put("Sec-Fetch-Dest", "document");
put("Accept-Encoding", "gzip, deflate, br");
put("Accept-Language", "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7");
put("Cookie", "Idea-1c77831=5ced54c8-cabd-4355-ae5a-97b17f9d7443");
}}
)
);
}

@ParameterizedTest
@MethodSource
void getStatusLineAttributes(String headerText, Map<String, String> expectedAttributes) {
assertThat(Header.of(headerText, "request").getStatusLineAttributes())
.isEqualTo(expectedAttributes);
}

@SuppressWarnings("unused")
static Stream<Arguments> getStatusLineAttributes() {
return Stream.of(
Arguments.of("GET / HTTP/1.1" + System.lineSeparator() +
"Host: localhost:8080" + System.lineSeparator() +
"Connection: keep-alive" + System.lineSeparator() +
"Cache-Control: max-age=0" + System.lineSeparator() +
"sec-ch-ua: \"Google Chrome\";v=\"89\", \"Chromium\";v=\"89\", \";Not A Brand\";v=\"99\"" + System.lineSeparator() +
"sec-ch-ua-mobile: ?0" + System.lineSeparator() +
"Upgrade-Insecure-Requests: 1" + System.lineSeparator() +
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36" + System.lineSeparator() +
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" + System.lineSeparator() +
"Sec-Fetch-Site: none" + System.lineSeparator() +
"Sec-Fetch-Mode: navigate" + System.lineSeparator() +
"Sec-Fetch-User: ?1" + System.lineSeparator() +
"Sec-Fetch-Dest: document" + System.lineSeparator() +
"Accept-Encoding: gzip, deflate, br" + System.lineSeparator() +
"Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7" + System.lineSeparator() +
"Cookie: Idea-1c77831=5ced54c8-cabd-4355-ae5a-97b17f9d7443" + System.lineSeparator() +
System.lineSeparator(),
new HashMap() {{
put("method", "GET");
put("path", "/");
put("protocolVersion", "HTTP/1.1");
}}
)
);
}

@ParameterizedTest
@MethodSource
void toByte(String headerText, byte[] expectedHeaderByte) {
byte[] headerByte = Header.of(headerText, "request").toByte();

assertAll(
() -> assertThat(headerByte).isEqualTo(expectedHeaderByte),
() -> assertThat(new String(headerByte)).isEqualTo(new String(expectedHeaderByte))
);

}

@SuppressWarnings("unused")
static Stream<Arguments> toByte() {
return Stream.of(
Arguments.of(
"GET / HTTP/1.1" + System.lineSeparator() +
"Host: localhost:8080" + System.lineSeparator() +
"Connection: keep-alive" + System.lineSeparator() +
"Cache-Control: max-age=0" + System.lineSeparator() +
"sec-ch-ua: \"Google Chrome\";v=\"89\", \"Chromium\";v=\"89\", \";Not A Brand\";v=\"99\"" + System.lineSeparator() +
"sec-ch-ua-mobile: ?0" + System.lineSeparator() +
"Upgrade-Insecure-Requests: 1" + System.lineSeparator() +
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36" + System.lineSeparator() +
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" + System.lineSeparator() +
"Sec-Fetch-Site: none" + System.lineSeparator() +
"Sec-Fetch-Mode: navigate" + System.lineSeparator() +
"Sec-Fetch-User: ?1" + System.lineSeparator() +
"Sec-Fetch-Dest: document" + System.lineSeparator() +
"Accept-Encoding: gzip, deflate, br" + System.lineSeparator() +
"Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7" + System.lineSeparator() +
"Cookie: Idea-1c77831=5ced54c8-cabd-4355-ae5a-97b17f9d7443" + System.lineSeparator() +
System.lineSeparator(),
("GET / HTTP/1.1" + System.lineSeparator() +
"Host: localhost:8080" + System.lineSeparator() +
"Connection: keep-alive" + System.lineSeparator() +
"Cache-Control: max-age=0" + System.lineSeparator() +
"sec-ch-ua: \"Google Chrome\";v=\"89\", \"Chromium\";v=\"89\", \";Not A Brand\";v=\"99\"" + System.lineSeparator() +
"sec-ch-ua-mobile: ?0" + System.lineSeparator() +
"Upgrade-Insecure-Requests: 1" + System.lineSeparator() +
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36" + System.lineSeparator() +
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" + System.lineSeparator() +
"Sec-Fetch-Site: none" + System.lineSeparator() +
"Sec-Fetch-Mode: navigate" + System.lineSeparator() +
"Sec-Fetch-User: ?1" + System.lineSeparator() +
"Sec-Fetch-Dest: document" + System.lineSeparator() +
"Accept-Encoding: gzip, deflate, br" + System.lineSeparator() +
"Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7" + System.lineSeparator() +
"Cookie: Idea-1c77831=5ced54c8-cabd-4355-ae5a-97b17f9d7443" + System.lineSeparator() +
System.lineSeparator()).getBytes(StandardCharsets.UTF_8)
)
);
}
}
Loading

0 comments on commit d20ecb2

Please sign in to comment.