Skip to content
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

[톰캣 구현하기 1, 2단계] 재즈(함석명) 미션 제출합니다. #581

Merged
merged 31 commits into from
Sep 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
fed02f6
fix: remove implementation logback-classic on gradle (#501)
geoje Sep 5, 2024
7e91356
fix: add threads min-spare configuration on properties (#502)
geoje Sep 5, 2024
b1871e8
test: File 학습 테스트
seokmyungham Sep 4, 2024
97af7b2
test: IOStream 학습 테스트
seokmyungham Sep 4, 2024
50f9b6a
feat: index.html 응답 기능 추가
seokmyungham Sep 4, 2024
b628ead
feat: ContentType Enum 정의
seokmyungham Sep 5, 2024
0a8101e
feat: 요청 URI 확장자에 따라 ContentType을 동적으로 응답
seokmyungham Sep 5, 2024
024165d
feat: 로그인 HTML 응답 기능 추가
seokmyungham Sep 5, 2024
e5cd643
feat: 로그인 시 성공, 실패 여부에 따라 리다이렉트 처리
seokmyungham Sep 5, 2024
23f9092
feat: HTTP GET 메서드 조건을 추가
seokmyungham Sep 5, 2024
2c65d2d
feat: 로그인 요청을 GET에서 POST로 변경
seokmyungham Sep 5, 2024
d66eedf
feat: 회원가입 HTML 응답 기능 추가
seokmyungham Sep 5, 2024
f31812d
feat: 회원가입 기능 추가
seokmyungham Sep 5, 2024
a56d23a
refactor: 로그인 성공 로그 메시지 변경
seokmyungham Sep 5, 2024
1b7af13
feat: 로그인 성공 시 JSESSIONID 쿠키를 반환하는 기능 추가
seokmyungham Sep 5, 2024
c7ba93e
feat: 세션 객체와 세션을 관리하는 세션 매니저 추가
seokmyungham Sep 6, 2024
6eff5e8
feat: 로그인 성공 시 세션 저장소에 유저 세션을 저장
seokmyungham Sep 6, 2024
606730b
feat: 로그인 페이지 접근 시 이미 로그인 상태일 경우 리다이렉트 처리
seokmyungham Sep 6, 2024
a15a1cb
refactor: HttpMethod Enum 정의
seokmyungham Sep 6, 2024
7900c28
refactor: HttpStatus Enum 정의
seokmyungham Sep 6, 2024
a8569c3
refactor: RequestLine 정의
seokmyungham Sep 6, 2024
b815063
refactor: HttpRequest 정의
seokmyungham Sep 6, 2024
536b2d8
feat: 휴리스틱 캐싱 제거 학습 테스트 추가
seokmyungham Sep 6, 2024
be0b642
feat: HTTP 응답 압축 학습 테스트 추가
seokmyungham Sep 6, 2024
ee9d484
feat: ETag/If-None-Match 적용 학습 테스트 추가
seokmyungham Sep 6, 2024
fa6ad24
feat: 캐시 무효화 학습 테스트 추가
seokmyungham Sep 6, 2024
4654f58
refactor: RequestHeaders 구조 개선
seokmyungham Sep 6, 2024
953992c
refactor: RequestLine에 HttpMethod를 물어보도록 개선
seokmyungham Sep 6, 2024
12b4dba
refactor: 핸들러 분기 처리 개선
seokmyungham Sep 6, 2024
e92c2ec
move: ContentType 패키지 위치 이동
seokmyungham Sep 6, 2024
e72f18a
refactor: HttpResponse 공백 추가
seokmyungham Sep 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion study/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'ch.qos.logback:logback-classic:1.5.7'
implementation 'org.apache.commons:commons-lang3:3.14.0'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1'
implementation 'pl.allegro.tech.boot:handlebars-spring-boot-starter:0.4.1'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package cache.com.example;

import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.CacheControl;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import jakarta.servlet.http.HttpServletResponse;

@Controller
public class GreetingController {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package cache.com.example.cachecontrol;

import static com.google.common.net.HttpHeaders.CACHE_CONTROL;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.CacheControl;
import org.springframework.web.servlet.HandlerInterceptor;

public class CacheControlInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String cacheControl = CacheControl
.noCache()
.cachePrivate()
.getHeaderValue();

response.setHeader(CACHE_CONTROL, cacheControl);
return true;
}
}
Comment on lines +10 to +22
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스프링 단에서 캐시 설정을 위해 WebContentInterceptor 를 제공해주는데, 이를 이용해 보아도 좋을 것 같아욥!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 캐시 정책과 관련하여 이미 제공해주는 인터셉터 구현체가 있었군요!!
알려주셔서 감사해요 낙낙 😊

Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ public class CacheWebConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(new CacheControlInterceptor());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package cache.com.example.cachecontrol;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/templates/", "classpath:/static/");
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
package cache.com.example.etag;

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

import cache.com.example.version.ResourceVersion;
import org.springframework.beans.factory.annotation.Autowired;
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;

@Configuration
public class EtagFilterConfiguration {

// @Bean
// public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() {
// return null;
// }
private final ResourceVersion version;

@Autowired
public EtagFilterConfiguration(ResourceVersion version) {
this.version = version;
}

@Bean
public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() {
FilterRegistrationBean<ShallowEtagHeaderFilter> filterRegistrationBean = new FilterRegistrationBean<>(
new ShallowEtagHeaderFilter());
filterRegistrationBean.addUrlPatterns("/etag", PREFIX_STATIC_RESOURCES + "/" + version.getVersion() + "/*");
return filterRegistrationBean;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package cache.com.example.version;

import java.util.concurrent.TimeUnit;
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;

Expand All @@ -20,6 +22,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(365, TimeUnit.DAYS).cachePublic());
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package cache.com.example.version;

import org.springframework.stereotype.Component;

import jakarta.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.springframework.stereotype.Component;

@Component
public class ResourceVersion {
Expand Down
8 changes: 8 additions & 0 deletions study/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,12 @@ server:
accept-count: 1
max-connections: 1
threads:
min-spare: 2
max: 2
compression:
enabled: true
min-response-size: 10
spring:
mvc:
view:
suffix: .html
20 changes: 11 additions & 9 deletions study/src/test/java/study/FileTest.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package study;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
* 웹서버는 사용자가 요청한 html 파일을 제공 할 수 있어야 한다.
Expand All @@ -28,7 +29,7 @@ class FileTest {
final String fileName = "nextstep.txt";

// todo
final String actual = "";
final String actual = getClass().getClassLoader().getResource(fileName).getPath();

assertThat(actual).endsWith(fileName);
}
Expand All @@ -40,14 +41,15 @@ class FileTest {
* File, Files 클래스를 사용하여 파일의 내용을 읽어보자.
*/
@Test
void 파일의_내용을_읽는다() {
void 파일의_내용을_읽는다() throws IOException {
final String fileName = "nextstep.txt";

// todo
final Path path = null;
String pathStr = getClass().getClassLoader().getResource(fileName).getPath();
Path path = new File(pathStr).toPath();

// todo
final List<String> actual = Collections.emptyList();
final List<String> actual = Files.readAllLines(path);

assertThat(actual).containsOnly("nextstep");
}
Expand Down
44 changes: 32 additions & 12 deletions study/src/test/java/study/IOStreamTest.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
package study;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import java.io.*;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;

/**
* 자바는 스트림(Stream)으로부터 I/O를 사용한다.
* 입출력(I/O)은 하나의 시스템에서 다른 시스템으로 데이터를 이동 시킬 때 사용한다.
Expand Down Expand Up @@ -39,7 +49,7 @@ class OutputStream_학습_테스트 {
* OutputStream의 서브 클래스(subclass)는 특정 매체에 데이터를 쓰기 위해 write(int b) 메서드를 사용한다.
* 예를 들어, FilterOutputStream은 파일로 데이터를 쓸 때,
* 또는 DataOutputStream은 자바의 primitive type data를 다른 매체로 데이터를 쓸 때 사용한다.
*
*
* write 메서드는 데이터를 바이트로 출력하기 때문에 비효율적이다.
* <code>write(byte[] data)</code>와 <code>write(byte b[], int off, int len)</code> 메서드는
* 1바이트 이상을 한 번에 전송 할 수 있어 훨씬 효율적이다.
Expand All @@ -53,6 +63,7 @@ class OutputStream_학습_테스트 {
* todo
* OutputStream 객체의 write 메서드를 사용해서 테스트를 통과시킨다
*/
outputStream.write(bytes);

final String actual = outputStream.toString();

Expand All @@ -63,7 +74,7 @@ class OutputStream_학습_테스트 {
/**
* 효율적인 전송을 위해 스트림에서 버퍼링을 사용 할 수 있다.
* BufferedOutputStream 필터를 연결하면 버퍼링이 가능하다.
*
*
* 버퍼링을 사용하면 OutputStream을 사용할 때 flush를 사용하자.
* flush() 메서드는 버퍼가 아직 가득 차지 않은 상황에서 강제로 버퍼의 내용을 전송한다.
* Stream은 동기(synchronous)로 동작하기 때문에 버퍼가 찰 때까지 기다리면
Expand All @@ -78,6 +89,7 @@ class OutputStream_학습_테스트 {
* flush를 사용해서 테스트를 통과시킨다.
* ByteArrayOutputStream과 어떤 차이가 있을까?
*/
outputStream.flush();

verify(outputStream, atLeastOnce()).flush();
outputStream.close();
Expand All @@ -96,6 +108,8 @@ class OutputStream_학습_테스트 {
* try-with-resources를 사용한다.
* java 9 이상에서는 변수를 try-with-resources로 처리할 수 있다.
*/
try (outputStream) {
}

verify(outputStream, atLeastOnce()).close();
}
Expand All @@ -108,7 +122,7 @@ class OutputStream_학습_테스트 {
* InputStream은 다른 매체로부터 바이트로 데이터를 읽을 때 사용한다.
* InputStream의 read() 메서드는 기반 메서드이다.
* <code>public abstract int read() throws IOException;</code>
*
*
* InputStream의 서브 클래스(subclass)는 특정 매체에 데이터를 읽기 위해 read() 메서드를 사용한다.
*/
@Nested
Expand All @@ -128,7 +142,7 @@ class InputStream_학습_테스트 {
* todo
* inputStream에서 바이트로 반환한 값을 문자열로 어떻게 바꿀까?
*/
final String actual = "";
final String actual = new String(inputStream.readAllBytes());

assertThat(actual).isEqualTo("🤩");
assertThat(inputStream.read()).isEqualTo(-1);
Expand All @@ -148,6 +162,8 @@ class InputStream_학습_테스트 {
* try-with-resources를 사용한다.
* java 9 이상에서는 변수를 try-with-resources로 처리할 수 있다.
*/
try (inputStream) {
}

verify(inputStream, atLeastOnce()).close();
}
Expand All @@ -169,12 +185,12 @@ class FilterStream_학습_테스트 {
* 버퍼 크기를 지정하지 않으면 버퍼의 기본 사이즈는 얼마일까?
*/
@Test
void 필터인_BufferedInputStream를_사용해보자() {
void 필터인_BufferedInputStream를_사용해보자() throws IOException {
final String text = "필터에 연결해보자.";
final InputStream inputStream = new ByteArrayInputStream(text.getBytes());
final InputStream bufferedInputStream = null;
final InputStream bufferedInputStream = new BufferedInputStream(inputStream);

final byte[] actual = new byte[0];
final byte[] actual = bufferedInputStream.readAllBytes();

assertThat(bufferedInputStream).isInstanceOf(FilterInputStream.class);
assertThat(actual).isEqualTo("필터에 연결해보자.".getBytes());
Expand Down Expand Up @@ -204,8 +220,12 @@ class InputStreamReader_학습_테스트 {
"😋😛😝😜🤪🤨🧐🤓😎🥸🤩",
"");
final InputStream inputStream = new ByteArrayInputStream(emoji.getBytes());
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

final StringBuilder actual = new StringBuilder();
bufferedReader.lines()
.forEach(str -> actual.append(str).append("\r\n"));

assertThat(actual).hasToString(emoji);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package org.apache.catalina;

import jakarta.servlet.http.HttpSession;
package org.apache.catalina.manager;

import java.io.IOException;
import java.util.Optional;

/**
* A <b>Manager</b> manages the pool of Sessions that are associated with a
Expand All @@ -29,7 +28,7 @@ public interface Manager {
*
* @param session Session to be added
*/
void add(HttpSession session);
void add(Session session);

/**
* Return the active Session, associated with this Manager, with the
Expand All @@ -45,12 +44,12 @@ public interface Manager {
* @return the request session or {@code null} if a session with the
* requested ID could not be found
*/
HttpSession findSession(String id) throws IOException;
Optional<Session> findSession(String id) throws IOException;

/**
* Remove this Session from the active Sessions for this Manager.
*
* @param session Session to be removed
*/
void remove(HttpSession session);
void remove(Session session);
}
27 changes: 27 additions & 0 deletions tomcat/src/main/java/org/apache/catalina/manager/Session.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.apache.catalina.manager;

import java.util.HashMap;
import java.util.Map;

public class Session {

private final String id;
private final Map<String, Object> values;

public Session(String id) {
this.id = id;
values = new HashMap<>();
}

public String getId() {
return id;
}

public void setAttribute(String name, Object value) {
values.put(name, value);
}

public void removeAttribute(String name) {
values.remove(name);
}
}
Loading