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단계] 다즐(최우창) 미션 제출합니다. #316

Merged
merged 13 commits into from
Sep 5, 2023
Merged
12 changes: 7 additions & 5 deletions study/src/test/java/study/FileTest.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package study;

import java.nio.file.Files;
import java.nio.file.Paths;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import org.springframework.core.io.ClassPathResource;

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

Expand All @@ -28,7 +30,7 @@ class FileTest {
final String fileName = "nextstep.txt";

// todo
final String actual = "";
final String actual = new ClassPathResource(fileName).getPath();

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

// todo
final Path path = null;
final Path path = Paths.get(new ClassPathResource(fileName).getURI());

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

assertThat(actual).containsOnly("nextstep");
}
Expand Down
21 changes: 16 additions & 5 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 Down Expand Up @@ -53,6 +54,7 @@ class OutputStream_학습_테스트 {
* todo
* OutputStream 객체의 write 메서드를 사용해서 테스트를 통과시킨다
*/
outputStream.write(bytes);

final String actual = outputStream.toString();

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

verify(outputStream, atLeastOnce()).flush();
outputStream.close();
Expand All @@ -96,6 +99,7 @@ class OutputStream_학습_테스트 {
* try-with-resources를 사용한다.
* 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(), StandardCharsets.UTF_8);

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 Exception {
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,15 +202,21 @@ class InputStreamReader_학습_테스트 {
* 필터인 BufferedReader를 사용하면 readLine 메서드를 사용해서 문자열(String)을 한 줄 씩 읽어올 수 있다.
*/
@Test
void BufferedReader를_사용하여_문자열을_읽어온다() {
void BufferedReader를_사용하여_문자열을_읽어온다() throws Exception {
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);
actual.append("\r\n");
}

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

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import org.apache.coyote.request.Accept;

public enum ContentType {

APPLICATION_XML("application/xml", "xml"),
APPLICATION_JAVASCRIPT("application/javascript", "js"),
APPLICATION_JSON("application/json", "json"),
APPLICATION_OCTET_STREAM("application/octet-stream", ""),
APPLICATION_PDF("application/pdf", "pdf"),
TEXT_PLAIN("text/plain", "text"),
TEXT_HTML("text/html;charset=utf-8", "html"),
TEXT_CSS("text/css", "css"),
TEXT_JAVASCRIPT("text/javascript", "js"),
TEXT_XML("text/xml", "xml"),
IMAGE_JPEG("image/jpeg", "jpeg"),
IMAGE_PNG("image/png", "png"),
IMAGE_GIF("image/gif", "gif"),
IMAGE_SVG("image/svg+xml", "svg"),
AUDIO_MPEG("audio/mpeg", "mpeg"),
AUDIO_OGG("audio/ogg", "ogg"),
VIDEO_MP4("video/mp4", "mp4"),
MULTIPART_FOR_DATA("multipart/form-data", "");

private final String type;
private final String extension;

ContentType(final String type, final String extension) {
this.type = type;
this.extension = extension;
}

public static String getTypeFrom(final List<Accept> accepts) {
return accepts.stream()
.map(ContentType::from)
.filter(Objects::nonNull)
.findFirst()
.map(ContentType::getType)
.orElse(null);
}

private static ContentType from(final Accept accept) {
return Arrays.stream(ContentType.values())
.filter(contentType -> contentType.type.contains(accept.getAcceptType()))
.findFirst()
.orElse(null);
}

public static String getTypeFrom(final String extension) {
return Arrays.stream(ContentType.values())
.filter(contentType -> Objects.equals(contentType.extension, extension))
.findAny()
.map(ContentType::getType)
.orElse(null);
}

public String getType() {
return type;
}
}
25 changes: 25 additions & 0 deletions tomcat/src/main/java/org/apache/coyote/common/HttpMethod.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.apache.coyote.common;

import java.util.Arrays;
import java.util.Objects;
import org.apache.coyote.exception.http.HttpMethodNotMatchException;

public enum HttpMethod {

GET,
POST,
PUT,
DELETE,
HEAD,
OPTIONS,
PATCH,
CONNECT,
TRACE;

public static HttpMethod from(final String method) {
return Arrays.stream(HttpMethod.values())
.filter(httpMethod -> Objects.equals(httpMethod.name(), method))
.findFirst()
.orElseThrow(HttpMethodNotMatchException::new);
}
}
40 changes: 40 additions & 0 deletions tomcat/src/main/java/org/apache/coyote/common/HttpStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.apache.coyote.common;

public enum HttpStatus {

CONTINUE(100, "Continue"),
SWITCHING_PROTOCOLS(101, "Switching Protocols"),
OK(200, "OK"),
CREATED(201, "Created"),
ACCEPTED(202, "Accepted"),
NO_CONTENT(204, "No Content"),
MULTIPLE_CHOICES(300, "Multiple Choices"),
MOVED_PERMANENTLY(301, "Moved Permanently"),
FOUND(302, "Found"),
NOT_MODIFIED(304, "Not Modified"),
BAD_REQUEST(400, "Bad Request"),
UNAUTHORIZED(401, "Unauthorized"),
FORBIDDEN(403, "Forbidden"),
NOT_FOUND(404, "Not Found"),
METHOD_NOT_ALLOWED(405, "Method Not Allowed"),
INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
NOT_IMPLEMENTED(501, "Not Implemented"),
BAD_GATEWAY(502, "Bad Gateway"),
SERVICE_UNAVAILABLE(503, "Service Unavailable");

private final int code;
private final String message;

HttpStatus(final int code, final String message) {
this.code = code;
this.message = message;
}

public int getCode() {
return code;
}

public String getMessage() {
return message;
}
}
31 changes: 31 additions & 0 deletions tomcat/src/main/java/org/apache/coyote/common/HttpVersion.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.apache.coyote.common;

import java.util.Arrays;
import java.util.Objects;
import org.apache.coyote.exception.http.HttpVersionNotMatchException;

public enum HttpVersion {

HTTP_09("HTTP/0.9"),
HTTP_10("HTTP/1.0"),
HTTP_11("HTTP/1.1"),
HTTP_20("HTTP/2"),
HTTP_30("HTTP/3");

private final String version;

HttpVersion(final String version) {
this.version = version;
}

public static HttpVersion from(final String version) {
return Arrays.stream(HttpVersion.values())
.filter(httpVersion -> Objects.equals(httpVersion.version, version))
.findFirst()
.orElseThrow(HttpVersionNotMatchException::new);
}

public String getVersion() {
return version;
}
}
35 changes: 35 additions & 0 deletions tomcat/src/main/java/org/apache/coyote/common/Session.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.apache.coyote.common;

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

public class Session {

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

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

public String getId() {
return id;
}

public Object getAttribute(final String name) {
return values.get(name);
}

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

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

public void invalidate() {
values.clear();
}
}

24 changes: 24 additions & 0 deletions tomcat/src/main/java/org/apache/coyote/common/SessionManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.apache.coyote.common;

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

public class SessionManager {

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

private SessionManager() {
}

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

public static Session findSession(final String id) {
return SESSIONS.get(id);
}

public static void remove(final String id) {
SESSIONS.remove(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.apache.coyote.exception.http;

public class HttpException extends RuntimeException {

public HttpException(final String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.apache.coyote.exception.http;

public class HttpMethodNotMatchException extends HttpException {

public HttpMethodNotMatchException() {
super("Http Method Not Matched");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.apache.coyote.exception.http;

public class HttpVersionNotMatchException extends HttpException {

public HttpVersionNotMatchException() {
super("Http Version Not Matched");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.apache.coyote.exception.http;

public class InvalidHeaderException extends HttpException {

public InvalidHeaderException(final String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.apache.coyote.exception.http;

public class InvalidRequestPathException extends HttpException {

public InvalidRequestPathException(final String message) {
super(message);
}
}
Loading