Merge branch '5.3.x'

This commit is contained in:
Brian Clozel 2022-10-21 15:24:08 +02:00
commit 5e808ad018
4 changed files with 53 additions and 15 deletions

View File

@ -214,12 +214,12 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
// Evaluate conditions in order of precedence. // Evaluate conditions in order of precedence.
// See https://datatracker.ietf.org/doc/html/rfc9110#section-13.2.2 // See https://datatracker.ietf.org/doc/html/rfc9110#section-13.2.2
if (validateIfMatch(eTag)) { if (validateIfMatch(eTag)) {
updateResponseStateChanging(); updateResponseStateChanging(eTag, lastModifiedTimestamp);
return this.notModified; return this.notModified;
} }
// 2) If-Unmodified-Since // 2) If-Unmodified-Since
else if (validateIfUnmodifiedSince(lastModifiedTimestamp)) { else if (validateIfUnmodifiedSince(lastModifiedTimestamp)) {
updateResponseStateChanging(); updateResponseStateChanging(eTag, lastModifiedTimestamp);
return this.notModified; return this.notModified;
} }
// 3) If-None-Match // 3) If-None-Match
@ -309,10 +309,13 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
return first.equals(second); return first.equals(second);
} }
private void updateResponseStateChanging() { private void updateResponseStateChanging(String eTag, long lastModifiedTimestamp) {
if (this.notModified && getResponse() != null) { if (this.notModified && getResponse() != null) {
getResponse().setStatus(HttpStatus.PRECONDITION_FAILED.value()); getResponse().setStatus(HttpStatus.PRECONDITION_FAILED.value());
} }
else {
addCachingResponseHeaders(eTag, lastModifiedTimestamp);
}
} }
private boolean validateIfUnmodifiedSince(long lastModifiedTimestamp) { private boolean validateIfUnmodifiedSince(long lastModifiedTimestamp) {
@ -347,13 +350,17 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
getResponse().setStatus(isHttpGetOrHead ? getResponse().setStatus(isHttpGetOrHead ?
HttpStatus.NOT_MODIFIED.value() : HttpStatus.PRECONDITION_FAILED.value()); HttpStatus.NOT_MODIFIED.value() : HttpStatus.PRECONDITION_FAILED.value());
} }
if (isHttpGetOrHead) { addCachingResponseHeaders(eTag, lastModifiedTimestamp);
if (lastModifiedTimestamp > 0 && parseDateValue(getResponse().getHeader(HttpHeaders.LAST_MODIFIED)) == -1) { }
getResponse().setDateHeader(HttpHeaders.LAST_MODIFIED, lastModifiedTimestamp); }
}
if (StringUtils.hasLength(eTag) && getResponse().getHeader(HttpHeaders.ETAG) == null) { private void addCachingResponseHeaders(String eTag, long lastModifiedTimestamp) {
getResponse().setHeader(HttpHeaders.ETAG, padEtagIfNecessary(eTag)); if (SAFE_METHODS.contains(getRequest().getMethod())) {
} if (lastModifiedTimestamp > 0 && parseDateValue(getResponse().getHeader(HttpHeaders.LAST_MODIFIED)) == -1) {
getResponse().setDateHeader(HttpHeaders.LAST_MODIFIED, lastModifiedTimestamp);
}
if (StringUtils.hasLength(eTag) && getResponse().getHeader(HttpHeaders.ETAG) == null) {
getResponse().setHeader(HttpHeaders.ETAG, padEtagIfNecessary(eTag));
} }
} }
} }

View File

@ -259,12 +259,12 @@ public class DefaultServerWebExchange implements ServerWebExchange {
// See https://datatracker.ietf.org/doc/html/rfc9110#section-13.2.2 // See https://datatracker.ietf.org/doc/html/rfc9110#section-13.2.2
// 1) If-Match // 1) If-Match
if (validateIfMatch(eTag)) { if (validateIfMatch(eTag)) {
updateResponseStateChanging(); updateResponseStateChanging(eTag, lastModified);
return this.notModified; return this.notModified;
} }
// 2) If-Unmodified-Since // 2) If-Unmodified-Since
else if (validateIfUnmodifiedSince(lastModified)) { else if (validateIfUnmodifiedSince(lastModified)) {
updateResponseStateChanging(); updateResponseStateChanging(eTag, lastModified);
return this.notModified; return this.notModified;
} }
// 3) If-None-Match // 3) If-None-Match
@ -346,10 +346,13 @@ public class DefaultServerWebExchange implements ServerWebExchange {
return first.equals(second); return first.equals(second);
} }
private void updateResponseStateChanging() { private void updateResponseStateChanging(String eTag, Instant lastModified) {
if (this.notModified) { if (this.notModified) {
getResponse().setStatusCode(HttpStatus.PRECONDITION_FAILED); getResponse().setStatusCode(HttpStatus.PRECONDITION_FAILED);
} }
else {
addCachingResponseHeaders(eTag, lastModified);
}
} }
private boolean validateIfNoneMatch(@Nullable String eTag) { private boolean validateIfNoneMatch(@Nullable String eTag) {
@ -371,7 +374,11 @@ public class DefaultServerWebExchange implements ServerWebExchange {
getResponse().setStatusCode(isSafeMethod ? getResponse().setStatusCode(isSafeMethod ?
HttpStatus.NOT_MODIFIED : HttpStatus.PRECONDITION_FAILED); HttpStatus.NOT_MODIFIED : HttpStatus.PRECONDITION_FAILED);
} }
if (isSafeMethod) { addCachingResponseHeaders(eTag, lastModified);
}
private void addCachingResponseHeaders(@Nullable String eTag, Instant lastModified) {
if (SAFE_METHODS.contains(getRequest().getMethod())) {
if (lastModified.isAfter(Instant.EPOCH) && getResponseHeaders().getLastModified() == -1) { if (lastModified.isAfter(Instant.EPOCH) && getResponseHeaders().getLastModified() == -1) {
getResponseHeaders().setLastModified(lastModified.toEpochMilli()); getResponseHeaders().setLastModified(lastModified.toEpochMilli());
} }

View File

@ -126,6 +126,16 @@ class ServletWebRequestHttpMethodsTests {
assertPreconditionFailed(); assertPreconditionFailed();
} }
@SafeHttpMethodsTest
void ifUnModifiedSinceShouldSetHeadersWithSafeMethod(String method) {
setUpRequest(method);
Instant now = Instant.now().truncatedTo(ChronoUnit.SECONDS);
Instant oneMinuteAgo = now.minus(1, ChronoUnit.MINUTES);
servletRequest.addHeader(HttpHeaders.IF_UNMODIFIED_SINCE, now.toEpochMilli());
assertThat(request.checkNotModified(oneMinuteAgo.toEpochMilli())).isFalse();
assertOkWithLastModified(oneMinuteAgo);
}
@SafeHttpMethodsTest @SafeHttpMethodsTest
void ifNoneMatchShouldMatchIdenticalETagValue(String method) { void ifNoneMatchShouldMatchIdenticalETagValue(String method) {
setUpRequest(method); setUpRequest(method);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -307,4 +307,18 @@ class DefaultServerWebExchangeCheckNotModifiedTests {
assertThat(exchange.getResponse().getHeaders().getLastModified()).isEqualTo(-1); assertThat(exchange.getResponse().getHeaders().getLastModified()).isEqualTo(-1);
} }
@Test
void checkNotModifiedTimestampConditionalWithSafeMethod() throws Exception {
String eTag = "\"Foo\"";
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);
}
} }