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:
Brian Clozel
2022-10-21 15:11:08 +02:00
parent 12cc8a9f07
commit 9410998897
4 changed files with 52 additions and 15 deletions

View File

@@ -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");

View File

@@ -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);
}
}