Skip to content

Commit

Permalink
[톰캣 구현하기 1,2단계] 호이(이건호) 미션 제출합니다. (#313)
Browse files Browse the repository at this point in the history
  • Loading branch information
This2sho authored Sep 11, 2023
1 parent 68db530 commit 29421f3
Show file tree
Hide file tree
Showing 25 changed files with 972 additions and 51 deletions.
51 changes: 29 additions & 22 deletions study/src/test/java/study/FileTest.java
Original file line number Diff line number Diff line change
@@ -1,53 +1,60 @@
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.nio.file.Paths;
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 String fileName = "nextstep.txt";

// todo
final String actual = "";
final Path path = getFilePath(fileName);

assertThat(path.toString()).endsWith(fileName);
}

assertThat(actual).endsWith(fileName);
private Path getFilePath(String fileName) {
final String currentPath = Paths.get("").toAbsolutePath().toString();
final Path path = Paths.get(currentPath + "/src/test/resources/" + fileName);
final File file = path.toFile();
if (!file.exists()) {
System.out.println("파일이 존재하지 않습니다.");
return null;
}
return path;
}

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

// todo
final Path path = null;
final Path path = getFilePath(fileName);

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

assertThat(actual).containsOnly("nextstep");
}
Expand Down
40 changes: 28 additions & 12 deletions study/src/test/java/study/IOStreamTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,9 @@ class OutputStream_학습_테스트 {
final OutputStream outputStream = new ByteArrayOutputStream(bytes.length);

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

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

assertThat(actual).isEqualTo("nextstep");
Expand All @@ -74,10 +73,10 @@ class OutputStream_학습_테스트 {
final OutputStream outputStream = mock(BufferedOutputStream.class);

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

verify(outputStream, atLeastOnce()).flush();
outputStream.close();
Expand All @@ -92,11 +91,13 @@ class OutputStream_학습_테스트 {
final OutputStream outputStream = mock(OutputStream.class);

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

try (outputStream){
}

verify(outputStream, atLeastOnce()).close();
}
}
Expand Down Expand Up @@ -125,10 +126,16 @@ class InputStream_학습_테스트 {
final InputStream inputStream = new ByteArrayInputStream(bytes);

/**
* todo
* inputStream에서 바이트로 반환한 값을 문자열로 어떻게 바꿀까?
*/
final String actual = "";
int n;
String actual = "";
try(final ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
while((n = inputStream.read()) != -1) {
outputStream.write(n);
}
actual = outputStream.toString();
}

assertThat(actual).isEqualTo("🤩");
assertThat(inputStream.read()).isEqualTo(-1);
Expand All @@ -144,10 +151,11 @@ class InputStream_학습_테스트 {
final InputStream inputStream = mock(InputStream.class);

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

verify(inputStream, atLeastOnce()).close();
}
Expand All @@ -166,15 +174,15 @@ class FilterStream_학습_테스트 {
/**
* BufferedInputStream은 데이터 처리 속도를 높이기 위해 데이터를 버퍼에 저장한다.
* InputStream 객체를 생성하고 필터 생성자에 전달하면 필터에 연결된다.
* 버퍼 크기를 지정하지 않으면 버퍼의 기본 사이즈는 얼마일까?
* 버퍼 크기를 지정하지 않으면 버퍼의 기본 사이즈는 얼마일까? -> 8192 (8KiB)
*/
@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,16 +205,24 @@ 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 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));

final StringBuilder actual = new StringBuilder();

String line;
while ((line = bufferedReader.readLine()) != null) {
actual.append(line + "\r\n");
}
bufferedReader.close();
inputStream.close();
assertThat(actual).hasToString(emoji);
}
}
Expand Down
31 changes: 31 additions & 0 deletions tomcat/src/main/java/org/apache/catalina/Session.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.apache.catalina;

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

public class Session {

private final String id;
private final Map<String, Object> values = new HashMap<>();

public Session() {
this(UUID.randomUUID().toString());
}

public Session(final String id) {
this.id = id;
}

public String getId() {
return id;
}

public Object getAttribute(final String name) {
return values.getOrDefault(name, null);
}

public void setAttribute(final String name, final Object value) {
values.put(name, value);
}
}
20 changes: 20 additions & 0 deletions tomcat/src/main/java/org/apache/catalina/SessionManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.apache.catalina;

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

