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단계] 헤나(박현서) 미션 제출합니다. #378

Merged
merged 50 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
ac726f1
test: `@Override` 어노테이션 추가 작성
hyena0608 Sep 2, 2023
10c4979
test: step01 학습 테스트 추가
hyena0608 Sep 2, 2023
9bcbc57
feat: HTTP 버전, HTTP 상태 구현
hyena0608 Sep 2, 2023
ea7c224
feat: HTTP 헤더, HTTP 메서드, MediaType 구현
hyena0608 Sep 2, 2023
dbbddcd
refactor: HttpHeader 내 매핑 필드 삭제
hyena0608 Sep 2, 2023
666004f
refactor: Http 버전 내부 조회를 위한 정적 팩토리 메서드 내부 조건 수정 및 네이밍 변경
hyena0608 Sep 2, 2023
9e1615a
feat: 헤더 객체 구현
hyena0608 Sep 2, 2023
ca075d9
feat: Coyote 커스텀 예외 구현
hyena0608 Sep 2, 2023
ff25cff
feat: Http 요청 객체 구현
hyena0608 Sep 2, 2023
670ed00
feat: Http 응답 객체 구현
hyena0608 Sep 2, 2023
f6748c6
feat: HTTP 요청의 버전이 HTTP/1.1 일 때 HTTP 동적 응답 기능 구현
hyena0608 Sep 2, 2023
189127e
feat: 헤더 css 미디어 타입 인식 기능 구현
hyena0608 Sep 2, 2023
52508c8
feat: HTTP 요청, 응답 로깅 기능 구현
hyena0608 Sep 2, 2023
ce04d00
refactor: 헤더 toString에 ':'을 추가
hyena0608 Sep 3, 2023
202cd40
chore: 요청, 응답 패키지 분리
hyena0608 Sep 3, 2023
025c49f
feat: Request 핸들러 구현
hyena0608 Sep 3, 2023
74c512c
feat: Request 핸들러 컴포시트, 유저 로그인 Request 핸들러 구현
hyena0608 Sep 3, 2023
3ebc4f6
feat: 쿼리 파라미터 객체 구현
hyena0608 Sep 3, 2023
cd9a2b7
refactor: Http 요청 객체 쿼리 파라미터 받을 수 있도록 기능 리팩터링
hyena0608 Sep 3, 2023
918ab37
chore: login 태그 수정
hyena0608 Sep 3, 2023
fbb55a5
refactor: Http/1.1 프로세서 리팩터링
hyena0608 Sep 3, 2023
505d5fc
refactor: 요청 핸들러 정적 자원 중복 기능 리팩터링
hyena0608 Sep 3, 2023
46849a6
feat: Http 요청 객체 내부에 미디어 타입 추가
hyena0608 Sep 3, 2023
f069c62
feat: 로그인 성공, 실패에 대한 요청 핸들러 응답 처리 기능 구현
hyena0608 Sep 3, 2023
253252d
feat: 사용자 등록 페이지 응답 요청 핸들러 기능 구현
hyena0608 Sep 3, 2023
236d6cd
test: 로그인 요청 핸들러, 회원 등록 페이지 반환 핸들러 테스트 추가
hyena0608 Sep 3, 2023
dc9987b
feat: 요청 바디 객체 구현
hyena0608 Sep 3, 2023
5837689
feat: HTTP 요청 객체에 요청 바디 추가
hyena0608 Sep 3, 2023
33c6ffd
feat: 회원가입 요청 핸들러 구현
hyena0608 Sep 3, 2023
d19e246
feat: 요청 라인 객체 구현 및 HTTP 요청 객체 필드 수정
hyena0608 Sep 3, 2023
5008d64
refactor: Http 응답 바이트코드 기능 리팩터링
hyena0608 Sep 3, 2023
0ac0093
feat: 로그인 페이지 요청시 사용자 세션이 존재할 경우 메인 홈페이지로 리다이렉트 응답 기능 구현
hyena0608 Sep 4, 2023
a0bc9ec
test: 요청 핸들러 테스트 추가
hyena0608 Sep 4, 2023
1bae938
feat: Http 메서드 조회 예외 메시지 추가
hyena0608 Sep 4, 2023
cc6a30e
refactor: 요청 핸들러 컴포시트에 필요하지 않은 검증 삭제
hyena0608 Sep 4, 2023
7853075
feat: 쿼리 파라미터 equals & hashcode 재정의
hyena0608 Sep 4, 2023
1bd7f6c
feat: 요청 패스 equals & hashcode 재정의 및 예외 검증 추가
hyena0608 Sep 4, 2023
ae8adef
feat: 요청 바디 빈값 생성 기능 추가
hyena0608 Sep 4, 2023
78eb514
test: 요청 핸들러 매핑 객체 테스트 및 요청 라인 테스트 추가
hyena0608 Sep 4, 2023
0fb0e14
refactor: HTTP 헤더 enum 명 변경
hyena0608 Sep 4, 2023
e4d2494
feat: 쿠키 정적 팩토리 메서드 빈값 반환 기능 구현
hyena0608 Sep 4, 2023
4247c41
feat: 세션 객체 구현
hyena0608 Sep 4, 2023
9685c40
refactor: HTTP 헤더, 쿠키, 세션 객체 리팩터링
hyena0608 Sep 4, 2023
91528d6
refactor: 빈 요청 바디, 빈 응답 바디의 상태 안전을 위해 메서드로 리팩터링
hyena0608 Sep 4, 2023
99ddfe6
refactor: 쿠키 키값 목록 조회 메서드명 리팩터링
hyena0608 Sep 4, 2023
04cfce8
feat: HTTP 응답 객체 빌더 패턴 기능 구현
hyena0608 Sep 4, 2023
34e2879
refactor: 요청 핸들러의 HTTP 응답 리팩터링
hyena0608 Sep 4, 2023
e893200
refactor: Http 응답 객체 생성자 접근 제한 수정
hyena0608 Sep 4, 2023
35f96af
test: 응답 바디 객체 테스트 추가
hyena0608 Sep 4, 2023
6b6a645
refactor: HttpVersion 예외 메시지 추가 및 클래스 생성자 접근 제한 추가
hyena0608 Sep 4, 2023
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
47 changes: 37 additions & 10 deletions study/src/test/java/study/FileTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
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;
Expand All @@ -16,39 +23,59 @@
@DisplayName("File 클래스 학습 테스트")
class FileTest {

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

/**
* resource 디렉터리 경로 찾기
*
* <p>
* File 객체를 생성하려면 파일의 경로를 알아야 한다.
* 자바 애플리케이션은 resource 디렉터리에 HTML, CSS 같은 정적 파일을 저장한다.
* resource 디렉터리의 경로는 어떻게 알아낼 수 있을까?
*/
@Test
void resource_디렉터리에_있는_파일의_경로를_찾는다() {
// given
final String fileName = "nextstep.txt";

// todo
final String actual = "";
// when
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
final URL url = classLoader.getResource(fileName);
final String actual = url.getPath();

log.info("=================> classLoader = {}", classLoader);
log.info("=================> url = {}", url);
log.info("=================> path = {}", actual);

// then
assertThat(actual).endsWith(fileName);
}

/**
* 파일 내용 읽기
*
* <p>
* 읽어온 파일의 내용을 I/O Stream을 사용해서 사용자에게 전달 해야 한다.
* File, Files 클래스를 사용하여 파일의 내용을 읽어보자.
*/
@Test
void 파일의_내용을_읽는다() {
void 파일의_내용을_읽는다() throws URISyntaxException, IOException {
// given
final String fileName = "nextstep.txt";

// todo
final Path path = null;
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
final URI uri = classLoader.getResource(fileName).toURI();
final File file = new File(uri);
final Path path = file.getAbsoluteFile().toPath();

// when
final List<String> actual = Files.readAllLines(path);

// todo
final List<String> actual = Collections.emptyList();
log.info("=================> classLoader = {}", classLoader);
log.info("=================> uri = {}", uri);
log.info("=================> file = {}", file);
log.info("=================> path = {}", path);
log.info("=================> actual = {}", actual);

// then
assertThat(actual).containsOnly("nextstep");
}
}
135 changes: 103 additions & 32 deletions study/src/test/java/study/IOStreamTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,50 @@
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
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 java.util.List;
import java.util.stream.Collectors;

import static java.nio.charset.StandardCharsets.UTF_16;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

/**
* 자바는 스트림(Stream)으로부터 I/O를 사용한다.
* 입출력(I/O)은 하나의 시스템에서 다른 시스템으로 데이터를 이동 시킬 때 사용한다.
*
* <p>
* InputStream은 데이터를 읽고, OutputStream은 데이터를 쓴다.
* FilterStream은 InputStream이나 OutputStream에 연결될 수 있다.
* FilterStream은 읽거나 쓰는 데이터를 수정할 때 사용한다. (e.g. 암호화, 압축, 포맷 변환)
*
* <p>
* Stream은 데이터를 바이트로 읽고 쓴다.
* 바이트가 아닌 텍스트(문자)를 읽고 쓰려면 Reader와 Writer 클래스를 연결한다.
* Reader, Writer는 다양한 문자 인코딩(e.g. UTF-8)을 처리할 수 있다.
*/
@DisplayName("Java I/O Stream 클래스 학습 테스트")
class IOStreamTest {

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

/**
* OutputStream 학습하기
*
* <p>
* 자바의 기본 출력 클래스는 java.io.OutputStream이다.
* OutputStream의 write(int b) 메서드는 기반 메서드이다.
* <code>public abstract void write(int b) throws IOException;</code>
Expand All @@ -39,46 +59,55 @@ class OutputStream_학습_테스트 {
* OutputStream의 서브 클래스(subclass)는 특정 매체에 데이터를 쓰기 위해 write(int b) 메서드를 사용한다.
* 예를 들어, FilterOutputStream은 파일로 데이터를 쓸 때,
* 또는 DataOutputStream은 자바의 primitive type data를 다른 매체로 데이터를 쓸 때 사용한다.
*
* <p>
* write 메서드는 데이터를 바이트로 출력하기 때문에 비효율적이다.
* <code>write(byte[] data)</code>와 <code>write(byte b[], int off, int len)</code> 메서드는
* 1바이트 이상을 한 번에 전송 할 수 있어 훨씬 효율적이다.
*/
@Test
void OutputStream은_데이터를_바이트로_처리한다() throws IOException {
final byte[] bytes = {110, 101, 120, 116, 115, 116, 101, 112};
final OutputStream outputStream = new ByteArrayOutputStream(bytes.length);

/**
* todo
* OutputStream 객체의 write 메서드를 사용해서 테스트를 통과시킨다
*/

// given
final byte[] bytes = {110, 101, 120, 116, 115, 116, 101, 112};
final OutputStream outputStream = new ByteArrayOutputStream(bytes.length);

// when
outputStream.write(bytes);
final String actual = outputStream.toString();

// then
assertThat(actual).isEqualTo("nextstep");
outputStream.close();
}

/**
* 효율적인 전송을 위해 스트림에서 버퍼링을 사용 할 수 있다.
* BufferedOutputStream 필터를 연결하면 버퍼링이 가능하다.
*
* <p>
* 버퍼링을 사용하면 OutputStream을 사용할 때 flush를 사용하자.
* flush() 메서드는 버퍼가 아직 가득 차지 않은 상황에서 강제로 버퍼의 내용을 전송한다.
* Stream은 동기(synchronous)로 동작하기 때문에 버퍼가 찰 때까지 기다리면
* 데드락(deadlock) 상태가 되기 때문에 flush로 해제해야 한다.
*/
@Test
void BufferedOutputStream을_사용하면_버퍼링이_가능하다() throws IOException {
final OutputStream outputStream = mock(BufferedOutputStream.class);

/**
* todo
* flush를 사용해서 테스트를 통과시킨다.
* ByteArrayOutputStream과 어떤 차이가 있을까?
*/

// given
final OutputStream outputStream = mock(BufferedOutputStream.class);

// when
outputStream.flush();

// then
verify(outputStream, atLeastOnce()).flush();
outputStream.close();
}
Expand All @@ -89,26 +118,35 @@ class OutputStream_학습_테스트 {
*/
@Test
void OutputStream은_사용하고_나서_close_처리를_해준다() throws IOException {
final OutputStream outputStream = mock(OutputStream.class);

/**
* todo
* try-with-resources를 사용한다.
* java 9 이상에서는 변수를 try-with-resources로 처리할 수 있다.
*/

// given
OutputStream outputStream = mock(OutputStream.class);

// when
try (outputStream) {
outputStream.flush();
} catch (IOException e) {
throw new AssertionError();
}

// then
verify(outputStream, atLeastOnce()).close();
}
}

/**
* InputStream 학습하기
*
* <p>
* 자바의 기본 입력 클래스는 java.io.InputStream이다.
* InputStream은 다른 매체로부터 바이트로 데이터를 읽을 때 사용한다.
* InputStream의 read() 메서드는 기반 메서드이다.
* <code>public abstract int read() throws IOException;</code>
*
* <p>
* InputStream의 서브 클래스(subclass)는 특정 매체에 데이터를 읽기 위해 read() 메서드를 사용한다.
*/
@Nested
Expand All @@ -121,17 +159,24 @@ class InputStream_학습_테스트 {
*/
@Test
void InputStream은_데이터를_바이트로_읽는다() throws IOException {
byte[] bytes = {-16, -97, -92, -87};
final InputStream inputStream = new ByteArrayInputStream(bytes);

/**
* todo
* inputStream에서 바이트로 반환한 값을 문자열로 어떻게 바꿀까?
*/
final String actual = "";

assertThat(actual).isEqualTo("🤩");
assertThat(inputStream.read()).isEqualTo(-1);
// given
byte[] bytes = {-16, -97, -92, -87};
final InputStream inputStream = new ByteArrayInputStream(bytes);

// when
final String actual = new String(inputStream.readAllBytes());

// then
assertAll(
() -> assertThat(actual).isEqualTo("🤩"),
() -> assertThat(inputStream.read()).isEqualTo(-1)
);

inputStream.close();
}

Expand All @@ -141,21 +186,30 @@ class InputStream_학습_테스트 {
*/
@Test
void InputStream은_사용하고_나서_close_처리를_해준다() throws IOException {
final InputStream inputStream = mock(InputStream.class);

/**
* todo
* try-with-resources를 사용한다.
* java 9 이상에서는 변수를 try-with-resources로 처리할 수 있다.
*/

// given
final InputStream inputStream = mock(InputStream.class);

// when
try (inputStream) {
inputStream.readAllBytes();
} catch (IOException e) {
throw new AssertionError();
}

// then
verify(inputStream, atLeastOnce()).close();
}
}

/**
* FilterStream 학습하기
*
* <p>
* 필터는 필터 스트림, reader, writer로 나뉜다.
* 필터는 바이트를 다른 데이터 형식으로 변환 할 때 사용한다.
* reader, writer는 UTF-8, ISO 8859-1 같은 형식으로 인코딩된 텍스트를 처리하는 데 사용된다.
Expand All @@ -169,13 +223,21 @@ class FilterStream_학습_테스트 {
* 버퍼 크기를 지정하지 않으면 버퍼의 기본 사이즈는 얼마일까?
*/
@Test
void 필터인_BufferedInputStream를_사용해보자() {
void 필터인_BufferedInputStream를_사용해보자() throws IOException {
// given
final String text = "필터에 연결해보자.";
final InputStream inputStream = new ByteArrayInputStream(text.getBytes());
final InputStream bufferedInputStream = null;
final InputStream inputStream = new ByteArrayInputStream(text.getBytes(UTF_8));
final InputStream bufferedInputStream = new BufferedInputStream(inputStream);

final byte[] actual = new byte[0];
log.info("===========> inputStream = {}", new ByteArrayInputStream(text.getBytes(UTF_8)).readAllBytes());
log.info("===========> bufferedInputStream = {}", new BufferedInputStream(new ByteArrayInputStream(text.getBytes(UTF_8))).readAllBytes());

// when
final byte[] actual = bufferedInputStream.readAllBytes();

log.info("===========> actual = {}", actual);

// then
assertThat(bufferedInputStream).isInstanceOf(FilterInputStream.class);
assertThat(actual).isEqualTo("필터에 연결해보자.".getBytes());
}
Expand All @@ -198,16 +260,25 @@ class InputStreamReader_학습_테스트 {
*/
@Test
void BufferedReader를_사용하여_문자열을_읽어온다() {
// given
final String emoji = String.join("\r\n",
"😀😃😄😁😆😅😂🤣🥲☺️😊",
"😇🙂🙃😉😌😍🥰😘😗😙😚",
"😋😛😝😜🤪🤨🧐🤓😎🥸🤩",
"");
final InputStream inputStream = new ByteArrayInputStream(emoji.getBytes());

final StringBuilder actual = new StringBuilder();
// when
final InputStream inputStream = new ByteArrayInputStream(emoji.getBytes(UTF_16));
final BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, UTF_16));

final StringBuilder actual = new StringBuilder();;
final List<String> readLines = br.lines().collect(Collectors.toList());
for (String readLine : readLines) {
actual.append(readLine).append("\r\n");
}

assertThat(actual).hasToString(emoji);
// then
assertThat(actual.toString()).contains(emoji);
}
}
}
18 changes: 18 additions & 0 deletions tomcat/src/main/java/org/apache/coyote/common/CharacterSet.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.apache.coyote.common;

public enum CharacterSet {

UTF_8("utf-8");

private static final String CHARACTER_SET = "charset";

private final String source;

CharacterSet(final String source) {
this.source = source;
}

public String source() {
return CHARACTER_SET + "=" + source;
}
}
Loading