Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
bb0ef8b
Merge branch 'woowacourse:main' into step2&3
shin-jisong Jan 14, 2025
478d5b3
feat: 로그인 구현
shin-jisong Jan 14, 2025
cddbc48
feat: body 형태 변경
shin-jisong Jan 18, 2025
4493fd6
feat: POST 도입
shin-jisong Jan 18, 2025
4522148
feat: 회원가입 구현
shin-jisong Jan 18, 2025
e2519a5
feat: login 요청 시 get 요청이 아닌 post 요청으로 수정
shin-jisong Jan 21, 2025
f888191
feat: HttpCookie 구현
shin-jisong Jan 21, 2025
f0f75b2
feat(ResponseWriter): Cookie 작성 부분 추가
shin-jisong Jan 21, 2025
20c0c14
feat(ResponseHeader): 쿠키 부분 추가
shin-jisong Jan 21, 2025
b234c42
feat(ResponseBuilder): 쿠키 부분 추가
shin-jisong Jan 21, 2025
3556b68
feat(RequestHeader): 쿠키 부분 추가
shin-jisong Jan 21, 2025
bc78983
feat(RequestBuilder): 쿠키 부분 추가
shin-jisong Jan 21, 2025
3c28f4a
feat(Http11Request): body value 가져오기
shin-jisong Jan 21, 2025
76d84bc
feat(Http11Processor): 로그인 후 쿠키 처리
shin-jisong Jan 21, 2025
c77ba68
feat: Session 구현
shin-jisong Jan 21, 2025
1f9608e
feat(requestBuilder): body가 key value 아닐 때 처리
shin-jisong Jan 21, 2025
b7e2193
feat(ResponseWriter): Location 추가
shin-jisong Jan 21, 2025
bfb1e54
feat(ResponserBuilder): response를 받도록 구현
shin-jisong Jan 21, 2025
d2d54e2
feat(HttpCookie): 값 밖에서 주입하도록 변경
shin-jisong Jan 21, 2025
54a682f
feat: 필드 변경
shin-jisong Jan 21, 2025
43a6382
feat: Controller 분리
shin-jisong Jan 21, 2025
353f4ce
feat: 동시성 확장 구현
shin-jisong Jan 21, 2025
73ec960
feat: ControllerRegistry 분리
shin-jisong Feb 24, 2025
57337b3
feat: AbstractController 도입
shin-jisong Feb 24, 2025
48a44c8
feat(LoginController): 세션값 있을 때 Post 시 리다이렉트 구현
shin-jisong Feb 24, 2025
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/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.apache.commons:commons-lang3:3.14.0'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1'
implementation 'pl.allegro.tech.boot:handlebars-spring-boot-starter:0.4.1'
implementation 'com.github.jknack:handlebars-springmvc:4.4.0'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.assertj:assertj-core:3.26.0'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cache.com.example.version;

import com.github.jknack.handlebars.springmvc.HandlebarsViewResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;

