-
Notifications
You must be signed in to change notification settings - Fork 309
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
[톰캣 구현하기 - 3, 4단계] 망고(고재철) 미션 제출합니다. #460
Changes from all commits
ff07df4
0e15705
b042c08
d20cedc
6f254a0
ab51a09
e29572f
9c8a923
930fb10
8214f8a
cd9bd02
1dca664
2cc4449
38f1da8
78cf2d5
6ed87da
51529d5
4b8aefc
7081ed7
baf5476
c6e0955
8cbc245
a386e73
aafb47e
37a4aaa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,19 @@ | ||
package cache.com.example.cachecontrol; | ||
|
||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.http.CacheControl; | ||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; | ||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||
import org.springframework.web.servlet.mvc.WebContentInterceptor; | ||
|
||
@Configuration | ||
public class CacheWebConfig implements WebMvcConfigurer { | ||
|
||
@Override | ||
public void addInterceptors(final InterceptorRegistry registry) { | ||
final var cacheControl = CacheControl.noCache().cachePrivate(); | ||
final var webContentInterceptor = new WebContentInterceptor(); | ||
webContentInterceptor.addCacheMapping(cacheControl, "/**"); | ||
registry.addInterceptor(webContentInterceptor); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,19 @@ | ||
package cache.com.example.etag; | ||
|
||
import org.springframework.boot.web.servlet.FilterRegistrationBean; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.web.filter.ShallowEtagHeaderFilter; | ||
|
||
import static cache.com.example.version.CacheBustingWebConfig.PREFIX_STATIC_RESOURCES; | ||
|
||
@Configuration | ||
public class EtagFilterConfiguration { | ||
|
||
// @Bean | ||
// public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() { | ||
// return null; | ||
// } | ||
@Bean | ||
public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() { | ||
final var filterRegistrationBean = new FilterRegistrationBean<>(new ShallowEtagHeaderFilter()); | ||
filterRegistrationBean.addUrlPatterns("/etag", PREFIX_STATIC_RESOURCES + "/*"); | ||
return filterRegistrationBean; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,3 +7,6 @@ server: | |
max-connections: 1 | ||
threads: | ||
max: 2 | ||
compression: | ||
enabled: true | ||
min-response-size: 10 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package nextstep.jwp.controller; | ||
|
||
import nextstep.jwp.exception.HttpRequestException; | ||
import org.apache.coyote.http11.request.HttpRequest; | ||
import org.apache.coyote.http11.response.HttpResponse; | ||
|
||
import java.io.IOException; | ||
|
||
public abstract class AbstractController implements Controller { | ||
|
||
protected static final String TEXT_HTML = "text/html;charset=utf-8"; | ||
protected static final String TEXT_CSS = "text/css;"; | ||
protected static final String INDEX_PAGE = "/index.html"; | ||
protected static final String UNAUTHORIZED_PAGE = "/401.html"; | ||
protected static final String HEADER_LOCATION = "Location"; | ||
protected static final String HEADER_SET_COOKIE = "Set-Cookie"; | ||
protected static final String HEADER_CONTENT_TYPE = "Content-Type"; | ||
protected static final String HEADER_CONTENT_LENGTH = "Content-Length"; | ||
protected static final String HTTP_METHOD_EXCEPTION_MESSAGE = "올바르지 않은 HTTP Method 입니다."; | ||
|
||
@Override | ||
public void service(final HttpRequest httpRequest, final HttpResponse httpResponse) throws IOException { | ||
if (httpRequest.getRequestLine().getMethod().equals("POST")) { | ||
doPost(httpRequest, httpResponse); | ||
return; | ||
} | ||
if (httpRequest.getRequestLine().getMethod().equals("GET")) { | ||
doGet(httpRequest, httpResponse); | ||
return; | ||
} | ||
throw new HttpRequestException(HTTP_METHOD_EXCEPTION_MESSAGE); | ||
} | ||
|
||
protected abstract void doPost(final HttpRequest httpRequest, final HttpResponse httpResponse) throws IOException; | ||
|
||
protected abstract void doGet(final HttpRequest httpRequest, final HttpResponse httpResponse) throws IOException; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package nextstep.jwp.controller; | ||
|
||
import org.apache.coyote.http11.request.HttpRequest; | ||
import org.apache.coyote.http11.response.HttpResponse; | ||
|
||
import java.io.IOException; | ||
|
||
public interface Controller { | ||
|
||
void service(final HttpRequest request, final HttpResponse httpResponse) throws IOException; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 파라미터 네이밍을 httpRequest나 response로 통일 시키면 좋을 것 가타요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 요구사항 템플릿 그대로 했더니..! |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package nextstep.jwp.controller; | ||
|
||
import org.apache.coyote.http11.request.HttpRequest; | ||
import org.apache.coyote.http11.response.HttpResponse; | ||
import org.apache.coyote.http11.response.HttpStatus; | ||
import org.apache.coyote.http11.response.ResponseBody; | ||
import org.apache.coyote.http11.response.StatusLine; | ||
|
||
public class HomeController extends AbstractController { | ||
|
||
@Override | ||
protected void doPost(final HttpRequest httpRequest, final HttpResponse httpResponse) { | ||
final var statusLine = StatusLine.of(httpRequest.getRequestLine().getProtocol(), HttpStatus.METHOD_NOT_ALLOWED); | ||
httpResponse.setStatusLine(statusLine); | ||
} | ||
|
||
@Override | ||
protected void doGet(final HttpRequest httpRequest, final HttpResponse httpResponse) { | ||
final var statusLine = StatusLine.of(httpRequest.getRequestLine().getProtocol(), HttpStatus.OK); | ||
final var responseBody = ResponseBody.fromText("Hello world!"); | ||
httpResponse.setStatusLine(statusLine); | ||
httpResponse.addResponseHeader("Content-Type", TEXT_HTML); | ||
httpResponse.addResponseHeader("Content-Length", String.valueOf(responseBody.getBody().getBytes().length)); | ||
httpResponse.setResponseBody(responseBody); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package nextstep.jwp.controller; | ||
|
||
import nextstep.jwp.db.InMemoryUserRepository; | ||
import nextstep.jwp.model.User; | ||
import org.apache.coyote.http11.HttpCookie; | ||
import org.apache.coyote.http11.Session; | ||
import org.apache.coyote.http11.SessionManager; | ||
import org.apache.coyote.http11.request.HttpRequest; | ||
import org.apache.coyote.http11.response.HttpResponse; | ||
import org.apache.coyote.http11.response.HttpStatus; | ||
import org.apache.coyote.http11.response.ResponseBody; | ||
import org.apache.coyote.http11.response.StatusLine; | ||
|
||
import java.io.IOException; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
|
||
public class LoginController extends AbstractController { | ||
|
||
private static final SessionManager SESSION_MANAGER = new SessionManager(); | ||
|
||
@Override | ||
protected void doPost(final HttpRequest httpRequest, final HttpResponse httpResponse) { | ||
final HttpCookie cookie = httpRequest.getCookie(); | ||
final User user = findUserBySessionId(cookie.getJSessionId()); | ||
final var statusLine = StatusLine.of(httpRequest.getRequestLine().getProtocol(), HttpStatus.FOUND); | ||
httpResponse.setStatusLine(statusLine); | ||
if (user == null) { | ||
handleFirstLogin(httpRequest, httpResponse); | ||
return; | ||
} | ||
httpResponse.addResponseHeader(HEADER_LOCATION, INDEX_PAGE); | ||
} | ||
|
||
private void handleFirstLogin(final HttpRequest httpRequest, final HttpResponse httpResponse) { | ||
final Map<String, String> requestBodyValues = httpRequest.getRequestParameters(); | ||
final Optional<User> user = InMemoryUserRepository.findByAccount(requestBodyValues.get("account")); | ||
if (user.isEmpty() || !user.get().checkPassword(requestBodyValues.get("password"))) { | ||
httpResponse.addResponseHeader(HEADER_LOCATION, UNAUTHORIZED_PAGE); | ||
return; | ||
} | ||
final String sessionId = addSession(user.get()); | ||
httpResponse.addResponseHeader(HEADER_LOCATION, INDEX_PAGE); | ||
httpResponse.addResponseHeader(HEADER_SET_COOKIE, "JSESSIONID=" + sessionId); | ||
} | ||
|
||
@Override | ||
protected void doGet(final HttpRequest httpRequest, final HttpResponse httpResponse) throws IOException { | ||
final HttpCookie cookie = httpRequest.getCookie(); | ||
final User user = findUserBySessionId(cookie.getJSessionId()); | ||
if (user != null) { | ||
doPost(httpRequest, httpResponse); | ||
return; | ||
} | ||
final var statusLine = StatusLine.of(httpRequest.getRequestLine().getProtocol(), HttpStatus.OK); | ||
final var responseBody = ResponseBody.fromUri("/login.html"); | ||
httpResponse.setStatusLine(statusLine); | ||
httpResponse.addResponseHeader(HEADER_CONTENT_TYPE, TEXT_HTML); | ||
httpResponse.addResponseHeader(HEADER_CONTENT_LENGTH, String.valueOf(responseBody.getBody().getBytes().length)); | ||
httpResponse.setResponseBody(responseBody); | ||
} | ||
|
||
private User findUserBySessionId(final String sessionId) { | ||
if (sessionId == null) { | ||
return null; | ||
} | ||
final Session session = SESSION_MANAGER.findSession(sessionId) | ||
.orElseGet(Session::create); | ||
return (User) session.getAttribute("user"); | ||
Comment on lines
+67
to
+69
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Session은 위에서 언급한 Cookie와 다른 역할을 하고 있지만, 무상태성인 HTTP 통신을 하다가 서버가 특정 브라우저(클라이언트) 임을 알기 위해서 저장하고 그에 대한 아이디를 쿠키에 넘겨주는 형식으로 사용이 되는데요. Cookie와 마찬가지로 어떤 요청이 오면 그 클라이언트가 Session이 이미 저장이 되어있는 유저라면 Cookie 헤더의 SessionId를 통해서 알 수 있을거에요. 그렇다면 이 책임 또한 Request같은 객체로 옮길 수 있지 않을까요? 만약 그렇다면 어떤 방식으로 Response에게 Set-Cookie 헤더를 명시하라고 지시할 수 있을까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 사실 유저 입장에서는 Session Manager가 무엇인지 인지하거나 그것을 통해서 세션을 저장하는 행위가 매우 어려울 수 있다고 생각합니다. 우리 톰캣을 사용하는 유저는
위에서 나온 객체들로 거의 대부분의 일을 해내야될 수 있다고 생각합니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
이 부분은 위 피드백을 반영하면서 Cookie 값을 가져오는 책임을 상위 객체가 너무 많은 책임을 가지기보다는 각각 객체마다 가장 어울리는 책임을 담당하도록 하는게 좋다고 생각해서, JSESSIONID를 받아오는 책임은 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 제 생각은 Session과 Cookie의 값들은 어떤 요청에 의한 값이므로 그에 대한 접근 정보 권한도 Request에 있어야된다고 생각했습니다!! 그래서 유저가
|
||
} | ||
|
||
private String addSession(final User user) { | ||
final var session = Session.create(); | ||
session.setAttribute("user", user); | ||
SESSION_MANAGER.add(session); | ||
return session.getId(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package nextstep.jwp.controller; | ||
|
||
import nextstep.jwp.db.InMemoryUserRepository; | ||
import nextstep.jwp.model.User; | ||
import org.apache.coyote.http11.request.HttpRequest; | ||
import org.apache.coyote.http11.response.HttpResponse; | ||
import org.apache.coyote.http11.response.HttpStatus; | ||
import org.apache.coyote.http11.response.ResponseBody; | ||
import org.apache.coyote.http11.response.StatusLine; | ||
|
||
import java.io.IOException; | ||
import java.util.Map; | ||
|
||
public class RegisterController extends AbstractController { | ||
|
||
@Override | ||
protected void doPost(final HttpRequest httpRequest, final HttpResponse httpResponse) { | ||
final Map<String, String> requestBodyValues = httpRequest.getRequestParameters(); | ||
final var user = new User(requestBodyValues.get("account"), requestBodyValues.get("password"), | ||
requestBodyValues.get("email")); | ||
InMemoryUserRepository.save(user); | ||
|
||
final var statusLine = StatusLine.of(httpRequest.getRequestLine().getProtocol(), HttpStatus.FOUND); | ||
httpResponse.setStatusLine(statusLine); | ||
httpResponse.addResponseHeader("Location", INDEX_PAGE); | ||
} | ||
|
||
@Override | ||
protected void doGet(final HttpRequest httpRequest, final HttpResponse httpResponse) throws IOException { | ||
final var statusLine = StatusLine.of(httpRequest.getRequestLine().getProtocol(), HttpStatus.OK); | ||
final var responseBody = ResponseBody.fromUri("/register.html"); | ||
httpResponse.setStatusLine(statusLine); | ||
httpResponse.addResponseHeader("Content-Type", TEXT_HTML); | ||
httpResponse.addResponseHeader("Content-Length", String.valueOf(responseBody.getBody().getBytes().length)); | ||
httpResponse.setResponseBody(responseBody); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package nextstep.jwp.controller; | ||
|
||
import org.apache.coyote.http11.request.HttpRequest; | ||
import org.apache.coyote.http11.response.HttpResponse; | ||
import org.apache.coyote.http11.response.HttpStatus; | ||
import org.apache.coyote.http11.response.ResponseBody; | ||
import org.apache.coyote.http11.response.StatusLine; | ||
|
||
import java.io.IOException; | ||
|
||
public class ResourceController extends AbstractController { | ||
|
||
@Override | ||
protected void doPost(final HttpRequest httpRequest, final HttpResponse httpResponse) { | ||
final var statusLine = StatusLine.of(httpRequest.getRequestLine().getProtocol(), HttpStatus.METHOD_NOT_ALLOWED); | ||
httpResponse.setStatusLine(statusLine); | ||
} | ||
|
||
@Override | ||
protected void doGet(final HttpRequest httpRequest, final HttpResponse httpResponse) throws IOException { | ||
final var statusLine = StatusLine.of(httpRequest.getRequestLine().getProtocol(), HttpStatus.OK); | ||
final String uri = httpRequest.getRequestLine().getPath().split("\\?")[0]; | ||
final var responseBody = ResponseBody.fromUri(uri); | ||
httpResponse.setStatusLine(statusLine); | ||
httpResponse.addResponseHeader("Content-Type", getContentType(uri)); | ||
httpResponse.addResponseHeader("Content-Length", String.valueOf(responseBody.getBody().getBytes().length)); | ||
httpResponse.setResponseBody(responseBody); | ||
} | ||
|
||
private String getContentType(final String uri) { | ||
if (uri.endsWith(".css")) { | ||
return TEXT_CSS; | ||
} | ||
return TEXT_HTML; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package nextstep.jwp.exception; | ||
|
||
public class HttpRequestException extends RuntimeException { | ||
|
||
public HttpRequestException(final String message) { | ||
super(message); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
위 의존성 순환에 대한 언급을 했던 것에 연장선일 수 있는데요.
이 Controller는 인터페이스인데요.
이 인터페이스는 Tomcat을 개발하는 우리가 이 톰캣을 사용해서 웹 개발을 할 유저에게 어떤 요청을 핸들링할 때는 이 컨트롤러와 Abstract컨트롤러를 구현해서 사용하라는 일종의 설명서 같은 것일텐데요.
그렇다는 것은 jwp(유저의 영역)와 koyote(톰캣 영역) 둘중 어디에 있는 것이 적절할까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.