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단계] 여우(조승현) 미션 제출합니다. #314

Merged
merged 22 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
24b9593
docs: 레벨 1에서 할 일 정리
BackFoxx Sep 3, 2023
3b92e1c
study: IOStreamTest 완료하기
BackFoxx Sep 3, 2023
5a884e3
feat: http request의 uri와 method 읽어오기
BackFoxx Sep 3, 2023
7fea21e
feat: /index.html로 요청을 보내면 index.html 응답하기
BackFoxx Sep 3, 2023
73c9cfd
refactor: httpRequest를 uri별로 핸들링하는 작업 깔끔하게 분리하기
BackFoxx Sep 3, 2023
f0cb40c
feat: css와 js를 받는 핸들러 추가하기
BackFoxx Sep 3, 2023
b67653f
feat: login 페이지 접속시 쿼리 파라미터를 뽑아 회원 정보를 조회하는 핸들러 구현하기
BackFoxx Sep 3, 2023
6c5a808
refactor: HttpResponse와 관련된 작업을 별도의 도메인으로 분리하기
BackFoxx Sep 3, 2023
1279a46
feat: 로그인 실패시 401 처리, 성공시 302 처리하는 핸들러 개발
BackFoxx Sep 3, 2023
8671ad4
feat: 회원가입 작업 수행 후 index 페이지로 리다이렉트하는 핸들러 개발하기
BackFoxx Sep 3, 2023
1af8240
feat: 로그인 성공시 쿠키에 JSESSION을 등록하는 기능 개발하기
BackFoxx Sep 3, 2023
a822e9a
refactor: 쿠키 값을 cookie라는 일급 컬렉션에 캡슐화하기
BackFoxx Sep 3, 2023
106a3d6
feat: 로그인된 상태에서 로그인 페이지로 접속하려는 경우 index.html로 강제 리다이렉트하는 기능 개발하기
BackFoxx Sep 3, 2023
ea8e5cd
test: 테스트의 개행 방식을 운영체제에 무관하게 수정
BackFoxx Sep 4, 2023
624c6a6
docs: 관리 안하는 요구사항 문서 제거하기
BackFoxx Sep 4, 2023
98bb692
refactor: 프로세서에서 inputStream과 outputStream을 가져오는 작업을 실행하도록 변경
BackFoxx Sep 5, 2023
c8e2cfb
refactor: StringBuilder 대신 String을 반환하도록 변경
BackFoxx Sep 5, 2023
0d0e768
refactor: String.split 대신 StringTokenizer를 사용하도록 변경
BackFoxx Sep 5, 2023
ec561fe
refactor: 로그인 요청으로 GET 대신 POST를 사용하도록 변경
BackFoxx Sep 5, 2023
963d7d3
refactor: 로그인 성공시 / 로그인 상태로 로그인 페이지 접속시 200 + responseBody 대신 /index.…
BackFoxx Sep 5, 2023
d50b248
refactor: 패키지 정리 후 response 응답 메시지를 만드는 작업을 별도 utils 클래스로 분리
BackFoxx Sep 5, 2023
339bf87
refactor: register 작업 이후 index.html로 리다이렉트하도록 변경
BackFoxx Sep 5, 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
56 changes: 32 additions & 24 deletions study/src/test/java/study/FileTest.java
Original file line number Diff line number Diff line change
@@ -1,53 +1,61 @@
package study;

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

import java.nio.file.Path;
import java.util.Collections;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
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 파일을 제공 할 수 있어야 한다.
* File 클래스를 사용해서 파일을 읽어오고, 사용자에게 전달한다.
* 웹서버는 사용자가 요청한 html 파일을 제공 할 수 있어야 한다. File 클래스를 사용해서 파일을 읽어오고, 사용자에게 전달한다.
*/
@DisplayName("File 클래스 학습 테스트")
class FileTest {

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

// todo
final String actual = "";
final String actual = url.getPath();

assertThat(actual).endsWith(fileName);
}

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

