Skip to content
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

[톰캣 구현하기 - 1,2단계] 에단(김석호) 미션 제출합니다. #372

Merged
merged 34 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
92cba7e
test: FileTest 작성
cookienc Sep 3, 2023
d115a52
test: IOEStreamTest 작성
cookienc Sep 3, 2023
2a7c45c
feat: 정적파일 파싱 기능 구현
cookienc Sep 4, 2023
4507b9d
feat: header 파싱 기능 분리
cookienc Sep 4, 2023
cdd1155
feat: queryString 파싱 기능 구현
cookienc Sep 4, 2023
63eb5f4
feat: login 기능 구현
cookienc Sep 4, 2023
159001a
refactor: login을 post로 변경
cookienc Sep 4, 2023
0a2fbfd
refactor: fronthandler를 생성해서 정적파일을 처리하도록 변경
cookienc Sep 4, 2023
c1a1e25
refactor: fronthandler가 GET을 처리하도록 변경
cookienc Sep 4, 2023
59d8f91
refactor: LoginMapping, LoginPageMapping으로 리팩토링
cookienc Sep 4, 2023
8e5ff25
feat: 회원가입 화면 추가
cookienc Sep 4, 2023
45180a3
feat: 회원가입 기능 구현
cookienc Sep 4, 2023
7720677
refactor: handle 메서드에 header 추가
cookienc Sep 4, 2023
e3be3bc
feat: Cookie를 파싱하는 HttpCookie 클래스 구현
cookienc Sep 4, 2023
3f4bb9e
feat: Cookie 추가
cookienc Sep 4, 2023
96e89a9
feat: Session 추가
cookienc Sep 4, 2023
0e56b55
refactor: 필요없는 throws 제거
cookienc Sep 4, 2023
df69091
refactor: HttpMethod enum 추가
cookienc Sep 4, 2023
ded667a
refactor: HttpHeaders 로 추가
cookienc Sep 4, 2023
c839e59
refactor: 필요없는 static 제거
cookienc Sep 6, 2023
b406c5e
refactor: BufferedReader를 try with resources로 변겨
cookienc Sep 6, 2023
d0380db
refactor: HttpRequest로 변경
cookienc Sep 6, 2023
cff2f3b
refactor: HttpRequest로 변경
cookienc Sep 6, 2023
cf9b8bc
refactor: session 및 request 패키지 변경
cookienc Sep 6, 2023
fae548b
refactor: startLine 을 requestLine 으로 변경
cookienc Sep 6, 2023
3b0c080
refactor: httpResponse 로 리팩토링
cookienc Sep 6, 2023
01ef6dd
refactor: 레거시 코드 제거
cookienc Sep 6, 2023
59b8ca5
refactor: hello world가 text로 나가도록 변경
cookienc Sep 6, 2023
b332054
fix: 리다이렉트가 될 때 html이 안보이는 문제 해결
cookienc Sep 6, 2023
aa5d8ec
refactor: isAlreadyLogined 메서드를 한 줄로 변경
cookienc Sep 6, 2023
8bf5593
refactor: "/" url을 homePageMapping으로 변경
cookienc Sep 6, 2023
5f5c2cb
refactor: body에 빈 공백 제거
cookienc Sep 6, 2023
8db321f
refactor: header관련 설정 변경
cookienc Sep 6, 2023
feb7a82
refactor: ContentLength가 잘못나오던 문제 해결
cookienc Sep 6, 2023
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
19 changes: 8 additions & 11 deletions tomcat/src/main/java/org/apache/coyote/handler/FrontHandler.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package org.apache.coyote.handler;

import org.apache.coyote.handler.mapping.HandlerMapping;
import org.apache.coyote.handler.mapping.HomePageMapping;
import org.apache.coyote.handler.mapping.LoginMapping;
import org.apache.coyote.handler.mapping.LoginPageMapping;
import org.apache.coyote.handler.mapping.RegisterMapping;
import org.apache.coyote.handler.mapping.RegisterPageMapping;
import org.apache.coyote.handler.mapping.StaticFileMapping;
import org.apache.coyote.http.HttpHeaders;
import org.apache.coyote.http.HttpMethod;
import org.apache.coyote.http.request.HttpRequest;
import org.apache.coyote.http.response.HttpResponse;

