-
Notifications
You must be signed in to change notification settings - Fork 309
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[톰캣 구현하기 - 1, 2단계] 말랑(신동훈) 미션 제출합니다. (#310)
* IOStreamTest 학습 테스트 * File 학습 테스트 * Http 요청의 StartLine 클래스 생성 * Http 요청의 Header 클래스 생성 * Http 요청의 Body 클래스 생성 * Http 요청 클래스 생성 * Http 요청 파서 구현 * 정적 리소스 응답 기능 구현 * css 리소스 응답 기능 구현 * Query String 파싱 기능 추가 * login으로 요청이 들어온 경우 처리 * HttpRequestParser 에서 null request를 반환하지 않도록 함 * 여러 헤더들을 관리할 일급 컬렉션인 Headers 생성 * Header 클래스 제거하고 Headers에서만 관리 * 요청 처리를 담당할 인터페이스인 RequestHandler 작성 * 응답을 위한 StatusLine 생성 * 응답을 위한 ResponseHeaders 생성 * 요청 Headers 객체의 이름을 RequestHeaders로 변경 * 요청 메세지를 처리하는 HttpResponse 클래스 생성 * 기존 코드에서 HttpResponse를 사용하도록 리팩터링 * ResponseHeaders 테스트코드 패키지 위치 수정 * 정적 파일을 처리하는 RequestHandler 구현 * Http11Processor에서 RequestHandler 사용하도록 수정 * 루트 접근을 처리하는 RootPageRequestHandler 구현 * Http11Processor에서 RootPageRequestHandler 사용하도록 수정 * 로그인 요청을 처리하는 LoginRequestHandler 구현 * Http11Processor에서 LoginRequestHandler 사용하도록 수정 * Resource 폴더에서 파일을 읽는 기능 Util로 추출하여 중복 제거 * RequestHandler에서 직접 요청 처리가 가능한지 판단하도록 구현 * Http11Processor 에서 Handler들을 관리하지 않고 외부에서 주입받을 수 있도록 변경 * StaticResourceRequestHandler 에서 파일의 확장자가 없는 경우 기본적으로 html 파일 찾도록 구현 * 로그인 기능 구현 * 로그인 기능을 담당하는 AuthService 구현 * LoginRequestHandler에서 AuthService 사용 * 로그인 시 post 메서드 사용하도록 변경 get을 사용하면 queryString으로 붙고, post를 사용하면 body에 붙는다! * AuthService에 회원가입 기능 추가 * RequestParam 추출하는 기능 Util 클래스로 분리 * 회원가입 처리하는 Handler 구현 * 쿠키 클래스 추가 * 요청과 응답 객체에 Cookie 관련 기능 추가 * 로그인 성공 시 JSESSIONID 쿠키 발급 * 세션 관련 클래스 작성 * get으로 Login 페이지 접속 시 인증되었다면 index로 redirect * 회원가입 시 비밀번호와 이메일이 바뀌는 오류 해결 * 로그인 시 SessionManager에 세션 정보 저장 * favicon 추가 * request, response 관련 클래스 apache.catalina.servlet 패키지로 이동 * DispatcherServlet 도입 * 응답 메세지 작성을 Http11Processor가 아닌 Servlet에서 진행하도록 책임 위임 * RequestHandler 패키지를 애플리케이션으로 이동 - 톰캣에서 Handler를 모르도록 하기 위함 * RequestHandler 메서드 시그니처 변경 * import 순서 변경 * Polish ResponseEntity * ResponseBody 작성의 책임을 다시 Http11Processor로 이동 * HttpResponse를 통해 응답 메세지 만드는 책임을 HttpResponseMessageMaker로 위임 * HttpRequestParser static하게 변경 * 자동 정렬된 Index.html 수정 * html수정 2번째 * 컴파일 에러 수정 * StartLine에서 RequestLine으로 변경 * HttpHeader 추출 * 정적 파일 content type 추가 * 버그 수정 & Polishing
- Loading branch information
1 parent
68db530
commit 3ea87ea
Showing
62 changed files
with
2,521 additions
and
229 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,54 +1,49 @@ | ||
package study; | ||
|
||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import java.nio.file.Path; | ||
import java.util.Collections; | ||
import java.io.BufferedReader; | ||
import java.io.InputStream; | ||
import java.io.InputStreamReader; | ||
import java.net.URL; | ||
import java.util.List; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import java.util.stream.Collectors; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
|
||
/** | ||
* 웹서버는 사용자가 요청한 html 파일을 제공 할 수 있어야 한다. | ||
* File 클래스를 사용해서 파일을 읽어오고, 사용자에게 전달한다. | ||
* 웹서버는 사용자가 요청한 html 파일을 제공 할 수 있어야 한다. File 클래스를 사용해서 파일을 읽어오고, 사용자에게 전달한다. | ||
*/ | ||
@DisplayName("File 클래스 학습 테스트") | ||
class FileTest { | ||
|
||
/** | ||
* resource 디렉터리 경로 찾기 | ||
* | ||
* File 객체를 생성하려면 파일의 경로를 알아야 한다. | ||
* 자바 애플리케이션은 resource 디렉터리에 HTML, CSS 같은 정적 파일을 저장한다. | ||
* resource 디렉터리의 경로는 어떻게 알아낼 수 있을까? | ||
* <p> | ||
* File 객체를 생성하려면 파일의 경로를 알아야 한다. 자바 애플리케이션은 resource 디렉터리에 HTML, CSS 같은 정적 파일을 저장한다. resource 디렉터리의 경로는 어떻게 알아낼 수 | ||
* 있을까? | ||
*/ | ||
@Test | ||
void resource_디렉터리에_있는_파일의_경로를_찾는다() { | ||
final String fileName = "nextstep.txt"; | ||
|
||
// todo | ||
final String actual = ""; | ||
|
||
String fileName = "nextstep.txt"; | ||
URL resource = getClass().getClassLoader().getResource(fileName); | ||
String actual = resource.getFile(); | ||
assertThat(actual).endsWith(fileName); | ||
} | ||
|
||
/** | ||
* 파일 내용 읽기 | ||
* | ||
* 읽어온 파일의 내용을 I/O Stream을 사용해서 사용자에게 전달 해야 한다. | ||
* File, Files 클래스를 사용하여 파일의 내용을 읽어보자. | ||
* <p> | ||
* 읽어온 파일의 내용을 I/O Stream을 사용해서 사용자에게 전달 해야 한다. File, Files 클래스를 사용하여 파일의 내용을 읽어보자. | ||
*/ | ||
@Test | ||
void 파일의_내용을_읽는다() { | ||
final String fileName = "nextstep.txt"; | ||
|
||
// todo | ||
final Path path = null; | ||
|
||
// todo | ||
final List<String> actual = Collections.emptyList(); | ||
|
||
assertThat(actual).containsOnly("nextstep"); | ||
String fileName = "nextstep.txt"; | ||
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(fileName); | ||
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) { | ||
List<String> actual = bufferedReader.lines().collect(Collectors.toList()); | ||
assertThat(actual).containsOnly("nextstep"); | ||
} catch (Exception e) { | ||
} | ||
} | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
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,11 +1,27 @@ | ||
package nextstep; | ||
|
||
import java.util.List; | ||
import nextstep.jwp.handler.DispatcherServlet; | ||
import nextstep.jwp.handler.LoginPageHandler; | ||
import nextstep.jwp.handler.LoginRequestHandler; | ||
import nextstep.jwp.handler.RootPageRequestHandler; | ||
import nextstep.jwp.handler.SignUpRequestHandler; | ||
import nextstep.jwp.handler.StaticResourceRequestHandler; | ||
import nextstep.jwp.handler.mappaing.HandlerMapping; | ||
import org.apache.catalina.startup.Tomcat; | ||
|
||
public class Application { | ||
|
||
public static void main(String[] args) { | ||
final var tomcat = new Tomcat(); | ||
HandlerMapping mapping = new HandlerMapping(List.of( | ||
new RootPageRequestHandler(), | ||
new LoginPageHandler(), | ||
new LoginRequestHandler(), | ||
new SignUpRequestHandler(), | ||
new StaticResourceRequestHandler() | ||
)); | ||
DispatcherServlet dispatcherServlet = new DispatcherServlet(mapping); | ||
final var tomcat = new Tomcat(dispatcherServlet); | ||
tomcat.start(); | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
tomcat/src/main/java/nextstep/jwp/exception/UnAuthenticatedException.java
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 nextstep.jwp.exception; | ||
|
||
public class UnAuthenticatedException extends RuntimeException { | ||
|
||
public UnAuthenticatedException() { | ||
super("인증에 실패하였습니다."); | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
tomcat/src/main/java/nextstep/jwp/handler/DispatcherServlet.java
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,22 @@ | ||
package nextstep.jwp.handler; | ||
|
||
import nextstep.jwp.handler.mappaing.HandlerMapping; | ||
import org.apache.catalina.servlet.Servlet; | ||
import org.apache.catalina.servlet.request.HttpRequest; | ||
import org.apache.catalina.servlet.response.HttpResponse; | ||
|
||
public class DispatcherServlet implements Servlet { | ||
|
||
private final HandlerMapping mapping; | ||
|
||
public DispatcherServlet(HandlerMapping mapping) { | ||
this.mapping = mapping; | ||
} | ||
|
||
@Override | ||
public void service(HttpRequest request, HttpResponse response) { | ||
RequestHandler handler = mapping.getHandler(request); | ||
// TODO null 처리 | ||
handler.handle(request, response); | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
tomcat/src/main/java/nextstep/jwp/handler/LoginPageHandler.java
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,38 @@ | ||
package nextstep.jwp.handler; | ||
|
||
import static org.apache.catalina.servlet.response.HttpStatus.FOUND; | ||
import static org.apache.catalina.servlet.response.HttpStatus.OK; | ||
import static org.apache.catalina.servlet.session.SessionConstant.JSESSIONID; | ||
|
||
import nextstep.jwp.util.ResourceFileUtil; | ||
import org.apache.catalina.servlet.request.HttpRequest; | ||
import org.apache.catalina.servlet.response.HttpResponse; | ||
import org.apache.catalina.servlet.response.StatusLine; | ||
import org.apache.catalina.servlet.session.SessionManager; | ||
|
||
public class LoginPageHandler implements RequestHandler { | ||
|
||
@Override | ||
public boolean canHandle(HttpRequest request) { | ||
return request.requestLine().uri().path().equals("/login") | ||
&& request.requestLine().method().equals("GET"); | ||
} | ||
|
||
@Override | ||
public void handle(HttpRequest request, HttpResponse response) { | ||
if (isAuthenticated(request)) { | ||
response.setStatusLine(new StatusLine(FOUND)); | ||
response.addHeader("Location", "/index.html"); | ||
} else { | ||
response.setStatusLine(new StatusLine(OK)); | ||
response.addHeader("Content-Type", "text/html;charset=utf-8"); | ||
String resource = ResourceFileUtil.readAll("static" + "/login.html"); | ||
response.setMessageBody(resource); | ||
} | ||
} | ||
|
||
private boolean isAuthenticated(HttpRequest request) { | ||
String id = request.cookies().get(JSESSIONID); | ||
return SessionManager.findSession(id) != null; | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
tomcat/src/main/java/nextstep/jwp/handler/LoginRequestHandler.java
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,47 @@ | ||
package nextstep.jwp.handler; | ||
|
||
import static org.apache.catalina.servlet.response.HttpStatus.FOUND; | ||
import static org.apache.catalina.servlet.session.SessionConstant.JSESSIONID; | ||
|
||
import java.util.Map; | ||
import java.util.UUID; | ||
import nextstep.jwp.exception.UnAuthenticatedException; | ||
import nextstep.jwp.model.User; | ||
import nextstep.jwp.service.AuthService; | ||
import org.apache.catalina.servlet.request.HttpRequest; | ||
import org.apache.catalina.servlet.response.HttpResponse; | ||
import org.apache.catalina.servlet.response.StatusLine; | ||
import org.apache.catalina.servlet.session.Session; | ||
import org.apache.catalina.servlet.session.SessionManager; | ||
import org.apache.catalina.servlet.util.RequestParamUtil; | ||
import org.apache.coyote.http11.Http11Processor; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
public class LoginRequestHandler implements RequestHandler { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(Http11Processor.class); | ||
private final AuthService authService = new AuthService(); | ||
|
||
@Override | ||
public boolean canHandle(HttpRequest request) { | ||
return request.requestLine().uri().path().equals("/login") | ||
&& request.requestLine().method().equals("POST"); | ||
} | ||
|
||
@Override | ||
public void handle(HttpRequest request, HttpResponse response) { | ||
Map<String, String> formData = RequestParamUtil.parse(request.body()); | ||
response.setStatusLine(new StatusLine(FOUND)); | ||
try { | ||
User user = authService.login(formData.get("account"), formData.get("password")); | ||
log.info("User={}", user); | ||
response.addHeader("Location", "/index.html"); | ||
Session session = new Session(UUID.randomUUID().toString()); | ||
SessionManager.add(session); | ||
response.addCookie(JSESSIONID, session.id()); | ||
} catch (UnAuthenticatedException e) { | ||
response.addHeader("Location", "/401.html"); | ||
} | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
tomcat/src/main/java/nextstep/jwp/handler/RequestHandler.java
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,11 @@ | ||
package nextstep.jwp.handler; | ||
|
||
import org.apache.catalina.servlet.request.HttpRequest; | ||
import org.apache.catalina.servlet.response.HttpResponse; | ||
|
||
public interface RequestHandler { | ||
|
||
boolean canHandle(HttpRequest request); | ||
|
||
void handle(HttpRequest request, HttpResponse response); | ||
} |
23 changes: 23 additions & 0 deletions
23
tomcat/src/main/java/nextstep/jwp/handler/RootPageRequestHandler.java
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,23 @@ | ||
package nextstep.jwp.handler; | ||
|
||
import static org.apache.catalina.servlet.response.HttpStatus.OK; | ||
|
||
import org.apache.catalina.servlet.request.HttpRequest; | ||
import org.apache.catalina.servlet.response.HttpResponse; | ||
import org.apache.catalina.servlet.response.StatusLine; | ||
|
||
public class RootPageRequestHandler implements RequestHandler { | ||
|
||
@Override | ||
public boolean canHandle(HttpRequest request) { | ||
return request.requestLine().uri().path().equals("/"); | ||
} | ||
|
||
@Override | ||
public void handle(HttpRequest request, HttpResponse response) { | ||
String responseBody = "Hello world!"; | ||
response.setStatusLine(new StatusLine(OK)); | ||
response.addHeader("Content-Type", "text/html;charset=utf-8"); | ||
response.setMessageBody(responseBody); | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
tomcat/src/main/java/nextstep/jwp/handler/SignUpRequestHandler.java
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,29 @@ | ||
package nextstep.jwp.handler; | ||
|
||
import static org.apache.catalina.servlet.response.HttpStatus.FOUND; | ||
|
||
import java.util.Map; | ||
import nextstep.jwp.service.AuthService; | ||
import org.apache.catalina.servlet.request.HttpRequest; | ||
import org.apache.catalina.servlet.response.HttpResponse; | ||
import org.apache.catalina.servlet.response.StatusLine; | ||
import org.apache.catalina.servlet.util.RequestParamUtil; | ||
|
||
public class SignUpRequestHandler implements RequestHandler { | ||
|
||
private final AuthService authService = new AuthService(); | ||
|
||
@Override | ||
public boolean canHandle(HttpRequest request) { | ||
return request.requestLine().uri().path().equals("/register") | ||
&& request.requestLine().method().equals("POST"); | ||
} | ||
|
||
@Override | ||
public void handle(HttpRequest request, HttpResponse response) { | ||
Map<String, String> formData = RequestParamUtil.parse(request.body()); | ||
authService.signUp(formData.get("account"), formData.get("email"), formData.get("password")); | ||
response.setStatusLine(new StatusLine(FOUND)); | ||
response.addHeader("Location", "/index.html"); | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
tomcat/src/main/java/nextstep/jwp/handler/StaticResourceRequestHandler.java
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,47 @@ | ||
package nextstep.jwp.handler; | ||
|
||
import static org.apache.catalina.servlet.response.HttpStatus.OK; | ||
|
||
import nextstep.jwp.util.ResourceFileUtil; | ||
import org.apache.catalina.servlet.request.HttpRequest; | ||
import org.apache.catalina.servlet.request.URI; | ||
import org.apache.catalina.servlet.response.HttpResponse; | ||
import org.apache.catalina.servlet.response.StatusLine; | ||
|
||
public class StaticResourceRequestHandler implements RequestHandler { | ||
|
||
@Override | ||
public boolean canHandle(HttpRequest request) { | ||
return true; | ||
} | ||
|
||
@Override | ||
public void handle(HttpRequest request, HttpResponse response) { | ||
URI uri = request.requestLine().uri(); | ||
String responseBody = ResourceFileUtil.readAll("static" + path(uri)); | ||
response.setStatusLine(new StatusLine(OK)); | ||
response.addHeader("Content-Type", contentType(uri) + "charset=utf-8"); | ||
response.setMessageBody(responseBody); | ||
} | ||
|
||
private String path(URI uri) { | ||
if (uri.path().contains(".")) { | ||
return uri.path(); | ||
} else { | ||
return uri.path() + ".html"; | ||
} | ||
} | ||
|
||
private String contentType(URI uri) { | ||
if (uri.path().endsWith(".css")) { | ||
return "text/css;"; | ||
} | ||
if (uri.path().endsWith(".js")) { | ||
return "text/javascript;"; | ||
} | ||
if (uri.path().endsWith(".svg")) { | ||
return "image/svg+xml;"; | ||
} | ||
return "text/html;"; | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
tomcat/src/main/java/nextstep/jwp/handler/mappaing/HandlerMapping.java
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,24 @@ | ||
package nextstep.jwp.handler.mappaing; | ||
|
||
import java.util.List; | ||
import javax.annotation.Nullable; | ||
import nextstep.jwp.handler.RequestHandler; | ||
import org.apache.catalina.servlet.request.HttpRequest; | ||
|
||
public class HandlerMapping { | ||
|
||
private final List<RequestHandler> handler; | ||
|
||
public HandlerMapping(List<RequestHandler> handler) { | ||
this.handler = handler; | ||
} | ||
|
||
public @Nullable RequestHandler getHandler(HttpRequest request) { | ||
for (RequestHandler requestHandler : handler) { | ||
if (requestHandler.canHandle(request)) { | ||
return requestHandler; | ||
} | ||
} | ||
return null; | ||
} | ||
} |
31 changes: 31 additions & 0 deletions
31
tomcat/src/main/java/nextstep/jwp/service/AuthService.java
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,31 @@ | ||
package nextstep.jwp.service; | ||
|
||
import nextstep.jwp.db.InMemoryUserRepository; | ||
import nextstep.jwp.exception.UnAuthenticatedException; | ||
import nextstep.jwp.model.User; | ||
|
||
public class AuthService { | ||
|
||
public User login(String account, String password) { | ||
validateNonNull(account, password); | ||
User user = InMemoryUserRepository.findByAccount(account) | ||
.orElseThrow(UnAuthenticatedException::new); | ||
if (!user.checkPassword(password)) { | ||
throw new UnAuthenticatedException(); | ||
} | ||
return user; | ||
} | ||
|
||
private void validateNonNull(Object... objects) { | ||
for (Object object : objects) { | ||
if (object == null) { | ||
throw new UnAuthenticatedException(); | ||
} | ||
} | ||
} | ||
|
||
public void signUp(String account, String email, String password) { | ||
validateNonNull(account, email, password); | ||
InMemoryUserRepository.save(new User(account, password, email)); | ||
} | ||
} |
Oops, something went wrong.