// todo
final Path path = null;

// todo
final List<String> actual = Collections.emptyList();
void 파일의_내용을_읽는다() throws URISyntaxException, IOException {
final URL url = this.getClass().getClassLoader().getResource("nextstep.txt");
final File file = new File(url.toURI());

final List<String> actual = new ArrayList<>();
try (
final FileInputStream fileInputStream = new FileInputStream(file);
final BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(fileInputStream, StandardCharsets.UTF_8))
) {
actual.add(bufferedReader.readLine());
}

assertThat(actual).containsOnly("nextstep");
}
Expand Down
49 changes: 28 additions & 21 deletions study/src/test/java/study/IOStreamTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package study;

import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
Expand All @@ -24,6 +25,8 @@
@DisplayName("Java I/O Stream 클래스 학습 테스트")
class IOStreamTest {

public static final byte[] BYTES = new byte[]{110, 101, 120, 116, 115, 116, 101, 112};

/**
* OutputStream 학습하기
*
Expand All @@ -46,13 +49,9 @@ class OutputStream_학습_테스트 {
*/
@Test
void OutputStream은_데이터를_바이트로_처리한다() throws IOException {
final byte[] bytes = {110, 101, 120, 116, 115, 116, 101, 112};
final OutputStream outputStream = new ByteArrayOutputStream(bytes.length);
final OutputStream outputStream = new ByteArrayOutputStream(BYTES.length);

/**
* todo
* OutputStream 객체의 write 메서드를 사용해서 테스트를 통과시킨다
*/
outputStream.write(BYTES);

final String actual = outputStream.toString();

Expand All @@ -73,11 +72,8 @@ class OutputStream_학습_테스트 {
void BufferedOutputStream을_사용하면_버퍼링이_가능하다() throws IOException {
final OutputStream outputStream = mock(BufferedOutputStream.class);

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

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

}

verify(outputStream, atLeastOnce()).close();
}
Expand Down Expand Up @@ -124,13 +123,10 @@ class InputStream_학습_테스트 {
byte[] bytes = {-16, -97, -92, -87};
final InputStream inputStream = new ByteArrayInputStream(bytes);

/**
* todo
* inputStream에서 바이트로 반환한 값을 문자열로 어떻게 바꿀까?
*/
final String actual = "";
final InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

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

}

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

final byte[] actual = new byte[0];
final byte[] actual = bufferedInputStream.readAllBytes();
// 기본 버퍼의 크기는 8192Byte(8KB)

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


final StringBuilder actual = new StringBuilder();
try(final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
while (bufferedReader.ready()) {
actual.append(bufferedReader.readLine());
actual.append("\r\n");
}
}

assertThat(actual).hasToString(emoji);
}
Expand Down
46 changes: 29 additions & 17 deletions tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,35 @@
package org.apache.coyote.http11;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.List;
import nextstep.jwp.exception.UncheckedServletException;
import org.apache.coyote.Processor;
import org.apache.coyote.http11.handler.*;
import org.apache.coyote.http11.request.HttpRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.Socket;

public class Http11Processor implements Runnable, Processor {

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

private final Socket connection;

private final List<HttpRequestHandler> requestHandlers = List.of(
new BasicURIHandler(),
new IndexPageHandler(),
new IndexCSSHandler(),
new HttpJavascriptHandler(),
new HttpAssetHandler(),
new LoginPageHandler(),
new LoginHandler(),
new RegistrationPageHandler(),
new RegistrationHandler()
);

public Http11Processor(final Socket connection) {
this.connection = connection;
}
Expand All @@ -26,20 +42,16 @@ public void run() {

@Override
public void process(final Socket connection) {
try (final var inputStream = connection.getInputStream();
final var outputStream = connection.getOutputStream()) {

final var responseBody = "Hello world!";

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);

outputStream.write(response.getBytes());
outputStream.flush();
try (
final InputStream inputStream = connection.getInputStream();
final OutputStream outputStream = connection.getOutputStream()
) {
final HttpRequest httpRequest = HttpRequest.from(inputStream);
for (HttpRequestHandler requestHandler : this.requestHandlers) {
if (requestHandler.support(httpRequest)) {
requestHandler.handle(httpRequest, outputStream);
}
}
} catch (IOException | UncheckedServletException e) {
log.error(e.getMessage(), e);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.apache.coyote.http11.handler;

import java.io.IOException;
import java.io.OutputStream;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;

public class BasicURIHandler implements HttpRequestHandler {
@Override
public boolean support(final HttpRequest httpRequest) {
return httpRequest.isMethodEqualTo("GET") && httpRequest.isUriEqualTo("/");
}

@Override
public void handle(final HttpRequest httpRequest, final OutputStream outputStream) throws IOException {
final var responseBody = "Hello world!";

final HttpResponse httpResponse = new HttpResponse.Builder()
.responseBody(responseBody)
.build(outputStream);
httpResponse.flush();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.apache.coyote.http11.handler;

import org.apache.coyote.http11.resource.FileHandler;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;

import java.io.IOException;
import java.io.OutputStream;

public class HttpAssetHandler implements HttpRequestHandler {

public static final String ASSETS_PATH_PREFIX = "static/assets/";

@Override
public boolean support(final HttpRequest httpRequest) {
return httpRequest.isAssetRequest();
}

@Override
public void handle(final HttpRequest httpRequest, final OutputStream outputStream) throws IOException {
final HttpResponse httpResponse = new HttpResponse.Builder()
.contentType("text/javascript")
.responseBody(new FileHandler().readFromResourcePath(ASSETS_PATH_PREFIX + httpRequest.getEndPoint()))
.build(outputStream);
httpResponse.flush();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.apache.coyote.http11.handler;

import org.apache.coyote.http11.resource.FileHandler;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;

import java.io.IOException;
import java.io.OutputStream;

public class HttpJavascriptHandler implements HttpRequestHandler {

public static final String JAVASCRIPT_PATH_PREFIX = "static/js/";

@Override
public boolean support(final HttpRequest httpRequest) {
return httpRequest.isJavascriptRequest();
}

@Override
public void handle(final HttpRequest httpRequest, final OutputStream outputStream) throws IOException {
final HttpResponse httpResponse = new HttpResponse.Builder()
.contentType("text/javascript")
.responseBody(new FileHandler().readFromResourcePath(JAVASCRIPT_PATH_PREFIX + httpRequest.getEndPoint()))
.build(outputStream);
httpResponse.flush();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.apache.coyote.http11.handler;

import java.io.IOException;
import java.io.OutputStream;
import org.apache.coyote.http11.request.HttpRequest;

public interface HttpRequestHandler {
boolean support(HttpRequest httpRequest);

void handle(HttpRequest httpRequest, OutputStream outputStream) throws IOException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.apache.coyote.http11.handler;

import org.apache.coyote.http11.resource.FileHandler;
import org.apache.coyote.http11.request.HttpRequest;
import org.apache.coyote.http11.response.HttpResponse;

import java.io.IOException;
import java.io.OutputStream;

public class IndexCSSHandler implements HttpRequestHandler {

public static final String CSS_PATH_PREFIX = "static/css/";

@Override
public boolean support(final HttpRequest httpRequest) {
return httpRequest.isMethodEqualTo("GET") && httpRequest.isUriEqualTo("/css/styles.css");
}

@Override
public void handle(final HttpRequest httpRequest, final OutputStream outputStream) throws IOException {
final HttpResponse httpResponse = new HttpResponse.Builder()
.contentType("text/css")
.responseBody(new FileHandler().readFromResourcePath(CSS_PATH_PREFIX + httpRequest.getEndPoint()))
.build(outputStream);
httpResponse.flush();
}
}
Loading