-
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단계] 리오(오영택) 미션 제출합니다. #496
Changes from all commits
201e52f
a3a8543
b089e99
e71df35
c8dfb84
9f091bb
e10d7fa
da9fff9
bed381f
aa96ef7
695e7d8
eb069cc
04cc270
bc072b1
5e48432
cf1e1e6
163f07a
f5f027d
4333099
816870e
9d67479
29da1dc
1833e9f
2f7ee65
5e82471
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,18 @@ | ||
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) { | ||
WebContentInterceptor webContentInterceptor = new WebContentInterceptor(); | ||
webContentInterceptor.addCacheMapping(CacheControl.noCache().cachePrivate(), "/**"); | ||
registry.addInterceptor(webContentInterceptor); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,18 @@ | ||
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; | ||
|
||
@Configuration | ||
public class EtagFilterConfiguration { | ||
|
||
// @Bean | ||
// public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() { | ||
// return null; | ||
// } | ||
@Bean | ||
public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() { | ||
ShallowEtagHeaderFilter shallowEtagHeaderFilter = new ShallowEtagHeaderFilter(); | ||
FilterRegistrationBean<ShallowEtagHeaderFilter> filterRegistrationBean = new FilterRegistrationBean<>(shallowEtagHeaderFilter); | ||
filterRegistrationBean.addUrlPatterns("/etag", "/resources/*"); | ||
return filterRegistrationBean; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package common.http; | ||
|
||
import java.util.EnumMap; | ||
import java.util.Map; | ||
import java.util.function.BiConsumer; | ||
|
||
import static common.http.HttpMethod.DELETE; | ||
import static common.http.HttpMethod.GET; | ||
import static common.http.HttpMethod.PATCH; | ||
import static common.http.HttpMethod.POST; | ||
import static common.http.HttpMethod.PUT; | ||
|
||
public abstract class AbstractController implements Controller { | ||
|
||
public static final String EXCEPTION_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD = "요청에 해당하는 메서드가 없습니다."; | ||
private final Map<HttpMethod, BiConsumer<Request, Response>> methodMapping = new EnumMap<>(HttpMethod.class); | ||
|
||
protected AbstractController() { | ||
methodMapping.put(GET, this::doGet); | ||
methodMapping.put(POST, this::doPost); | ||
methodMapping.put(PUT, this::doPut); | ||
methodMapping.put(PATCH, this::doPatch); | ||
methodMapping.put(DELETE, this::doDelete); | ||
} | ||
|
||
@Override | ||
public void service(Request request, Response response) { | ||
methodMapping.get(request.getHttpMethod()).accept(request, response); | ||
} | ||
|
||
protected void doGet(Request request, Response response) { | ||
throw new IllegalArgumentException(EXCEPTION_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); | ||
} | ||
|
||
protected void doPost(Request request, Response response) { | ||
throw new IllegalArgumentException(EXCEPTION_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); | ||
} | ||
|
||
protected void doPut(Request request, Response response) { | ||
throw new IllegalArgumentException(EXCEPTION_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); | ||
} | ||
|
||
protected void doPatch(Request request, Response response) { | ||
throw new IllegalArgumentException(EXCEPTION_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); | ||
} | ||
|
||
protected void doDelete(Request request, Response response) { | ||
throw new IllegalArgumentException(EXCEPTION_MESSAGE_WHEN_CALL_NOT_DECLARED_METHOD); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package common.http; | ||
|
||
import java.util.Arrays; | ||
|
||
public enum ContentType { | ||
HTML("text/html", "html"), | ||
CSS("text/css", "css"), | ||
JS("text/javascript", "js"), | ||
ICO("image/ico", "ico"), | ||
; | ||
|
||
public static final String DELIMITER_FOR_EXTENSION = "."; | ||
|
||
private final String type; | ||
private final String extension; | ||
|
||
ContentType(String type, String extension) { | ||
this.type = type; | ||
this.extension = extension; | ||
} | ||
|
||
public static ContentType findByExtension(String extension) { | ||
return Arrays.stream(ContentType.values()) | ||
.filter(contentType -> contentType.extension.equals(extension)) | ||
.findFirst() | ||
.orElseThrow(() -> new IllegalArgumentException("유효하지 않은 확장자명 입니다.")); | ||
} | ||
|
||
public static ContentType findByPath(String path) { | ||
int indexBeforeExtension = path.lastIndexOf(DELIMITER_FOR_EXTENSION); | ||
if (indexBeforeExtension == -1) { | ||
throw new IllegalArgumentException("파일의 확장자명이 없습니다."); | ||
} | ||
|
||
String extension = path.substring(indexBeforeExtension + 1); | ||
return findByExtension(extension); | ||
} | ||
|
||
public String getType() { | ||
return type; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package common.http; | ||
|
||
public interface Controller { | ||
void service(Request request, Response response); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package common.http; | ||
|
||
public interface ControllerManager { | ||
|
||
void add(String path, Controller controller); | ||
|
||
void service(Request request, Response response); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package common.http; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
public class Cookie { | ||
|
||
private static final String SEPARATOR = "; "; | ||
public static final String DELIMITER = "="; | ||
|
||
private static final int ATTRIBUTE_INDEX = 0; | ||
private static final int VALUE_INDEX = 1; | ||
|
||
private final Map<String, String> items; | ||
|
||
private Cookie(Map<String, String> items) { | ||
this.items = items; | ||
} | ||
|
||
public static Cookie from(String cookieHeader) { | ||
if (cookieHeader == null || cookieHeader.isEmpty()) { | ||
return new Cookie(new HashMap<>()); | ||
} | ||
return new Cookie(parse(cookieHeader)); | ||
} | ||
|
||
private static Map<String, String> parse(String values) { | ||
Map<String, String> items = new HashMap<>(); | ||
String[] attributesAndValues = values.split(SEPARATOR); | ||
for (String cookie : attributesAndValues) { | ||
String[] attributeAndValue = cookie.split(DELIMITER); | ||
items.put(attributeAndValue[ATTRIBUTE_INDEX], attributeAndValue[VALUE_INDEX].trim()); | ||
} | ||
return items; | ||
} | ||
|
||
public void addAttribute(String attribute, String value) { | ||
items.put(attribute, value); | ||
} | ||
|
||
boolean hasAttribute(String attribute) { | ||
return items.containsKey(attribute); | ||
} | ||
|
||
String getAttribute(String attribute) { | ||
return items.get(attribute); | ||
} | ||
|
||
public String getValue() { | ||
StringBuilder stringBuilder = new StringBuilder(); | ||
for (Map.Entry<String, String> item : items.entrySet()) { | ||
stringBuilder.append(item.getKey()).append(DELIMITER).append(item.getValue()).append(SEPARATOR); | ||
} | ||
String cookie = stringBuilder.toString(); | ||
return cookie.substring(0, cookie.length()-2); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package common.http; | ||
|
||
public class Cookies { | ||
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. 일급컬렉션 같은 이름이지만 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. CookieManager와 같다고 생각하고 클래스를 만들었습니다! 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의 일급컬렉션 같은 느낌이 들지 않아 질문 남겼었습니다. 지금 역할은 public Cookie {}
public Cookies {
List<Cookie> cookies;
}
public HttpRequestHeaders {
Cookies cookies;
}
public HttpRequest {
HttpRequestHeaders headers;
}
---
doGet(HttpRequest request, ...) {
request.getCookie("")
} 저는 Cookie가 HTTP 요청에 꽤나 의존적(?)이라(요청에 담겨서 오고 이를 꺼내서 활용 ≈ 헤더) 생각해서요. |
||
|
||
private static final String JSESSIONID = "JSESSIONID"; | ||
|
||
private Cookies() {} | ||
|
||
public static Cookie ofJSessionId(String id) { | ||
return Cookie.from(JSESSIONID + Cookie.DELIMITER + id); | ||
} | ||
|
||
public static String getJsessionid(Cookie cookie) { | ||
if (cookie.hasAttribute(JSESSIONID)) { | ||
return cookie.getAttribute(JSESSIONID); | ||
} | ||
throw new IllegalArgumentException("쿠키에 세션 아이디가 없습니다."); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package org.apache.coyote.http11; | ||
package common.http; | ||
|
||
public enum HttpMethod { | ||
GET, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package common.http; | ||
|
||
public enum HttpStatus { | ||
OK("OK", 200), | ||
CREATED("Created", 201), | ||
ACCEPTED("Accepted", 202), | ||
NO_CONTENT("No Content", 204), | ||
MOVED_PERMANENTLY("Moved Permanently", 301), | ||
FOUND("Found", 302), | ||
PERMANENT_REDIRECT("Permanent Redirect", 308), | ||
BAD_REQUEST("Bad Request", 400), | ||
UNAUTHORIZED("Unauthorized", 401), | ||
FORBIDDEN("Forbidden", 403), | ||
NOT_FOUND("Not Found", 404), | ||
CONFLICT("Conflict", 409), | ||
INTERNAL_SERVER_ERROR("Internal Server Error", 500); | ||
|
||
private final String statusMessage; | ||
private final int statusCode; | ||
|
||
HttpStatus(String statusMessage, int statusCode) { | ||
this.statusMessage = statusMessage; | ||
this.statusCode = statusCode; | ||
} | ||
|
||
public String getStatusMessage() { | ||
return statusMessage; | ||
} | ||
|
||
public int getStatusCode() { | ||
return statusCode; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package common.http; | ||
|
||
public interface Request { | ||
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. Request를 추상화한 이유가 무엇인가요?! 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와 HttpResponse 모두 원래 coyote에 있었던 걸로 기억합니다! 프로토콜이 변경됐을때도 사용할 수 있도록 하고 싶은데 이건 조금 더 추상화를 해봐야겠네요.. 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. 그런 의도셨군요. 패키지간의 응집도와 역할, 의존도를 엄청 신경 쓰신게 느껴졌습니다.👍👍 |
||
|
||
HttpMethod getHttpMethod(); | ||
|
||
String getVersionOfTheProtocol(); | ||
|
||
String getAccount(); | ||
|
||
String getPassword(); | ||
|
||
Session getSession(boolean create); | ||
|
||
Session getSession(); | ||
|
||
String getCookie(); | ||
|
||
String getPath(); | ||
|
||
boolean hasValidSession(); | ||
|
||
String getEmail(); | ||
|
||
void addSession(Session session); | ||
|
||
boolean hasStaticResourcePath(); | ||
} |
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.
WebContentInterceptor
는 어떤 역할을 하나요?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.
학습테스트에서 캐시를 설정해주는 역할입니다!