Skip to content

[Hamill&Dan][HTTP 웹서버 구현하기] Step1, 2 Submit #50

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Mar 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e3d8116
refactor : README.md update
hanurii Mar 16, 2020
040732b
Merge pull request #3 from Hamill210/Hamill210
Hyune-c Mar 16, 2020
5543bbd
refactor : README.md 업데이트, Handlebar 수정
Hyune-c Mar 16, 2020
cac0a79
refactor : Handlebar 수정
hanurii Mar 16, 2020
4116dd6
refactor : Day1 추가, Step1.요구사항1 완료
Hyune-c Mar 16, 2020
84abfbb
refactor : 문서를 wiki 로 이동
Hyune-c Mar 16, 2020
200f7e6
refactor : RequestHandler 리팩토링
Hyune-c Mar 17, 2020
d48b5d3
feat : 회원가입 기능 구현중
hanurii Mar 17, 2020
a46ac31
feat : 회원가입 구현
Hyune-c Mar 17, 2020
f0511ee
feat : 회원가입 구현
Hyune-c Mar 17, 2020
82dce59
feat : 회원 가입 구현
hanurii Mar 17, 2020
809b980
feat : 회원가입 구현 2
Hyune-c Mar 17, 2020
6365a0b
feat : 요구사항 2, 3 구현
Hyune-c Mar 17, 2020
35daff4
refactor : RequestHandler 개선
Hyune-c Mar 17, 2020
ce94f26
refactor : 요구사항 4 구현 성공
hanurii Mar 18, 2020
0e71953
feat : PageController 추가 구현, inmemory DB 연결
Hyune-c Mar 18, 2020
821df0f
feat : login_failed 추가, User 테스트 데이터 추가
hanurii Mar 18, 2020
7fa68b5
feat : PageController 추가 구현, HttpRequestUtils 메서드 추가
hanurii Mar 18, 2020
cd6f632
feat : 요구사항 5 구현
Hyune-c Mar 18, 2020
6b2ce2b
feat : 로그인 성공했을 때만 유저 목록 출력 구현
hanurii Mar 19, 2020
4360a92
feat : 요구사항 6, 7 구현
Hyune-c Mar 19, 2020
3ab72c8
Merge pull request #17 from Hamill210/Dan
hanurii Mar 19, 2020
4821d2a
refactor : request 파싱 로직 변경
hanurii Mar 20, 2020
ec17872
feat : requestHeader 파싱 로직 추가
Hyune-c Mar 20, 2020
b4e698b
refactor : requestBody 파싱 로직 추가
hanurii Mar 20, 2020
a30861a
refactor : requestBody 파싱 로직 추가
hanurii Mar 20, 2020
758b519
feat : response 구현 완료
Hyune-c Mar 20, 2020
a9daa61
refactor : 새로운 로직에서의 Client에 응답 구현
hanurii Mar 20, 2020
4179774
Merge pull request #19 from Hamill210/Hamill210
hanurii Mar 20, 2020
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
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,8 @@
## 온라인 코드 리뷰 과정
* [텍스트와 이미지로 살펴보는 코드스쿼드의 온라인 코드 리뷰 과정](https://github.com/code-squad/codesquad-docs/blob/master/codereview/README.md)
* [동영상으로 살펴보는 코드스쿼드의 온라인 코드 리뷰 과정](https://youtu.be/a5c9ku-_fok)

-------------

## Ground Rule
https://github.com/Hamill210/java-was.wiki.git
18 changes: 18 additions & 0 deletions retrospective/Day1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Day 1

## 오늘한 것
- [ ] http://localhost:8080/index.html 로 접속했을 때 webapp 디렉토리의 index.html 파일을 읽어 클라이언트에 응답합니다.
- index.html 호출 성공
- css 호출 실패

## 내일 할 것
- Step1. 요구사항 2

## 키워드
- favicon.ico
- 이게 되는 이유
`!"".equals(line = br.readLine())`
- 프로그램을 종료해도 Listen 이 남아있는 이유
- Http request, response Header 만 공부
- request line

121 changes: 121 additions & 0 deletions src/main/java/Controller/PageController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package Controller;

import db.DataBase;
import model.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import util.HttpRequestUtils;
import webserver.RequestHandler;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class PageController {
private static final Logger log = LoggerFactory.getLogger(RequestHandler.class);

public Map<String, String> doWork(Map<String, String> requestLine,
Map<String, String> requestHeader, String requestBody)
{
Map<String, String> response = new HashMap<>();
log.debug("### dowork");

String responseBodyUrl = "";
String statusCode = "";
String location = "";
String message = "";

String method = requestLine.get("method");
String requestUrl = requestLine.get("requestUrl");
String protocol = requestLine.get("protocol");
String contentType = requestHeader.get("Accept");

switch (method) {
Copy link

@imjinbro imjinbro Mar 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요청 url이 추가될 때마다 if ~ else if ~ else 문이 길어지겠군요
Spring mvc 처리 흐름에서 HandlerMapping / Controller 역할 구분에 대하여 학습해보시고 리팩토링 해보시길 권해드려요

case "GET":
log.debug("### dowork : GET");
if (requestUrl.equals("/user/form.html")) {
responseBodyUrl = "/user/form.html";
statusCode = "200";
message = "OK";
} else if (requestUrl.equals("/index.html")) {
responseBodyUrl = "/index.html";
statusCode = "200";
message = "OK";
} else if (requestUrl.equals("/user/login.html")) {
responseBodyUrl = "/user/login.html";
statusCode = "200";
message = "OK";
} else if (requestUrl.equals("/user/login_failed.html")) {
responseBodyUrl = "/user/login_failed.html";
statusCode = "200";
message = "OK";
} else if (requestUrl.equals("/user/list")) {
boolean de = false;
log.debug("### requestHeader : {}", requestHeader);
log.debug("### requestHeader : {}", requestHeader.get("Cookie"));

String cookie = requestHeader.get("Cookie").replaceAll(" ", "");
boolean delemeter = Arrays.stream(cookie.split(";")).anyMatch(token -> token.startsWith("logined=true"));

if (delemeter) {
responseBodyUrl = "/user/list.html";
statusCode = "200";
message = "OK";
} else {
responseBodyUrl = "/user/list.html";
statusCode = "302";
location = "http://localhost:8080/user/login.html";
message = "Found";
}
} else {
responseBodyUrl = requestLine.get("requestUrl");
statusCode = "200";
message = "OK";
}
break;
case "POST":
if (requestUrl.equals("/user/create")) {
UserController.create(requestBody);
log.debug("### DataBase : {}", DataBase.findAll());
log.debug("### requestBody : {}", requestBody);
responseBodyUrl = "/index.html";
statusCode = "302";
location = "http://localhost:8080/index.html";
message = "Found";
}
if (requestUrl.equals("/user/login")) {
Map<String, String> parsedRequestBody = HttpRequestUtils.parseRequestBody(requestBody);
User loginUser =
new User(parsedRequestBody.get("userId"), parsedRequestBody.get("password"), "", "");
User findUser = DataBase.findUserById(parsedRequestBody.get("userId"));
log.debug("### login check : {}", loginUser.getPassword().equals(findUser.getPassword()));

if (loginUser.getPassword().equals(findUser.getPassword())) {
responseBodyUrl = "/index.html";
statusCode = "302";
location = "http://localhost:8080/index.html";
message = "Found";
response.put("Set-Cookie", "logined=true; Path=/");
} else {
responseBodyUrl = "/user/login_failed.html";
statusCode = "302";
location = "http://localhost:8080/user/login_failed.html";
message = "Found";
response.put("Set-Cookie", "logined=false; Path=/");
}
}
break;
default:
break;
}

response.put("responseBodyUrl", responseBodyUrl);
response.put("statusCode", statusCode);
response.put("location", location);
response.put("message", message);
response.put("protocol", protocol);
response.put("contentType", contentType);

return response;
}
}
23 changes: 23 additions & 0 deletions src/main/java/Controller/UserController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package Controller;

import db.DataBase;
import model.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import util.HttpRequestUtils;
import webserver.RequestHandler;

import java.util.Map;

public class UserController {
private static final Logger log = LoggerFactory.getLogger(RequestHandler.class);

public static User create(String uriString) {
Map<String, String> parameters = HttpRequestUtils.parseUriString(uriString);
log.debug("### parameters : {}", parameters);
User user = new User(parameters);
DataBase.addUser(user);

return DataBase.findUserById(user.getUserId());
}
}
18 changes: 18 additions & 0 deletions src/main/java/Controller/WelcomeController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package Controller;

import model.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import webserver.RequestHandler;

public class WelcomeController {

private static final Logger log = LoggerFactory.getLogger(RequestHandler.class);

public void doWork(String[] tokens) {

if (tokens[1].startsWith("/user/create")) {
User createdUser = UserController.create(tokens[1].substring(tokens[1].indexOf("?") + 1));
}
}
}
27 changes: 13 additions & 14 deletions src/main/java/db/DataBase.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
package db;

import java.util.Collection;
import java.util.Map;

import com.google.common.collect.Maps;

import model.User;

import java.util.Collection;
import java.util.Map;

public class DataBase {
private static Map<String, User> users = Maps.newHashMap();
private static Map<String, User> users = Maps.newHashMap();

public static void addUser(User user) {
users.put(user.getUserId(), user);
}
public static void addUser(User user) {
users.put(user.getUserId(), user);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User null 케이스에 대한 예외처리가 필요해보입니다.
항상 워스트 케이스를 생각하시면서 그에 대한 처리를 하시길 권해드려요

}

public static User findUserById(String userId) {
return users.get(userId);
}
public static User findUserById(String userId) {
return users.get(userId);
}

public static Collection<User> findAll() {
return users.values();
}
public static Collection<User> findAll() {
return users.values();
}
}
91 changes: 91 additions & 0 deletions src/main/java/model/HttpRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package model;

import util.HttpRequestUtils;
import util.IOUtils;

import java.io.BufferedReader;
import java.util.HashMap;
import java.util.Map;

public class HttpRequest extends HttpTemplate {
private BufferedReader br;

public HttpRequest(BufferedReader br) throws Exception {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요청 데이터 읽는 방식이 파일 읽기 혹은 다른 방식으로 변경될 경우 HttpRequest 객체 코드에도 영향이 있지 않을까요?
BufferedReader를 생성자로 가지고 있다면 입력값을 읽는 방법 변경이 해당 클래스 코드에도 영향이 있을 것 입니다.
HttpRequest는 말그대로 Request에 대한 정보를 가지고 있는 객체로 한정 짓는 것이 필요합니다.

this.br = br;
this.startLine = requestLine();
this.header = requestHeader();
this.body = requestBody();
}

/**
* Feat : parsing 된 requestLine 을 리턴해줍니다.
* Desc : parseRequestLine() 을 통해 method, requestUrl, protocol 로 parsing 됩니다.
* Return : Map<String, String>
*/
private Map<String, String> requestLine() throws Exception {
if (!br.ready()) return new HashMap<>();
String line = br.readLine();

Map<String, String> requestLine = new HashMap<>();
String[] splitedLine = line.split(" ");

requestLine.put("method", splitedLine[0]);
requestLine.put("requestUrl", splitedLine[1]);
requestLine.put("protocol", splitedLine[2]);

return requestLine;
}

/**
* Feat : parsing 된 requestHeader 를 리턴해줍니다.
* Desc :
* Return : Map<String, Stirng>
*/
private Map<String, String> requestHeader() throws Exception {
if (!br.ready()) return new HashMap<>();
String line;

Map<String, String> requestHeader = new HashMap<>();
while (br.ready()) {
if ("".equals(line = br.readLine())) break;
requestHeader.putAll(HttpRequestUtils.getKeyValueMap(line, ": "));
}

return requestHeader;
}

/**
* Feat : requestBody 를 만듭니다.
* Desc : contentLength 만큼 만듭니다.
* Return : String
*/
private String requestBody() throws Exception {
return (br.ready() && header.containsKey("Content-Length"))
? IOUtils.readData(br, Integer.parseInt(header.get("Content-Length")))
: "";
}

public String getMethod() {
return this.startLine.get("method");
}

public String getProtocol() {
return this.startLine.get("protocol");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

생성자에서 Request를 받아서 모두 쪼개어 놓으면 되지 않을까요?
그렇게 하면 예외처리도 쉬울 것 같습니다.

}

public String getPath() {
return this.startLine.get("requestUrl");
}

public Map<String, String> getHeader() {
return this.header;
}

public String getBody() {
return this.body;
}

public Map<String, String> getStartLine() {
return this.startLine;
}
}
Loading