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단계] 채채(신채원) 미션 제출합니다 #485

Merged
merged 12 commits into from
Sep 14, 2023
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@
- [x] Session 구현하기
- [x] 로그인된 상태에서 /login 페이지에 HTTP GET method로 접근하면 이미 로그인한 상태니 index.html 페이지로 리다이렉트 처리한다.
- [x] 로그인에 성공하면 Session 객체의 값으로 User 객체를 저장해보자.

## step3
- [x] 요청 응답의 구조를 공부하여 코드를 리팩터링 한다.
- [x] 스레드풀을 적용시킨다.
11 changes: 5 additions & 6 deletions tomcat/src/main/java/org/apache/catalina/Manager.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package org.apache.catalina;

import jakarta.servlet.http.HttpSession;

import java.io.IOException;
import org.apache.catalina.session.HttpSession;

/**
* A <b>Manager</b> manages the pool of Sessions that are associated with a
Expand All @@ -27,9 +26,9 @@ public interface Manager {
/**
* Add this Session to the set of active Sessions for this Manager.
*
* @param session Session to be added
* @param httpSession Session to be added
*/
void add(HttpSession session);
void add(HttpSession httpSession);

/**
* Return the active Session, associated with this Manager, with the
Expand All @@ -50,7 +49,7 @@ public interface Manager {
/**
* Remove this Session from the active Sessions for this Manager.
*
* @param session Session to be removed
* @param httpSession Session to be removed
*/
void remove(HttpSession session);
void remove(HttpSession httpSession);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package org.apache.coyote.http11;
package org.apache.catalina;

import nextstep.jwp.LoginHandler;
import nextstep.jwp.model.User;
import org.apache.catalina.session.HttpSession;
import org.apache.catalina.session.SessionManager;
import org.apache.coyote.http11.request.HttpMethod;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.*;
Expand All @@ -10,32 +14,52 @@

