Skip to content

Commit

Permalink
[2단계 - Tomcat 구현하기] 짱수(장혁수) 미션 제출합니다. (#602)
Browse files Browse the repository at this point in the history
* 이슈 템플릿 생성

* fix: remove implementation logback-classic on gradle (#501)

* fix: add threads min-spare configuration on properties (#502)

* fix: content 가 null 일 경우 응답이 생성되지 않도록 변경

* refactor: 학습테스트를 무시하도록 변경

* fix: 로그인 페이지를 보여주도록 변경

* refactor: 응답 전체 로깅 삭제

* refactor: 사용하지 않는 핸들러 삭제

* fix: 로그인 요청에서 로그인 페이지 응답으로 변경

* refactor: 파일에 따라 MIME TYPE 을 결정하는 메서드 사용

* refactor: handle 메서드에서 null 반환 제거

* refactor: Resource -> StaticResource 로 이름 및 역할 변경

* refactor: 정적 파일 응답을 분리

* test: 로그인 시도에 대한 요청 테스트

* feat: 로그인 결과에 따라 응답 분기

* refactor: 메서드 이름 변경

* test: 회원가입 테스트 작성

* test: 회원가입 로직 작성

* test: 테스트에 @nested 사용

* refactor: ResourceHandler 구현체 이름 변경

* refactor: 필요없는 MethodRequest 삭제

* refactor: MethodQueryParameters 이름 변경

* refactor: 전체 요청을 읽는 메서드의 구현 변경

* refactor: 쿼리 파라미터 파싱을 존재할 때만 수행하도록 변경

* test: 회원가입 로직 테스트 변경

* feat: 회원가입 기능 구현

* refactor: 요청을 읽는 방식 변경

- BufferedReader.readLine() 사용부 제거

* refactor: 핸들러 하나만을 사용하도록 변경

* test: body 가 존재하는 요청에 Content-Length 헤더 추가

* test: 요청 생성 메서드 변경

* test: 확인 로직을 헤더 순서와 관련 없이 확인하도록 변경

* refactor: 지정된 응답만 생성하는 대신 응답이 상태를 가지도록 변경

* refactor: Response 를 생성하는 책임을 스스로 가지고, builder 패턴을 사용하도록 변경

* chore: 패키지 변경

* refactor: 필요없는 로그 제거

* refactor: 필드 순서 변경

* refactor: 필요없는 출력 삭제

* refactor: 로그인을 POST 방식으로 변경

* feat: 쿠키 클래스 생성

* feat: LMS 에 명시된 Session 관련 객체들 구현

* feat: 로그인 기능에서 세션을 유지하도록 변경

* refactor: 유저 조회시 해당 유저가 존재하지 않으면 예외를 반환하는 메서드 구현

* feat: 로그인, 회원가입시 세션을 생성하도록 구현

* refactor: Http 헤더 이름 상수화

* refactor: 사용되지 않는 import 문 제거

* refactor: final 필드로 변경

* refactor: 상수화

* refactor: 쿠키 관련 값 상수화

* test: 루트 요청에 대한 테스트 추가

* fix: 정적 리소스 요청 확인을 path 전체에서 파일 확장자를 찾도록 변경

* test: 로그인 로직을 POST 로 보내도록 변경

* refactor: QueryParameters -> RequestParameters 클래스 이름 변경

* refactor: 파라미터 명 변경

* test: Content-Length 를 실제 읽은 값을 사용하도록 변경

* refactor: login 요청에 대해 정확하게 매핑하도록 변경

* test: 로그인이 되어 있을 경우 로그인 뷰 조회 테스트

* fix: 로그인이 된 경우 로그인 페이지로 접근하면 index.html 로 리다이렉트 하도록 변경

---------

Co-authored-by: Gyeongho Yang <[email protected]>
  • Loading branch information
zangsu and geoje authored Sep 12, 2024
1 parent 826d84b commit c698c5e
Show file tree
Hide file tree
Showing 32 changed files with 860 additions and 317 deletions.
6 changes: 0 additions & 6 deletions study/src/test/java/study/FileTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,11 @@

import static org.assertj.core.api.Assertions.assertThat;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
Expand Down
1 change: 0 additions & 1 deletion study/src/test/java/study/IOStreamTest.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package study;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.in;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.coyote.http11.exception.NoSuchUserException;

import com.techcourse.model.User;

public class InMemoryUserRepository {
Expand All @@ -19,6 +21,11 @@ public static void save(User user) {
database.put(user.getAccount(), user);
}

public static User fetchByAccount(String account) {
return findByAccount(account)
.orElseThrow(() -> new NoSuchUserException(account + " 에 해당하는 유저를 찾을 수 없습니다."));
}

public static Optional<User> findByAccount(String account) {
return Optional.ofNullable(database.get(account));
}
Expand Down
8 changes: 4 additions & 4 deletions tomcat/src/main/java/org/apache/catalina/Manager.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import java.io.IOException;

import jakarta.servlet.http.HttpSession;
import org.apache.coyote.http11.session.Session;

/**
* A <b>Manager</b> manages the pool of Sessions that are associated with a
Expand All @@ -29,7 +29,7 @@ public interface Manager {
*
* @param session Session to be added
*/
void add(HttpSession session);
void add(Session session);

/**
* Return the active Session, associated with this Manager, with the
Expand All @@ -43,12 +43,12 @@ public interface Manager {
* @throws IOException if an input/output error occurs while
* processing this request
*/
HttpSession findSession(String id) throws IOException;
Session findSession(String id) throws IOException;

/**
* Remove this Session from the active Sessions for this Manager.
*
* @param session Session to be removed
*/
void remove(HttpSession session);
void remove(String session);
}
35 changes: 6 additions & 29 deletions tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,10 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.coyote.Processor;
import org.apache.coyote.http11.exception.NoHandlerException;
import org.apache.coyote.http11.handler.MethodHandler;
import org.apache.coyote.http11.handler.StaticResourceHandler;
import org.apache.coyote.http11.request.Request;
import org.apache.coyote.http11.handler.DefaultResourceHandler;
import org.apache.coyote.http11.httpmessage.request.Request;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -21,15 +16,12 @@
public class Http11Processor implements Runnable, Processor {

private static final Logger log = LoggerFactory.getLogger(Http11Processor.class);
private final List<RequestHandler> requestHandlers;
private final RequestHandler requestHandler;
private final Socket connection;

public Http11Processor(Socket connection) {
this.connection = connection;
this.requestHandlers = List.of(
new MethodHandler(),
new StaticResourceHandler()
);
this.requestHandler = new DefaultResourceHandler();
}

@Override
Expand All @@ -45,7 +37,7 @@ public void process(Socket connection) {
BufferedReader requestBufferedReader = new BufferedReader(inputStreamReader);
var outputStream = connection.getOutputStream()) {

Request request = Request.parseFrom(getPlainRequest(requestBufferedReader));
Request request = Request.readFrom(requestBufferedReader);
log.info("request : {}", request);
String response = getResponse(request);

Expand All @@ -57,21 +49,6 @@ public void process(Socket connection) {
}

private String getResponse(Request request) throws IOException {
for (RequestHandler requestHandler : requestHandlers) {
String response = requestHandler.handle(request);
if (response != null) {
return response;
}
}
throw new NoHandlerException("핸들러가 존재하지 않습니다. request : " + request);
}

private List<String> getPlainRequest(BufferedReader requestBufferedReader) throws IOException {
List<String> plainRequest = new ArrayList<>();
while (requestBufferedReader.ready()) {
String line = requestBufferedReader.readLine();
plainRequest.add(line);
}
return Collections.unmodifiableList(plainRequest);
return requestHandler.handle(request);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import java.io.IOException;

import org.apache.coyote.http11.request.Request;
import org.apache.coyote.http11.httpmessage.request.Request;

@FunctionalInterface
public interface RequestHandler {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.apache.coyote.http11.exception;

public class CanNotHandleRequest extends RuntimeException {
public CanNotHandleRequest(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.apache.coyote.http11.exception;

public class NoSuchUserException extends RuntimeException {
public NoSuchUserException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.apache.coyote.http11.exception;

public class NotCompleteResponseException extends RuntimeException {
public NotCompleteResponseException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package org.apache.coyote.http11.handler;

import java.io.IOException;
import java.util.Objects;

import org.apache.coyote.http11.RequestHandler;
import org.apache.coyote.http11.exception.CanNotHandleRequest;
import org.apache.coyote.http11.exception.NoSuchUserException;
import org.apache.coyote.http11.httpmessage.request.Request;
import org.apache.coyote.http11.httpmessage.response.Response;
import org.apache.coyote.http11.httpmessage.response.StaticResource;
import org.apache.coyote.http11.session.Session;
import org.apache.coyote.http11.session.SessionManager;

import com.techcourse.db.InMemoryUserRepository;
import com.techcourse.model.User;

public class DefaultResourceHandler implements RequestHandler {

@Override
public String handle(Request request) throws IOException {
if (request.isStaticResourceRequest()) {
return Response.builder()
.versionOf(request.getHttpVersion())
.ofStaticResource(new StaticResource(request.getTarget()))
.toHttpMessage();
}
if (request.getTarget().equals("/")) {
return Response.builder()
.versionOf(request.getHttpVersion())
.ofStaticResource(new StaticResource("/index.html"))
.toHttpMessage();
}
if (request.getTarget().equals("/login")) {
return loginResponse(request);
}
if (request.getTarget().contains("register")) {
return registerResponse(request);
}
throw new CanNotHandleRequest("처리할 수 없는 요청입니다. : " + request.getTarget());
}

private String loginResponse(Request request) throws IOException {
if (request.isPost()) {
return login(request).toHttpMessage();
}
if (isLoggedIn(request)) {
return Response.builder()
.versionOf(request.getHttpVersion())
.found("/index.html")
.toHttpMessage();
}
return Response.builder()
.versionOf(request.getHttpVersion())
.ofStaticResource(new StaticResource("/login.html"))
.toHttpMessage();
}

private boolean isLoggedIn(Request request) {
return Objects.nonNull(request.getSession(false));
}

private Response login(Request request) throws NoSuchUserException {
RequestParameters requestParams = RequestParameters.parseFrom(request.getBody());
String account = requestParams.getParam("account");
String password = requestParams.getParam("password");
User user = InMemoryUserRepository.fetchByAccount(account);
if (user.checkPassword(password)) {
Session session = request.getSession(true);
session.setAttribute("user", user);
SessionManager.getInstance().add(session);
return Response.builder()
.versionOf(request.getHttpVersion())
.addCookie("JSESSIONID", session.getId())
.found("/index.html");
}

return Response.builder()
.versionOf(request.getHttpVersion())
.found("/401.html");
}

private String registerResponse(Request request) throws IOException {
if (request.isPost()) {
RequestParameters methodRequest = RequestParameters.parseFrom(request.getBody());
User user = register(methodRequest);
Session session = request.getSession(true);
session.setAttribute("user", user);
SessionManager.getInstance().add(session);
return Response.builder()
.versionOf(request.getHttpVersion())
.addCookie("JSESSIONID", session.getId())
.found("/index.html")
.toHttpMessage();
}

return Response.builder()
.versionOf(request.getHttpVersion())
.ofStaticResource(new StaticResource("/register.html"))
.toHttpMessage();
}

private User register(RequestParameters requestParams) {
String account = requestParams.getParam("account");
User user = new User(
account,
requestParams.getParam("password"),
requestParams.getParam("email")
);
InMemoryUserRepository.save(user);
return InMemoryUserRepository.fetchByAccount(account);
}
}

This file was deleted.

This file was deleted.

This file was deleted.

Loading

0 comments on commit c698c5e

Please sign in to comment.