-
Notifications
You must be signed in to change notification settings - Fork 90
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[또링] 1단계 - HTTP 웹 서버 구현 미션 제출합니다. (#105)
* docs : 요구사항1의 구현 기능 목록 작성 * feat : 모든 Request Header 출력하기 * feat : Request에서 path 분리하기 * feat : path에 해당하는 파일 읽어 응답하기 * docs : 요구사항2의 구현 기능 목록 작성 * feat : Request Parameter 추출 및 User 객체 생성 * docs : 요구사항3의 구현 기능 목록 작성 * feat : form.html 파일의 form 태그 method를 get에서 post로 수정 * refactor : HttpRequest를 RequestHeader와 RequestBody로 분리 * refactor : HttpRequestParser 유틸 클래스 생성 * docs : 요구사항4의 구현 기능 목록 작성 * feat : 요청에 따라 다른 HttpResponse를 내려준다. * docs : 요구사항5의 구현 기능 목록 작성 * refactor : RequestHeader 값의 자료형을 List에서 Map으로 변경 * refactor : HeaderProperty 생성 * feat : 응답에 따라 Content-Type을 변경하여 Stylesheet 파일을 지원하도록 구현 * feat : status code를 302로 변경한 후, Location 값에 리다이렉션 할 페이지를 넣어 응답 * refactor : 변수 정리 및 파일 끝 개행 추가
- Loading branch information
Showing
26 changed files
with
822 additions
and
52 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,38 @@ | ||
# 웹 애플리케이션 서버 | ||
## 진행 방법 | ||
* 웹 애플리케이션 서버 요구사항을 파악한다. | ||
* 요구사항에 대한 구현을 완료한 후 자신의 github 아이디에 해당하는 브랜치에 Pull Request(이하 PR)를 통해 코드 리뷰 요청을 한다. | ||
* 코드 리뷰 피드백에 대한 개선 작업을 하고 다시 PUSH한다. | ||
* 모든 피드백을 완료하면 다음 단계를 도전하고 앞의 과정을 반복한다. | ||
|
||
## 우아한테크코스 코드리뷰 | ||
* [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) | ||
## 1단계 - HTTP 웹 서버 구현 | ||
### 요구사항1 | ||
> http://localhost:8080/index.html 로 접속했을 때 webapp 디렉토리의 index.html 파일을 읽어 클라이언트에 응답한다. | ||
*구현 기능 목록* | ||
- [x] Request Header를 파싱하여 원하는 정보 찾기 | ||
- [x] 모든 Request header 출력하기 | ||
- [x] Request에서 path 분리하기 | ||
- [x] path에 해당하는 파일 읽어 응답하기 | ||
|
||
### 요구사항2 | ||
> “회원가입” 메뉴를 클릭하면 http://localhost:8080/user/form.html 으로 이동하면서 회원가입할 수 있다. | ||
*구현 기능 목록* | ||
- [x] Request Parameter 추출 | ||
- [x] 사용자가 입력한 값 저장 | ||
|
||
### 요구사항3 | ||
> http://localhost:8080/user/form.html 파일의 form 태그 method를 get에서 post로 수정한 후 회원가입 기능이 정상적으로 동작하도록 구현한다. | ||
*구현 기능 목록* | ||
- [x] form.html 파일의 form 태그 method를 get에서 post로 수정 | ||
- [x] Request Body의 값 추출하기 | ||
|
||
### 요구사항4 | ||
> “회원가입”을 완료하면 /index.html 페이지로 이동하고 싶다. 현재는 URL이 /user/create 로 유지되는 상태로 읽어서 전달할 파일이 없다. | ||
> 따라서 redirect 방식처럼 회원가입을 완료한 후 “index.html”로 이동해야 한다.즉, 브라우저의 URL이 /index.html로 변경해야 한다. | ||
*구현 기능 목록* | ||
- [x] 요청에 따라 다른 HttpResponse를 응답 | ||
- [x] status code를 302로 변경한 후, Location 값에 리다이렉션 할 페이지를 넣어 응답 | ||
|
||
### 요구사항5 | ||
> 지금까지 구현한 소스 코드는 stylesheet 파일을 지원하지 못하고 있다. Stylesheet 파일을 지원하도록 구현하도록 한다. | ||
*구현 기능 목록* | ||
- [x] 응답에 따라 Content-Type을 변경하여 Stylesheet 파일을 지원하도록 구현 |
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
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,36 @@ | ||
package utils; | ||
|
||
import static web.HeaderProperty.*; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.IOException; | ||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.stream.Collectors; | ||
|
||
public class HttpRequestParser { | ||
public static final String EMPTY = ""; | ||
public static final String HEADER_DATA_DELIMITER = ": "; | ||
public static final int KEY_INDEX = 0; | ||
public static final int VALUE_INDEX = 1; | ||
private static final String KEY_VALUE_DELIMITER = "="; | ||
|
||
public static Map<String, String> parsingRequestHeader(BufferedReader br) throws IOException { | ||
Map<String, String> request = new HashMap<>(); | ||
request.put(REQUEST_LINE.getName(), br.readLine()); | ||
String line = br.readLine(); | ||
while (line != null && !EMPTY.equals(line)) { | ||
String[] headerData = line.split(HEADER_DATA_DELIMITER); | ||
request.put(headerData[KEY_INDEX], headerData[VALUE_INDEX]); | ||
line = br.readLine(); | ||
} | ||
return request; | ||
} | ||
|
||
public static Map<String, String> parsingData(String s) { | ||
return Arrays.stream(s.split("&")) | ||
.collect(Collectors.toMap(param -> param.split(KEY_VALUE_DELIMITER)[0], | ||
param -> param.split(KEY_VALUE_DELIMITER)[1])); | ||
} | ||
} |
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,19 @@ | ||
package web; | ||
|
||
public enum HeaderProperty { | ||
REQUEST_LINE("requestLine"), | ||
HOST("Host"), | ||
CONNECTION("Connection"), | ||
CONTENT_LENGTH("Content-Length"), | ||
ACCEPT("Accept"); | ||
|
||
private String name; | ||
|
||
HeaderProperty(String name) { | ||
this.name = name; | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
} |
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,45 @@ | ||
package web; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.IOException; | ||
|
||
import utils.IOUtils; | ||
|
||
public class HttpRequest { | ||
private RequestHeader requestHeader; | ||
private RequestBody requestBody; | ||
|
||
public HttpRequest(BufferedReader br) throws IOException { | ||
this.requestHeader = new RequestHeader(br); | ||
if (requestHeader.isPost()) { | ||
String data = IOUtils.readData(br, requestHeader.getContentLength()); | ||
this.requestBody = new RequestBody(data); | ||
} else { | ||
this.requestBody = null; | ||
} | ||
} | ||
|
||
public boolean isPost() { | ||
return requestHeader.isPost(); | ||
} | ||
|
||
public boolean isStaticFile() { | ||
return requestHeader.isStaticFile(); | ||
} | ||
|
||
public RequestUri getRequestUri() { | ||
return requestHeader.getRequestUri(); | ||
} | ||
|
||
public RequestBody getRequestBody() { | ||
return requestBody; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "HttpRequest{" + | ||
"requestHeader=" + requestHeader + | ||
", 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,95 @@ | ||
package web; | ||
|
||
import static web.RequestUri.*; | ||
|
||
import java.io.DataOutputStream; | ||
import java.io.IOException; | ||
import java.util.Map; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import db.DataBase; | ||
import model.User; | ||
import utils.FileIoUtils; | ||
|
||
public class HttpResponse { | ||
public static final String NEW_LINE = System.lineSeparator(); | ||
|
||
private static final Logger logger = LoggerFactory.getLogger(HttpResponse.class); | ||
|
||
private DataOutputStream dos; | ||
|
||
public HttpResponse(DataOutputStream dos) { | ||
this.dos = dos; | ||
} | ||
|
||
public void process(HttpRequest httpRequest) { | ||
if (httpRequest.isStaticFile()) { | ||
processFile(httpRequest); | ||
} else { | ||
processApi(httpRequest); | ||
} | ||
} | ||
|
||
private void processFile(HttpRequest httpRequest) { | ||
RequestUri requestUri = httpRequest.getRequestUri(); | ||
String resourcePath = requestUri.findPath() + requestUri.getUri(); | ||
byte[] content = null; | ||
try { | ||
content = FileIoUtils.loadFileFromClasspath( | ||
resourcePath); | ||
} catch (IOException e) { | ||
logger.error(e.getMessage()); | ||
} | ||
if (content != null) { | ||
String response = response200Header(requestUri.findContentType(), content); | ||
toDataOutputStream(response); | ||
responseBody(content); | ||
} | ||
} | ||
|
||
private void processApi(HttpRequest httpRequest) { | ||
if (httpRequest.isPost()) { | ||
Map<String, String> body = httpRequest.getRequestBody().getFormData(); | ||
User user = new User(body.get("userId"), body.get("password"), body.get("name"), | ||
body.get("email")); | ||
DataBase.addUser(user); | ||
String response = response302Header("http://localhost:8080" + INDEX_HTML); | ||
toDataOutputStream(response); | ||
responseBody(new byte[0]); | ||
} | ||
} | ||
|
||
private String response200Header(String contentType, byte[] content) { | ||
return "HTTP/1.1 200 OK" + NEW_LINE | ||
+ "Content-Type: " + contentType + NEW_LINE | ||
+ "Content-Length: " + content.length + NEW_LINE | ||
+ NEW_LINE; | ||
} | ||
|
||
private String response302Header(String redirectUrl) { | ||
return "HTTP/1.1 302 Found" + NEW_LINE | ||
+ "Location: " + redirectUrl + NEW_LINE | ||
+ NEW_LINE; | ||
} | ||
|
||
private void toDataOutputStream(String response) { | ||
try { | ||
dos.writeBytes(response); | ||
logger.debug(response); | ||
} catch (IOException e) { | ||
logger.error(e.getMessage()); | ||
e.printStackTrace(); | ||
} | ||
} | ||
|
||
private void responseBody(byte[] content) { | ||
try { | ||
dos.write(content, 0, content.length); | ||
dos.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,12 @@ | ||
package web; | ||
|
||
public enum Method { | ||
OPTIONS, | ||
HEAD, | ||
POST, | ||
GET, | ||
PUT, | ||
DELETE, | ||
TRACE, | ||
CONNECT; | ||
} |
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,51 @@ | ||
package web; | ||
|
||
import static utils.HttpRequestParser.*; | ||
import static web.HttpResponse.*; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.IOException; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
|
||
public class RequestBody { | ||
Map<String, String> requestBody; | ||
|
||
public RequestBody(String data) { | ||
this.requestBody = parsingData(data); | ||
} | ||
|
||
public RequestBody(BufferedReader br) throws IOException { | ||
String line = br.readLine(); | ||
while (line == null || line.isEmpty() || NEW_LINE.equals(line)) { | ||
line = br.readLine(); | ||
} | ||
this.requestBody = parsingData(line); | ||
} | ||
|
||
public Map<String, String> getFormData() { | ||
return requestBody; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) | ||
return true; | ||
if (o == null || getClass() != o.getClass()) | ||
return false; | ||
RequestBody that = (RequestBody)o; | ||
return Objects.equals(requestBody, that.requestBody); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(requestBody); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "RequestBody{" + | ||
"requestBody=" + requestBody + | ||
'}'; | ||
} | ||
} |
Oops, something went wrong.