diff --git a/src/main/java/webserver/http/attribute/Attributes.java b/src/main/java/webserver/http/attribute/Attributes.java index 12a6907..8fded54 100644 --- a/src/main/java/webserver/http/attribute/Attributes.java +++ b/src/main/java/webserver/http/attribute/Attributes.java @@ -3,10 +3,7 @@ import util.HttpRequestUtils; import webserver.Const; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Objects; -import java.util.StringJoiner; +import java.util.*; public class Attributes { private final Map attributes = new LinkedHashMap<>(); @@ -16,36 +13,52 @@ public static Attributes from(Map attributes) { } public static Attributes from(String headerText) { - Map attributes = new LinkedHashMap<>(); + Attributes attributes = new Attributes(); String[] splittedHeaderTexts = headerText.split(Const.CRLF); for (String splittedHeaderText : splittedHeaderTexts) { HttpRequestUtils.Pair pair = HttpRequestUtils.parseHeader(splittedHeaderText); if (pair != null) { - attributes.put(pair.getKey(), pair.getValue()); + attributes.add(pair.getKey(), pair.getValue()); } } - return from(attributes); + return attributes; } - public Attributes add(String key, String value) { - attributes.put(key, value); + public Attributes add(String targetKey, String value) { + for (String currentKey : attributes.keySet()) { + if (currentKey.equalsIgnoreCase(targetKey)) { + return this; + } + } + + attributes.put(targetKey, value); return this; } public Attributes addAll(Map attributes) { - this.attributes.putAll(attributes); + for (String currentKey : attributes.keySet()) { + add(currentKey, attributes.get(currentKey)); + } + return this; } - public String get(String key) { - return attributes.get(key); + public String get(String targetKey) { + return getOrDefault(targetKey, null); } - public String getOrDefault(String key, String defaultValue) { - return attributes.getOrDefault(key, defaultValue); + public String getOrDefault(String targetKey, String defaultValue) { + + for (String currentKey : attributes.keySet()) { + if (currentKey.equalsIgnoreCase(targetKey)) { + return attributes.get(currentKey); + } + } + + return defaultValue; } public String toHeaderText() { @@ -70,4 +83,9 @@ public int hashCode() { return Objects.hash(attributes); } + + public int size(){ + return attributes.size(); + } + } diff --git a/src/main/java/webserver/http/startline/RequestLine.java b/src/main/java/webserver/http/startline/RequestLine.java index b97e722..b892ef7 100644 --- a/src/main/java/webserver/http/startline/RequestLine.java +++ b/src/main/java/webserver/http/startline/RequestLine.java @@ -1,24 +1,28 @@ package webserver.http.startline; +import webserver.http.attribute.Attributes; + import java.net.URI; import java.net.URISyntaxException; -import java.util.*; +import java.util.List; +import java.util.Objects; +import java.util.StringJoiner; public class RequestLine extends StartLine { private static final String METHOD_KEY = "method"; private static final String PATH_KEY = "path"; - public RequestLine(Map statusLineAttributes) { + public RequestLine(Attributes statusLineAttributes) { super(statusLineAttributes); } public static RequestLine from(List statusLine) { - Map statusLineAttributes = new HashMap<>(); + Attributes statusLineAttributes = new Attributes(); - statusLineAttributes.put(METHOD_KEY, statusLine.get(0)); - statusLineAttributes.put(PATH_KEY, statusLine.get(1)); - statusLineAttributes.put(PROTOCOL_VERSION_KEY, statusLine.get(2)); + statusLineAttributes.add(METHOD_KEY, statusLine.get(0)); + statusLineAttributes.add(PATH_KEY, statusLine.get(1)); + statusLineAttributes.add(PROTOCOL_VERSION_KEY, statusLine.get(2)); return new RequestLine(statusLineAttributes); } diff --git a/src/main/java/webserver/http/startline/StartLine.java b/src/main/java/webserver/http/startline/StartLine.java index 3989ca0..d5cfb18 100644 --- a/src/main/java/webserver/http/startline/StartLine.java +++ b/src/main/java/webserver/http/startline/StartLine.java @@ -2,15 +2,13 @@ import webserver.http.attribute.Attributes; -import java.util.Map; - public abstract class StartLine { protected static final String PROTOCOL_VERSION_KEY = "protocolVersion"; private Attributes attributes; - public StartLine(Map attributes) { - this.attributes = Attributes.from(attributes); + public StartLine(Attributes attributes) { + this.attributes = attributes; } public String getProtocol() { diff --git a/src/main/java/webserver/http/startline/StatusLine.java b/src/main/java/webserver/http/startline/StatusLine.java index 7f56f26..378eb5b 100644 --- a/src/main/java/webserver/http/startline/StatusLine.java +++ b/src/main/java/webserver/http/startline/StatusLine.java @@ -1,24 +1,25 @@ package webserver.http.startline; -import java.util.HashMap; +import webserver.http.attribute.Attributes; + import java.util.List; -import java.util.Map; import java.util.StringJoiner; public class StatusLine extends StartLine { private static final String STATUS_CODE_KEY = "statusCode"; private static final String STATUS_TEXT_KEY = "statusText"; - public StatusLine(Map statusLineAttributes) { + + public StatusLine(Attributes statusLineAttributes) { super(statusLineAttributes); } public static StatusLine from(List statusLine) { - Map statusLineAttributes = new HashMap<>(); + Attributes statusLineAttributes = new Attributes(); - statusLineAttributes.put(PROTOCOL_VERSION_KEY, statusLine.get(0)); - statusLineAttributes.put(STATUS_CODE_KEY, statusLine.get(1)); - statusLineAttributes.put(STATUS_TEXT_KEY, statusLine.get(2)); + statusLineAttributes.add(PROTOCOL_VERSION_KEY, statusLine.get(0)); + statusLineAttributes.add(STATUS_CODE_KEY, statusLine.get(1)); + statusLineAttributes.add(STATUS_TEXT_KEY, statusLine.get(2)); return new StatusLine(statusLineAttributes); } diff --git a/src/test/java/webserver/http/attribute/AttributesTest.java b/src/test/java/webserver/http/attribute/AttributesTest.java index ce04955..68dfa1d 100644 --- a/src/test/java/webserver/http/attribute/AttributesTest.java +++ b/src/test/java/webserver/http/attribute/AttributesTest.java @@ -1,5 +1,7 @@ package webserver.http.attribute; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -11,6 +13,8 @@ import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.junit.jupiter.api.Assertions.assertAll; class AttributesTest { @@ -39,14 +43,42 @@ void fromHeaderText() { assertThat(actualAttributes).isEqualTo(expectedAttributes); } - @Test - void add() { + + @ParameterizedTest + @DisplayName("add 테스트: key의 대소문자 관계 없이 Attributes를 꺼내올 수 있음") + @MethodSource("add") + void add(String expectedValue, String actualValue) { + assertThat(actualValue).isEqualTo(expectedValue); + } + + static Stream add() { Attributes attributes = new Attributes(); attributes.add("key", "value"); + + return Stream.of( + Arguments.of( + "value", + attributes.get("KEY") + ), + Arguments.of( + "value", + attributes.get("key") + ), + Arguments.of( + "value", + attributes.get("kEy") + ) + ); } - @Test - void addAll() { + @ParameterizedTest + @DisplayName("addAll 테스트: key의 대소문자 관계 없이 Attributes를 꺼내올 수 있음") + @MethodSource("addAll") + void addAll(String expectedValue, String actualValue) { + assertThat(actualValue).isEqualTo(expectedValue); + } + + static Stream addAll() { Attributes attributes = new Attributes(); Map attributeMap = new LinkedHashMap<>(); attributeMap.put("key", "value"); @@ -55,7 +87,20 @@ void addAll() { Attributes expectedAttributes = new Attributes(); expectedAttributes.add("key", "value"); - assertThat(attributes).isEqualTo(expectedAttributes); + return Stream.of( + Arguments.of( + expectedAttributes.get("key"), + attributes.get("KEY") + ), + Arguments.of( + expectedAttributes.get("KEY"), + attributes.get("kEY") + ), + Arguments.of( + expectedAttributes.get("kEy"), + attributes.get("kEy") + ) + ); } @Test @@ -66,6 +111,13 @@ void get() { assertThat(attributes.get("key")).isEqualTo("value"); } + @Test + @DisplayName("존재하지 않는 키를 조회하면 null") + void getValueWithEmptyKey() { + Attributes attributes = new Attributes(); + assertThat(attributes.get("null")).isNull(); + } + @ParameterizedTest @MethodSource("getOrDefault") void getOrDefault(Attributes attributes, String defaultValue, String key, String expectedValue) { @@ -73,6 +125,13 @@ void getOrDefault(Attributes attributes, String defaultValue, String key, String assertThat(actualValue).isEqualTo(expectedValue); } + @Test + @DisplayName("존재하지 않는 키를 조회하면 주어진 기본값으로 대체") + void getDefaultValueWithEmptyKey() { + Attributes attributes = new Attributes(); + assertThat(attributes.getOrDefault("emptyKey", "happy")).isEqualTo("happy"); + } + static Stream getOrDefault() { return Stream.of( Arguments.of( @@ -99,4 +158,19 @@ void toHeaderText() { assertThat(attributes.toHeaderText()).isEqualTo("key1: value1\r\nkey2: value2"); } + + @Test + @DisplayName("value가 null인 key") + void isNullWhenNullValue(){ + Attributes attributes = new Attributes(); + attributes.add("null", null); + attributes.add("null2", null); + + assertAll( + ()-> assertThat(attributes.size()).isEqualTo(2), + ()-> assertThat(attributes.get("null")).isNull(), + ()-> assertThat(attributes.get("null2")).isNull() + ); + + } } diff --git a/src/test/java/webserver/http/header/RequestHeaderTest.java b/src/test/java/webserver/http/header/RequestHeaderTest.java index e840977..23cda10 100644 --- a/src/test/java/webserver/http/header/RequestHeaderTest.java +++ b/src/test/java/webserver/http/header/RequestHeaderTest.java @@ -138,11 +138,11 @@ static Stream getRequestLineAttributes() { "Cookie: Idea-1c77831=5ced54c8-cabd-4355-ae5a-97b17f9d7443" + Const.CRLF + Const.CRLF, new RequestLine( - new HashMap() {{ + Attributes.from(new HashMap() {{ put("method", "GET"); put("path", "/"); put("protocolVersion", "HTTP/1.1"); - }} + }}) ) ) ); @@ -217,10 +217,10 @@ static Stream getPath() { Arguments.of( "쿼리스트링이 없는 GET 메세지", new RequestLine( - new HashMap() {{ - put("path", "/user/create"); - put("method", "GET"); - put("protocolVersion", "HTTP/1.1"); + new Attributes() {{ + add("path", "/user/create"); + add("method", "GET"); + add("protocolVersion", "HTTP/1.1"); }} ), "/user/create" @@ -228,10 +228,10 @@ static Stream getPath() { Arguments.of( "쿼리스트링이 포함된 GET 메세지 path를 출력할때도 쿼리스트링이 포함되지 않아야 함", new RequestLine( - new HashMap() {{ - put("path", "/user/create?userId=javajigi&password=password&name=%EB%B0%95%EC%9E%AC%EC%84%B1&email=javajigi%40slipp.net"); - put("method", "GET"); - put("protocolVersion", "HTTP/1.1"); + new Attributes() {{ + add("path", "/user/create?userId=javajigi&password=password&name=%EB%B0%95%EC%9E%AC%EC%84%B1&email=javajigi%40slipp.net"); + add("method", "GET"); + add("protocolVersion", "HTTP/1.1"); }} ), "/user/create" diff --git a/src/test/java/webserver/http/header/ResponseHeaderTest.java b/src/test/java/webserver/http/header/ResponseHeaderTest.java index 146ca0c..46b4c97 100644 --- a/src/test/java/webserver/http/header/ResponseHeaderTest.java +++ b/src/test/java/webserver/http/header/ResponseHeaderTest.java @@ -57,11 +57,11 @@ static Stream getStatusLineAttributes() { "Content-Length: " + "Hello World".getBytes().length + Const.CRLF + Const.CRLF, new StatusLine( - new HashMap() {{ + Attributes.from(new HashMap() {{ put("protocolVersion", "HTTP/1.1"); put("statusText", "OK"); put("statusCode", "200"); - }} + }}) ) ) ); diff --git a/src/test/java/webserver/http/startline/RequestLineTest.java b/src/test/java/webserver/http/startline/RequestLineTest.java index 78b8167..88ce285 100644 --- a/src/test/java/webserver/http/startline/RequestLineTest.java +++ b/src/test/java/webserver/http/startline/RequestLineTest.java @@ -3,8 +3,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import webserver.http.attribute.Attributes; -import java.util.HashMap; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; @@ -24,10 +24,10 @@ static Stream getMethod() { Arguments.of( "GET 메소드", new RequestLine( - new HashMap() {{ - put("path", "/user/create"); - put("method", "GET"); - put("protocolVersion", "HTTP/1.1"); + new Attributes() {{ + add("path", "/user/create"); + add("method", "GET"); + add("protocolVersion", "HTTP/1.1"); }} ), "GET" @@ -48,20 +48,20 @@ static Stream getPath() { Arguments.of( "쿼리스트링이 없는 path", new RequestLine( - new HashMap() {{ - put("path", "/user/create"); - put("method", "GET"); - put("protocolVersion", "HTTP/1.1"); + new Attributes() {{ + add("path", "/user/create"); + add("method", "GET"); + add("protocolVersion", "HTTP/1.1"); }} ), "/user/create" ), Arguments.of( "쿼리스트링이 있는 path", new RequestLine( - new HashMap() {{ - put("path", "/user/create?userId=javajigi&password=password&name=%EB%B0%95%EC%9E%AC%EC%84%B1&email=javajigi%40slipp.net"); - put("method", "GET"); - put("protocolVersion", "HTTP/1.1"); + new Attributes() {{ + add("path", "/user/create?userId=javajigi&password=password&name=%EB%B0%95%EC%9E%AC%EC%84%B1&email=javajigi%40slipp.net"); + add("method", "GET"); + add("protocolVersion", "HTTP/1.1"); }} ), "/user/create" @@ -82,10 +82,10 @@ static Stream getProtocol() { Arguments.of( "HTTP/1.1", new RequestLine( - new HashMap() {{ - put("path", "/user/create"); - put("method", "GET"); - put("protocolVersion", "HTTP/1.1"); + new Attributes() {{ + add("path", "/user/create"); + add("method", "GET"); + add("protocolVersion", "HTTP/1.1"); }} ), "HTTP/1.1" @@ -106,20 +106,20 @@ static Stream getQueryString() { Arguments.of( "쿼리스트링이 없는 path", new RequestLine( - new HashMap() {{ - put("path", "/user/create"); - put("method", "GET"); - put("protocolVersion", "HTTP/1.1"); + new Attributes() {{ + add("path", "/user/create"); + add("method", "GET"); + add("protocolVersion", "HTTP/1.1"); }} ), "" ), Arguments.of( "쿼리스트링이 있는 path", new RequestLine( - new HashMap() {{ - put("path", "/user/create?userId=javajigi&password=password&name=%EB%B0%95%EC%9E%AC%EC%84%B1&email=javajigi%40slipp.net"); - put("method", "GET"); - put("protocolVersion", "HTTP/1.1"); + new Attributes() {{ + add("path", "/user/create?userId=javajigi&password=password&name=%EB%B0%95%EC%9E%AC%EC%84%B1&email=javajigi%40slipp.net"); + add("method", "GET"); + add("protocolVersion", "HTTP/1.1"); }} ), "userId=javajigi&password=password&name=박재성&email=javajigi@slipp.net" diff --git a/src/test/java/webserver/http/startline/StatusLineTest.java b/src/test/java/webserver/http/startline/StatusLineTest.java index 40f7eaa..2ba4d4d 100644 --- a/src/test/java/webserver/http/startline/StatusLineTest.java +++ b/src/test/java/webserver/http/startline/StatusLineTest.java @@ -3,6 +3,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import webserver.http.attribute.Attributes; import java.util.HashMap; import java.util.stream.Stream; @@ -24,11 +25,11 @@ static Stream getStatusCode() { Arguments.of( "200 OK", new StatusLine( - new HashMap() {{ + Attributes.from(new HashMap() {{ put("protocolVersion", "HTTP/1.1"); put("statusCode", "200"); put("statusText", "OK"); - }} + }}) ), "200" ) @@ -48,11 +49,11 @@ static Stream getStatusText() { Arguments.of( "200 OK", new StatusLine( - new HashMap() {{ + Attributes.from(new HashMap() {{ put("protocolVersion", "HTTP/1.1"); put("statusCode", "200"); put("statusText", "OK"); - }} + }}) ), "OK" ) @@ -72,11 +73,11 @@ static Stream getProtocol() { Arguments.of( "HTTP/1.1", new StatusLine( - new HashMap() {{ + Attributes.from(new HashMap() {{ put("protocolVersion", "HTTP/1.1"); put("statusCode", "200"); put("statusText", "OK"); - }} + }}) ), "HTTP/1.1" )