forked from woowacourse/jwp-was
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[알트] 1단계 - HTTP 웹 서버 구현 미션 제출합니다. (woowacourse#123)
* docs: 기능목록 작성 * feat: Request를 읽어 Line 생성 * feat: path에 해당하는 html 파일을 읽어 응답 * feat: Request Parameter를 추출 * feat: RequestHeaders 추가 * feat: QueryParameters 추가 * feat: Uri 추가 * feat: GET 메서드로 회원가입하는 기능 구현 * RequestHeader 인스턴스 생성시 BufferedReader를 통해 직접 읽지 않고 IOUtils를 통해 읽어온 문자열 리스트를 받도록 변경 * refactor: http request 검증로직 추가 * refactor: POST 메서드로 회원가입 가능하도록 수정 * feat: 회원가입 완료 후 index.html 페이지로 이동하는 기능 구현 * feat: stylesheet 파일을 지원하도록 구현 * feat: 정적 리소스 식별을 위한 StaticResourceType 추가 * feat: 리소스 요청 처리를 위한 ResourceRequestHandler 추가 * feat: HttpResponse 추가 * RequestHandler에 집중된 응답 처리 책임을 위임하기 위한 조치 * refactor: 리소스 이외의 요청 처리를 위한 FrontController 추가 * BufferedReader를 통해 생성 가능하도록 변경 * 기타 Request 관련 리팩토링 수행 * 요청을 수행하는 Handler 추가 * 미리 지정된 요청을 찾기 위한 HandlerMapping 추가 * fix: Not Found 예외처리 코드 변경 * test: StaticResourceType 테스트케이스 추가 * 기존에 주석처리 되어있는 테스트 케이스 리팩토링 * feat: 처리되지 않은 예외에 대한 응답 형식 추가 * refactor: 문자열 일치 여부를 확인하는 일부 메서드명 변경 * matches -> equals~로 통일 * 문자열 패턴이 일치하는지 여부를 확인하는 메서드 matches -> anyMatch로 변경 * refactor: requestLine 변수명 수정 protocol -> version * refactor: OS에 적합한 개행 문자를 사용하도록 변경 * refactor: 디미터 법칙 위반 사례 수정 * refactor: 불필요한 LinkedHashMap 사용 제거 * refactor: body가 없는 응답 객체 생성 방식 변경 * test: 프로젝트에서 사용되지 않는 테스트코드 제거 * refactor: Resource, Path에 따른 Handler Mapping방식 변경
- Loading branch information
Showing
40 changed files
with
1,016 additions
and
319 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package http; | ||
|
||
public enum HttpMethod { | ||
GET, | ||
POST, | ||
PUT, | ||
DELETE; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package http; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.IOException; | ||
import java.util.List; | ||
import java.util.Objects; | ||
|
||
import utils.IOUtils; | ||
|
||
public class HttpRequest { | ||
private final RequestLine requestLine; | ||
private final RequestHeaders requestHeaders; | ||
private final RequestBody requestBody; | ||
|
||
public HttpRequest(final RequestLine requestLine, final RequestHeaders requestHeaders, | ||
final RequestBody requestBody) { | ||
this.requestLine = Objects.requireNonNull(requestLine, "request line이 존재하지 않습니다."); | ||
this.requestHeaders = Objects.requireNonNull(requestHeaders, "request headers가 존재하지 않습니다."); | ||
this.requestBody = requestBody; | ||
} | ||
|
||
public static HttpRequest from(final BufferedReader bufferedReader) throws IOException { | ||
List<String> requestLineAndHeader = IOUtils.readHeader(bufferedReader); | ||
RequestLine line = RequestLine.from(requestLineAndHeader.get(0)); | ||
RequestHeaders headers = RequestHeaders.from(requestLineAndHeader.subList(1, requestLineAndHeader.size())); | ||
if (headers.hasContentLength()) { | ||
int contentLength = headers.getContentLength(); | ||
RequestBody body = RequestBody.from(IOUtils.readBody(bufferedReader, contentLength)); | ||
return new HttpRequest(line, headers, body); | ||
} | ||
return new HttpRequest(line, headers, null); | ||
} | ||
|
||
public boolean equalsMethod(final HttpMethod httpMethod) { | ||
return requestLine.equalsMethod(httpMethod); | ||
} | ||
|
||
public boolean equalsPath(final String path) { | ||
return requestLine.equalsPath(path); | ||
} | ||
|
||
public String getPath() { | ||
return requestLine.getPath(); | ||
} | ||
|
||
public Uri getUri() { | ||
return requestLine.getUri(); | ||
} | ||
|
||
public String getBodyValue(final String key) { | ||
return requestBody.getValue(key); | ||
} | ||
|
||
public RequestLine getRequestLine() { | ||
return requestLine; | ||
} | ||
|
||
public RequestHeaders getRequestHeaders() { | ||
return requestHeaders; | ||
} | ||
|
||
public RequestBody getRequestBody() { | ||
return requestBody; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "HttpRequest{" + | ||
"requestLine=" + requestLine + | ||
", requestHeaders=" + requestHeaders + | ||
", requestBody=" + requestBody + | ||
'}'; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package http; | ||
|
||
import java.io.DataOutputStream; | ||
import java.io.IOException; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
public class HttpResponse { | ||
private static final Logger logger = LoggerFactory.getLogger(HttpResponse.class); | ||
|
||
private final DataOutputStream dataOutputStream; | ||
|
||
public HttpResponse(final DataOutputStream dataOutputStream) { | ||
this.dataOutputStream = dataOutputStream; | ||
} | ||
|
||
public void response200Header(String contentType, int lengthOfBodyContent) { | ||
try { | ||
dataOutputStream.writeBytes("HTTP/1.1 200 OK " + System.lineSeparator()); | ||
dataOutputStream.writeBytes("Content-Type: " + contentType + ";charset=utf-8" + System.lineSeparator()); | ||
dataOutputStream.writeBytes("Content-Length: " + lengthOfBodyContent + System.lineSeparator()); | ||
dataOutputStream.writeBytes(System.lineSeparator()); | ||
} catch (IOException e) { | ||
logger.error(e.getMessage()); | ||
} | ||
} | ||
|
||
public void response302Header(String location) { | ||
try { | ||
dataOutputStream.writeBytes("HTTP/1.1 302 Found " + System.lineSeparator()); | ||
dataOutputStream.writeBytes("Location: " + location + System.lineSeparator()); | ||
dataOutputStream.writeBytes(System.lineSeparator()); | ||
} catch (IOException e) { | ||
logger.error(e.getMessage()); | ||
} | ||
} | ||
|
||
public void response404Header() { | ||
try { | ||
dataOutputStream.writeBytes("HTTP/1.1 404 Not Found " + System.lineSeparator()); | ||
dataOutputStream.writeBytes(System.lineSeparator()); | ||
} catch (IOException e) { | ||
logger.error(e.getMessage()); | ||
} | ||
} | ||
|
||
public void response500Header() { | ||
try { | ||
dataOutputStream.writeBytes("HTTP/1.1 500 Internal Server Error " + System.lineSeparator()); | ||
dataOutputStream.writeBytes(System.lineSeparator()); | ||
} catch (IOException e) { | ||
logger.error(e.getMessage()); | ||
} | ||
} | ||
|
||
public void responseBody(byte[] body) { | ||
try { | ||
dataOutputStream.write(body, 0, body.length); | ||
dataOutputStream.flush(); | ||
} catch (IOException e) { | ||
logger.error(e.getMessage()); | ||
} | ||
} | ||
|
||
public void emptyBody() { | ||
try { | ||
dataOutputStream.flush(); | ||
} catch (IOException e) { | ||
logger.error(e.getMessage()); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package http; | ||
|
||
import static java.util.stream.Collectors.*; | ||
|
||
import java.util.Collections; | ||
import java.util.Map; | ||
import java.util.stream.Stream; | ||
|
||
public class QueryParameters { | ||
private static final String QUERY_DELIMITER = "&"; | ||
private static final String QUERY_KEY_VALUE_DELIMITER = "="; | ||
|
||
private final Map<String, String> parameters; | ||
|
||
private QueryParameters(final Map<String, String> parameters) { | ||
this.parameters = parameters; | ||
} | ||
|
||
public static QueryParameters from(final String queries) { | ||
return Stream.of(queries.split(QUERY_DELIMITER)) | ||
.map(query -> query.split(QUERY_KEY_VALUE_DELIMITER)) | ||
.collect(collectingAndThen(toMap(query -> query[0], query -> query[1]), QueryParameters::new)); | ||
} | ||
|
||
public String getParameter(final String key) { | ||
return parameters.get(key); | ||
} | ||
|
||
public Map<String, String> getParameters() { | ||
return Collections.unmodifiableMap(parameters); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "QueryParameters{" + | ||
"parameters=" + parameters + | ||
'}'; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package http; | ||
|
||
import static java.util.stream.Collectors.*; | ||
|
||
import java.util.Map; | ||
import java.util.stream.Stream; | ||
|
||
public class RequestBody { | ||
private static final String BODY_DELIMITER = "&"; | ||
private static final String BODY_KEY_VALUE_DELIMITER = "="; | ||
|
||
private final Map<String, String> values; | ||
|
||
public RequestBody(Map<String, String> values) { | ||
this.values = values; | ||
} | ||
|
||
public static RequestBody from(final String values) { | ||
return Stream.of(values.split(BODY_DELIMITER)) | ||
.map(value -> value.split(BODY_KEY_VALUE_DELIMITER)) | ||
.collect(collectingAndThen(toMap(value -> value[0], value -> value[1]), RequestBody::new)); | ||
} | ||
|
||
public String getValue(final String key) { | ||
return values.get(key); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package http; | ||
|
||
import static java.util.stream.Collectors.*; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
|
||
public class RequestHeaders { | ||
private static final String HEADER_KEY_VALUE_DELIMITER = ": "; | ||
|
||
private final Map<String, String> headers; | ||
|
||
private RequestHeaders(Map<String, String> headers) { | ||
this.headers = Objects.requireNonNull(headers, "request headers가 존재하지 않습니다."); | ||
} | ||
|
||
public static RequestHeaders from(final List<String> headers) { | ||
return headers.stream() | ||
.map(header -> header.split(HEADER_KEY_VALUE_DELIMITER)) | ||
.collect(collectingAndThen(toMap(header -> header[0], header -> header[1]), RequestHeaders::new)); | ||
} | ||
|
||
public boolean hasContentLength() { | ||
return headers.containsKey("Content-Length"); | ||
} | ||
|
||
public int getContentLength() { | ||
return Integer.parseInt(getHeader("Content-Length")); | ||
} | ||
|
||
public String getHeader(final String key) { | ||
return headers.get(key); | ||
} | ||
|
||
public Map<String, String> getHeaders() { | ||
return Collections.unmodifiableMap(headers); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package http; | ||
|
||
import java.util.Objects; | ||
|
||
public class RequestLine { | ||
private static final String REQUEST_LINE_DELIMITER = " "; | ||
private static final int REQUEST_LINE_TOTAL_PARTS_COUNT = 3; | ||
|
||
private final HttpMethod method; | ||
private final Uri uri; | ||
private final String version; | ||
|
||
private RequestLine(final HttpMethod method, final Uri uri, final String version) { | ||
this.method = Objects.requireNonNull(method, "http 메서드가 존재하지 않습니다."); | ||
this.uri = Objects.requireNonNull(uri, "uri가 존재하지 않습니다."); | ||
this.version = Objects.requireNonNull(version, "protocol이 존재하지 않습니다."); | ||
} | ||
|
||
public static RequestLine from(final String requestLine) { | ||
String[] splitLine = requestLine.split(REQUEST_LINE_DELIMITER); | ||
if (splitLine.length != REQUEST_LINE_TOTAL_PARTS_COUNT) { | ||
throw new IllegalArgumentException("request line 형식이 올바르지 않습니다. request line count: " + splitLine.length); | ||
} | ||
return new RequestLine(HttpMethod.valueOf(splitLine[0]), Uri.from(splitLine[1]), splitLine[2]); | ||
} | ||
|
||
public boolean equalsMethod(final HttpMethod httpMethod) { | ||
return method.equals(httpMethod); | ||
} | ||
|
||
public boolean equalsPath(final String path) { | ||
return uri.equalsPath(path); | ||
} | ||
|
||
public String getPath() { | ||
return uri.getPath(); | ||
} | ||
|
||
public HttpMethod getMethod() { | ||
return method; | ||
} | ||
|
||
public Uri getUri() { | ||
return uri; | ||
} | ||
|
||
public String getVersion() { | ||
return version; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "RequestLine{" + | ||
"method=" + method + | ||
", uri='" + uri + '\'' + | ||
", protocol='" + version + '\'' + | ||
'}'; | ||
} | ||
} |
Oops, something went wrong.