@Configuration
public class HandlebarsConfig {

private final VersionHandlebarsHelper versionHandlebarsHelper;

public HandlebarsConfig(VersionHandlebarsHelper versionHandlebarsHelper) {
this.versionHandlebarsHelper = versionHandlebarsHelper;
}

@Bean
public ViewResolver handlebarsViewResolver() {
HandlebarsViewResolver viewResolver = new HandlebarsViewResolver();
viewResolver.registerHelper("staticUrls", versionHandlebarsHelper);
viewResolver.setPrefix("classpath:/templates/");
viewResolver.setSuffix(".html");

return viewResolver;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package cache.com.example.version;

import com.github.jknack.handlebars.Helper;
import com.github.jknack.handlebars.Options;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import pl.allegro.tech.boot.autoconfigure.handlebars.HandlebarsHelper;
import org.springframework.stereotype.Component;

@HandlebarsHelper
public class VersionHandlebarsHelper {
@Component
public class VersionHandlebarsHelper implements Helper<Object> {

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

Expand All @@ -22,4 +23,9 @@ public String staticUrls(String path, Options options) {
log.debug("static url : {}", path);
return String.format("/resources/%s%s", version.getVersion(), path);
}

@Override
public Object apply(Object context, Options options) {
return staticUrls(context.toString(), options);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.techcourse.controller;

import org.apache.catalina.Controller;
import java.util.Map;

public class ControllerRegistry {
public static void registerControllers(Map<String, Controller> controllers) {
controllers.put("/", new HomeController());
controllers.put("/login", new LoginController());
controllers.put("/register", new RegisterController());
}
}
17 changes: 17 additions & 0 deletions tomcat/src/main/java/com/techcourse/controller/HomeController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.techcourse.controller;

import org.apache.catalina.AbstractController;
import org.apache.catalina.Controller;
import org.apache.coyote.http11.request.Http11Request;
import org.apache.coyote.http11.response.ContentType;
import org.apache.coyote.http11.response.Http11Response;
import org.apache.coyote.http11.response.Http11ResponseBuilder;
import org.apache.coyote.http11.response.StatusCode;

public class HomeController extends AbstractController {

@Override
protected void doGet(Http11Request request, Http11Response response) throws Exception {
Http11ResponseBuilder.build(response, StatusCode.OK, ContentType.TEXT_HTML_UTF8, "Hello world!");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.techcourse.controller;

import com.techcourse.db.InMemoryUserRepository;
import com.techcourse.model.User;
import org.apache.catalina.AbstractController;
import org.apache.coyote.http11.cookie.HttpCookie;
import org.apache.coyote.http11.cookie.Session;
import org.apache.coyote.http11.cookie.SessionManager;
import org.apache.coyote.http11.request.Http11Request;
import org.apache.coyote.http11.response.ContentType;
import org.apache.coyote.http11.response.Http11Response;
import org.apache.coyote.http11.response.Http11ResponseBuilder;
import org.apache.coyote.http11.response.StatusCode;
import java.util.Optional;
import java.util.UUID;

public class LoginController extends AbstractController {

private final SessionManager sessionManager = new SessionManager();

@Override
protected void doGet(Http11Request request, Http11Response response) throws Exception {

Choose a reason for hiding this comment

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

AbstractController를 활용하지 않고 doGet, doPost를 구현하신 이유가 따로 있나요??

Copy link
Owner Author

Choose a reason for hiding this comment

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

해당 부분을 추상화할 생각을 하지 않았는데 추상화한다면 좀 더 복잡한 로직이 줄어들겠네요!

String sessionId = request.getSessionCookie();
if (sessionId != null && sessionManager.findSession(sessionId) != null) {
Http11ResponseBuilder.buildRedirect(response, request.getCookie(), "index.html");
return;
}
Http11ResponseBuilder.buildFile(response, StatusCode.OK, ContentType.TEXT_HTML_UTF8, "login.html");
}

@Override
protected void doPost(Http11Request request, Http11Response response) throws Exception {
String sessionId = request.getSessionCookie();
if (sessionId != null && sessionManager.findSession(sessionId) != null) {
Http11ResponseBuilder.buildRedirect(response, request.getCookie(), "index.html");
return;
}

String account = request.getBodyValue("account");
String password = request.getBodyValue("password");

if (account == null || password == null) {
Http11ResponseBuilder.buildFile(response, StatusCode.OK, ContentType.TEXT_HTML_UTF8, "login.html");
return;
}
Comment on lines +39 to +45

Choose a reason for hiding this comment

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

세션값이 존재할 때 리다이렉트되어야하는데 해당 코드가 보이지 않는 것 같아요 어떻게 처리되고 있는걸까요??

Copy link
Owner Author

Choose a reason for hiding this comment

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

추가로 구현하였습니다!


Optional<User> user = InMemoryUserRepository.findByAccount(account);
if (user.isPresent() && user.get().checkPassword(password)) {
sessionId = UUID.randomUUID().toString();
sessionManager.add(new Session(sessionId));
HttpCookie httpCookie = new HttpCookie();
httpCookie.putSessionCookie(sessionId);
Http11ResponseBuilder.buildRedirect(response, request.getCookie(), "index.html");
return;
}

Http11ResponseBuilder.buildFile(response, StatusCode.UNAUTHORIZED, ContentType.TEXT_HTML_UTF8, "401.html");
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.techcourse.controller;

import com.techcourse.db.InMemoryUserRepository;
import com.techcourse.model.User;
import org.apache.catalina.AbstractController;
import org.apache.catalina.Controller;
import org.apache.coyote.http11.request.Http11Request;
import org.apache.coyote.http11.request.HttpMethod;
import org.apache.coyote.http11.response.ContentType;
import org.apache.coyote.http11.response.Http11Response;
import org.apache.coyote.http11.response.Http11ResponseBuilder;
import org.apache.coyote.http11.response.StatusCode;

public class RegisterController extends AbstractController {

@Override
protected void doGet(Http11Request request, Http11Response response) throws Exception {
Http11ResponseBuilder.buildFile(response, StatusCode.OK, ContentType.TEXT_HTML_UTF8, "register.html");
}

@Override
protected void doPost(Http11Request request, Http11Response response) throws Exception {
String account = request.getBodyValue("account");
String email = request.getBodyValue("email");
String password = request.getBodyValue("password");

if (account == null || email == null || password == null) {
Http11ResponseBuilder.buildFile(response, StatusCode.OK, ContentType.TEXT_HTML_UTF8, "register.html");
return;
}

User user = new User(account, email, password);
InMemoryUserRepository.save(user);
Http11ResponseBuilder.buildRedirect(response, null, "index.html");
}
}
26 changes: 26 additions & 0 deletions tomcat/src/main/java/org/apache/catalina/AbstractController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.apache.catalina;

import org.apache.coyote.http11.request.Http11Request;
import org.apache.coyote.http11.request.HttpMethod;
import org.apache.coyote.http11.response.Http11Response;

public abstract class AbstractController implements Controller {

@Override
public void service(Http11Request request, Http11Response response) throws Exception {
HttpMethod method = request.getHttpMethod();
if (method.equals(HttpMethod.GET)) {
doGet(request, response);
} else if (method.equals(HttpMethod.POST)) {
doPost(request, response);
}
}

protected void doGet(Http11Request request, Http11Response response) throws Exception {
throw new UnsupportedOperationException("GET method not implemented");
}

protected void doPost(Http11Request request, Http11Response response) throws Exception {
throw new UnsupportedOperationException("POST method not implemented");
}
}
8 changes: 8 additions & 0 deletions tomcat/src/main/java/org/apache/catalina/Controller.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.apache.catalina;

import org.apache.coyote.http11.request.Http11Request;
import org.apache.coyote.http11.response.Http11Response;

public interface Controller {
void service(Http11Request request, Http11Response response) throws Exception;
}
19 changes: 19 additions & 0 deletions tomcat/src/main/java/org/apache/catalina/FileController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.apache.catalina;

import org.apache.coyote.http11.request.Http11Request;
import org.apache.coyote.http11.response.ContentType;
import org.apache.coyote.http11.response.Http11Response;
import org.apache.coyote.http11.response.Http11ResponseBuilder;
import org.apache.coyote.http11.response.StatusCode;

public class FileController implements Controller {

@Override
public void service(Http11Request request, Http11Response response) throws Exception {
String fileName = request.getUri();
int dotIndex = fileName.lastIndexOf('.');
String extension = fileName.substring(dotIndex + 1);

Http11ResponseBuilder.buildFile(response, StatusCode.OK, ContentType.fromExtension(extension), fileName);
}
}
22 changes: 16 additions & 6 deletions tomcat/src/main/java/org/apache/catalina/connector/Connector.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,36 @@
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 MAX_THREADS = 200;
private static final int ACCEPT_COUNT = 25;

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

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

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

private ServerSocket createServerSocket(final int port, final int acceptCount) {
private ServerSocket createServerSocket(final int port) {
try {
final int checkedPort = checkPort(port);
final int checkedAcceptCount = checkAcceptCount(acceptCount);
final int checkedAcceptCount = checkAcceptCount(ACCEPT_COUNT);
return new ServerSocket(checkedPort, checkedAcceptCount);
} catch (IOException e) {
throw new UncheckedIOException(e);
Expand Down Expand Up @@ -67,7 +73,9 @@ private void process(final Socket connection) {
return;
}
var processor = new Http11Processor(connection);
new Thread(processor).start();
executor.submit(() -> {
new Thread(processor).start();
});
}

public void stop() {
Expand All @@ -76,6 +84,8 @@ public void stop() {
serverSocket.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
} finally {
executor.shutdown();
}
}

Expand Down
53 changes: 12 additions & 41 deletions tomcat/src/main/java/org/apache/coyote/http11/Http11Processor.java
Original file line number Diff line number Diff line change
@@ -1,30 +1,22 @@
package org.apache.coyote.http11;

import com.techcourse.db.InMemoryUserRepository;
import com.techcourse.exception.UncheckedServletException;
import com.techcourse.model.User;
import org.apache.catalina.Controller;
import org.apache.catalina.FileController;
import org.apache.coyote.Processor;
import org.apache.coyote.http11.request.Http11Request;
import org.apache.coyote.http11.request.Http11RequestBuilder;
import org.apache.coyote.http11.response.ContentType;
import org.apache.coyote.http11.response.Http11Response;
import org.apache.coyote.http11.response.Http11ResponseBuilder;
import org.apache.coyote.http11.response.Http11ResponseWriter;
import org.apache.coyote.http11.response.StatusCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.net.URL;
import java.nio.file.Files;
import java.util.Optional;

public class Http11Processor implements Runnable, Processor {

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

private final Socket connection;
private final RequestMapping requestMapping = new RequestMapping();

public Http11Processor(final Socket connection) {
this.connection = connection;
Expand All @@ -42,44 +34,23 @@ public void process(final Socket connection) {
final var outputStream = connection.getOutputStream()) {

Http11Request request = Http11RequestBuilder.build(inputStream);
Http11Response response = catalina(request);
Http11Response response = handleRequest(request);

outputStream.write(Http11ResponseWriter.write(response));
outputStream.flush();

} catch (IOException | UncheckedServletException e) {
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}

public Http11Response catalina(Http11Request request) throws IOException {
if (request.getUri().equals("/")) {
return Http11ResponseBuilder.build(StatusCode.OK, ContentType.TEXT_HTML_UTF8, "Hello world!");
private Http11Response handleRequest(Http11Request request) throws Exception {
Controller controller = requestMapping.getController(request);
if (controller == null) {
controller = new FileController();
Copy link
Owner Author

Choose a reason for hiding this comment

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

fileController는 유저가 관리하는 코드는 아니라고 생각해서 catalina 부분에 배치한 뒤
RequestMapping에서도 완전히 배제시켰습니다!
모든 컨트롤러에 해당되지 않을 때 FileController를 기본으로 들고 나오게 돼요
해당하는 파일이 없다면 예외가 발생합니다

}

if (request.getUri().equals("/login")) {
final URL resource = getClass().getClassLoader().getResource("static/login.html");
String filePath = resource.getFile();
final String responseBody = new String(Files.readAllBytes(new File(filePath).toPath()));
String account = request.getQueryParameter("account");
String password = request.getQueryParameter("password");
Optional<User> user = InMemoryUserRepository.findByAccount(account);
if (user.isPresent()) {
User currentUser = user.get();
if (currentUser.checkPassword(password)) {
System.out.println(currentUser);
}
}
return Http11ResponseBuilder.build(StatusCode.OK, ContentType.TEXT_HTML_UTF8, responseBody);
}

final URL resource = getClass().getClassLoader().getResource("static/" + request.getUri());
String filePath = resource.getFile();
int dotIndex = filePath.lastIndexOf('.');
String extension = filePath.substring(dotIndex + 1);

final String responseBody = new String(Files.readAllBytes(new File(filePath).toPath()));

return Http11ResponseBuilder.build(StatusCode.OK, ContentType.fromExtension(extension), responseBody);
Http11Response response = new Http11Response();
controller.service(request, response);
return response;
}
}
Loading