Skip to content

Commit 3bae892

Browse files
[톰캣 구현하기 - 3, 4단계] 베베(최원용) 미션 제출합니다. (#417)
* refactor: request 객체 분리 * refactor: 매직넘버 상수화 * refactor: 패키지 구조 변경 * refactor: Status Code와 Redirect URL Enum으로 분리 * refactor: AuthService 메서드 분리 * refactor: ResponsePage 패키지 변경 * refactor: Login, Register 서비스, 컨트롤러 분리 * refactor: 스레드 풀 설정 * refactor: null이면 ConcurrentHashMap에 데이터를 넣지 않는다. * chore: 사용하지 않는 import 제거 * refactor: request, response add prefix http * refactor: 로그인이 되어있다면 인덱스 페이지로 리다이렉트 * refactor: controller 추상화 * fix: 테스트케이스 수정 * refactor: service layer에서는 request와 response의 연관관계를 끊는다. * chore: 사용하지 않는 import 제거 * refactor: HttpRequestGenerator 필드 제거 * refactor: 다중 cookie header 설정 지원 * refactor: RequestHeader 추상화 * refactor: Session에서 null 대신 Optional 반환 * refactor: null 검증 로직 제거 * fix: 테스트 코드 수정 * refactor: cookie 값 Optional로 반환 * refactor: 여러개의 cookie 값 처리 * chore: 사용하지 않는 메서드 제거
1 parent 6054c80 commit 3bae892

33 files changed

+745
-487
lines changed

tomcat/src/main/java/org/apache/catalina/connector/Connector.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@
44
import java.io.UncheckedIOException;
55
import java.net.ServerSocket;
66
import java.net.Socket;
7+
import java.util.Arrays;
8+
import java.util.List;
9+
import java.util.concurrent.ExecutorService;
10+
import java.util.concurrent.Executors;
11+
import java.util.concurrent.Future;
12+
import java.util.concurrent.LinkedBlockingQueue;
13+
import java.util.concurrent.ThreadPoolExecutor;
14+
import java.util.concurrent.TimeUnit;
715
import org.apache.coyote.http11.Http11Processor;
816
import org.slf4j.Logger;
917
import org.slf4j.LoggerFactory;
@@ -14,8 +22,12 @@ public class Connector implements Runnable {
1422

1523
private static final int DEFAULT_PORT = 8080;
1624
private static final int DEFAULT_ACCEPT_COUNT = 100;
25+
private static final int MAX_THREAD_POOL_SIZE = 250;
26+
private static final int CORE_POOL_SIZE = 10;
27+
private static final Long KEEP_ALIVE_TIME = 60L;
1728

1829
private final ServerSocket serverSocket;
30+
private final ThreadPoolExecutor threadPoolExecutor;
1931
private boolean stopped;
2032

2133
public Connector() {
@@ -24,6 +36,12 @@ public Connector() {
2436

2537
public Connector(final int port, final int acceptCount) {
2638
this.serverSocket = createServerSocket(port, acceptCount);
39+
this.threadPoolExecutor = new ThreadPoolExecutor(
40+
CORE_POOL_SIZE,
41+
MAX_THREAD_POOL_SIZE,
42+
KEEP_ALIVE_TIME,
43+
TimeUnit.SECONDS,
44+
new LinkedBlockingQueue<>(DEFAULT_ACCEPT_COUNT));
2745
this.stopped = false;
2846
}
2947

@@ -66,13 +84,14 @@ private void process(final Socket connection) {
6684
return;
6785
}
6886
var processor = new Http11Processor(connection);
69-
new Thread(processor).start();
87+
threadPoolExecutor.execute(processor);
7088
}
7189

7290
public void stop() {
7391
stopped = true;
7492
try {
7593
serverSocket.close();
94+
threadPoolExecutor.shutdown();
7695
} catch (IOException e) {
7796
log.error(e.getMessage(), e);
7897
}

tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java

Lines changed: 11 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,13 @@
66
import java.io.InputStreamReader;
77
import java.io.OutputStream;
88
import java.net.Socket;
9-
import java.util.ArrayList;
10-
import java.util.List;
119
import nextstep.jwp.exception.UncheckedServletException;
1210
import org.apache.coyote.Processor;
13-
import org.apache.coyote.http11.request.body.RequestBody;
14-
import org.apache.coyote.http11.request.handler.RequestHandler;
15-
import org.apache.coyote.http11.request.header.RequestHeader;
16-
import org.apache.coyote.http11.request.line.RequestLine;
11+
import org.apache.coyote.http11.request.HttpRequest;
12+
import org.apache.coyote.http11.request.HttpRequestGenerator;
13+
import org.apache.coyote.http11.request.RequestHandler;
14+
import org.apache.coyote.http11.response.HttpResponse;
1715
import org.apache.coyote.http11.response.HttpResponseGenerator;
18-
import org.apache.coyote.http11.response.ResponseEntity;
1916
import org.slf4j.Logger;
2017
import org.slf4j.LoggerFactory;
2118

@@ -24,7 +21,7 @@ public class Http11Processor implements Runnable, Processor {
2421
private static final Logger LOG = LoggerFactory.getLogger(Http11Processor.class);
2522

2623
private final Socket connection;
27-
private final HttpResponseGenerator httpResponseGenerator = new HttpResponseGenerator();
24+
private final HttpResponseGenerator responseGenerator = new HttpResponseGenerator();
2825
private final RequestHandler requestHandler = new RequestHandler();
2926

3027
public Http11Processor(Socket connection) {
@@ -42,44 +39,17 @@ public void process(final Socket connection) {
4239
try (final InputStream inputStream = connection.getInputStream();
4340
final OutputStream outputStream = connection.getOutputStream();
4441
final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {
45-
final String firstLine = bufferedReader.readLine();
46-
if (firstLine == null) {
47-
return;
48-
}
49-
final String response = getResponse(bufferedReader, firstLine);
42+
43+
final HttpRequest httpRequest = HttpRequestGenerator.generate(bufferedReader);
44+
final HttpResponse httpResponse = requestHandler.getResponse(httpRequest);
45+
final String response = responseGenerator.generate(httpResponse);
46+
System.out.println("response = " + response);
47+
5048
outputStream.write(response.getBytes());
5149
outputStream.flush();
5250
} catch (IOException | UncheckedServletException e) {
5351
LOG.error(e.getMessage(), e);
5452
}
5553
}
5654

57-
private String getResponse(BufferedReader bufferedReader, String firstLine) throws IOException {
58-
final RequestLine requestLine = RequestLine.from(firstLine);
59-
final RequestHeader requestHeader = getHeader(bufferedReader);
60-
final RequestBody requestBody = getBody(bufferedReader, requestHeader);
61-
final ResponseEntity responseEntity = requestHandler.getResponse(requestLine, requestHeader, requestBody);
62-
return httpResponseGenerator.generate(responseEntity);
63-
}
64-
65-
private RequestHeader getHeader(final BufferedReader bufferedReader) throws IOException {
66-
List<String> requestHeaders = new ArrayList<>();
67-
for (String line = bufferedReader.readLine(); !"".equals(line); line = bufferedReader.readLine()) {
68-
requestHeaders.add(line);
69-
}
70-
return RequestHeader.from(requestHeaders);
71-
}
72-
73-
private RequestBody getBody(final BufferedReader bufferedReader, final RequestHeader requestHeader)
74-
throws IOException {
75-
List<String> contentLengths = requestHeader.headers().get("Content-Length");
76-
if (contentLengths == null) {
77-
return RequestBody.from(null);
78-
}
79-
int contentLength = Integer.parseInt(contentLengths.get(0));
80-
char[] buffer = new char[contentLength];
81-
bufferedReader.read(buffer, 0, contentLength);
82-
return RequestBody.from(new String(buffer));
83-
}
84-
8555
}

tomcat/src/main/java/org/apache/coyote/http11/auth/AuthService.java

Lines changed: 0 additions & 76 deletions
This file was deleted.

tomcat/src/main/java/org/apache/coyote/http11/auth/Cookie.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.HashMap;
44
import java.util.List;
55
import java.util.Map;
6+
import java.util.Optional;
67

78
import static java.util.Collections.EMPTY_MAP;
89
import static java.util.stream.Collectors.collectingAndThen;
@@ -32,12 +33,22 @@ public static Cookie from(final List<String> elements) {
3233
));
3334
}
3435

36+
public Cookie() {
37+
}
38+
39+
public void setSession(Session session) {
40+
elements.put("JSESSIONID", session.getId());
41+
}
42+
3543
public void put(final String key, final String value) {
3644
elements.put(key, value);
3745
}
3846

39-
public String get(final String key) {
40-
return elements.get(key);
47+
public Optional<String> get(final String key) {
48+
if (key == null) {
49+
return Optional.empty();
50+
}
51+
return Optional.ofNullable(elements.get(key));
4152
}
4253

4354
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package org.apache.coyote.http11.auth;
2+
3+
import java.util.Optional;
4+
import java.util.UUID;
5+
import nextstep.jwp.db.InMemoryUserRepository;
6+
import nextstep.jwp.model.User;
7+
import org.apache.coyote.http11.request.line.Protocol;
8+
import org.apache.coyote.http11.response.HttpResponse;
9+
10+
import static org.apache.coyote.http11.response.ResponsePage.INDEX_PAGE;
11+
import static org.apache.coyote.http11.response.ResponsePage.LOGIN_PAGE;
12+
import static org.apache.coyote.http11.response.ResponsePage.UNAUTHORIZED_PAGE;
13+
14+
public class LoginService {
15+
16+
public static final String COOKIE_KEY = "JSESSIONID";
17+
18+
private final SessionRepository sessionRepository = new SessionRepository();
19+
20+
public HttpResponse getLoginViewResponse(Cookie cookie, Protocol protocol) {
21+
Optional<String> cookieOption = cookie.get(COOKIE_KEY);
22+
if (cookieOption.isEmpty()) {
23+
return HttpResponse.getCookieNullResponseEntity(protocol, LOGIN_PAGE);
24+
}
25+
final Optional<Session> session = sessionRepository.getSession(cookieOption.get());
26+
if (session.isEmpty()) {
27+
return HttpResponse.getCookieNullResponseEntity(protocol, LOGIN_PAGE);
28+
}
29+
return HttpResponse.getCookieNullResponseEntity(protocol, INDEX_PAGE);
30+
}
31+
32+
public HttpResponse getLoginOrElseUnAuthorizedResponse(Protocol protocol, String account, String password) {
33+
return InMemoryUserRepository.findByAccount(account)
34+
.filter(user -> user.checkPassword(password))
35+
.map(user -> getSuccessLoginResponse(user, protocol))
36+
.orElseGet(() -> HttpResponse.getCookieNullResponseEntity(protocol, UNAUTHORIZED_PAGE));
37+
}
38+
39+
private HttpResponse getSuccessLoginResponse(final User user, final Protocol protocol) {
40+
final String uuid = UUID.randomUUID().toString();
41+
final Session session = Session.from(uuid);
42+
session.setAttribute("user", user);
43+
sessionRepository.create(session);
44+
Cookie cookie = new Cookie();
45+
cookie.setSession(session);
46+
return HttpResponse.getResponseEntity(protocol, cookie, INDEX_PAGE);
47+
}
48+
49+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.apache.coyote.http11.auth;
2+
3+
import nextstep.jwp.db.InMemoryUserRepository;
4+
import nextstep.jwp.model.User;
5+
import org.apache.coyote.http11.request.line.Protocol;
6+
import org.apache.coyote.http11.response.HttpResponse;
7+
8+
import static org.apache.coyote.http11.response.ResponsePage.CONFLICT_PAGE;
9+
import static org.apache.coyote.http11.response.ResponsePage.INDEX_PAGE;
10+
11+
public class RegisterService {
12+
13+
public HttpResponse getIndexOrConflictResponse(String account, String password, String email, Protocol protocol) {
14+
if (InMemoryUserRepository.findByAccount(account).isPresent()) {
15+
return HttpResponse.getCookieNullResponseEntity(protocol, CONFLICT_PAGE);
16+
}
17+
18+
InMemoryUserRepository.save(new User(account, password, email));
19+
return HttpResponse.getCookieNullResponseEntity(protocol, INDEX_PAGE);
20+
}
21+
22+
}

tomcat/src/main/java/org/apache/coyote/http11/auth/Session.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
import java.util.HashMap;
44
import java.util.Map;
5+
import nextstep.jwp.model.User;
56

67
public class Session {
78

89
private final String id;
9-
private final Map<String, Object> items = new HashMap<>();
10+
private final Map<String, User> items = new HashMap<>();
1011

1112
private Session(final String id) {
1213
this.id = id;
@@ -16,7 +17,7 @@ public static Session from(String id) {
1617
return new Session(id);
1718
}
1819

19-
public void setAttribute(final String key, final Object value) {
20+
public void setAttribute(final String key, final User value) {
2021
items.put(key, value);
2122
}
2223

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
package org.apache.coyote.http11.auth;
22

3-
import java.util.HashMap;
43
import java.util.Map;
4+
import java.util.Optional;
5+
import java.util.concurrent.ConcurrentHashMap;
56

67
public class SessionRepository {
78

8-
private static final Map<String, Session> SESSIONS = new HashMap<>();
9+
private static final Map<String, Session> SESSIONS = new ConcurrentHashMap<>();
910

1011
public static void create(Session session) {
1112
SESSIONS.put(session.getId(), session);
1213
}
1314

14-
public Session getSession(String id) {
15-
return SESSIONS.get(id);
16-
}
17-
18-
public static void clearSessions() {
19-
SESSIONS.clear();
15+
public Optional<Session> getSession(String id) {
16+
if (id == null) {
17+
return Optional.ofNullable(null);
18+
}
19+
return Optional.ofNullable(SESSIONS.get(id));
2020
}
2121

2222
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.apache.coyote.http11.controller;
2+
3+
import org.apache.coyote.http11.request.HttpRequest;
4+
import org.apache.coyote.http11.request.line.HttpMethod;
5+
import org.apache.coyote.http11.response.HttpResponse;
6+
7+
public abstract class AbstractController implements Controller {
8+
9+
@Override
10+
public HttpResponse service(final HttpRequest request, final HttpResponse response) {
11+
if (request.methodIsEqualTo(HttpMethod.GET)) {
12+
return doGet(request, response);
13+
}
14+
if (request.methodIsEqualTo(HttpMethod.POST)) {
15+
return doPost(request, response);
16+
}
17+
return null;
18+
}
19+
20+
protected abstract HttpResponse doPost(final HttpRequest request, final HttpResponse response);
21+
22+
protected abstract HttpResponse doGet(final HttpRequest request, final HttpResponse response);
23+
24+
}

0 commit comments

Comments
 (0)