Skip to content

Commit

Permalink
[톰캣 구현하기 - 3, 4단계] 베베(최원용) 미션 제출합니다. (#417)
Browse files Browse the repository at this point in the history
* 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: 사용하지 않는 메서드 제거
  • Loading branch information
wonyongChoi05 authored Sep 14, 2023
1 parent 6054c80 commit 3bae892
Show file tree
Hide file tree
Showing 33 changed files with 745 additions and 487 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@
import java.io.UncheckedIOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.coyote.http11.Http11Processor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -14,8 +22,12 @@ public class Connector implements Runnable {

private static final int DEFAULT_PORT = 8080;
private static final int DEFAULT_ACCEPT_COUNT = 100;
private static final int MAX_THREAD_POOL_SIZE = 250;
private static final int CORE_POOL_SIZE = 10;
private static final Long KEEP_ALIVE_TIME = 60L;

private final ServerSocket serverSocket;
private final ThreadPoolExecutor threadPoolExecutor;
private boolean stopped;

public Connector() {
Expand All @@ -24,6 +36,12 @@ public Connector() {

public Connector(final int port, final int acceptCount) {
this.serverSocket = createServerSocket(port, acceptCount);
this.threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_THREAD_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(DEFAULT_ACCEPT_COUNT));
this.stopped = false;
}

Expand Down Expand Up @@ -66,13 +84,14 @@ private void process(final Socket connection) {
return;
}
var processor = new Http11Processor(connection);
new Thread(processor).start();
threadPoolExecutor.execute(processor);
}

public void stop() {
stopped = true;
try {
serverSocket.close();
threadPoolExecutor.shutdown();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
Expand Down
52 changes: 11 additions & 41 deletions tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,13 @@
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import nextstep.jwp.exception.UncheckedServletException;
import org.apache.coyote.Processor;
import org.apache.coyote.http11.request.body.RequestBody;
import org.apache.coyote.http11.request.handler.RequestHandler;
import org.apache.coyote.http11.request.header.RequestHeader;
import org.apache.coyote.http11.request.line.RequestLine;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.request.HttpRequestGenerator;
import org.apache.coyote.http11.request.RequestHandler;
import org.apache.coyote.http11.response.HttpResponse;
import org.apache.coyote.http11.response.HttpResponseGenerator;
import org.apache.coyote.http11.response.ResponseEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

private final Socket connection;
private final HttpResponseGenerator httpResponseGenerator = new HttpResponseGenerator();
private final HttpResponseGenerator responseGenerator = new HttpResponseGenerator();
private final RequestHandler requestHandler = new RequestHandler();

public Http11Processor(Socket connection) {
Expand All @@ -42,44 +39,17 @@ public void process(final Socket connection) {
try (final InputStream inputStream = connection.getInputStream();
final OutputStream outputStream = connection.getOutputStream();
final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {
final String firstLine = bufferedReader.readLine();
if (firstLine == null) {
return;
}
final String response = getResponse(bufferedReader, firstLine);

final HttpRequest httpRequest = HttpRequestGenerator.generate(bufferedReader);
final HttpResponse httpResponse = requestHandler.getResponse(httpRequest);
final String response = responseGenerator.generate(httpResponse);
System.out.println("response = " + response);

outputStream.write(response.getBytes());
outputStream.flush();
} catch (IOException | UncheckedServletException e) {
LOG.error(e.getMessage(), e);
}
}

private String getResponse(BufferedReader bufferedReader, String firstLine) throws IOException {
final RequestLine requestLine = RequestLine.from(firstLine);
final RequestHeader requestHeader = getHeader(bufferedReader);
final RequestBody requestBody = getBody(bufferedReader, requestHeader);
final ResponseEntity responseEntity = requestHandler.getResponse(requestLine, requestHeader, requestBody);
return httpResponseGenerator.generate(responseEntity);
}

private RequestHeader getHeader(final BufferedReader bufferedReader) throws IOException {
List<String> requestHeaders = new ArrayList<>();
for (String line = bufferedReader.readLine(); !"".equals(line); line = bufferedReader.readLine()) {
requestHeaders.add(line);
}
return RequestHeader.from(requestHeaders);
}

private RequestBody getBody(final BufferedReader bufferedReader, final RequestHeader requestHeader)
throws IOException {
List<String> contentLengths = requestHeader.headers().get("Content-Length");
if (contentLengths == null) {
return RequestBody.from(null);
}
int contentLength = Integer.parseInt(contentLengths.get(0));
char[] buffer = new char[contentLength];
bufferedReader.read(buffer, 0, contentLength);
return RequestBody.from(new String(buffer));
}

}

This file was deleted.

15 changes: 13 additions & 2 deletions tomcat/src/main/java/org/apache/coyote/http11/auth/Cookie.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

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

public Cookie() {
}

public void setSession(Session session) {
elements.put("JSESSIONID", session.getId());
}

public void put(final String key, final String value) {
elements.put(key, value);
}

public String get(final String key) {
return elements.get(key);
public Optional<String> get(final String key) {
if (key == null) {
return Optional.empty();
}
return Optional.ofNullable(elements.get(key));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.apache.coyote.http11.auth;

import java.util.Optional;
import java.util.UUID;
import nextstep.jwp.db.InMemoryUserRepository;
import nextstep.jwp.model.User;
import org.apache.coyote.http11.request.line.Protocol;
import org.apache.coyote.http11.response.HttpResponse;

import static org.apache.coyote.http11.response.ResponsePage.INDEX_PAGE;
import static org.apache.coyote.http11.response.ResponsePage.LOGIN_PAGE;
import static org.apache.coyote.http11.response.ResponsePage.UNAUTHORIZED_PAGE;

public class LoginService {

public static final String COOKIE_KEY = "JSESSIONID";

private final SessionRepository sessionRepository = new SessionRepository();

public HttpResponse getLoginViewResponse(Cookie cookie, Protocol protocol) {
Optional<String> cookieOption = cookie.get(COOKIE_KEY);
if (cookieOption.isEmpty()) {
return HttpResponse.getCookieNullResponseEntity(protocol, LOGIN_PAGE);
}
final Optional<Session> session = sessionRepository.getSession(cookieOption.get());
if (session.isEmpty()) {
return HttpResponse.getCookieNullResponseEntity(protocol, LOGIN_PAGE);
}
return HttpResponse.getCookieNullResponseEntity(protocol, INDEX_PAGE);
}

public HttpResponse getLoginOrElseUnAuthorizedResponse(Protocol protocol, String account, String password) {
return InMemoryUserRepository.findByAccount(account)
.filter(user -> user.checkPassword(password))
.map(user -> getSuccessLoginResponse(user, protocol))
.orElseGet(() -> HttpResponse.getCookieNullResponseEntity(protocol, UNAUTHORIZED_PAGE));
}

private HttpResponse getSuccessLoginResponse(final User user, final Protocol protocol) {
final String uuid = UUID.randomUUID().toString();
final Session session = Session.from(uuid);
session.setAttribute("user", user);
sessionRepository.create(session);
Cookie cookie = new Cookie();
cookie.setSession(session);
return HttpResponse.getResponseEntity(protocol, cookie, INDEX_PAGE);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.apache.coyote.http11.auth;

import nextstep.jwp.db.InMemoryUserRepository;
import nextstep.jwp.model.User;
import org.apache.coyote.http11.request.line.Protocol;
import org.apache.coyote.http11.response.HttpResponse;

import static org.apache.coyote.http11.response.ResponsePage.CONFLICT_PAGE;
import static org.apache.coyote.http11.response.ResponsePage.INDEX_PAGE;

public class RegisterService {

public HttpResponse getIndexOrConflictResponse(String account, String password, String email, Protocol protocol) {
if (InMemoryUserRepository.findByAccount(account).isPresent()) {
return HttpResponse.getCookieNullResponseEntity(protocol, CONFLICT_PAGE);
}

InMemoryUserRepository.save(new User(account, password, email));
return HttpResponse.getCookieNullResponseEntity(protocol, INDEX_PAGE);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

import java.util.HashMap;
import java.util.Map;
import nextstep.jwp.model.User;

public class Session {

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

private Session(final String id) {
this.id = id;
Expand All @@ -16,7 +17,7 @@ public static Session from(String id) {
return new Session(id);
}

public void setAttribute(final String key, final Object value) {
public void setAttribute(final String key, final User value) {
items.put(key, value);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
package org.apache.coyote.http11.auth;

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

public class SessionRepository {

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

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

public Session getSession(String id) {
return SESSIONS.get(id);
}

public static void clearSessions() {
SESSIONS.clear();
public Optional<Session> getSession(String id) {
if (id == null) {
return Optional.ofNullable(null);
}
return Optional.ofNullable(SESSIONS.get(id));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.apache.coyote.http11.controller;

import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.request.line.HttpMethod;
import org.apache.coyote.http11.response.HttpResponse;

public abstract class AbstractController implements Controller {

@Override
public HttpResponse service(final HttpRequest request, final HttpResponse response) {
if (request.methodIsEqualTo(HttpMethod.GET)) {
return doGet(request, response);
}
if (request.methodIsEqualTo(HttpMethod.POST)) {
return doPost(request, response);
}
return null;
}

protected abstract HttpResponse doPost(final HttpRequest request, final HttpResponse response);

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

}
Loading

0 comments on commit 3bae892

Please sign in to comment.