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

[톰캣 구현하기 - 3, 4단계] 다즐(최우창) 미션 제출합니다. #470

Merged
merged 7 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package nextstep.jwp.controller;

import nextstep.jwp.handle.ViewResolver;
import org.apache.coyote.common.HttpMethod;
import org.apache.coyote.common.HttpStatus;
import org.apache.coyote.request.HttpRequest;
import org.apache.coyote.response.HttpResponse;

public abstract class AbstractController implements Controller {

@Override
public void service(final HttpRequest request, final HttpResponse response) throws Exception {
if (request.getHttpMethod() == HttpMethod.GET) {
doGet(request, response);
return;
}
if (request.getHttpMethod() == HttpMethod.POST) {
doPost(request, response);
return;
}
ViewResolver.renderPage(response, HttpStatus.NOT_FOUND, "404.html");
}

protected abstract void doPost(final HttpRequest request, final HttpResponse response) throws Exception;

protected abstract void doGet(final HttpRequest request, final HttpResponse response) throws Exception;
}
9 changes: 9 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/controller/Controller.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package nextstep.jwp.controller;

import org.apache.coyote.request.HttpRequest;
import org.apache.coyote.response.HttpResponse;

public interface Controller {

void service(final HttpRequest request, final HttpResponse response) throws Exception;
}
27 changes: 27 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/controller/FileController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package nextstep.jwp.controller;

import nextstep.jwp.handle.ViewResolver;
import org.apache.coyote.common.HttpStatus;
import org.apache.coyote.request.HttpRequest;
import org.apache.coyote.response.HttpResponse;