import java.io.IOException;
import java.util.HashSet;
Expand All @@ -18,25 +19,21 @@ public class FrontHandler {
private static final Set<HandlerMapping> handlerMapping = new HashSet<>();

static {
handlerMapping.add(new HomePageMapping());
handlerMapping.add(new StaticFileMapping());
handlerMapping.add(new LoginMapping());
handlerMapping.add(new LoginPageMapping());
handlerMapping.add(new RegisterMapping());
handlerMapping.add(new RegisterPageMapping());
}

public String handle(final String firstLine, final HttpHeaders headers, final String requestBody) throws IOException {
String response = "";
final String[] parsedFirstLine = firstLine.split(" ");
public HttpResponse handle(final HttpRequest httpRequest) throws IOException {
for (final HandlerMapping mapping : handlerMapping) {
final HttpMethod httpMethod = HttpMethod.from(parsedFirstLine[0]);
final String requestUri = parsedFirstLine[1];
if (mapping.supports(httpMethod, requestUri)) {
response = mapping.handle(parsedFirstLine[1], headers, requestBody);
break;
if (mapping.supports(httpRequest)) {
return mapping.handle(httpRequest);
}
}

return response;
return HttpResponse.redirect("/404.html");
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package org.apache.coyote.handler.mapping;

import org.apache.coyote.http.HttpHeaders;
import org.apache.coyote.http.HttpMethod;
import org.apache.coyote.http.request.HttpRequest;
import org.apache.coyote.http.response.HttpResponse;

import java.io.IOException;

public interface HandlerMapping {

boolean supports(final HttpMethod httpMethod, final String requestUri);
boolean supports(final HttpRequest httpRequest);

String handle(final String requestUri, final HttpHeaders httpHeaders, final String requestBody) throws IOException;
HttpResponse handle(final HttpRequest httpRequest) throws IOException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.apache.coyote.handler.mapping;

import org.apache.coyote.http.common.HttpBody;
import org.apache.coyote.http.common.HttpHeaders;
import org.apache.coyote.http.request.HttpRequest;
import org.apache.coyote.http.response.ContentType;
import org.apache.coyote.http.response.HttpResponse;
import org.apache.coyote.http.response.StatusCode;
import org.apache.coyote.http.response.StatusLine;

import java.io.IOException;
import java.util.Map;

import static org.apache.coyote.http.common.HttpHeader.CONTENT_TYPE;

public class HomePageMapping implements HandlerMapping {

@Override
public boolean supports(final HttpRequest httpRequest) {
return httpRequest.isGetRequest() && "/".equals(httpRequest.getRequestUri().getRequestUri());
}

@Override
public HttpResponse handle(final HttpRequest httpRequest) throws IOException {
return HttpResponse.builder()
.statusLine(StatusLine.from(StatusCode.OK))
.httpHeaders(new HttpHeaders(Map.of(CONTENT_TYPE, ContentType.HTML.getValue())))
.body(new HttpBody("Hello world!"))
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package org.apache.coyote.handler.mapping;

import org.apache.coyote.http.LoginManager;
import org.apache.coyote.http.Session;
import org.apache.coyote.http.SessionManager;
import org.apache.coyote.http.session.Session;
import org.apache.coyote.http.session.SessionManager;

import java.util.Map;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,33 @@

import nextstep.jwp.db.InMemoryUserRepository;
import nextstep.jwp.model.User;
import org.apache.coyote.http.HttpHeaders;
import org.apache.coyote.http.HttpMethod;
import org.apache.coyote.http.common.HttpBody;
import org.apache.coyote.http.common.HttpHeader;
import org.apache.coyote.http.common.HttpHeaders;
import org.apache.coyote.http.request.HttpRequest;
import org.apache.coyote.http.response.HttpResponse;
import org.apache.coyote.http.response.StatusCode;
import org.apache.coyote.http.response.StatusLine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.io.IOException;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

import static org.apache.coyote.http.HttpMethod.POST;

public class LoginMapping extends LoginFilter implements HandlerMapping {
Copy link
Member

Choose a reason for hiding this comment

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

혹시 loginFilter를 상속으로 적용하신 이유가 있을까요?

Copy link
Author

Choose a reason for hiding this comment

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

다른 화면이나 기능이 추가되면 인증이 필요한 부분이 자연스럽게 생길것 같다는 생각이 들었습니다. 그래서 공통적인 기능을 추상화 했습니다.


public static final String TARGET_URI = "login";
Copy link
Member

Choose a reason for hiding this comment

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

접근제어자 private으로 해주면 더 좋을 것 같아요.

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

@Override
public boolean supports(final HttpMethod httpMethod, final String requestUri) {
return POST == httpMethod &&
requestUri.contains("login");
public boolean supports(final HttpRequest httpRequest) {
return httpRequest.isPostRequest() && httpRequest.containsRequestUri(TARGET_URI);
}

@Override
public String handle(final String requestUri, final HttpHeaders httpHeaders, final String requestBody) {
final Map<String, String> bodyParams = Arrays.stream(requestBody.split("&"))
.map(param -> param.split("="))
.collect(Collectors.toMap(param -> param[0], param -> param[1]));

public HttpResponse handle(final HttpRequest httpRequest) throws IOException {
final Map<String, String> bodyParams = httpRequest.getParsedBody();
final String account = bodyParams.get("account");
final String password = bodyParams.get("password");

Expand All @@ -44,17 +43,18 @@ public String handle(final String requestUri, final HttpHeaders httpHeaders, fin
log.info("로그인 성공! user = {}", user);
} catch (final IllegalArgumentException e) {
log.warn("login error = {}", e);
return String.join("\r\n",
"HTTP/1.1 302 Found ",
"Location: /401.html ");
return HttpResponse.redirect("/401.html");
}

final UUID uuid = UUID.randomUUID();
setSession(uuid.toString(), Map.of("account", user.getAccount()));

return String.join("\r\n",
"HTTP/1.1 302 Found ",
"Location: /index.html ",
"Set-Cookie: JSESSIONID=" + uuid + " ");
return HttpResponse.builder()
.statusLine(StatusLine.from(StatusCode.FOUND))
.httpHeaders(new HttpHeaders(
Map.of(HttpHeader.LOCATION, "/index.html",
HttpHeader.SET_COOKIE, "JSESSIONID=" + uuid)))
.body(HttpBody.empty())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,46 @@

import nextstep.jwp.db.InMemoryUserRepository;
import nextstep.jwp.model.User;
import org.apache.coyote.http.HttpCookie;
import org.apache.coyote.http.HttpHeaders;
import org.apache.coyote.http.HttpMethod;
import org.apache.coyote.http.common.HttpBody;
import org.apache.coyote.http.common.HttpHeaders;
import org.apache.coyote.http.request.HttpCookie;
import org.apache.coyote.http.request.HttpRequest;
import org.apache.coyote.http.response.ContentType;
import org.apache.coyote.http.response.HttpResponse;
import org.apache.coyote.http.response.StatusCode;
import org.apache.coyote.http.response.StatusLine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;

import static org.apache.coyote.http.HttpMethod.GET;
import static org.apache.coyote.http.common.HttpHeader.CONTENT_TYPE;
import static org.apache.coyote.http.common.HttpHeader.COOKIE;

public class LoginPageMapping extends LoginFilter implements HandlerMapping {

public static final String TARGET_URI = "login";
private static final Logger log = LoggerFactory.getLogger(LoginPageMapping.class);

@Override
public boolean supports(final HttpMethod httpMethod, final String requestUri) {
return GET == httpMethod &&
requestUri.contains("login");
public boolean supports(final HttpRequest httpRequest) {
return httpRequest.isGetRequest() && httpRequest.containsRequestUri(TARGET_URI);
}

@Override
public String handle(final String requestUri, final HttpHeaders httpHeaders, final String requestBody) throws IOException {
if (httpHeaders.containsKey("Cookie")) {
final HttpCookie cookies = HttpCookie.from(httpHeaders.get("Cookie"));
public HttpResponse handle(final HttpRequest httpRequest) throws IOException {
if (httpRequest.containsHeader(COOKIE)) {
final HttpCookie cookies = HttpCookie.from(httpRequest.getHeader(COOKIE));
if (isAlreadyLogined(cookies.get("JSESSIONID"))) {
return String.join("\r\n",
"HTTP/1.1 302 Found ",
"Location: /index.html ");
return HttpResponse.redirect("/index.html");
}
}

final String[] parsedRequestUri = requestUri.split("\\?");
if (requestUri.contains("?")) {
final String[] parsedRequestUri = httpRequest.getRequestUri().getRequestUri().split("\\?");
Copy link
Member

Choose a reason for hiding this comment

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

parsedRequestUri가 아래 if문 안에서만 사용되는 거 같아서 If문 안에 들어가도 될 것 같아요!

if (httpRequest.getRequestUri().getRequestUri().contains("?")) {
final Map<String, String> queryStrings = Arrays.stream(parsedRequestUri[1].split("&"))
.map(param -> param.split("="))
.collect(Collectors.toMap(param -> param[0], param -> param[1]));
Copy link
Member

Choose a reason for hiding this comment

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

이 부분도 HttpRequest안으로 분리해보는 건 어떨까요?

Expand All @@ -59,26 +59,17 @@ public String handle(final String requestUri, final HttpHeaders httpHeaders, fin
log.info("로그인 성공! user = {}", user);
} catch (final IllegalArgumentException e) {
log.warn("login error = {}", e);
return String.join("\r\n",
"HTTP/1.1 302 Found ",
"Location: /401.html ");
return HttpResponse.redirect("/401.html");
}

return String.join("\r\n",
"HTTP/1.1 302 Found ",
"Location: /index.html ");
return HttpResponse.redirect("/index.html");
}

final String filePath = "static/login.html";
final URL fileUrl = getClass().getClassLoader().getResource(filePath);
final Path path = new File(fileUrl.getPath()).toPath();
final String responseBody = new String(Files.readAllBytes(path));

return String.join("\r\n",
"HTTP/1.1 200 OK ",
"Content-Type: text/html;charset=utf-8 ",
"Content-Length: " + responseBody.getBytes().length + " ",
"",
responseBody);
return HttpResponse.builder()
.statusLine(StatusLine.from(StatusCode.OK))
.httpHeaders(new HttpHeaders(Map.of(CONTENT_TYPE, ContentType.HTML.getValue())))
.body(HttpBody.file("static/login.html"))
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,24 @@

import nextstep.jwp.db.InMemoryUserRepository;
import nextstep.jwp.model.User;
import org.apache.coyote.http.HttpHeaders;
import org.apache.coyote.http.HttpMethod;
import org.apache.coyote.http.request.HttpRequest;
import org.apache.coyote.http.response.HttpResponse;

import java.util.Arrays;
import java.io.IOException;
import java.util.Map;
import java.util.stream.Collectors;

import static org.apache.coyote.http.HttpMethod.POST;

public class RegisterMapping implements HandlerMapping {

public static final String TARGET_URI = "register";

@Override
public boolean supports(final HttpMethod httpMethod, final String requestUri) {
return POST == httpMethod &&
requestUri.contains("register");
public boolean supports(final HttpRequest httpRequest) {
return httpRequest.isPostRequest() && httpRequest.containsRequestUri(TARGET_URI);
}

@Override
public String handle(final String requestUri, final HttpHeaders httpHeaders, final String requestBody) {
final Map<String, String> bodyParams = Arrays.stream(requestBody.split("&"))
.map(param -> param.split("="))
.collect(Collectors.toMap(param -> param[0], param -> param[1]));
public HttpResponse handle(final HttpRequest httpRequest) throws IOException {
final Map<String, String> bodyParams = httpRequest.getParsedBody();

final String account = bodyParams.get("account");
final String password = bodyParams.get("password");
Expand All @@ -32,8 +28,6 @@ public String handle(final String requestUri, final HttpHeaders httpHeaders, fin
final User user = new User(account, password, email);
Copy link
Member

Choose a reason for hiding this comment

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

음... 이번 톰켓 미션과 크게 관련있는지는 모르겠는데 똑같은 걸로 계속 가입이 되더라구요.
요 부분은 에단이 원하면 반영해주세요!

InMemoryUserRepository.save(user);

return String.join("\r\n",
"HTTP/1.1 302 Found ",
"Location: /index.html ");
return HttpResponse.redirect("/index.html");
}
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,33 @@
package org.apache.coyote.handler.mapping;

import org.apache.coyote.http.HttpHeaders;
import org.apache.coyote.http.HttpMethod;
import org.apache.coyote.http.common.HttpBody;
import org.apache.coyote.http.common.HttpHeaders;
import org.apache.coyote.http.request.HttpRequest;
import org.apache.coyote.http.response.ContentType;
import org.apache.coyote.http.response.HttpResponse;
import org.apache.coyote.http.response.StatusCode;
import org.apache.coyote.http.response.StatusLine;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;

import static org.apache.coyote.http.common.HttpHeader.CONTENT_TYPE;

public class RegisterPageMapping implements HandlerMapping {

public static final String TARGET_URI = "register";

@Override
public boolean supports(final HttpMethod httpMethod, final String requestUri) {
return HttpMethod.GET == httpMethod &&
requestUri.contains("register");
public boolean supports(final HttpRequest httpRequest) {
return httpRequest.isGetRequest() && httpRequest.containsRequestUri(TARGET_URI);
}

@Override
public String handle(final String requestUri, final HttpHeaders httpHeaders, final String requestBody) throws IOException {
final String filePath = "static/register.html";
final URL fileUrl = getClass().getClassLoader().getResource(filePath);
final Path path = new File(fileUrl.getPath()).toPath();
final String responseBody = new String(Files.readAllBytes(path));

return String.join("\r\n",
"HTTP/1.1 200 OK ",
"Content-Type: text/html;charset=utf-8 ",
"Content-Length: " + responseBody.getBytes().length + " ",
"",
responseBody);
public HttpResponse handle(final HttpRequest httpRequest) throws IOException {
return HttpResponse.builder()
.statusLine(StatusLine.from(StatusCode.OK))
.httpHeaders(new HttpHeaders(Map.of(CONTENT_TYPE, ContentType.HTML.getValue())))
.body(HttpBody.file("static/register.html"))
.build();
}
}
Loading