Browse Source

Handle multiple conditional request headers

Prior to this change, setting both "If-None-Match" and
"If-Unmodified-Since" conditional request headers would check for both
conditions to be met.

This commit fixes this behavior to follow the RFC7232 Section 6:
> entity tags are presumed to be more accurate than date validators

So in case both conditions are present, the "If-None-Match" condition
takes precedence.

Issue: SPR-14224
pull/1046/head
Brian Clozel 9 years ago
parent
commit
a50ea80e4e
  1. 18
      spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java
  2. 5
      spring-web/src/test/java/org/springframework/web/context/request/ServletWebRequestHttpMethodsTests.java

18
spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java

@ -237,7 +237,12 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
if (StringUtils.hasLength(etag) && !this.notModified) { if (StringUtils.hasLength(etag) && !this.notModified) {
if (isCompatibleWithConditionalRequests(response)) { if (isCompatibleWithConditionalRequests(response)) {
etag = addEtagPadding(etag); etag = addEtagPadding(etag);
this.notModified = isEtagNotModified(etag) && isTimestampNotModified(lastModifiedTimestamp); if (hasRequestHeader(HEADER_IF_NONE_MATCH)) {
this.notModified = isEtagNotModified(etag);
}
else if (hasRequestHeader(HEADER_IF_MODIFIED_SINCE)) {
this.notModified = isTimestampNotModified(lastModifiedTimestamp);
}
if (response != null) { if (response != null) {
if (supportsNotModifiedStatus()) { if (supportsNotModifiedStatus()) {
if (this.notModified) { if (this.notModified) {
@ -287,6 +292,10 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
return (response.getHeader(header) == null); return (response.getHeader(header) == null);
} }
private boolean hasRequestHeader(String headerName) {
return StringUtils.hasLength(getHeader(headerName));
}
private boolean supportsNotModifiedStatus() { private boolean supportsNotModifiedStatus() {
String method = getRequest().getMethod(); String method = getRequest().getMethod();
return (METHOD_GET.equals(method) || METHOD_HEAD.equals(method)); return (METHOD_GET.equals(method) || METHOD_HEAD.equals(method));
@ -294,9 +303,8 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
private boolean supportsConditionalUpdate() { private boolean supportsConditionalUpdate() {
String method = getRequest().getMethod(); String method = getRequest().getMethod();
String ifUnmodifiedHeader = getRequest().getHeader(HEADER_IF_UNMODIFIED_SINCE);
return (METHOD_POST.equals(method) || METHOD_PUT.equals(method) || METHOD_DELETE.equals(method)) return (METHOD_POST.equals(method) || METHOD_PUT.equals(method) || METHOD_DELETE.equals(method))
&& StringUtils.hasLength(ifUnmodifiedHeader); && hasRequestHeader(HEADER_IF_UNMODIFIED_SINCE);
} }
private boolean isTimestampNotModified(long lastModifiedTimestamp) { private boolean isTimestampNotModified(long lastModifiedTimestamp) {
@ -318,7 +326,7 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
dateValue = getRequest().getDateHeader(headerName); dateValue = getRequest().getDateHeader(headerName);
} }
catch (IllegalArgumentException ex) { catch (IllegalArgumentException ex) {
String headerValue = getRequest().getHeader(headerName); String headerValue = getHeader(headerName);
// Possibly an IE 10 style value: "Wed, 09 Apr 2014 09:57:42 GMT; length=13774" // Possibly an IE 10 style value: "Wed, 09 Apr 2014 09:57:42 GMT; length=13774"
int separatorIndex = headerValue.indexOf(';'); int separatorIndex = headerValue.indexOf(';');
if (separatorIndex != -1) { if (separatorIndex != -1) {
@ -336,7 +344,7 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
private boolean isEtagNotModified(String etag) { private boolean isEtagNotModified(String etag) {
if (StringUtils.hasLength(etag)) { if (StringUtils.hasLength(etag)) {
String ifNoneMatch = getRequest().getHeader(HEADER_IF_NONE_MATCH); String ifNoneMatch = getHeader(HEADER_IF_NONE_MATCH);
if (StringUtils.hasLength(ifNoneMatch)) { if (StringUtils.hasLength(ifNoneMatch)) {
String[] clientEtags = StringUtils.delimitedListToStringArray(ifNoneMatch, ",", " "); String[] clientEtags = StringUtils.delimitedListToStringArray(ifNoneMatch, ",", " ");
for (String clientEtag : clientEtags) { for (String clientEtag : clientEtags) {

5
spring-web/src/test/java/org/springframework/web/context/request/ServletWebRequestHttpMethodsTests.java

@ -204,6 +204,7 @@ public class ServletWebRequestHttpMethodsTests {
assertEquals(dateFormat.format(currentDate.getTime()), servletResponse.getHeader("Last-Modified")); assertEquals(dateFormat.format(currentDate.getTime()), servletResponse.getHeader("Last-Modified"));
} }
// SPR-14224
@Test @Test
public void checkNotModifiedETagAndModifiedTimestamp() { public void checkNotModifiedETagAndModifiedTimestamp() {
String eTag = "\"Foo\""; String eTag = "\"Foo\"";
@ -212,9 +213,9 @@ public class ServletWebRequestHttpMethodsTests {
long oneMinuteAgo = currentEpoch - (1000 * 60); long oneMinuteAgo = currentEpoch - (1000 * 60);
servletRequest.addHeader("If-Modified-Since", oneMinuteAgo); servletRequest.addHeader("If-Modified-Since", oneMinuteAgo);
assertFalse(request.checkNotModified(eTag, currentEpoch)); assertTrue(request.checkNotModified(eTag, currentEpoch));
assertEquals(200, servletResponse.getStatus()); assertEquals(304, servletResponse.getStatus());
assertEquals(eTag, servletResponse.getHeader("ETag")); assertEquals(eTag, servletResponse.getHeader("ETag"));
assertEquals(dateFormat.format(currentEpoch), servletResponse.getHeader("Last-Modified")); assertEquals(dateFormat.format(currentEpoch), servletResponse.getHeader("Last-Modified"));
} }

Loading…
Cancel
Save