Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
caae46a
이슈 템플릿 생성
zangsu Sep 4, 2024
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
04921cb
Merge branch 'woowacourse:main' into main
zangsu Sep 6, 2024
a7f5de9
Merge branch 'woowacourse:zangsu' into zangsu
zangsu Sep 8, 2024
17dbf54
Merge branch 'woowacourse:zangsu' into zangsu
zangsu Sep 12, 2024
2466ea2
chore: 세션은 Http 에 종속되는 존재가 아니므로 패키지 변경
zangsu Sep 12, 2024
76e056b
chore: RequestParameters 는 Http 요청에 관련된 객체이므로 패키지 이동
zangsu Sep 12, 2024
5faa10c
refactor: RequestLine 테스트 추가 및 예외처리
zangsu Sep 12, 2024
e63b72d
test: RequestParameters 파싱 테스트 생성
zangsu Sep 12, 2024
479ee7a
refactor: Http 관련 객체들에게 Http 네이밍 추가
zangsu Sep 12, 2024
83e8552
feat: 미션 요구사항에 있는 뼈대 코드 추가
zangsu Sep 12, 2024
44aca15
feat: Http 메서드에 따른 분기 추가
zangsu Sep 12, 2024
611a3f5
feat: 요청에 따라 Servlet 을 다르게 처리하는 흐름 구현
zangsu Sep 12, 2024
fc8dbd3
feat: 실제로 요청이 Controller 까지 전달될 수 있도록 변경
zangsu Sep 12, 2024
7201ebc
feat: HttpResponse 를 가변객체로 변경
zangsu Sep 13, 2024
6c2ab74
feat: HttpResponse 를 가변객체로 변경
zangsu Sep 13, 2024
dabb8d5
refactor: Request URL 과 컨트롤러 연결 설정 분리
zangsu Sep 13, 2024
cf26247
refactor: LoginController 분리
zangsu Sep 13, 2024
c48cd92
refactor: RegisterController 분리
zangsu Sep 13, 2024
c3ede1c
fix: 로그인 뷰 조회 기능 수정
zangsu Sep 13, 2024
2a115e6
refactor: 정적 파일 Controller 분리
zangsu Sep 13, 2024
3107316
refactor: 상수 사용
zangsu Sep 13, 2024
9ff1722
refactor: 정적 파일 POST 요청 예외처리
zangsu Sep 13, 2024
410c5c5
refactor: 요청 처리 실패 예외처리
zangsu Sep 13, 2024
810e43f
refactor: 예외 이름 변경
zangsu Sep 13, 2024
ccd4e88
refactor: 사용하지 않는 메서드 삭제
zangsu Sep 13, 2024
a997bf1
refactor: HttpStatus enum 분리
zangsu Sep 13, 2024
f7af5c5
refactor: 필드에 final 추가
zangsu Sep 13, 2024
f08952b
refactor: 기본적으로 생성되는 응답을 NOT_FOUND 로 생성
zangsu Sep 13, 2024
a5e7582
test: PathMatchServletContainer 테스트 작성
zangsu Sep 13, 2024
d025b9b
test: SessionManager 테스트
zangsu Sep 13, 2024
9c836ef
test: Session 테스트 추가
zangsu Sep 13, 2024
59adc64
refactor: 사용하지 않는 메서드 삭제
zangsu Sep 13, 2024
0c6e4e2
test: 세션 요청 관련 객체 테스트
zangsu Sep 13, 2024
c15f56d
test: 학습 테스트 stage 0 완료
zangsu Sep 13, 2024
fc516c0
test: 학습 테스트 stage 1 완료
zangsu Sep 13, 2024
3761410
fix: config 로 분리한 RequestMapping 을 사용하도록 변경
zangsu Sep 13, 2024
d4b71c5
refactor: 휴리스틱 캐시 제거
zangsu Sep 13, 2024
679b872
fix: 요청 매핑을 정규식을 통해 진행하도록 변경
zangsu 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
2 changes: 1 addition & 1 deletion study/src/test/java/thread/stage0/SynchronizationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ private static final class SynchronizedMethods {

private int sum = 0;

public void calculate() {
public synchronized void calculate() {
setSum(getSum() + 1);
}

Expand Down
20 changes: 17 additions & 3 deletions study/src/test/java/thread/stage0/ThreadPoolsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@ void testNewFixedThreadPool() {
final var executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
executor.submit(logWithSleep("hello fixed thread pools"));
executor.submit(logWithSleep("hello fixed thread pools"));
//() -> {} 로 바꾸면 queueSize 가 0으로 줄어듬
executor.submit(logWithSleep("hello fixed thread pools"));

// 올바른 값으로 바꿔서 테스트를 통과시키자.
final int expectedPoolSize = 0;
final int expectedQueueSize = 0;
final int expectedPoolSize = 2;
//-> 스레드 개수를 2개로 지정해 두었으니 expectedPoolSize 는 2개
final int expectedQueueSize = 1;
//-> 아직 2개의 스레드가 모두 종료되지 않았기 때문에 대기중인 스레드 Queue 의 size() 값은 1

assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize());
assertThat(expectedQueueSize).isEqualTo(executor.getQueue().size());
Expand All @@ -41,16 +44,27 @@ void testNewFixedThreadPool() {
@Test
void testNewCachedThreadPool() {
final var executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
//이 테스트는 뭐지??
//https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newCachedThreadPool--
executor.submit(logWithSleep("hello cached thread pools"));
executor.submit(logWithSleep("hello cached thread pools"));
executor.submit(logWithSleep("hello cached thread pools"));

// 올바른 값으로 바꿔서 테스트를 통과시키자.
final int expectedPoolSize = 0;
final int expectedPoolSize = 3;
final int expectedQueueSize = 0;

assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize());
assertThat(expectedQueueSize).isEqualTo(executor.getQueue().size());
/**
* 정리하면
* newFixedThreadPool : 최대 스레드 크기를 지정
* 매번 새로운 스레드 생성?
*
* Cached~
* : 가용 시스템 리소스가 없을 때 까지 생성 가능
* : 재활용할 수 있음
*/
}

private Runnable logWithSleep(final String message) {
Expand Down
19 changes: 13 additions & 6 deletions study/src/test/java/thread/stage0/ThreadTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package thread.stage0;

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

import java.lang.Thread.State;

import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -8,10 +12,10 @@
* 자바로 동시에 여러 작업을 처리할 때 스레드를 사용한다.
* 스레드 객체를 직접 생성하는 방법부터 알아보자.
* 진행하면서 막히는 부분은 아래 링크를 참고해서 해결한다.
*
* <p>
* Thread Objects
* https://docs.oracle.com/javase/tutorial/essential/concurrency/threads.html
*
* <p>
* Defining and Starting a Thread
* https://docs.oracle.com/javase/tutorial/essential/concurrency/runthread.html
*/
Expand All @@ -28,12 +32,15 @@ class ThreadTest {
void testExtendedThread() throws InterruptedException {
// 하단의 ExtendedThread 클래스를 Thread 클래스로 상속하고 스레드 객체를 생성한다.
Thread thread = new ExtendedThread("hello thread");
assertThat(thread.getState()).isEqualTo(State.NEW);

// 생성한 thread 객체를 시작한다.
thread.start();
thread.start();
assertThat(thread.getState()).isEqualTo(State.RUNNABLE);

// thread의 작업이 완료될 때까지 기다린다.
thread.join();
thread.join();
assertThat(thread.getState()).isEqualTo(State.TERMINATED);
}

/**
Expand All @@ -46,10 +53,10 @@ void testRunnableThread() throws InterruptedException {
Thread thread = new Thread(new RunnableThread("hello thread"));

// 생성한 thread 객체를 시작한다.
thread.start();
thread.start();

// thread의 작업이 완료될 때까지 기다린다.
thread.join();
thread.join();
}

private static final class ExtendedThread extends Thread {
Expand Down
2 changes: 1 addition & 1 deletion study/src/test/java/thread/stage1/UserServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public void service(final User user) {
join(user);
}

private void join(final User user) {
private synchronized void join(final User user) {
if (!users.contains(user)) {
users.add(user);
}
Expand Down
1 change: 1 addition & 0 deletions tomcat/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ dependencies {
testImplementation 'org.mockito:mockito-core:5.12.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.2'
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.2'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.2'
}

test {
Expand Down
8 changes: 7 additions & 1 deletion tomcat/src/main/java/com/techcourse/Application.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package com.techcourse;

import org.apache.catalina.servlet.PathMatchServletContainer;
import org.apache.catalina.servlet.RequestMapping;
import org.apache.catalina.startup.Tomcat;

import com.techcourse.config.RequestMappingConfig;

public class Application {

public static void main(String[] args) {
var tomcat = new Tomcat();
RequestMapping requestMapping = RequestMappingConfig.getRequestMapping();
PathMatchServletContainer servletContainer = new PathMatchServletContainer(requestMapping);
var tomcat = new Tomcat(servletContainer);
tomcat.start();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.techcourse.config;

import java.util.Map;

import org.apache.catalina.servlet.RequestMapping;

import com.techcourse.controller.LoginController;
import com.techcourse.controller.RegisterController;
import com.techcourse.controller.StaticResourceController;

public class RequestMappingConfig {
public static RequestMapping getRequestMapping() {
var staticResourceController = new StaticResourceController();
return new RequestMapping(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트용 메서드인가요? staticResource들 매핑하는 것이 확장성이 떨어져보입니다. /static/assets 애들은 부를 수가 없는 것 같아요

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗, 테스트용 메서드는 아니었습니다!!
정규식 매칭으로 컨트롤러 조회 방법을 변경하였어요. 이제는 문제가 해결되었습니다!!

Map.of(
"/login", new LoginController(),
"/register", new RegisterController(),
"/", staticResourceController,
".*\\.(js|html|css)$", staticResourceController
)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.techcourse.controller;

import static org.apache.coyote.http11.httpmessage.HttpHeaders.JSESSIONID;

import java.util.Objects;

import org.apache.catalina.servlet.AbstractController;
import org.apache.coyote.http11.httpmessage.request.HttpRequest;
import org.apache.coyote.http11.httpmessage.request.HttpRequestParameters;
import org.apache.coyote.http11.httpmessage.response.HttpResponse;
import org.apache.coyote.http11.httpmessage.response.StaticResource;
import org.apache.coyote.session.Session;
import org.apache.coyote.session.SessionManager;

import com.techcourse.db.InMemoryUserRepository;
import com.techcourse.model.User;

public class LoginController extends AbstractController {
@Override
protected void doPost(HttpRequest request, HttpResponse response) throws Exception {
HttpRequestParameters requestParams = HttpRequestParameters.parseFrom(request.getBody());
String account = requestParams.getParam("account");
String password = requestParams.getParam("password");
User user = InMemoryUserRepository.fetchByAccount(account);
if (user.checkPassword(password)) {
Session session = request.getSession(true);
session.setAttribute("user", user);
SessionManager.getInstance().add(session);

response.addCookie(JSESSIONID, session.getId());
response.setStatusFound("/index.html");
return;
}

response.setStatusFound("/401.html");
}

@Override
protected void doGet(HttpRequest request, HttpResponse response) throws Exception {
if (isLoggedIn(request)) {
response.setStatusFound("/index.html");
return;
}
response.setResponseOfStaticResource(new StaticResource("/login.html"));
}

private boolean isLoggedIn(HttpRequest httpRequest) {
return Objects.nonNull(httpRequest.getSession(false));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.techcourse.controller;

import org.apache.catalina.servlet.AbstractController;
import org.apache.coyote.http11.httpmessage.request.HttpRequest;
import org.apache.coyote.http11.httpmessage.request.HttpRequestParameters;
import org.apache.coyote.http11.httpmessage.response.HttpResponse;
import org.apache.coyote.http11.httpmessage.response.StaticResource;
import org.apache.coyote.session.Session;
import org.apache.coyote.session.SessionManager;

import com.techcourse.db.InMemoryUserRepository;
import com.techcourse.model.User;

public class RegisterController extends AbstractController {
@Override
protected void doPost(HttpRequest request, HttpResponse response) throws Exception {
HttpRequestParameters methodRequest = HttpRequestParameters.parseFrom(request.getBody());
User user = register(methodRequest);
Session session = request.getSession(true);
session.setAttribute("user", user);
SessionManager.getInstance().add(session);

response.addCookie("JSESSIONID", session.getId());
response.setStatusFound("/index.html");
}

private User register(HttpRequestParameters requestParams) {
String account = requestParams.getParam("account");
User user = new User(
account,
requestParams.getParam("password"),
requestParams.getParam("email")
);
InMemoryUserRepository.save(user);
return InMemoryUserRepository.fetchByAccount(account);
}

@Override
protected void doGet(HttpRequest request, HttpResponse response) throws Exception {
response.setResponseOfStaticResource(new StaticResource("/register.html"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.techcourse.controller;

import org.apache.catalina.servlet.AbstractController;
import org.apache.coyote.http11.exception.CantHandleRequestException;
import org.apache.coyote.http11.httpmessage.request.HttpRequest;
import org.apache.coyote.http11.httpmessage.response.HttpResponse;
import org.apache.coyote.http11.httpmessage.response.StaticResource;

public class StaticResourceController extends AbstractController {
@Override
protected void doPost(HttpRequest request, HttpResponse response) throws Exception {
throw new CantHandleRequestException(
String.format("%s %s 요청을 처리할 수 없습니다.", request.getMethod().name(), request.getTarget())
);
}

@Override
protected void doGet(HttpRequest request, HttpResponse response) throws Exception {
if (request.getTarget().equals("/")) {
StaticResource staticResource = new StaticResource("/index.html");
response.setResponseOfStaticResource(staticResource);
return;
}
StaticResource staticResource = new StaticResource(request.getTarget());
response.setResponseOfStaticResource(staticResource);
}
}
2 changes: 1 addition & 1 deletion tomcat/src/main/java/org/apache/catalina/Manager.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import java.io.IOException;

import org.apache.coyote.http11.session.Session;
import org.apache.coyote.session.Session;

/**
* A <b>Manager</b> manages the pool of Sessions that are associated with a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.net.ServerSocket;
import java.net.Socket;

import org.apache.coyote.ServletContainer;
import org.apache.coyote.http11.Http11Processor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -17,13 +18,16 @@ public class Connector implements Runnable {
private static final int DEFAULT_ACCEPT_COUNT = 100;

private final ServerSocket serverSocket;
private final ServletContainer servletContainer;

private boolean stopped;

public Connector() {
this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT);
public Connector(ServletContainer servletContainer) {
this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, servletContainer);
}

public Connector(int port, int acceptCount) {
public Connector(int port, int acceptCount, ServletContainer servletContainer) {
this.servletContainer = servletContainer;
this.serverSocket = createServerSocket(port, acceptCount);
this.stopped = false;
}
Expand Down Expand Up @@ -66,7 +70,7 @@ private void process(Socket connection) {
if (connection == null) {
return;
}
var processor = new Http11Processor(connection);
var processor = new Http11Processor(servletContainer, connection);
new Thread(processor).start();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.apache.catalina.exception;

public class NoMatchedControllerException extends RuntimeException {
public NoMatchedControllerException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.apache.catalina.servlet;

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

public abstract class AbstractController implements Controller {

@Override
public void service(HttpRequest request, HttpResponse response) throws Exception {
if (request.isGet()) {
doGet(request, response);
}
if (request.isPost()) {
doPost(request, response);
}
}

protected abstract void doPost(HttpRequest request, HttpResponse response) throws Exception;

protected abstract void doGet(HttpRequest request, HttpResponse response) throws Exception;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.apache.catalina.servlet;

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

public interface Controller {
void service(HttpRequest request, HttpResponse response) throws Exception;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.apache.catalina.servlet;

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

public class PathMatchServletContainer implements ServletContainer {
private final RequestMapping requestMapping;

public PathMatchServletContainer(RequestMapping requestMapping) {
this.requestMapping = requestMapping;
}

@Override
public void service(HttpRequest request, HttpResponse response) throws Exception {
Controller controller = requestMapping.getController(request);
controller.service(request, response);
}
}
Loading