Skip to content

Commit ec1ccde

Browse files
authored
[톰캣 구현하기 - 3, 4단계] 라온(문채원) 미션 제출합니다. (#433)
* refactor: request에 HttpHeaders 적용 * refactor: 패키지 구조 변경 * refactor: HttpHeaders 내부에 HttpCookie 추가 * refactor: RequestHandler 및 Controller 객체 생성 * test: Http 활용하기 테스트 구현 * refactor: Controller 핸들링 구조 변경 * refactor: Controller 메서드별 로직 추가 * refactor: Processor 내부의 handleRequest 메서드 삭제 * refactor: ResourceReader 구현 * refactor: ResourceContentTypeResolver 구현 * refactor: MediaType 구현 * refactor: MediaType value 수정 * refactor: 리소스 핸들링하는 메소드 분리 * refactor: 패키지 구조 분리 * test: thread test 진행 * feat: step4 - executors로 thread pool 적용 * feat: step4 - 동시성 컬렉션 사용 * refactor: 세션 패키지 분리 * refactor: HttpController의 추상메서드를 구체메서드로 변경 * refactor: session 패키지 위치 변경 * test: coyote 패키지 테스트 구현 * test: Controller 테스트 구현 * refactor: SessionManager 싱글톤 구현 * refactor: todo 주석 제거 * refactor: RequestMapping 주입 수정 * refactor: content type 헤더 제거 * refactor: exception handling 로직 수정 * refactor: ResourceContentTypeResolver static 메서드로 수정 * refactor: getResourceContentType 로직 수정 * refactor: url encoded 메서드 분리 * refactor: null 반환 Optional 처리 * refactor: Controller의 handle 메서드 수정
1 parent 035a2c8 commit ec1ccde

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1655
-335
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
package cache.com.example.cachecontrol;
22

33
import org.springframework.context.annotation.Configuration;
4+
import org.springframework.http.CacheControl;
45
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
56
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
7+
import org.springframework.web.servlet.mvc.WebContentInterceptor;
68

79
@Configuration
810
public class CacheWebConfig implements WebMvcConfigurer {
911

1012
@Override
1113
public void addInterceptors(final InterceptorRegistry registry) {
14+
final WebContentInterceptor interceptor = new WebContentInterceptor();
15+
final CacheControl cacheControl = CacheControl
16+
.noCache()
17+
.cachePrivate();
18+
interceptor.addCacheMapping(cacheControl, "/**");
19+
registry.addInterceptor(interceptor);
1220
}
1321
}
Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
package cache.com.example.etag;
22

3+
import org.springframework.boot.web.servlet.FilterRegistrationBean;
4+
import org.springframework.context.annotation.Bean;
35
import org.springframework.context.annotation.Configuration;
6+
import org.springframework.web.filter.ShallowEtagHeaderFilter;
7+
8+
import static cache.com.example.version.CacheBustingWebConfig.PREFIX_STATIC_RESOURCES;
49

510
@Configuration
611
public class EtagFilterConfiguration {
712

8-
// @Bean
9-
// public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() {
10-
// return null;
11-
// }
13+
@Bean
14+
public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() {
15+
16+
FilterRegistrationBean<ShallowEtagHeaderFilter> filterFilterRegistrationBean
17+
= new FilterRegistrationBean<>(new ShallowEtagHeaderFilter());
18+
filterFilterRegistrationBean.addUrlPatterns("/etag", PREFIX_STATIC_RESOURCES + "/*");
19+
return filterFilterRegistrationBean;
20+
}
1221
}

study/src/main/java/cache/com/example/version/CacheBustingWebConfig.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
import org.springframework.beans.factory.annotation.Autowired;
44
import org.springframework.context.annotation.Configuration;
5+
import org.springframework.http.CacheControl;
56
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
67
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
78

9+
import java.time.Duration;
10+
811
@Configuration
912
public class CacheBustingWebConfig implements WebMvcConfigurer {
1013

@@ -20,6 +23,7 @@ public CacheBustingWebConfig(ResourceVersion version) {
2023
@Override
2124
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
2225
registry.addResourceHandler(PREFIX_STATIC_RESOURCES + "/" + version.getVersion() + "/**")
23-
.addResourceLocations("classpath:/static/");
26+
.addResourceLocations("classpath:/static/")
27+
.setCacheControl(CacheControl.maxAge(Duration.ofDays(365)).cachePublic());
2428
}
2529
}

study/src/main/resources/application.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ handlebars:
22
suffix: .html
33

44
server:
5+
compression:
6+
enabled: true
7+
min-response-size: 10
58
tomcat:
69
accept-count: 1
710
max-connections: 1

study/src/test/java/thread/stage0/SynchronizationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ private static final class SynchronizedMethods {
4141

4242
private int sum = 0;
4343

44-
public void calculate() {
44+
public synchronized void calculate() {
4545
setSum(getSum() + 1);
4646
}
4747

study/src/test/java/thread/stage0/ThreadPoolsTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ void testNewFixedThreadPool() {
3131
executor.submit(logWithSleep("hello fixed thread pools"));
3232

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

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

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

5252
assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize());

tomcat/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@ dependencies {
2121
testImplementation "org.assertj:assertj-core:3.24.2"
2222
testImplementation "org.mockito:mockito-core:5.4.0"
2323
testImplementation "org.junit.jupiter:junit-jupiter-api:5.7.2"
24+
testImplementation "org.junit.jupiter:junit-jupiter-params:5.7.2"
2425
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.7.2"
2526
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package nextstep.jwp.controller;
2+
3+
import nextstep.jwp.db.InMemoryUserRepository;
4+
import nextstep.jwp.model.User;
5+
import org.apache.catalina.controller.HttpController;
6+
import org.apache.catalina.session.Session;
7+
import org.apache.catalina.session.SessionManager;
8+
import org.apache.coyote.http11.common.HttpCookie;
9+
import org.apache.coyote.http11.request.HttpRequest;
10+
import org.apache.coyote.http11.response.HttpResponse;
11+
12+
import java.io.IOException;
13+
import java.util.*;
14+
15+
import static org.apache.coyote.http11.common.HttpHeaderType.LOCATION;
16+
import static org.apache.coyote.http11.common.HttpHeaderType.SET_COOKIE;
17+
import static org.apache.coyote.http11.response.HttpStatusCode.FOUND;
18+
19+
public class LoginController extends HttpController {
20+
21+
@Override
22+
public boolean canHandle(final HttpRequest httpRequest) {
23+
final Set<String> requestType = Set.of("/login");
24+
return requestType.contains(httpRequest.getTarget());
25+
}
26+
27+
@Override
28+
protected void doGet(final HttpRequest httpRequest, final HttpResponse httpResponse) throws IOException {
29+
final HttpCookie httpCookie = httpRequest.getCookie();
30+
String sessionId = httpCookie.getCookie("JSESSIONID");
31+
if (sessionId != null && SessionManager.getInstance().findSession(sessionId) != null) { // already login user
32+
httpResponse.addHeader(LOCATION, "/index.html");
33+
httpResponse.setStatusCode(FOUND);
34+
} else { // not login user
35+
handleResource("/login.html", httpRequest, httpResponse);
36+
}
37+
}
38+
39+
@Override
40+
protected void doPost(final HttpRequest httpRequest, final HttpResponse httpResponse) {
41+
final HttpCookie httpCookie = httpRequest.getCookie();
42+
String sessionId = httpCookie.getCookie("JSESSIONID");
43+
44+
final Optional<User> user = InMemoryUserRepository.findByAccount(httpRequest.getBody().get("account"));
45+
if (user.isEmpty() || !user.get().checkPassword(httpRequest.getBody().get("password"))) {
46+
// invalid user
47+
httpResponse.addHeader(LOCATION, "/401.html");
48+
httpResponse.setStatusCode(FOUND);
49+
return;
50+
}
51+
52+
if (sessionId != null) { // if already have session
53+
httpResponse.addHeader(LOCATION, "/index.html");
54+
httpResponse.setStatusCode(FOUND);
55+
return;
56+
}
57+
58+
// if no session
59+
final Session session = new Session(String.valueOf(UUID.randomUUID()));
60+
session.setAttribute("user", user);
61+
SessionManager.getInstance().add(session);
62+
sessionId = session.getId();
63+
64+
httpResponse.addHeader(LOCATION, "/index.html");
65+
httpResponse.addHeader(SET_COOKIE, "JSESSIONID=" + sessionId);
66+
httpResponse.setStatusCode(FOUND);
67+
}
68+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package nextstep.jwp.controller;
2+
3+
import nextstep.jwp.db.InMemoryUserRepository;
4+
import nextstep.jwp.model.User;
5+
import org.apache.catalina.controller.HttpController;
6+
import org.apache.coyote.http11.request.HttpRequest;
7+
import org.apache.coyote.http11.response.HttpResponse;
8+
9+
import java.io.IOException;
10+
import java.util.Set;
11+
12+
import static org.apache.coyote.http11.common.HttpHeaderType.LOCATION;
13+
import static org.apache.coyote.http11.response.HttpStatusCode.FOUND;
14+
15+
public class RegisterController extends HttpController {
16+
17+
@Override
18+
public boolean canHandle(final HttpRequest httpRequest) {
19+
final Set<String> requestType = Set.of("/register");
20+
return requestType.contains(httpRequest.getTarget());
21+
}
22+
23+
@Override
24+
public void doGet(final HttpRequest httpRequest, final HttpResponse httpResponse) throws IOException {
25+
handleResource("/register.html", httpRequest, httpResponse);
26+
}
27+
28+
@Override
29+
public void doPost(final HttpRequest httpRequest, final HttpResponse httpResponse) {
30+
final User newUser = new User(
31+
httpRequest.getBody().get("account"),
32+
httpRequest.getBody().get("password"),
33+
httpRequest.getBody().get("email")
34+
);
35+
InMemoryUserRepository.save(newUser);
36+
httpResponse.addHeader(LOCATION, "/index.html");
37+
httpResponse.setStatusCode(FOUND);
38+
}
39+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package nextstep.jwp.controller;
2+
3+
import org.apache.catalina.controller.HttpController;
4+
import org.apache.coyote.http11.request.HttpRequest;
5+
import org.apache.coyote.http11.response.HttpResponse;
6+
7+
import java.util.*;
8+
9+
import static org.apache.coyote.http11.common.HttpHeaderType.CONTENT_TYPE;
10+
import static org.apache.coyote.http11.common.MediaType.TEXT_HTML;
11+
import static org.apache.coyote.http11.response.HttpStatusCode.OK;
12+
13+
public class RootController extends HttpController {
14+
15+
@Override
16+
public boolean canHandle(final HttpRequest httpRequest) {
17+
final Set<String> requestType = Set.of("/");
18+
return requestType.contains(httpRequest.getTarget());
19+
}
20+
21+
@Override
22+
protected void doGet(final HttpRequest httpRequest, final HttpResponse httpResponse) {
23+
httpResponse.addHeader(CONTENT_TYPE, TEXT_HTML.stringifyWithUtf());
24+
httpResponse.setStatusCode(OK);
25+
httpResponse.setBody("Hello world!");
26+
}
27+
}

0 commit comments

Comments
 (0)