Add caching headers to If-Unmodified-Since responses
Conditional requests using "If-Unmodified-Since" headers are generally used as precondition checks for state-changing methods (POST, PUT, DELETE). See https://datatracker.ietf.org/doc/html/rfc7232#section-3.4 The spec also allows for idempotent methods like GET and HEAD. Prior to this commit, the "If-Unmodified-Since" processing done in `checkNotModified` (see `ServletWebRequest` and `DefaultServerWebExchange`) would only focus on the state changing methods and not take into account the safe methods. For those cases, the "ETag" and "Last-Modified" would be missing from the response. This commit ensures that such headers are added as expected in these cases. Fixes gh-29362
This commit is contained in:
@@ -23,6 +23,7 @@ import java.lang.annotation.Target;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
@@ -286,9 +287,9 @@ class ServletWebRequestHttpMethodsTests {
|
||||
assertThat(servletResponse.getHeader("ETag")).isEqualTo(etag);
|
||||
}
|
||||
|
||||
@ParameterizedHttpMethodTest
|
||||
void checkNotModifiedTimestampWithLengthPart(String method) {
|
||||
setUpRequest(method);
|
||||
@Test
|
||||
void checkNotModifiedTimestampWithLengthPart() {
|
||||
setUpRequest("GET");
|
||||
|
||||
long epochTime = ZonedDateTime.parse(CURRENT_TIME, RFC_1123_DATE_TIME).toInstant().toEpochMilli();
|
||||
servletRequest.setMethod("GET");
|
||||
@@ -299,12 +300,11 @@ class ServletWebRequestHttpMethodsTests {
|
||||
assertThat(servletResponse.getDateHeader("Last-Modified") / 1000).isEqualTo(epochTime / 1000);
|
||||
}
|
||||
|
||||
@ParameterizedHttpMethodTest
|
||||
void checkModifiedTimestampWithLengthPart(String method) {
|
||||
setUpRequest(method);
|
||||
@Test
|
||||
void checkModifiedTimestampWithLengthPart() {
|
||||
setUpRequest("GET");
|
||||
|
||||
long epochTime = ZonedDateTime.parse(CURRENT_TIME, RFC_1123_DATE_TIME).toInstant().toEpochMilli();
|
||||
servletRequest.setMethod("GET");
|
||||
servletRequest.addHeader("If-Modified-Since", "Wed, 08 Apr 2014 09:57:42 GMT; length=13774");
|
||||
|
||||
assertThat(request.checkNotModified(epochTime)).isFalse();
|
||||
@@ -312,13 +312,12 @@ class ServletWebRequestHttpMethodsTests {
|
||||
assertThat(servletResponse.getDateHeader("Last-Modified") / 1000).isEqualTo(epochTime / 1000);
|
||||
}
|
||||
|
||||
@ParameterizedHttpMethodTest
|
||||
void checkNotModifiedTimestampConditionalPut(String method) {
|
||||
setUpRequest(method);
|
||||
@Test
|
||||
void checkNotModifiedTimestampConditionalPut() {
|
||||
setUpRequest("PUT");
|
||||
|
||||
long currentEpoch = currentDate.getTime();
|
||||
long oneMinuteAgo = currentEpoch - (1000 * 60);
|
||||
servletRequest.setMethod("PUT");
|
||||
servletRequest.addHeader("If-UnModified-Since", currentEpoch);
|
||||
|
||||
assertThat(request.checkNotModified(oneMinuteAgo)).isFalse();
|
||||
@@ -326,13 +325,12 @@ class ServletWebRequestHttpMethodsTests {
|
||||
assertThat(servletResponse.getHeader("Last-Modified")).isNull();
|
||||
}
|
||||
|
||||
@ParameterizedHttpMethodTest
|
||||
void checkNotModifiedTimestampConditionalPutConflict(String method) {
|
||||
setUpRequest(method);
|
||||
@Test
|
||||
void checkNotModifiedTimestampConditionalPutConflict() {
|
||||
setUpRequest("PUT");
|
||||
|
||||
long currentEpoch = currentDate.getTime();
|
||||
long oneMinuteAgo = currentEpoch - (1000 * 60);
|
||||
servletRequest.setMethod("PUT");
|
||||
servletRequest.addHeader("If-UnModified-Since", oneMinuteAgo);
|
||||
|
||||
assertThat(request.checkNotModified(currentEpoch)).isTrue();
|
||||
@@ -340,6 +338,19 @@ class ServletWebRequestHttpMethodsTests {
|
||||
assertThat(servletResponse.getHeader("Last-Modified")).isNull();
|
||||
}
|
||||
|
||||
@ParameterizedHttpMethodTest
|
||||
void checkNotModifiedConditionalGet(String method) {
|
||||
setUpRequest(method);
|
||||
long currentEpoch = currentDate.getTime();
|
||||
long oneMinuteAgo = currentEpoch - (1000 * 60);
|
||||
servletRequest.addHeader("If-UnModified-Since", currentEpoch);
|
||||
|
||||
assertThat(request.checkNotModified(oneMinuteAgo)).isFalse();
|
||||
assertThat(servletResponse.getStatus()).isEqualTo(200);
|
||||
assertThat(servletResponse.getDateHeader("Last-Modified") / 1000).isEqualTo(oneMinuteAgo / 1000);
|
||||
}
|
||||
|
||||
|
||||
private void setUpRequest(String method) {
|
||||
this.servletRequest.setMethod(method);
|
||||
this.servletRequest.setRequestURI("https://example.org");
|
||||
|
||||
@@ -307,4 +307,17 @@ class DefaultServerWebExchangeCheckNotModifiedTests {
|
||||
assertThat(exchange.getResponse().getHeaders().getLastModified()).isEqualTo(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void checkNotModifiedTimestampConditionalGet() throws Exception {
|
||||
String eTag = "\"Test\"";
|
||||
Instant oneMinuteAgo = currentDate.minusSeconds(60);
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/").ifUnmodifiedSince(currentDate.toEpochMilli()).build();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||
|
||||
assertThat(exchange.checkNotModified(eTag, oneMinuteAgo)).isFalse();
|
||||
assertThat(exchange.getResponse().getStatusCode()).isNull();
|
||||
assertThat(exchange.getResponse().getHeaders().getLastModified()).isEqualTo(oneMinuteAgo.toEpochMilli());
|
||||
assertThat(exchange.getResponse().getHeaders().getETag()).isEqualTo(eTag);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user