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단계] 디노(신종화) 미션 제출합니다. #441

Merged
merged 16 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 6 additions & 0 deletions tomcat/src/main/java/nextstep/Application.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package nextstep;

import nextstep.jwp.HomeController;
import nextstep.jwp.LoginController;
import nextstep.jwp.RegisterController;
import org.apache.catalina.startup.Tomcat;

public class Application {

public static void main(String[] args) {
final var tomcat = new Tomcat();
tomcat.addController("/", new HomeController());
tomcat.addController("/login", new LoginController());
tomcat.addController("register", new RegisterController());
tomcat.start();
}
}
31 changes: 31 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/HomeController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package nextstep.jwp;

import java.io.IOException;
import org.apache.catalina.AbstractController;
import org.apache.coyote.http11.ExtensionType;
import org.apache.coyote.http11.HttpStatusCode;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;
import org.apache.coyote.http11.response.ResponseBody;
import org.apache.coyote.http11.response.ResponseHeader;
import org.apache.coyote.http11.response.StatusLine;

public class HomeController extends AbstractController {

private static final String DEFAULT_CONTENT = "Hello world!";

@Override
protected void doPost(final HttpRequest request, final HttpResponse response) throws IOException {
setResponse(response, "/500", HttpStatusCode.INTERNAL_SERVER_ERROR);
}

@Override
protected void doGet(final HttpRequest request, final HttpResponse response) throws IOException {
final ResponseBody responseBody = ResponseBody.of(ExtensionType.HTML.getExtension(), DEFAULT_CONTENT);
final ResponseHeader responseHeader = ResponseHeader.from(responseBody);

response.setStatusLine(new StatusLine(HttpStatusCode.OK));
response.setResponseHeader(responseHeader);
response.setResponseBody(responseBody);
}
}
78 changes: 78 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/LoginController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package nextstep.jwp;

import java.io.IOException;
import java.util.UUID;
import nextstep.jwp.db.InMemoryUserRepository;
import nextstep.jwp.exception.LoginException;
import nextstep.jwp.model.User;
import org.apache.catalina.AbstractController;
import org.apache.catalina.SessionManager;
import org.apache.coyote.http11.HttpCookie;
import org.apache.coyote.http11.HttpStatusCode;
import org.apache.coyote.http11.Session;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;

public class LoginController extends AbstractController {

private static final SessionManager sessionManager = new SessionManager();

private static final String JSESSIONID = "JSESSIONID";
private static final String INDEX = "/index";
private static final String ACCOUNT = "account";
private static final String PASSWORD = "password";
private static final String COOKIE = "Cookie";
private static final String USER = "user";
private static final String UNAUTHORIZED = "/401";
private static final String BAD_REQUEST = "/400";

@Override
protected void doPost(final HttpRequest request, final HttpResponse response) throws IOException {
try {
final String userName = request.getRequestBody().get(ACCOUNT);
final String password = request.getRequestBody().get(PASSWORD);

if (userName.isBlank() || password.isBlank()) {
setResponse(response, BAD_REQUEST, HttpStatusCode.BAD_REQUEST);
return;
}

final User user = InMemoryUserRepository.findByAccount(userName)
.orElseThrow(LoginException::new);

if (user.checkPassword(password)) {
log.info("user : " + user);
final HttpCookie cookie = HttpCookie.from(request.getRequestHeaders().geHeaderValue(COOKIE));
checkSession(user, cookie);
setResponse(response, INDEX, HttpStatusCode.FOUND);
response.setCookie(cookie);
return;
}
throw new LoginException();
} catch (LoginException exception) {
setResponse(response, UNAUTHORIZED, HttpStatusCode.UNAUTHORIZED);
}
}

private void checkSession(final User user, final HttpCookie cookie) {
if (!cookie.contains(JSESSIONID)) {
final Session session = new Session(UUID.randomUUID().toString());
session.setAttribute(USER, user);
sessionManager.add(session);
cookie.setCookie(JSESSIONID, session.getId());
}
}

@Override
protected void doGet(final HttpRequest request, final HttpResponse response) throws IOException {
final HttpCookie cookie = HttpCookie.from(request.getRequestHeaders().geHeaderValue(COOKIE));
final String requestResource = request.getRequestPath().getResource();

if (cookie.contains(JSESSIONID)) {
setResponse(response, INDEX, HttpStatusCode.FOUND);
response.setCookie(cookie);
return;
}
setResponse(response, requestResource, HttpStatusCode.OK);
}
}
48 changes: 48 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/RegisterController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package nextstep.jwp;

