Skip to content

Commit

Permalink
[톰캣 구현하기 3, 4단계] 토리(천은정) 미션 제출합니다. (#491)
Browse files Browse the repository at this point in the history
* feat: GreetingControllerTest 변경

* refactor: HttpRequest 클래스 구현

- HttpRequest 클래스 구조 분리 및 역할 세분화
    - HttpRequestLine
        - HttpMethod
        - HttpPath
        - HttpProtocol
    - HttpRequestHeaders
    - HttpRequestBody
- HttpProtocol enum 클래스 추가

* refactor: Controller 인터페이스 추가

- HttpResponse 구조 변경
- HttpRequest 객체의 요청을 처리할 FrontController 추가
- Http11Processor 내부 HttpRequest path로 인한 분기 로직 책임 분리

* feat: 동시성 확장

* refactor: 의존 관계 분리

* fix: 로그인 문제 해결
  • Loading branch information
ezzanzzan authored Sep 14, 2023
1 parent 2353692 commit 364e4e0
Show file tree
Hide file tree
Showing 40 changed files with 745 additions and 295 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
package cache.com.example.cachecontrol;

import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.WebContentInterceptor;

@Configuration
public class CacheWebConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(final InterceptorRegistry registry) {
final WebContentInterceptor webContentInterceptor = new WebContentInterceptor();
final CacheControl cacheControl = CacheControl
.noCache()
.cachePrivate();
webContentInterceptor.addCacheMapping(cacheControl, "/**");
registry.addInterceptor(webContentInterceptor);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
package cache.com.example.etag;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.ShallowEtagHeaderFilter;

import static cache.com.example.version.CacheBustingWebConfig.PREFIX_STATIC_RESOURCES;

@Configuration
public class EtagFilterConfiguration {

// @Bean
// public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() {
// return null;
// }
@Bean
public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() {
FilterRegistrationBean<ShallowEtagHeaderFilter> filterFilterRegistrationBean
= new FilterRegistrationBean<>(new ShallowEtagHeaderFilter());
filterFilterRegistrationBean.addUrlPatterns("/etag", PREFIX_STATIC_RESOURCES + "/*");
return filterFilterRegistrationBean;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.time.Duration;

@Configuration
public class CacheBustingWebConfig implements WebMvcConfigurer {

Expand All @@ -20,6 +23,7 @@ public CacheBustingWebConfig(ResourceVersion version) {
@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler(PREFIX_STATIC_RESOURCES + "/" + version.getVersion() + "/**")
.addResourceLocations("classpath:/static/");
.addResourceLocations("classpath:/static/")
.setCacheControl(CacheControl.maxAge(Duration.ofDays(365)).cachePublic());
}
}
3 changes: 3 additions & 0 deletions study/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ handlebars:
suffix: .html

server:
compression:
enabled: true
min-response-size: 10
tomcat:
accept-count: 1
max-connections: 1
Expand Down
6 changes: 3 additions & 3 deletions study/src/test/java/thread/stage0/SynchronizationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* 다중 스레드 환경에서 두 개 이상의 스레드가 변경 가능한(mutable) 공유 데이터를 동시에 업데이트하면 경쟁 조건(race condition)이 발생한다.
* 자바는 공유 데이터에 대한 스레드 접근을 동기화(synchronization)하여 경쟁 조건을 방지한다.
* 동기화된 블록은 하나의 스레드만 접근하여 실행할 수 있다.
*
* <p>
* Synchronization
* https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html
*/
Expand All @@ -21,7 +21,7 @@ class SynchronizationTest {
/**
* 테스트가 성공하도록 SynchronizedMethods 클래스에 동기화를 적용해보자.
* synchronized 키워드에 대하여 찾아보고 적용하면 된다.
*
* <p>
* Guide to the Synchronized Keyword in Java
* https://www.baeldung.com/java-synchronized
*/
Expand All @@ -41,7 +41,7 @@ private static final class SynchronizedMethods {

private int sum = 0;

public void calculate() {
public synchronized void calculate() {
setSum(getSum() + 1);
}

Expand Down
10 changes: 5 additions & 5 deletions study/src/test/java/thread/stage0/ThreadPoolsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
/**
* 스레드 풀은 무엇이고 어떻게 동작할까?
* 테스트를 통과시키고 왜 해당 결과가 나왔는지 생각해보자.
*
* <p>
* Thread Pools
* https://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html
*
* <p>
* Introduction to Thread Pools in Java
* https://www.baeldung.com/thread-pool-java-and-guava
*/
Expand All @@ -31,8 +31,8 @@ void testNewFixedThreadPool() {
executor.submit(logWithSleep("hello fixed thread pools"));

// 올바른 값으로 바꿔서 테스트를 통과시키자.
final int expectedPoolSize = 0;
final int expectedQueueSize = 0;
final int expectedPoolSize = 2;
final int expectedQueueSize = 1;

assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize());
assertThat(expectedQueueSize).isEqualTo(executor.getQueue().size());
Expand All @@ -46,7 +46,7 @@ void testNewCachedThreadPool() {
executor.submit(logWithSleep("hello cached thread pools"));

// 올바른 값으로 바꿔서 테스트를 통과시키자.
final int expectedPoolSize = 0;
final int expectedPoolSize = 3;
final int expectedQueueSize = 0;

assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize());
Expand Down
6 changes: 6 additions & 0 deletions tomcat/src/main/java/nextstep/Application.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package nextstep;

import nextstep.jwp.controller.HomeController;
import nextstep.jwp.controller.LoginController;
import nextstep.jwp.controller.RegisterController;
import org.apache.catalina.startup.Tomcat;

public class Application {

public static void main(String[] args) {
final var tomcat = new Tomcat();
tomcat.addController("/", new HomeController());
tomcat.addController("/login", new LoginController());
tomcat.addController("/register", new RegisterController());
tomcat.start();
}
}
17 changes: 17 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/controller/HomeController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package nextstep.jwp.controller;

import org.apache.coyote.http11.controller.AbstractController;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;

public class HomeController extends AbstractController {
@Override
protected void doPost(final HttpRequest request, final HttpResponse response) throws Exception {
throw new UnsupportedOperationException();
}

@Override
protected void doGet(final HttpRequest request, final HttpResponse response) throws Exception {
response.home("Hello world!");
}
}
57 changes: 57 additions & 0 deletions tomcat/src/main/java/nextstep/jwp/controller/LoginController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package nextstep.jwp.controller;

import nextstep.jwp.db.InMemoryUserRepository;
import nextstep.jwp.model.User;
import org.apache.coyote.http11.common.HttpUri;
import org.apache.coyote.http11.controller.AbstractController;
import org.apache.coyote.http11.cookie.HttpCookie;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;
import org.apache.coyote.http11.session.HttpSession;
import org.apache.coyote.http11.session.SessionManager;

import java.util.Map;
import java.util.Objects;

public class LoginController extends AbstractController {
private static final SessionManager sessionManager = SessionManager.create();
private static final String USER = "user";
private static final String PASSWORD = "password";

@Override
protected void doPost(final HttpRequest request, final HttpResponse response) throws Exception {
final Map<String, String> loginData = request.getBody();
final User user = InMemoryUserRepository.findByAccount(loginData.get("account"))
.orElse(null);
if (!Objects.isNull(user) && user.checkPassword(loginData.get(PASSWORD))) {
final HttpCookie newCookie = HttpCookie.create();
saveSession(newCookie, user);
response.found(HttpUri.INDEX_HTML.getUri());
response.setCookie(newCookie.getJSESSIONID());
return;
}
response.ok(HttpUri.UNAUTHORIZED_HTML.getUri());
}

@Override
protected void doGet(final HttpRequest request, final HttpResponse response) {
final HttpCookie cookie = makeHttpCookie(request);
if (cookie.hasJSESSIONID()) {
final String jsessionid = cookie.getJSESSIONID();
final HttpSession httpSession = sessionManager.findSession(jsessionid);
if (Objects.isNull(httpSession)) {
response.ok(HttpUri.LOGIN_HTML.getUri());
return;
}
response.found(HttpUri.INDEX_HTML.getUri());
return;
}
response.ok(HttpUri.LOGIN_HTML.getUri());
}

private void saveSession(final HttpCookie newCookie, final User user) {
final HttpSession httpSession = new HttpSession(newCookie.getJSESSIONID());
httpSession.setAttribute(USER, user);
sessionManager.add(httpSession);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package nextstep.jwp.controller;

import nextstep.jwp.db.InMemoryUserRepository;
import nextstep.jwp.model.User;
import org.apache.coyote.http11.common.HttpUri;
import org.apache.coyote.http11.controller.AbstractController;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;

import java.util.Map;

public class RegisterController extends AbstractController {
@Override
protected void doPost(final HttpRequest request, final HttpResponse response) throws Exception {
final Map<String, String> registerData = request.getBody();
InMemoryUserRepository.save(new User(registerData.get("account"), registerData.get("password"), registerData.get("email")));
response.ok(HttpUri.INDEX_HTML.getUri());
}

@Override
protected void doGet(final HttpRequest request, final HttpResponse response) throws Exception {
response.ok(HttpUri.REGISTER.getUri());
}
}
20 changes: 13 additions & 7 deletions tomcat/src/main/java/org/apache/catalina/connector/Connector.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,37 @@
package org.apache.catalina.connector;

import org.apache.coyote.http11.Http11Processor;
import org.apache.coyote.http11.controller.FrontController;
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;

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 MAX_THREAD_POOL = 250;

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

public Connector() {
this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT);
public Connector(FrontController frontController) {
this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, MAX_THREAD_POOL, frontController);
}

public Connector(final int port, final int acceptCount) {
public Connector(final int port, final int acceptCount, final int maxThreads, FrontController frontController) {
this.executorService = Executors.newFixedThreadPool(maxThreads);
this.serverSocket = createServerSocket(port, acceptCount);
this.stopped = false;
this.frontController = frontController;
}

private ServerSocket createServerSocket(final int port, final int acceptCount) {
Expand Down Expand Up @@ -66,8 +72,8 @@ private void process(final Socket connection) {
if (connection == null) {
return;
}
var processor = new Http11Processor(connection);
new Thread(processor).start();
var processor = new Http11Processor(connection, frontController);
executorService.submit(processor);
}

public void stop() {
Expand Down
15 changes: 13 additions & 2 deletions tomcat/src/main/java/org/apache/catalina/startup/Tomcat.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
package org.apache.catalina.startup;

import org.apache.catalina.connector.Connector;
import org.apache.coyote.http11.controller.Controller;
import org.apache.coyote.http11.controller.FrontController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

public class Tomcat {

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

private final FrontController frontController;

public Tomcat() {
this.frontController = new FrontController();
}

public void start() {
var connector = new Connector();
var connector = new Connector(frontController);
connector.start();

try {
Expand All @@ -24,4 +31,8 @@ public void start() {
connector.stop();
}
}

public void addController(final String path, final Controller controller) {
frontController.addController(path, controller);
}
}
Loading

0 comments on commit 364e4e0

Please sign in to comment.