Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
fed02f6
fix: remove implementation logback-classic on gradle (#501)
geoje Sep 5, 2024
7e91356
fix: add threads min-spare configuration on properties (#502)
geoje Sep 5, 2024
f058739
[톰캣 구현하기 1, 2단계] 안나 (김민겸) 미션 제출합니다. (#571)
Mingyum-Kim Sep 8, 2024
dad03dc
refactor: Method, Body, Path 를 VO로 분리
Mingyum-Kim Sep 10, 2024
b918e81
refactor: HttpResponse와 하위 요소들을 VO로 분리
Mingyum-Kim Sep 10, 2024
810f9de
refactor: HttpResponse를 활용해 응답을 보내는 코드 리팩토링
Mingyum-Kim Sep 10, 2024
e1071c2
refactor: Content-Type을 객체로 생성하여 생성 로직 위임
Mingyum-Kim Sep 10, 2024
f480e0e
refactor: 응답 본문을 반환하는 부분을 객체로 대체
Mingyum-Kim Sep 10, 2024
001bebd
refactor: 생성자와 요청 HTTP 생성 용 정적 팩토리 메서드 분리
Mingyum-Kim Sep 10, 2024
44c993c
refactor: initialLine을 requestLine으로 변수명 변경
Mingyum-Kim Sep 10, 2024
7088860
refactor: 서블릿 컨테이너를 사용해서 요청을 처리하도록 수정
Mingyum-Kim Sep 12, 2024
d56668c
refactor: 동적 요청에 대해 분기하는 HandlerMapping 구현
Mingyum-Kim Sep 12, 2024
85ac473
refactor: Servlet이 적절한 컨트롤러를 꺼내어 처리하도록 구현
Mingyum-Kim Sep 12, 2024
6cf4dd3
refactor: HttpResponse 빈 객체 생성 후 값을 대입할 수 있도록 수정
Mingyum-Kim Sep 12, 2024
2fdc682
refactor: 응답을 출력하는 역할을 하는 클래스 분리
Mingyum-Kim Sep 12, 2024
da30303
feat: 로그인을 담당하는 컨트롤러 구현
Mingyum-Kim Sep 12, 2024
05d9229
feat: 회원 저장을 담당하는 Controller 구현
Mingyum-Kim Sep 12, 2024
0f19b10
feat: 정적 파일 처리를 담당하는 Controller 구현
Mingyum-Kim Sep 12, 2024
62572ca
refactor: 분기 처리를 구현 컨트롤러에게 위임하여 로직 개선
Mingyum-Kim Sep 12, 2024
4f78f67
refactor: Body에서 속성을 파싱할 수 있도록 Properties 클래스 분리
Mingyum-Kim Sep 12, 2024
7228258
fix: 로그인 페이지가 출력되지 않는 버그 해결
Mingyum-Kim Sep 12, 2024
e437b2c
fix: 세션 정보가 조회되지 않는 현상 해결
Mingyum-Kim Sep 12, 2024
e6ec7d1
fix: 실패하는 테스트 케이스 수정
Mingyum-Kim Sep 12, 2024
4da748d
feat: Executors로 Thread Pool 적용
Mingyum-Kim Sep 12, 2024
20e4d68
refactor: 기존의 Controller로 명명한 클래스 이름을 Servlet으로 수정
Mingyum-Kim Sep 12, 2024
50fc6b0
Merge branch 'mingyum-kim' into step2
Mingyum-Kim Sep 12, 2024
690657d
test: 로그인 및 회원가입 서블릿 테스트 구현
Mingyum-Kim Sep 12, 2024
1e45f68
Merge branch 'step2' of https://github.com/Mingyum-Kim/java-http into…
Mingyum-Kim Sep 12, 2024
485dd69
refactor: 필요한 설정 값을 상수화하도록 수정
Mingyum-Kim Sep 13, 2024
4748783
style: 클래스 상단 개행 컨벤션 맞춤
Mingyum-Kim Sep 13, 2024
159a396
refactor: 정적 파일 처리 시 . 을 붙여 확장자 판별
Mingyum-Kim Sep 13, 2024
8b2e874
refactor: 모든 헤더 키를 상수화
Mingyum-Kim Sep 13, 2024
903c610
refactor: 정적 메서드의 이름을 직관적으로 변경
Mingyum-Kim Sep 13, 2024
efb92a0
refactor: HTTP 파싱을 위한 Delimter 를 전부 상수화
Mingyum-Kim Sep 13, 2024
55e7ee6
refactor: 세션 값을 추출하는 메서드를 분리
Mingyum-Kim Sep 13, 2024
cd74cff
refactor: RequestLine과 ResponseLine으로 객체를 추상화
Mingyum-Kim Sep 13, 2024
493a502
refactor: 헤더 키를 상수화하여 생성하도록 수정
Mingyum-Kim Sep 13, 2024
b2f4f1b
fix: 인덱스 페이지가 연결되지 않는 현상 해결
Mingyum-Kim Sep 13, 2024
b5aeb32
refactor: GET, POST 이외의 메소드 요청이 온다면 405를 반환
Mingyum-Kim Sep 13, 2024
a9c8aae
refactor: 프로퍼티를 HttpRequest에서 바로 꺼내오도록 설정
Mingyum-Kim Sep 13, 2024
282d61d
refactor: 로그인이 실패한 경우 401 페이지를 응답
Mingyum-Kim Sep 13, 2024
26a1f62
refactor: RequestLine에서 한번에 처리하도록 수정
Mingyum-Kim Sep 13, 2024
5e69292
refactor: 이미 존재하는 유저가 아니라면 저장하도록 수정
Mingyum-Kim Sep 13, 2024
388fa61
fix: 불일치 메서드명 수정
Mingyum-Kim Sep 13, 2024
f01734e
fix: 요청과 응답이 동작하지 않는 문제 해결
Mingyum-Kim Sep 13, 2024
a09dce0
refactor: ContentLength가 0 이하인 경우 예외처리
Mingyum-Kim Sep 13, 2024
4be86de
refactor: 정적 메서드의 이름을 수정
Mingyum-Kim Sep 13, 2024
27492af
fix: 패스워드가 조회되지 않는 문제 해결
Mingyum-Kim Sep 13, 2024
4e384ae
fix: 루트경로 입력시 Hello, World가 보여지도록 수정
Mingyum-Kim Sep 13, 2024
fabd31a
test: 실패하는 테스트 케이스 수정
Mingyum-Kim Sep 13, 2024
7a8ca9d
fix: 잘못 올라간 파일 수정
Mingyum-Kim Sep 13, 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
22 changes: 15 additions & 7 deletions tomcat/src/main/java/com/techcourse/model/User.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.techcourse.model;

import java.util.Objects;

public class User {

private final Long id;
Expand Down Expand Up @@ -27,12 +29,18 @@ public String getAccount() {
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", account='" + account + '\'' +
", email='" + email + '\'' +
", password='" + password + '\'' +
'}';
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
User user = (User)o;
return Objects.equals(account, user.account) && Objects.equals(password, user.password)
&& Objects.equals(email, user.email);
}

@Override
public int hashCode() {
return Objects.hash(account, password, email);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,29 @@
import java.io.UncheckedIOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Connector implements Runnable {

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

private static final int DEFAULT_PORT = 8080;
private static final int DEFAULT_ACCEPT_COUNT = 100;
private static final int DEFAULT_ACCEPT_COUNT = 100;
private static final int DEFAULT_THREAD_COUNT = 250;

private final ServerSocket serverSocket;
private boolean stopped;
private final ExecutorService executorService;

public Connector() {
this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT);
this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, DEFAULT_THREAD_COUNT);
}

public Connector(final int port, final int acceptCount) {
public Connector(final int port, final int acceptCount, final int maxThreads) {
this.serverSocket = createServerSocket(port, acceptCount);
this.stopped = false;
this.executorService = Executors.newFixedThreadPool(maxThreads);
}

private ServerSocket createServerSocket(final int port, final int acceptCount) {
Expand Down Expand Up @@ -67,7 +72,7 @@ private void process(final Socket connection) {
return;
}
var processor = new Http11Processor(connection);
new Thread(processor).start();
this.executorService.execute(processor);
}

public void stop() {
Expand Down
75 changes: 30 additions & 45 deletions tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java
Original file line number Diff line number Diff line change
@@ -1,62 +1,47 @@
package org.apache.coyote.http11;


import com.techcourse.db.InMemoryUserRepository;
import com.techcourse.exception.UncheckedServletException;
import com.techcourse.model.User;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

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.apache.coyote.http11.response.HttpResponse;
import org.apache.coyote.http11.response.ResponsePrinter;
import org.apache.coyote.http11.servlet.ServletContainer;
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 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";
private final Socket connection;

public Http11Processor(final Socket connection) {
this.connection = connection;
}
public Http11Processor(final Socket connection) {
this.connection = connection;
}

@Override
public void run() {
log.info("connect host: {}, port: {}", connection.getInetAddress(), connection.getPort());
process(connection);
}
@Override
public void run() {
log.info("connect host: {}, port: {}", connection.getInetAddress(), connection.getPort());
process(connection);
}

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

BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
HttpRequest httpRequest = new HttpRequest(reader);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
HttpRequest request = new HttpRequest(reader);
HttpResponse response = new HttpResponse();

RequestHandler requestHandler = new RequestHandler(httpRequest, outputStream);
requestHandler.handleRequest();
new ServletContainer().invoke(request, response);

} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
new ResponsePrinter(outputStream).print(response);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}
42 changes: 42 additions & 0 deletions tomcat/src/main/java/org/apache/coyote/http11/common/Body.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.apache.coyote.http11.common;

import static org.apache.coyote.http11.common.HttpDelimiter.*;
import static org.apache.coyote.http11.common.HeaderKey.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.Arrays;

public record Body(String value) {
public static Body parseRequestBody(Headers headers, BufferedReader reader) throws IOException {
return new Body(parseBody(headers, reader));
}

private static String parseBody(Headers headers, BufferedReader reader) throws IOException {
String contentLength = headers.getValue(CONTENT_LENGTH);
if (contentLength == null) {
return null;
}

int length = Integer.parseInt(contentLength);
if (length > 0) {
char[] body = new char[length];
reader.read(body, 0, length);
return new String(body);
} else if (length < 0) {
throw new IOException("Invalid Content-Length: " + length);
}
return null;
}

public int getContentLength() {
return this.value.getBytes().length;
}

public Properties parseProperty() {
Properties properties = new Properties();
Arrays.asList(value.split(BODY_PROPERTY_DELIMITER.getValue()))
.forEach(properties::add);
return properties;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.apache.coyote.http11.common;

public enum ContentType {
APPLICATION_JSON("application/json"),
TEXT_CSS("text/css"),
TEXT_HTML("text/html"),
TEXT_JAVASCRIPT("text/javascript"),
;

private final String value;

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

public static ContentType fromPath(String path) {
if (path.endsWith(".css")) {
return ContentType.TEXT_CSS;
}
if (path.endsWith(".js")) {
return ContentType.APPLICATION_JSON;
}
if (path.endsWith(".html")) {
return ContentType.TEXT_HTML;
}
return ContentType.APPLICATION_JSON;
}

public String getValue() {
return value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.apache.coyote.http11.common;

public enum HeaderKey {
CONTENT_LENGTH("Content-Length"),
CONTENT_TYPE("Content-Type"),
COOKIE("Cookie"),
SET_COOKIE("Set-Cookie"),
LOCATION("Location")
;

private final String value;

HeaderKey(String value) {
this.value = value;
}

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

import static org.apache.coyote.http11.common.HttpDelimiter.*;

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

public record Headers(Map<String, String> headers) {
public Headers() {
this(new HashMap<>());
}

public static Headers parseRequestHeader(BufferedReader reader) throws IOException {
HashMap<String, String> headers = new HashMap<>();
String line;
while ((line = reader.readLine()) != null && !line.isEmpty()) {
String[] header = line.split(HEADER_KEY_VALUE_DELIMITER.getValue());
headers.put(header[0].trim(), header[1].trim());
}
return new Headers(headers);
}

public void add(HeaderKey key, String value) {
headers.put(key.getValue(), value);
}

public String getValue(HeaderKey headerKey) {
return headers.get(headerKey.getValue());
}

public String generatePlainText() {
StringBuilder sb = new StringBuilder();
headers().forEach((key, value) -> {
sb.append(String.format("%s: %s \r\n", key, value));
});
return sb.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.apache.coyote.http11.common;

public enum HttpDelimiter {
REQUEST_LINE_DELIMITER(" "),
BODY_PROPERTY_DELIMITER("&"),
BODY_KEY_VALUE_DELIMITER("="),
SESSION_DELIMITER("; "),
HEADER_KEY_VALUE_DELIMITER(":")
;

private final String value;

HttpDelimiter(String value) {
this.value = value;
}

public String getValue() {
return value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.apache.coyote.http11.common;

import static org.apache.coyote.http11.common.HttpDelimiter.*;

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

public class Properties {
private static final int KEY_INDEX_OF_PROPERTY = 0;
private static final int VALUE_INDEX_OF_PROPERTY = 1;

private final Map<String, String> properties;

public Properties() {
this.properties = new HashMap<>();
}

public void add(String rawProperty) {
String key = rawProperty.split(BODY_KEY_VALUE_DELIMITER.getValue())[KEY_INDEX_OF_PROPERTY];
String value = rawProperty.split(BODY_KEY_VALUE_DELIMITER.getValue())[VALUE_INDEX_OF_PROPERTY];
this.properties.put(key, value);
}

public String get(String key) {
return properties.get(key);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.apache.coyote.http11.common;

import static org.apache.coyote.http11.common.HttpDelimiter.*;

import java.util.Objects;

public record VersionOfProtocol(String value) {
private static final int INDEX_OF_VERSION_OF_PROTOCOL = 2;

public static VersionOfProtocol parseReqeustToVersionOfProtocol(String requestLine) {
return new VersionOfProtocol(requestLine.split(REQUEST_LINE_DELIMITER.getValue())[INDEX_OF_VERSION_OF_PROTOCOL]);
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
VersionOfProtocol that = (VersionOfProtocol)o;
return Objects.equals(value, that.value);
}

@Override
public int hashCode() {
return Objects.hashCode(value);
}
}
Loading