Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0e80615
test: 학습 테스트 진행
Mingyum-Kim Sep 3, 2024
fe4981a
feat: `/index.html`에 접근했을 때 static/index.html 파일이 보여지도록 구현
Mingyum-Kim Sep 4, 2024
9703596
feat: 자원에 따라 다른 Conent-Type으로 응답을 보내도록 수정
Mingyum-Kim Sep 4, 2024
fed02f6
fix: remove implementation logback-classic on gradle (#501)
geoje Sep 5, 2024
bffee2a
fix: 공백 처리 때문에 index.html이 불러와지지 않는 현상 해결
Mingyum-Kim Sep 5, 2024
0964ae2
feat: 로그인 요청 시 쿼리 파라미터를 파싱하는 기능 구현
Mingyum-Kim Sep 5, 2024
7e91356
fix: add threads min-spare configuration on properties (#502)
geoje Sep 5, 2024
72958ed
feat: 로그인에 성공하거나 실패한 경우 다른 페이지로 리다이렉트하는
Mingyum-Kim Sep 5, 2024
a535e6d
feat: POST 요청으로 회원 가입 시 요청 본문에서 정보를 가져와 사용자 저장하는 기능 구현
Mingyum-Kim Sep 5, 2024
3d9dc32
feat: 로그인 시 POST 요청을 보내도록 수정
Mingyum-Kim Sep 5, 2024
88c4a05
feat: 로그인 시 세션 아이디를 쿠키에 등록하는 기능 구현
Mingyum-Kim Sep 5, 2024
b0da4a6
feat: 쿠키에 JSESSIONID가 없을 때 Set-Cookie를 발급하는 기능 구현
Mingyum-Kim Sep 5, 2024
795a568
feat: 이미 로그인한 회원의 경우 로그인 페이지에 접근 불가하도록 구현
Mingyum-Kim Sep 5, 2024
9692263
refactor: 쿠키가 없을 때 일반 페이지에 접근하면 세션 아이디를 주지 않음
Mingyum-Kim Sep 5, 2024
38c53ab
Merge branch 'main' into step1
Mingyum-Kim Sep 6, 2024
66597e3
feat: HTTP 활용 학습 테스트 진행
Mingyum-Kim Sep 6, 2024
acde025
refactor: 뭉친 코드를 클래스 분리하여 리팩토링
Mingyum-Kim Sep 6, 2024
df161b1
fix: JSESSIONID를 추가하지 않아 로그인되지 않는 문제 해결
Mingyum-Kim Sep 6, 2024
5dc81b2
fix: 세션 아이디가 달라서 로그인 시 로그인 페이지가 접속되는 현상 해결
Mingyum-Kim Sep 7, 2024
da09610
refactor: 기존 코드 주석 제거
Mingyum-Kim Sep 7, 2024
85007fd
style: 주석 제거 및 컨벤션 맞추기
Mingyum-Kim Sep 7, 2024
f1aed30
refactor: 미구현된 파일 제거
Mingyum-Kim Sep 7, 2024
6211629
refactor: 필터를 사용하여 ETag를 지정하는 것으로 변경
Mingyum-Kim Sep 8, 2024
de9a2ef
refactor: 인터셉터에서 CacheControl을 적용할 수 있도록 수정
Mingyum-Kim Sep 8, 2024
95abccd
refactor: Content-Length 헤더 이름을 상수화
Mingyum-Kim Sep 8, 2024
605727c
refactor: 정적 파일을 한 번에 처리하도록 리팩토링
Mingyum-Kim Sep 8, 2024
539be1c
fix: Json 파일을 보내는 경우를 고려하여 MediaType 수정
Mingyum-Kim Sep 8, 2024
407826a
refactor: 로그인 시 쿠키 세션을 꺼내오는 코드 가독성 개선
Mingyum-Kim Sep 8, 2024
2439262
refactor: 요청한 사용자 정보가 없는 경우 401 페이지를 보여주도록 수정
Mingyum-Kim Sep 8, 2024
0bb26a7
refactor: 쿠키의 키값을 상수화하여 리팩토링
Mingyum-Kim Sep 8, 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
12 changes: 8 additions & 4 deletions study/src/main/java/cache/com/example/GreetingController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.springframework.http.CacheControl;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

Expand All @@ -11,8 +12,10 @@
public class GreetingController {

@GetMapping("/")
public String index() {
return "index";
public ResponseEntity<String> index() {
return ResponseEntity.ok()
.header("Content-Encoding", "gzip")
.body("index");
}

/**
Expand All @@ -29,8 +32,9 @@ public String cacheControl(final HttpServletResponse response) {
}

@GetMapping("/etag")
public String etag() {
return "index";
public ResponseEntity<String> etag() {
return ResponseEntity.ok()
.body("index");
}

@GetMapping("/resource-versioning")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package cache.com.example.cachecontrol;

import org.springframework.web.servlet.HandlerInterceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class CacheControlInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
response.addHeader("Cache-Control", "no-cache, private");
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ public class CacheWebConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(new CacheControlInterceptor())
.addPathPatterns("/");
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
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;

@Configuration
public class EtagFilterConfiguration {

// @Bean
// public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() {
// return null;
// }
}
@Bean
public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() {
FilterRegistrationBean<ShallowEtagHeaderFilter> filterRegistrationBean = new FilterRegistrationBean<>(new ShallowEtagHeaderFilter());
filterRegistrationBean.addUrlPatterns("/etag");
return filterRegistrationBean;
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
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;
import org.springframework.web.servlet.resource.VersionResourceResolver;

@Configuration
public class CacheBustingWebConfig implements WebMvcConfigurer {
Expand All @@ -20,6 +24,9 @@ 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())
.resourceChain(false)
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

import org.springframework.stereotype.Component;

import jakarta.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import jakarta.annotation.PostConstruct;

@Component
public class ResourceVersion {

Expand Down
3 changes: 3 additions & 0 deletions study/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@ server:
accept-count: 1
max-connections: 1
threads:
min-spare: 2
max: 2
compression:
enabled: true
24 changes: 19 additions & 5 deletions study/src/test/java/study/FileTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,23 @@

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDirFactory;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

import javax.swing.filechooser.FileSystemView;

/**
* 웹서버는 사용자가 요청한 html 파일을 제공 할 수 있어야 한다.
* File 클래스를 사용해서 파일을 읽어오고, 사용자에게 전달한다.
Expand All @@ -28,7 +38,10 @@ class FileTest {
final String fileName = "nextstep.txt";

// todo
final String actual = "";
URL url = getClass().getClassLoader().getResource(fileName);
File file = new File(url.getPath());

final String actual = file.getAbsolutePath();

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

// todo
final Path path = null;
final URL url = getClass().getClassLoader().getResource(fileName);
final File file = new File(url.getPath());
final Path path = file.toPath();

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

final List<String> actual = Files.readAllLines(path);
assertThat(actual).containsOnly("nextstep");
}
}
25 changes: 19 additions & 6 deletions study/src/test/java/study/IOStreamTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class OutputStream_학습_테스트 {
* todo
* OutputStream 객체의 write 메서드를 사용해서 테스트를 통과시킨다
*/
outputStream.write(bytes);

final String actual = outputStream.toString();

Expand All @@ -78,6 +79,7 @@ class OutputStream_학습_테스트 {
* flush를 사용해서 테스트를 통과시킨다.
* ByteArrayOutputStream과 어떤 차이가 있을까?
*/
outputStream.flush();

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

outputStream.close();

verify(outputStream, atLeastOnce()).close();
}
}
Expand Down Expand Up @@ -128,7 +132,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 +152,7 @@ class InputStream_학습_테스트 {
* try-with-resources를 사용한다.
* java 9 이상에서는 변수를 try-with-resources로 처리할 수 있다.
*/
inputStream.close();

verify(inputStream, atLeastOnce()).close();
}
Expand All @@ -169,12 +174,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 All @@ -197,7 +202,7 @@ class InputStreamReader_학습_테스트 {
* 필터인 BufferedReader를 사용하면 readLine 메서드를 사용해서 문자열(String)을 한 줄 씩 읽어올 수 있다.
*/
@Test
void BufferedReader를_사용하여_문자열을_읽어온다() {
void BufferedReader를_사용하여_문자열을_읽어온다() throws IOException {
final String emoji = String.join("\r\n",
"😀😃😄😁😆😅😂🤣🥲☺️😊",
"😇🙂🙃😉😌😍🥰😘😗😙😚",
Expand All @@ -206,7 +211,15 @@ class InputStreamReader_학습_테스트 {
final InputStream inputStream = new ByteArrayInputStream(emoji.getBytes());

final StringBuilder actual = new StringBuilder();

try (
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
) {
String line;
while((line = bufferedReader.readLine()) != null) {
actual.append(line + "\r\n");
}
}
assertThat(actual).hasToString(emoji);
}
}
Expand Down
35 changes: 25 additions & 10 deletions tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,38 @@
package org.apache.coyote.http11;


import com.techcourse.db.InMemoryUserRepository;
import com.techcourse.exception.UncheckedServletException;
import com.techcourse.model.User;

import org.apache.coyote.Processor;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.request.RequestHandler;
import org.apache.coyote.http11.session.SessionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

public class Http11Processor implements Runnable, Processor {

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

private final Socket connection;
private static final Map<String, String> httpRequestHeader = new HashMap<>();
private static final String sessionId = "JSESSIONID=sessionId";

public Http11Processor(final Socket connection) {
this.connection = connection;
Expand All @@ -29,18 +49,13 @@ public void process(final Socket connection) {
try (final var inputStream = connection.getInputStream();
final var outputStream = connection.getOutputStream()) {

final var responseBody = "Hello world!";
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
HttpRequest httpRequest = new HttpRequest(reader);

final var response = String.join("\r\n",
"HTTP/1.1 200 OK ",
"Content-Type: text/html;charset=utf-8 ",
"Content-Length: " + responseBody.getBytes().length + " ",
"",
responseBody);
RequestHandler requestHandler = new RequestHandler(httpRequest, outputStream);
requestHandler.handleRequest();

outputStream.write(response.getBytes());
outputStream.flush();
} catch (IOException | UncheckedServletException e) {
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.apache.coyote.http11.request;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class HttpRequest {

private static final String CONTENT_LENGTH = "Content-Length";

private final String method;
private final String path;
private final Map<String, String> headers = new HashMap<>();
private final String body;

public HttpRequest(BufferedReader reader) throws IOException {
String initialLine = reader.readLine();
this.method = initialLine.split(" ")[0];
this.path = initialLine.split(" ")[1];
String line;
while ((line = reader.readLine()) != null && !line.isEmpty()) {
String[] header = line.split(":");
headers.put(header[0].trim(), header[1].trim());
}
this.body = parseBody(reader);
}

private String parseBody(BufferedReader reader) throws IOException {
if(headers.get(CONTENT_LENGTH) == null) {
return null;
}
int contentLength = Integer.parseInt(headers.get(CONTENT_LENGTH));
if(contentLength > 0) {
char[] body = new char[contentLength];
reader.read(body, 0, contentLength);
return new String(body);
}
return null;
Comment on lines +38 to +39
Copy link
Member

Choose a reason for hiding this comment

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

이것은 궁금한 부분인데요!
contentLengthnull 도 아니며 0보다 작다면, 에러가 아닌 null을 보내주시는 것으로 결정하신 이유가 궁금합니다! ☺️

Copy link
Author

Choose a reason for hiding this comment

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

Content Length가 0이라면 본문이 없는 요청이에요 ! GET 메서드로 보내는 상황 등이 이에 해당할거에요.
이때는 Body가 아무것도 없다는 의미에서 NULL을 담기위해 NULL을 반환하게 되었습니다. 😀

Copy link
Member

Choose a reason for hiding this comment

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

헤더는 조작될 수도 있지 않을까요??
예를 들어 예상한 값보다 크게 작성한다면 헤더가 아직 전송중이라고 파악할 수도 있을 것 같습니다!

저도 몰랐는데 이를 찾아보니, Slow Http Dos 공격이라고도 부른다더라고요! 🫨

이 사실을 알고 저도 예외처리를 빡세게 해보려고 하는데 같이 시작하는 것은 어떨까요? 😋

}

public String getMethod() {
return method;
}

public String getPath() {
return path;
}

public String getCookie() {
return headers.get("Cookie");
}

public String getBody() {
return body;
}
}
Loading