import java.io.IOException;
import java.util.Map;
import nextstep.jwp.db.InMemoryUserRepository;
import nextstep.jwp.model.User;
import org.apache.catalina.AbstractController;
import org.apache.coyote.http11.HttpStatusCode;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;

public class RegisterController extends AbstractController {

private static final String BAD_REQUEST = "/400";
private static final String CONFLICT = "/409";
private static final String INDEX = "/index";
private static final String REGISTER = "/register";
private static final String ACCOUNT = "account";
private static final String PASSWORD = "password";
private static final String EMAIL = "email";

@Override
protected void doPost(final HttpRequest request, final HttpResponse response) throws IOException {
final Map<String, String> requestBody = request.getRequestBody();

final String account = requestBody.get(ACCOUNT);
final String password = requestBody.get(PASSWORD);
final String email = requestBody.get(EMAIL);

if (account.isBlank() || password.isBlank() || email.isBlank()) {
setResponse(response, BAD_REQUEST, HttpStatusCode.BAD_REQUEST);
return;
}

if (InMemoryUserRepository.checkExistingId(account)) {
setResponse(response, CONFLICT, HttpStatusCode.CONFLICT);
return;
}
final User user = new User(account, password, email);
InMemoryUserRepository.save(user);
setResponse(response, INDEX, HttpStatusCode.OK);
}

@Override
protected void doGet(final HttpRequest request, final HttpResponse response) throws IOException {
setResponse(response, REGISTER, HttpStatusCode.OK);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@
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 AtomicLong id = new AtomicLong(1L);

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

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

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

public static Optional<User> findByAccount(String account) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package nextstep.jwp.exception;

public class NotAllowedExtensionException extends RuntimeException {

public NotAllowedExtensionException() {
super("해당하는 Extension이 존재하지 않습니다.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
public class NotAllowedMethodException extends RuntimeException {

public NotAllowedMethodException() {
super("해당하는 HttpMethod가 존재하지 않습니다.");
super("해당하는 Method가 존재하지 않습니다.");
}
}
8 changes: 8 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/model/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ public String getAccount() {
return account;
}

public String getPassword() {
return password;
}

public String getEmail() {
return email;
}

@Override
public String toString() {
return "User{" +
Expand Down
48 changes: 48 additions & 0 deletions tomcat/src/main/java/org/apache/catalina/AbstractController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.apache.catalina;

import java.io.IOException;
import org.apache.coyote.http11.FileExtractor;
import org.apache.coyote.http11.HttpStatusCode;
import org.apache.coyote.http11.request.HttpMethod;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;
import org.apache.coyote.http11.response.ResponseBody;
import org.apache.coyote.http11.response.ResponseHeader;
import org.apache.coyote.http11.response.StatusLine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class AbstractController implements Controller {

protected static final Logger log = LoggerFactory.getLogger(AbstractController.class);

@Override
public void service(final HttpRequest request, final HttpResponse response) throws IOException {
if (request.getHttpMethod().equals(HttpMethod.GET)) {
doGet(request, response);
return;
}
if (request.getHttpMethod().equals(HttpMethod.POST)) {
doPost(request, response);
return;
}
throw new UnsupportedOperationException();

Choose a reason for hiding this comment

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

제가 제안드린 방향을 고려해서 해주신 것 같아요!! 👍🏻
다만 실제 동작은 Mapping된 컨트롤러에서 지원하지 않는 메소드면
405 method not allowed가 응답되기 때문에 405가 뜨도록 리팩토링해보는 것을 제안했던 것이었습니다!!

그래도 잘 고려해주셔서 감사합니다 ㅎㅎ
미션이니 이 부분은 언급만 하고 머지하도록 하겠습니다!! 👍🏻👍🏻👍🏻

}

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

protected abstract void doGet(final HttpRequest request, final HttpResponse response) throws IOException;

protected void setResponse(
final HttpResponse response,
final String resource,
final HttpStatusCode statusCode
) throws IOException {
final ResponseBody responseBody = FileExtractor.extractFile(resource);
final ResponseHeader responseHeader = ResponseHeader.from(responseBody);

response.setStatusLine(new StatusLine(statusCode));
response.setResponseHeader(responseHeader);
response.setResponseBody(responseBody);
}
Comment on lines +36 to +47

Choose a reason for hiding this comment

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

setResponse로 이렇게 Response를 설정해주고 Processor에서 Response를 사용하는 군요!
배워갑니다,, 👍🏻👍🏻👍🏻

}
10 changes: 10 additions & 0 deletions tomcat/src/main/java/org/apache/catalina/Controller.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.apache.catalina;

import java.io.IOException;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;

public interface Controller {

void service(final HttpRequest request, final HttpResponse response) throws IOException;
}
23 changes: 23 additions & 0 deletions tomcat/src/main/java/org/apache/catalina/DefaultController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.apache.catalina;

import java.io.IOException;
import org.apache.coyote.http11.HttpStatusCode;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;

public class DefaultController extends AbstractController {

private static final String INTERNAL_SERVER_ERROR = "/500";

@Override
protected void doPost(final HttpRequest request, final HttpResponse response) throws IOException {
setResponse(response, INTERNAL_SERVER_ERROR, HttpStatusCode.INTERNAL_SERVER_ERROR);
}

@Override
protected void doGet(final HttpRequest request, final HttpResponse response) throws IOException {
final String resource = request.getRequestPath().getResource();

setResponse(response, resource, HttpStatusCode.OK);
}
}
19 changes: 19 additions & 0 deletions tomcat/src/main/java/org/apache/catalina/RequestMapping.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.apache.catalina;

import java.util.HashMap;
import java.util.Map;
import org.apache.coyote.http11.request.HttpRequest;

public class RequestMapping {

private final Map<String, Controller> mapper = new HashMap<>();

public void put(final String path, final Controller controller) {
this.mapper.put(path, controller);
}

public Controller getController(final HttpRequest request) {
final String resource = request.getRequestPath().getResource();
return mapper.getOrDefault(resource, new DefaultController());
}
}
25 changes: 25 additions & 0 deletions tomcat/src/main/java/org/apache/catalina/SessionManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.apache.catalina;

Choose a reason for hiding this comment

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

sessionManager 어디에서 선언해야 하는가?

이 부분에 대해서 Apache의 coyote, catalina 패키지 중에 어떤 패키지에 둘지 고민하신 것 같아요!

찾아보니 톰캣의 catalina에서 Session 관리를 한다고 하더라구요!
그래서 catalina 패키지가 맞는 것 같습니다!!

실제 코드에서 구구가 만든 Manager를 구현하고 있는데 catalina 패키지에 만들어 놓으셨네요!

톰캣의 세션 관리 기능을 구현한 Manager 구성 요소 공식문서


import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.coyote.http11.Session;

public class SessionManager implements Manager {

private static final Map<String, Session> sessions = new ConcurrentHashMap<>();

@Override
public void add(final Session session) {
sessions.put(session.getId(), session);
}

@Override
public Session findSession(final String id) {
return sessions.get(id);
}

@Override
public void remove(final Session session) {
sessions.remove(session.getId());
}
}
Loading