-
Notifications
You must be signed in to change notification settings - Fork 309
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단계] 조이(김성연) 미션 제출합니다. #473
Changes from all commits
aa3dd74
1074eca
85c96fa
4ffb6f7
0bb79b4
59e023f
3bb9396
9674e73
0922f5d
c1870e8
7d6ae10
838dd4a
aa653ca
7bd3ef3
a814c60
c5ff7bf
c85a3e9
42235a3
ecbff03
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,5 @@ | ||
package org.apache.catalina; | ||
|
||
import jakarta.servlet.http.HttpSession; | ||
|
||
import java.io.IOException; | ||
|
||
/** | ||
* A <b>Manager</b> manages the pool of Sessions that are associated with a | ||
|
@@ -29,7 +26,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 | ||
|
@@ -39,18 +36,16 @@ public interface Manager { | |
* | ||
* @exception IllegalStateException if a new session cannot be | ||
* instantiated for any reason | ||
* @exception IOException if an input/output error occurs while | ||
* processing this request | ||
* | ||
* @return the request session or {@code null} if a session with the | ||
* requested ID could not be found | ||
*/ | ||
HttpSession findSession(String id) throws IOException; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
조이는 이번에 구구 코치가 Controller interface의 시그니처를 유지하라는 이야기를 해주셨잖아요. 이 부분도 마찬가지로 throws IOException이 기본적으로 붙어져있는 이유에 대해 생각해보신 적이 있으신지 궁금합니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저도 궁금해서 찾아봤는데, 정확한 이유는 잘 모르겠네요 ㅠ 실제 톰캣에서 적용되는 콩하나는 어떻게 생각하시나요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 그런 경우도 있겠군요! 저도 단순히 Manager를 구현한 클래스 중 어딘가에서 I/O 작업을 할 수 있으니 조이 덕분에 하나 더 알아갑니다~ |
||
Session findSession(String id); | ||
|
||
/** | ||
* Remove this Session from the active Sessions for this Manager. | ||
* | ||
* @param session Session to be removed | ||
*/ | ||
void remove(HttpSession session); | ||
void remove(Session session); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package org.apache.catalina; | ||
|
||
import static org.apache.catalina.controller.StaticResourceUri.DEFAULT_PAGE; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
import org.apache.catalina.controller.Controller; | ||
import org.apache.catalina.controller.DefaultController; | ||
import org.apache.catalina.controller.LoginController; | ||
import org.apache.catalina.controller.RegisterController; | ||
import org.apache.catalina.controller.RootController; | ||
import org.apache.catalina.controller.StaticResourceController; | ||
import org.apache.coyote.http11.request.HttpRequest; | ||
|
||
public class RequestMapping { | ||
|
||
private static final String ROOT_REQUEST_URL = "/"; | ||
private static final String LOGIN_REQUEST_URL = "/login"; | ||
private static final String REGISTER_REQUEST_URL = "/register"; | ||
private static final Map<String, Controller> controllers = new HashMap<>(); | ||
|
||
static { | ||
controllers.put(ROOT_REQUEST_URL, new RootController()); | ||
controllers.put(DEFAULT_PAGE.getUri(), new DefaultController()); | ||
controllers.put(LOGIN_REQUEST_URL, new LoginController()); | ||
controllers.put(REGISTER_REQUEST_URL, new RegisterController()); | ||
} | ||
|
||
public Controller getController(final HttpRequest request) { | ||
final String path = request.getUri().getPath(); | ||
|
||
if (controllers.containsKey(path)) { | ||
return controllers.get(path); | ||
} | ||
|
||
return new StaticResourceController(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package org.apache.catalina; | ||
|
||
import java.util.Map; | ||
import java.util.UUID; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
public class Session { | ||
|
||
private final String id; | ||
private final Map<String, Object> values = new ConcurrentHashMap<>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
good~~ |
||
|
||
public Session() { | ||
this(UUID.randomUUID().toString()); | ||
} | ||
|
||
public Session(final String id) { | ||
this.id = id; | ||
} | ||
|
||
public String getId() { | ||
return this.id; | ||
} | ||
|
||
public Object getAttribute(final String name) { | ||
return this.values.get(name); | ||
} | ||
|
||
public void setAttribute(final String name, final Object value) { | ||
values.put(name, value); | ||
} | ||
|
||
public void removeAttribute(final String name) { | ||
values.remove(name); | ||
} | ||
|
||
public void invalidate() { | ||
values.clear(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package org.apache.catalina; | ||
|
||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
public class SessionManger 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()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,35 @@ | ||
package org.apache.catalina.connector; | ||
|
||
import org.apache.coyote.http11.Http11Processor; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.io.IOException; | ||
import java.io.UncheckedIOException; | ||
import java.net.ServerSocket; | ||
import java.net.Socket; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.Executors; | ||
import org.apache.coyote.http11.Http11Processor; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
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_THREADS = 250; | ||
|
||
private final ServerSocket serverSocket; | ||
private boolean stopped; | ||
private final ExecutorService executorService; | ||
|
||
public Connector() { | ||
this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT); | ||
this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, DEFAULT_MAX_THREADS); | ||
} | ||
|
||
public Connector(final int port, final int acceptCount) { | ||
public Connector(final int port, final int acceptCount, final int maxThreads) { | ||
this.serverSocket = createServerSocket(port, acceptCount); | ||
this.stopped = false; | ||
this.executorService = Executors.newFixedThreadPool(maxThreads); | ||
} | ||
|
||
private ServerSocket createServerSocket(final int port, final int acceptCount) { | ||
|
@@ -67,7 +71,7 @@ private void process(final Socket connection) { | |
return; | ||
} | ||
var processor = new Http11Processor(connection); | ||
new Thread(processor).start(); | ||
executorService.submit(processor); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. submit할게요~ |
||
} | ||
|
||
public void stop() { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package org.apache.catalina.controller; | ||
|
||
import static org.apache.coyote.http11.response.ResponseHeaderType.ALLOW; | ||
|
||
import org.apache.coyote.http11.request.HttpRequest; | ||
import org.apache.coyote.http11.request.HttpRequestMethod; | ||
import org.apache.coyote.http11.response.HttpResponse; | ||
import org.apache.coyote.http11.response.HttpStatusCode; | ||
|
||
public abstract class AbstractController implements Controller { | ||
|
||
static final String RESOURCE_DIRECTORY = "static"; | ||
public static final String ALLOW_HEADER_DELIMITER = ", "; | ||
|
||
@Override | ||
public void service(HttpRequest request, HttpResponse response) throws Exception { | ||
HttpRequestMethod requestMethod = request.getMethod(); | ||
|
||
if (requestMethod == HttpRequestMethod.POST) { | ||
doPost(request, response); | ||
return; | ||
} | ||
|
||
if (requestMethod == HttpRequestMethod.GET) { | ||
doGet(request, response); | ||
return; | ||
} | ||
getMethodNotAllowedResponse(response); | ||
} | ||
|
||
protected void doPost(HttpRequest request, HttpResponse response) throws Exception { | ||
getMethodNotAllowedResponse(response); | ||
} | ||
|
||
protected void doGet(HttpRequest request, HttpResponse response) throws Exception { | ||
getMethodNotAllowedResponse(response); | ||
} | ||
|
||
private void getMethodNotAllowedResponse(final HttpResponse response) { | ||
final String allowedHeaders = String.join(ALLOW_HEADER_DELIMITER, | ||
HttpRequestMethod.POST.name(), | ||
HttpRequestMethod.GET.name()); | ||
|
||
response.setStatusCode(HttpStatusCode.METHOD_NOT_ALLOWED) | ||
.addHeader(ALLOW, allowedHeaders); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package org.apache.catalina.controller; | ||
|
||
import org.apache.coyote.http11.request.HttpRequest; | ||
import org.apache.coyote.http11.response.HttpResponse; | ||
|
||
public interface Controller { | ||
|
||
void service(HttpRequest request, HttpResponse response) throws Exception; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package org.apache.catalina.controller; | ||
|
||
import static java.util.Objects.requireNonNull; | ||
import static org.apache.catalina.controller.StaticResourceUri.*; | ||
import static org.apache.coyote.http11.response.ResponseContentType.TEXT_HTML; | ||
|
||
import org.apache.catalina.util.FileLoader; | ||
import org.apache.coyote.http11.request.HttpRequest; | ||
import org.apache.coyote.http11.response.HttpResponse; | ||
import org.apache.coyote.http11.response.HttpResponseBody; | ||
import org.apache.coyote.http11.response.HttpStatusCode; | ||
|
||
public class DefaultController extends AbstractController { | ||
|
||
@Override | ||
protected void doGet(final HttpRequest request, final HttpResponse response) throws Exception { | ||
final String resource = FileLoader.load(RESOURCE_DIRECTORY + DEFAULT_PAGE.getUri()); | ||
|
||
response.setStatusCode(HttpStatusCode.OK) | ||
.addContentTypeHeader(TEXT_HTML.getType()) | ||
.addContentLengthHeader(requireNonNull(resource).getBytes().length) | ||
.setResponseBody(new HttpResponseBody(resource)); | ||
Comment on lines
+19
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오... 저는 왜 빌더 패턴을 적용할 생각을 못했을까요?? |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package org.apache.catalina.controller; | ||
|
||
import static java.util.Objects.requireNonNull; | ||
import static org.apache.catalina.controller.StaticResourceUri.DEFAULT_PAGE; | ||
import static org.apache.catalina.controller.StaticResourceUri.LOGIN_PAGE; | ||
import static org.apache.coyote.http11.response.ResponseContentType.TEXT_HTML; | ||
|
||
import java.util.NoSuchElementException; | ||
import nextstep.jwp.db.InMemoryUserRepository; | ||
import nextstep.jwp.model.User; | ||
import org.apache.catalina.Session; | ||
import org.apache.catalina.util.Authorizer; | ||
import org.apache.catalina.util.FileLoader; | ||
import org.apache.coyote.http11.request.HttpRequest; | ||
import org.apache.coyote.http11.request.HttpRequestBody; | ||
import org.apache.coyote.http11.response.HttpResponse; | ||
import org.apache.coyote.http11.response.HttpResponseBody; | ||
import org.apache.coyote.http11.response.HttpStatusCode; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
public class LoginController extends AbstractController { | ||
|
||
private static final String USER_ATTRIBUTE_KEY = "user"; | ||
private static final Logger log = LoggerFactory.getLogger(LoginController.class); | ||
|
||
@Override | ||
protected void doPost(final HttpRequest request, final HttpResponse response) { | ||
if (Authorizer.hasValidSession(request)) { | ||
redirectToDefaultPage(response); | ||
return; | ||
} | ||
|
||
final HttpRequestBody requestBody = request.getBody(); | ||
final User reqeustUser = new User(requestBody.parse()); | ||
|
||
final User user = InMemoryUserRepository.findByAccount(reqeustUser.getAccount()) | ||
.orElseThrow(() -> new NoSuchElementException("존재하지 않는 사용자입니다.")); | ||
|
||
if (user.checkPassword(reqeustUser)) { | ||
final Session newSession = createSession(user); | ||
|
||
response.setStatusCode(HttpStatusCode.FOUND) | ||
.addContentTypeHeader(TEXT_HTML.getType()) | ||
.addLocationHeader(DEFAULT_PAGE.getUri()) | ||
.addSetCookieHeader("JSESSIONID=" + newSession.getId()); | ||
} | ||
} | ||
|
||
@Override | ||
protected void doGet(final HttpRequest request, final HttpResponse response) throws Exception { | ||
if (Authorizer.hasValidSession(request)) { | ||
redirectToDefaultPage(response); | ||
return; | ||
} | ||
|
||
final String resource = FileLoader.load(RESOURCE_DIRECTORY + LOGIN_PAGE.getUri()); | ||
|
||
response.setStatusCode(HttpStatusCode.OK) | ||
.addContentTypeHeader(TEXT_HTML.getType()) | ||
.addContentLengthHeader(requireNonNull(resource).getBytes().length) | ||
.setResponseBody(new HttpResponseBody(resource)); | ||
} | ||
|
||
private Session createSession(final User user) { | ||
Session newSession = new Session(); | ||
newSession.setAttribute(USER_ATTRIBUTE_KEY, user); | ||
Authorizer.addSession(newSession); | ||
log.info("로그인 성공! 아이디: {}", user.getAccount()); | ||
return newSession; | ||
} | ||
|
||
private void redirectToDefaultPage(final HttpResponse response) { | ||
response.setStatusCode(HttpStatusCode.FOUND) | ||
.addContentTypeHeader(TEXT_HTML.getType()) | ||
.addLocationHeader(DEFAULT_PAGE.getUri()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
발빠르게 적용하셨군요! ㅎㅎ
굿굿~~