public class SessionManager {

private SessionManager() {
}

private static final Map<String, Session> SESSIONS = new HashMap<>();

public static void add(final Session session) {
SESSIONS.put(session.getId(), session);
}

public static Session findSession(final String id) {
return SESSIONS.getOrDefault(id, null);
}
}
37 changes: 37 additions & 0 deletions tomcat/src/main/java/org/apache/coyote/http11/ContentType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.apache.coyote.http11;

public enum ContentType {

TEXT_HTML("text/html"),
TEXT_CSS("text/css"),
APPLICATION_JS("application/js"),
WILD_CARD("*/*");

private static final String CSS = "css";
private static final String HTML = "html";
private static final String JS = "js";
private static final String ENCODING = ";charset=utf-8";

private String value;

ContentType(final String value) {
this.value = value;
}

public String getValue() {
return value + ENCODING;
}

public static ContentType from(String extension) {
if (extension.equalsIgnoreCase(CSS)) {
return TEXT_CSS;
}
if (extension.equalsIgnoreCase(HTML)) {
return TEXT_HTML;
}
if (extension.equalsIgnoreCase(JS)) {
return APPLICATION_JS;
}
return WILD_CARD;
}
}
9 changes: 9 additions & 0 deletions tomcat/src/main/java/org/apache/coyote/http11/Controller.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.apache.coyote.http11;

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

public interface Controller {

HttpResponse handle(HttpRequest request);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.apache.coyote.http11;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.coyote.http11.controller.ErrorController;
import org.apache.coyote.http11.controller.LoginController;
import org.apache.coyote.http11.controller.RegisterController;
import org.apache.coyote.http11.controller.StaticController;
import org.apache.coyote.http11.request.HttpRequest;

public class ControllerAdapter {

private static final Map<String, Controller> map = new ConcurrentHashMap<>();
private static final String STATIC_CONTROLLER = "staticController";
private static final String ERROR_CONTROLLER = "errorController";

public ControllerAdapter() {
init();
}

private void init() {
map.put(STATIC_CONTROLLER, new StaticController());
map.put(ERROR_CONTROLLER, new ErrorController());
map.put("/login", new LoginController());
map.put("/register", new RegisterController());
}

public Controller findController(HttpRequest httpRequest) {
if (httpRequest.isStaticRequest()) {
return map.get(STATIC_CONTROLLER);
}
if (map.containsKey(httpRequest.getUri())) {
return map.get(httpRequest.getUri());
}
return map.get(ERROR_CONTROLLER);
}
}
55 changes: 55 additions & 0 deletions tomcat/src/main/java/org/apache/coyote/http11/Headers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.apache.coyote.http11;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.apache.coyote.http11.request.HttpCookie;

public class Headers {

private static final String HEADER_DELIMITER = ":";
private Map<String, String> values;
private HttpCookie cookie;

private Headers(Map<String, String> values, HttpCookie cookie) {
this.values = values;
this.cookie = cookie;
}

public static Headers from(final BufferedReader bufferedReader) throws IOException {
String line = "";
final HashMap<String, String> map = new HashMap<>();
HttpCookie httpCookie = null;

while (!(line = bufferedReader.readLine()).isBlank()) {
final String[] headerInfo = line.split(HEADER_DELIMITER);
final String headerName = headerInfo[0];
final String value = headerInfo[1].trim();

if (headerName.equals("Cookie")) {
httpCookie = new HttpCookie(value);
}
map.put(headerName, value);
}

return new Headers(map, httpCookie);
}

public boolean containsHeader(final String header) {
return values.containsKey(header);
}

public String get(final String header) {
return values.get(header);
}

public boolean hasCookie() {
return Objects.nonNull(cookie);
}

public HttpCookie getCookie() {
return cookie;
}
}
Loading

0 comments on commit 29421f3

Please sign in to comment.