public class FileController extends AbstractController {

private static final FileController fileController = new FileController();

private FileController() {
}

public static FileController getInstance() {
return fileController;
}

@Override
public void doGet(final HttpRequest request, final HttpResponse response) throws Exception {
ViewResolver.renderPage(response, HttpStatus.OK, request.getUriPath().substring(1));
}

@Override
public void doPost(final HttpRequest request, final HttpResponse response) throws Exception {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package nextstep.jwp.controller;

import org.apache.coyote.common.ContentType;
import org.apache.coyote.common.HttpStatus;
import org.apache.coyote.request.HttpRequest;
import org.apache.coyote.response.HttpResponse;

public class HelloWorldController extends AbstractController {

private static final String BODY = "Hello world!";
private static final HelloWorldController helloWorldController = new HelloWorldController();

private HelloWorldController() {
}

public static HelloWorldController getInstance() {
return helloWorldController;
}

@Override
public void doGet(final HttpRequest request, final HttpResponse response) throws Exception {
response.setStatus(HttpStatus.OK);
response.setContentType(ContentType.TEXT_HTML.getType());
response.setContent(BODY);
}

@Override
public void doPost(final HttpRequest request, final HttpResponse response) throws Exception {
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package org.apache.coyote.handle.handler;
package nextstep.jwp.controller;

import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import nextstep.jwp.db.InMemoryUserRepository;
import nextstep.jwp.handle.ViewResolver;
import nextstep.jwp.model.User;
import org.apache.coyote.common.ContentType;
import org.apache.coyote.common.HttpStatus;
Expand All @@ -17,57 +17,65 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoginHandler implements Handler {
public class LoginController extends AbstractController {

private static final Logger log = LoggerFactory.getLogger(LoginHandler.class);
private static final Logger log = LoggerFactory.getLogger(LoginController.class);
private static final String SLASH = File.separator;
private static final String LOGIN_PAGE = "login.html";
private static final String LOGIN_SUCCESS_PAGE = "index.html";
private static final String LOGIN_FAIL_PAGE = "401.html";
private static final String ACCOUNT = "account";
private static final String PASSWORD = "password";
private static final String JSESSIONID = "JSESSIONID";
private static final LoginController loginController = new LoginController();

private LoginController() {
}

public static LoginController getInstance() {
return loginController;
}

@Override
public void doGet(final HttpRequest httpRequest, final HttpResponse httpResponse) throws IOException {
final HttpCookie httpCookie = httpRequest.getCookie();
public void doGet(final HttpRequest request, final HttpResponse response) throws Exception {
final HttpCookie httpCookie = request.getCookie();
final String cookie = httpCookie.getCookie(JSESSIONID);
if (cookie == null || SessionManager.findSession(cookie) == null) {
viewResolver.renderPage(httpResponse, HttpStatus.OK, LOGIN_PAGE);
ViewResolver.renderPage(response, HttpStatus.OK, LOGIN_PAGE);
Copy link

Choose a reason for hiding this comment

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

저도 이 부분을 좀 고민하긴 했는데, 만약 유저가 로그인 된 상태에서 login.html로 직접 접근하는 경우는 어떻게 처리해야할까요? /login으로 들어올 경우는 이렇게 쿠키 확인을 해주지만, login.html로 직접 들어가는 경우는 따로 확인을 해줘야하지 않을까 싶기도 해요..!

Copy link
Member Author

Choose a reason for hiding this comment

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

설명해주신 접근은 전혀 생각지도 못했네요 ! 에코의 리뷰를 보고 저도 고민해볼 수 있었어요 ㅎㅎ

말씀하신대로 login.html 페이지에 접속했을 때 쿠키를 확인하고 쿠키가 존재한다면 리다이렉트하는 방법도 좋은 것 같아요 👍

저는 조금 다른 관점으로도 생각해본 것 같아요. 클라이언트가 파일을 요청하는 경우는 진짜 해당 정적 파일이 필요한 상황일 수도 있겠다는 생각이 들었어요. 로그인을 위해 페이지를 보여주는 것이 아닌 해당 페이지의 리소스가 필요한 상황? (마땅한 예시는 떠오르지가 않네요)에서 해당 요청을 통해 login.html 파일을 받아갈 수도 있을 것 같았어요. 이때 쿠기가 존재해서 리다이렉트가 되버리면 클라이언트가 예상했던 html 파일 응답을 받지 못할 것 같아요.

사실 이러한 고민이 발생하는 이유는 서버에서 JSON 형식의 응답만 제공하는 것이 아닌 정적 파일까지 제공하고 있기에, 다시 말해서 서버 사이드 렌더링이 발생하고 있기에 발생하는 문제라 생각이 들어요 🤓

Copy link

Choose a reason for hiding this comment

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

다즐의 의견 잘 들었습니다~! 저도 또하나 생각난 방법은 아예 html의 경우에는 정적 파일 경로로 접근할 수 없도록 하는것도 방법일 것 같네요. 사실 유저 입장에서는 .html로 접근한다는게 자연스럽지 않아서이기 때문인데요, 따라서 .html로 들어오는 접근의 경우 아예 막고 /login 경로로 들어왔을때에만 html 컨텐트를 응답으로 주는 것도 좋을 것 같아요!

return;
}
httpResponse.setStatus(HttpStatus.FOUND);
httpResponse.setLocation(SLASH + LOGIN_SUCCESS_PAGE);
response.setStatus(HttpStatus.FOUND);
response.setLocation(SLASH + LOGIN_SUCCESS_PAGE);
}

@Override
public void doPost(final HttpRequest httpRequest, final HttpResponse httpResponse) throws IOException {
final Map<String, String> body = httpRequest.getBody(ContentType.APPLICATION_JSON);
public void doPost(final HttpRequest request, final HttpResponse response) throws Exception {
final Map<String, String> body = request.getBody(ContentType.APPLICATION_JSON);
final String account = body.get(ACCOUNT);
final String password = body.get(PASSWORD);
if (account == null || password == null) {
log.warn("Account Or Password Not Exist");
httpResponse.setStatus(HttpStatus.FOUND);
httpResponse.setLocation(SLASH + LOGIN_FAIL_PAGE);
response.setStatus(HttpStatus.FOUND);
response.setLocation(SLASH + LOGIN_FAIL_PAGE);
return;
}

final Optional<User> findUser = InMemoryUserRepository.findByAccount(account);
if (findUser.isEmpty() || !findUser.get().checkPassword(password)) {
log.warn("Login Fail");
httpResponse.setStatus(HttpStatus.FOUND);
httpResponse.setLocation(SLASH + LOGIN_FAIL_PAGE);
response.setStatus(HttpStatus.FOUND);
response.setLocation(SLASH + LOGIN_FAIL_PAGE);
return;
}
loginSuccess(httpResponse, findUser.get());
loginSuccess(response, findUser.get());
}

private void loginSuccess(final HttpResponse httpResponse, final User user) {
private void loginSuccess(final HttpResponse response, final User user) {
final Session session = new Session(UUID.randomUUID().toString());
session.setAttribute("user", user);
SessionManager.add(session);
httpResponse.addCookie(JSESSIONID, session.getId());
httpResponse.setStatus(HttpStatus.FOUND);
httpResponse.setLocation(SLASH + LOGIN_SUCCESS_PAGE);
response.addCookie(JSESSIONID, session.getId());
response.setStatus(HttpStatus.FOUND);
response.setLocation(SLASH + LOGIN_SUCCESS_PAGE);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package org.apache.coyote.handle.handler;
package nextstep.jwp.controller;

import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import nextstep.jwp.db.InMemoryUserRepository;
import nextstep.jwp.handle.ViewResolver;
import nextstep.jwp.model.User;
import org.apache.coyote.common.ContentType;
import org.apache.coyote.common.HttpStatus;
Expand All @@ -16,9 +16,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RegisterHandler implements Handler {
public class RegisterController extends AbstractController {

private static final Logger log = LoggerFactory.getLogger(RegisterHandler.class);
private static final Logger log = LoggerFactory.getLogger(RegisterController.class);
private static final String SLASH = File.separator;
private static final String REGISTER_PAGE = "register.html";
private static final String REGISTER_SUCCESS_PAGE = "index.html";
Expand All @@ -27,43 +27,51 @@ public class RegisterHandler implements Handler {
private static final String PASSWORD = "password";
private static final String EMAIL = "email";
private static final String JSESSIONID = "JSESSIONID";
private static final RegisterController registerController = new RegisterController();

private RegisterController() {
}

public static RegisterController getInstance() {
return registerController;
}

@Override
public void doGet(final HttpRequest httpRequest, final HttpResponse httpResponse) throws IOException {
viewResolver.renderPage(httpResponse, HttpStatus.OK, REGISTER_PAGE);
public void doGet(final HttpRequest request, final HttpResponse response) throws Exception {
ViewResolver.renderPage(response, HttpStatus.OK, REGISTER_PAGE);
}

@Override
public void doPost(final HttpRequest httpRequest, final HttpResponse httpResponse) throws IOException {
final Map<String, String> body = httpRequest.getBody(ContentType.APPLICATION_JSON);
public void doPost(final HttpRequest request, final HttpResponse response) throws Exception {
final Map<String, String> body = request.getBody(ContentType.APPLICATION_JSON);
final String account = body.get(ACCOUNT);
final String password = body.get(PASSWORD);
final String email = body.get(EMAIL);
if (account == null || password == null || email == null) {
log.warn("Account Or Password Or Email Not Exist");
httpResponse.setStatus(HttpStatus.FOUND);
httpResponse.setLocation(SLASH + REGISTER_FAIL_PAGE);
response.setStatus(HttpStatus.FOUND);
response.setLocation(SLASH + REGISTER_FAIL_PAGE);
return;
}

final Optional<User> findUser = InMemoryUserRepository.findByAccount(body.get(ACCOUNT));
if (findUser.isPresent()) {
log.warn("Registered Account");
httpResponse.setStatus(HttpStatus.FOUND);
httpResponse.setLocation(SLASH + REGISTER_FAIL_PAGE);
response.setStatus(HttpStatus.FOUND);
response.setLocation(SLASH + REGISTER_FAIL_PAGE);
return;
}
final User user = new User(account, password, email);
registerSuccess(httpResponse, user);
registerSuccess(response, user);
}

private void registerSuccess(final HttpResponse httpResponse, final User user) {
private void registerSuccess(final HttpResponse response, final User user) {
InMemoryUserRepository.save(user);
final Session session = new Session(UUID.randomUUID().toString());
session.setAttribute("user", user);
SessionManager.add(session);
httpResponse.addCookie(JSESSIONID, session.getId());
httpResponse.setStatus(HttpStatus.FOUND);
httpResponse.setLocation(SLASH + REGISTER_SUCCESS_PAGE);
response.addCookie(JSESSIONID, session.getId());
response.setStatus(HttpStatus.FOUND);
response.setLocation(SLASH + REGISTER_SUCCESS_PAGE);
}
}
18 changes: 13 additions & 5 deletions tomcat/src/main/java/nextstep/jwp/db/InMemoryUserRepository.java
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
package nextstep.jwp.db;

import nextstep.jwp.model.User;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import nextstep.jwp.model.User;

public class InMemoryUserRepository {

private static final Map<String, User> database = new ConcurrentHashMap<>();
private static final AtomicLong auto_increment = new AtomicLong(1);

static {
final User user = new User(1L, "gugu", "password", "[email protected]");
final User user = new User(auto_increment.getAndIncrement(), "gugu", "password", "[email protected]");
database.put(user.getAccount(), user);
}

public static void save(User user) {
database.put(user.getAccount(), user);
final User saveUser = new User(
auto_increment.getAndIncrement(),
user.getAccount(),
user.getPassword(),
user.getEmail()
);
database.put(saveUser.getAccount(), saveUser);
}

public static Optional<User> findByAccount(String account) {
return Optional.ofNullable(database.get(account));
}

private InMemoryUserRepository() {}
private InMemoryUserRepository() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package nextstep.jwp.exception;

public class HandlerMappingException extends RuntimeException {

public HandlerMappingException() {
super("Handler Mapping Fail");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package nextstep.jwp.exception;

public class NotFoundHandlerException extends RuntimeException {

public NotFoundHandlerException() {
super("Match Handler Not Found");
}
}
28 changes: 28 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/handle/HandlerMapping.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package nextstep.jwp.handle;

import java.util.HashMap;
import java.util.Map;
import nextstep.jwp.exception.NotFoundHandlerException;
import nextstep.jwp.handle.mapping.MappingInfo;
import org.apache.coyote.request.HttpRequest;

public class HandlerMapping {

private final Map<MappingInfo, HandlerMethod> handlerMapping = new HashMap<>();

public HandlerMapping() {
}

public void addMappingInfo(final MappingInfo mappingInfo, final HandlerMethod handlerMethod) {
handlerMapping.put(mappingInfo, handlerMethod);
}

public HandlerMethod getHandlerMethod(final HttpRequest request) {
return handlerMapping.keySet()
.stream()
.filter(mappingInfo -> mappingInfo.support(request))
.findFirst()
.map(handlerMapping::get)
.orElseThrow(NotFoundHandlerException::new);
}
}
23 changes: 23 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/handle/HandlerMethod.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package nextstep.jwp.handle;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import nextstep.jwp.controller.Controller;
import org.apache.coyote.request.HttpRequest;
import org.apache.coyote.response.HttpResponse;

public class HandlerMethod {

private final Controller controller;
private final Method method;

public HandlerMethod(final Controller controller, final Method method) {
this.controller = controller;
this.method = method;
}

public void invokeMethod(final HttpRequest request, final HttpResponse response)
throws InvocationTargetException, IllegalAccessException {
method.invoke(controller, request, response);
}
}
Loading