public enum RequestMappingHandler {

STRING(RequestMappingHandler::isStringGetUrl, new HelloResponseMaker()),
FILE(RequestMappingHandler::isFileGetUrl, new FileGetResponseMaker()),
LOGIN_GET(RequestMappingHandler::isLoginGetUrl, new LoginGetResponseMaker()),
LOGIN_POST(RequestMappingHandler::isLoginPostUrl, new LoginPostResponseMaker()),
REGISTER_GET(RequestMappingHandler::isRegisterGetUrl, new RegisterGetResponseMaker()),
REGISTER_POST(RequestMappingHandler::isRegisterPostUrl, new RegisterPostResponseMaker());
STRING(RequestMappingHandler::isStringGetUrl, new HelloController()),
FILE(RequestMappingHandler::isFileGetUrl, new FileGetController()),
LOGIN_GET(RequestMappingHandler::isLoginGetUrl, new LoginGetController()),
LOGIN_POST(RequestMappingHandler::isLoginPostUrl, new LoginPostController()),
REGISTER_GET(RequestMappingHandler::isRegisterGetUrl, new RegisterGetController()),
REGISTER_POST(RequestMappingHandler::isRegisterPostUrl, new RegisterPostController());

private static final Pattern FILE_REGEX = Pattern.compile(".+\\.(html|css|js|ico)");

private final BiPredicate<String, HttpMethod> condition;
private final ResponseMaker responseMaker;
private final Controller controller;

RequestMappingHandler(final BiPredicate<String, HttpMethod> condition, final ResponseMaker responseMaker) {
RequestMappingHandler(final BiPredicate<String, HttpMethod> condition, final Controller controller) {
this.condition = condition;
this.responseMaker = responseMaker;
this.controller = controller;
}

public static ResponseMaker findResponseMaker(final HttpRequest request) {
String resourcePath = request.getRequestLine().getRequestUrl();
HttpMethod requestMethod = request.getRequestLine().getHttpMethod();
public static Controller findController(final HttpRequest request) {
final String resourcePath = request.getRequestLine().getRequestUrl();
final HttpMethod requestMethod = request.getRequestLine().getHttpMethod();

return Arrays.stream(values())
final Controller findController = Arrays.stream(values())
.filter(value -> value.condition.test(resourcePath, requestMethod))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("잘못된 url 요청입니다."))
.getResponseMaker();
.getController();

if (findController instanceof LoginPostController
&& (!request.hasJSessionId())) {
addSession(request);
}
if (findController instanceof LoginGetController
&& request.hasJSessionId()
&& SessionManager.isExist(request.getJSessionId())) {
request.setHasValidatedSessionTrue();
}
return findController;
}

private static void addSession(final HttpRequest request) {
final SessionManager sessionManager = new SessionManager();
final LoginHandler loginHandler = new LoginHandler();
final User user = loginHandler.getUser(request.getRequestBody());
final HttpSession httpSession = new HttpSession("user", user);
sessionManager.add(httpSession);
request.addJSessionId(httpSession.getId());
}

public static boolean isFileGetUrl(final String resourcePath, final HttpMethod requestMethod) {
Expand All @@ -62,8 +86,8 @@ public static boolean isRegisterPostUrl(final String requestUrl, final HttpMetho
return requestUrl.startsWith("/register") && requestMethod == HttpMethod.POST;
}

public ResponseMaker getResponseMaker() {
return responseMaker;
public Controller getController() {
return controller;
}

}
46 changes: 43 additions & 3 deletions tomcat/src/main/java/org/apache/catalina/connector/Connector.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,39 @@
import java.io.UncheckedIOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Connector implements Runnable {

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

private static final int DEFAULT_PORT = 8080;
private static final int DEFAULT_ACCEPT_COUNT = 100;
private static final int DEFAULT_MAX_THREAD = 250;

private final ServerSocket serverSocket;
private final ExecutorService executorService;
private boolean stopped;

public Connector() {
this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT);
this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, DEFAULT_MAX_THREAD);
}

public Connector(final int port, final int acceptCount) {
public Connector(final int port,
final int acceptCount,
final int maxTread) {
this.serverSocket = createServerSocket(port, acceptCount);
this.stopped = false;
this.executorService = new ThreadPoolExecutor(
checkMaxThread(maxTread),
checkMaxThread(maxTread),
0,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(acceptCount)
);
}

private ServerSocket createServerSocket(final int port, final int acceptCount) {
Expand All @@ -38,6 +53,10 @@ private ServerSocket createServerSocket(final int port, final int acceptCount) {
}
}

private int checkMaxThread(int maxThread) {
return Math.max(maxThread, DEFAULT_MAX_THREAD);
}

public void start() {
var thread = new Thread(this);
thread.setDaemon(true);
Expand Down Expand Up @@ -67,11 +86,12 @@ private void process(final Socket connection) {
return;
}
var processor = new Http11Processor(connection);
new Thread(processor).start();
executorService.execute(processor);
}

public void stop() {
stopped = true;
gracefulShutdown();

Choose a reason for hiding this comment

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

👍

try {
serverSocket.close();
} catch (IOException e) {
Expand All @@ -92,4 +112,24 @@ private int checkPort(final int port) {
private int checkAcceptCount(final int acceptCount) {
return Math.max(acceptCount, DEFAULT_ACCEPT_COUNT);
}

private void gracefulShutdown() {
executorService.shutdown(); // Disable new tasks from being submitted
try {
// Wait a while for existing tasks to terminate
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
executorService.shutdownNow(); // Cancel currently executing tasks
// Wait a while for tasks to respond to being cancelled
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
log.error("Pool did not terminate");
}
}
} catch (InterruptedException ie) {
// (Re-)Cancel if current thread also interrupted
executorService.shutdownNow();
// Preserve interrupt status
Thread.currentThread().interrupt();
}
}

}
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package org.apache.coyote.http11.session;
package org.apache.catalina.session;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public class Session {
public class HttpSession {

private final String id;
private final Map<String, Object> values = new HashMap<>();
private final Map<String, Object> values;

public Session() {
public HttpSession(final String name, final Object value) {
this.id = UUID.randomUUID().toString();
values = new ConcurrentHashMap<>();
addAttribute(name, value);
}

public String getId() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.apache.catalina.session;

import org.apache.catalina.Manager;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class SessionManager implements Manager {

// static!
private static final Map<String, HttpSession> sessions = new ConcurrentHashMap<>();

@Override
public void add(HttpSession httpSession) {
sessions.put(httpSession.getId(), httpSession);
}

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

@Override
public void remove(HttpSession httpSession) {
sessions.remove(httpSession.getId());
}

public static boolean isExist(final String id) {
return sessions.containsKey(id);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ private boolean isSameExtension(String extension) {
public String getContentType() {
return contentType;
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package org.apache.coyote.http11;

import nextstep.jwp.exception.UncheckedServletException;
import org.apache.catalina.RequestMappingHandler;
import org.apache.coyote.Processor;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.ResponseMaker;
import org.apache.coyote.http11.response.Controller;
import org.apache.coyote.http11.response.HttpResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -35,11 +37,12 @@ public void process(final Socket connection) {
final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {

final HttpRequest httpRequest = new HttpRequest(reader);
final HttpResponse httpResponse = new HttpResponse();

final ResponseMaker responseMaker = RequestMappingHandler.findResponseMaker(httpRequest);
final String response = responseMaker.createResponse(httpRequest);
final Controller controller = RequestMappingHandler.findController(httpRequest);
controller.service(httpRequest, httpResponse);

outputStream.write(response.getBytes());
outputStream.write(httpResponse.getResponse().getBytes());
outputStream.flush();

} catch (IOException | UncheckedServletException e) {
Expand All @@ -48,4 +51,5 @@ public void process(final Socket connection) {
throw new RuntimeException(e);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ public class HttpRequest {
private final RequestLine requestLine;
private final RequestHeader requestHeader;
private final String requestBody;
private boolean hasValidatedSession;

public HttpRequest(final BufferedReader reader) throws IOException, URISyntaxException {
final List<String> lines = readAllLines(reader);

requestLine = createRequestLine(lines);
requestHeader = createRequestHeader(lines);
requestBody = addRequestBody(lines, reader);
hasValidatedSession = false;
}

private static List<String> readAllLines(final BufferedReader reader) {
Expand Down Expand Up @@ -70,10 +72,18 @@ private int getContentLength(List<String> lines) {
return 0;
}

public void addJSessionId(final String jSessionId) {
requestHeader.addSession(jSessionId);
}

public boolean hasJSessionId() {
return requestHeader.hasJSessionId();
}

public void setHasValidatedSessionTrue() {
this.hasValidatedSession = true;
}

public String getJSessionId() {
return requestHeader.getJSessionId();
}
Expand All @@ -89,4 +99,8 @@ public RequestHeader getRequestHeader() {
public String getRequestBody() {
return requestBody;
}

public boolean isHasValidatedSession() {
return hasValidatedSession;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,8 @@ public String getJSessionId() {
final String[] parts = cookie.split("=");
return parts[1];
}

public void addSession(String jSessionId){
headers.put(COOKIE, "JSessionId=" + jSessionId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.apache.coyote.http11.response;

import org.apache.coyote.http11.request.HttpRequest;

import java.io.IOException;

public interface Controller {
void service(final HttpRequest request,
final HttpResponse response) throws IOException;
}
Loading