-
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단계] 로이 미션 제출합니다. #494
Changes from 5 commits
e4900af
63b4190
13f8efd
6cb1b9a
c3ecfe8
938aaba
118768f
0cf6cd6
7a95a6c
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 |
---|---|---|
|
@@ -30,7 +30,9 @@ protected HttpResponse doGet(HttpRequest request) throws Exception { | |
String requestUrl = request.getRequestUrl(); | ||
Path path = PathFinder.findPath(requestUrl + ".html"); | ||
var responseBody = new String(Files.readAllBytes(path)); | ||
return new HttpResponse(HttpStatus.OK, responseBody, ContentType.HTML); | ||
return new HttpResponse.Builder(HttpStatus.OK, responseBody, ContentType.HTML) | ||
.build(); | ||
|
||
} | ||
|
||
@Override | ||
|
@@ -44,7 +46,11 @@ protected HttpResponse doPost(HttpRequest request) throws Exception { | |
saveUser(registerData); | ||
Path path = PathFinder.findPath(requestUrl + ".html"); | ||
String responseBody = new String(Files.readAllBytes(path)); | ||
return new HttpResponse(HttpStatus.FOUND, responseBody, ContentType.HTML, "/index.html"); | ||
return new HttpResponse.Builder(HttpStatus.FOUND, responseBody, ContentType.HTML) | ||
.redirect("/index.html") | ||
.build(); | ||
|
||
|
||
} | ||
|
||
private boolean checkInputForm(final HashMap<String, String> registerData) { | ||
|
@@ -56,7 +62,10 @@ private boolean checkInputForm(final HashMap<String, String> registerData) { | |
private HttpResponse generateBadRequestResponse() throws URISyntaxException, IOException { | ||
Path path = PathFinder.findPath("/400.html"); | ||
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. 혹시 400.html은 별도로 존재했다가 삭제된 정적 파일일까요? 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. 샘플 코드로 주어진 것으로 잘못 알았네요ㅜ 400에러 페이지를 추가해봤습니다! 세심한 피드백 감사합니다👍 |
||
String responseBody = new String(Files.readAllBytes(path)); | ||
return new HttpResponse(HttpStatus.FOUND, responseBody, ContentType.HTML, "/400.html"); | ||
return new HttpResponse.Builder(HttpStatus.FOUND, responseBody, ContentType.HTML) | ||
.redirect("/400.html") | ||
.build(); | ||
|
||
} | ||
|
||
private void saveUser(HashMap<String, String> registerData) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,24 +8,32 @@ | |
import java.io.UncheckedIOException; | ||
import java.net.ServerSocket; | ||
import java.net.Socket; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.Executors; | ||
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_THREADS = 250; | ||
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. 완전히 부가적인 부분인데요, |
||
|
||
private static final int TERMINATION_LIMIT_SECONDS = 60; | ||
|
||
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_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,15 +75,28 @@ private void process(final Socket connection) { | |
return; | ||
} | ||
var processor = new Http11Processor(connection); | ||
executorService.execute(processor); | ||
new Thread(processor).start(); | ||
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. 이 부분은 중요한 부분이어서 말씀드려요! newFixedThreadPool로 스레드 풀을 만들어서 사용하신 것은 적잘한데 혹시 필요하실까봐 부가적으로 설명드리자면 현재 execute 메서드와 Thread의 생성자로 넘기는 파라미터인 무튼 같은 요청을 두 스레드에서 처리하려고 하니 늦은 스레드는 예외가 발생한다고 보면 되겠네요~! 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. 사실 쓰레드 작동 원리에 관해서 제대로 이해하지 못한 채 구현하다 보니 이런 문제가 있었네요ㅠ 잘못된 부분을 알려주신 것 뿐만 아니라, 그 내용까지 상세히 알려주셔서 너무 감사합니다! 말씀하신대로 수정해봤구요! 이번 계기로 스레드에 대한 공부를 깊이 있게 좀 해봐야겠네요!! |
||
} | ||
|
||
public void stop() { | ||
stopped = true; | ||
try { | ||
executorService.shutdown(); | ||
terminateWhenTimeLimitExceed(); | ||
serverSocket.close(); | ||
} catch (IOException e) { | ||
log.error(e.getMessage(), e); | ||
} catch (InterruptedException e) { | ||
executorService.shutdownNow(); | ||
log.error(e.getMessage(), e); | ||
Thread.currentThread().interrupt(); | ||
} | ||
} | ||
Comment on lines
83
to
+94
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. 스레드 풀을 닫는 부분 고민해주신거 너무 좋네요~~ 저도 배웠습니다! |
||
|
||
private void terminateWhenTimeLimitExceed() throws InterruptedException { | ||
if (!executorService.awaitTermination(TERMINATION_LIMIT_SECONDS, TimeUnit.SECONDS)) { | ||
executorService.shutdownNow(); | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
package org.apache.coyote.http11.request; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
public class Cookie { | ||
|
||
|
@@ -16,12 +16,12 @@ private Cookie(Map<String, String> cookies) { | |
|
||
public static Cookie from(String cookie) { | ||
String[] params = cookie.split("; "); | ||
HashMap<String, String> cookies = initCookies(params); | ||
ConcurrentHashMap<String, String> cookies = initCookies(params); | ||
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. 말씀을 듣고보니 공용으로 사용되지 않는 Cookie에는 Concurrent 자료 구조를 적용할 필요가 없다는 생각이 드네요!! 좋은 고민거리를 주셔서 감사합니다!! |
||
return new Cookie(cookies); | ||
} | ||
|
||
private static HashMap<String, String> initCookies(String[] params) { | ||
HashMap<String, String> cookies = new HashMap<>(); | ||
private static ConcurrentHashMap<String, String> initCookies(String[] params) { | ||
ConcurrentHashMap<String, String> cookies = new ConcurrentHashMap<>(); | ||
for (String param : params) { | ||
String[] splitParam = param.split("="); | ||
String key = splitParam[PARAM_KEY_INDEX]; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,14 +3,14 @@ | |
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
public class SessionManager { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(SessionManager.class); | ||
|
||
private static final Map<String, Session> SESSIONS = new HashMap<>(); | ||
private static final Map<String, Session> SESSIONS = 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. 여기는 ConcurrentHashMap이 필요하겠네요~~ 👍 |
||
|
||
public static void add(Session session) { | ||
log.info("session add 완료: {}", session.getId()); | ||
|
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.
3단계 요구사항이 Controller의 service()메서드의 시그니처가
service(HttpRequest request, HttpResponse response)
이런 식으로 강제되게 되었네요 ㅠㅠ 이런 방식으로 가게 되면 response를 외부에서 만들어 넣어주는 방식으로 구성해야 하고 Servlet과 HttpServlet의 정확한 표준을 따르게 되면 제안드렸던 builder패턴 사용으로 response 객체를 나중에 만드는 방식은 조오금 어려워질 것 같기도 합니다..😂고치기에는 변경점이 많아서 가볍게 어떻게 바뀔지 고민만 해보시면 좋을 것 같아서요~
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.
일단 빌더 패턴을 한번 적용해보는 것 부터가 많은 공부가 되었습니다😀
말씀하신 것 처럼 service(HttpRequest request, HttpResponse response) 로 구성하는 게 올바른 리팩터링 방향이라고 생각합니다!
기한 내 적용은 못했지만.. 나중에 여유를 좀 가지고 천천히 구조 변경을 해보겠습니다!