Support ETags with special chars in ServletWebRequest
This commit makes sure that HTTP request headers containing ETag values are properly parsed and not simply tokenized using a "," separator. Indeed, ETags can legally contain separator characters such as " " and ",". Issue: SPR-14216
This commit is contained in:
parent
fdb31cd715
commit
29da44c8dc
|
|
@ -21,6 +21,8 @@ import java.util.Date;
|
|||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
|
@ -64,6 +66,12 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
|
|||
|
||||
private static final String METHOD_DELETE = "DELETE";
|
||||
|
||||
/**
|
||||
* Pattern matching ETag multiple field values in headers such as "If-Match", "If-None-Match"
|
||||
* @see <a href="https://tools.ietf.org/html/rfc7232#section-2.3">Section 2.3 of RFC 7232</a>
|
||||
*/
|
||||
private static final Pattern ETAG_HEADER_VALUE_PATTERN = Pattern.compile("\\*|\\s*((W\\/)?(\"[^\"]*\"))\\s*,?");
|
||||
|
||||
|
||||
/** Checking for Servlet 3.0+ HttpServletResponse.getHeader(String) */
|
||||
private static final boolean servlet3Present =
|
||||
|
|
@ -217,7 +225,9 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
|
|||
if (StringUtils.hasLength(etag) && !this.notModified) {
|
||||
if (isCompatibleWithConditionalRequests(response)) {
|
||||
etag = addEtagPadding(etag);
|
||||
this.notModified = isEtagNotModified(etag);
|
||||
if (hasRequestHeader(HEADER_IF_NONE_MATCH)) {
|
||||
this.notModified = isEtagNotModified(etag);
|
||||
}
|
||||
if (response != null) {
|
||||
if (this.notModified && supportsNotModifiedStatus()) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
||||
|
|
@ -343,18 +353,14 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
|
|||
}
|
||||
|
||||
private boolean isEtagNotModified(String etag) {
|
||||
if (StringUtils.hasLength(etag)) {
|
||||
String ifNoneMatch = getHeader(HEADER_IF_NONE_MATCH);
|
||||
if (StringUtils.hasLength(ifNoneMatch)) {
|
||||
String[] clientEtags = StringUtils.delimitedListToStringArray(ifNoneMatch, ",", " ");
|
||||
for (String clientEtag : clientEtags) {
|
||||
// compare weak/strong ETag as per https://tools.ietf.org/html/rfc7232#section-2.3
|
||||
if (StringUtils.hasLength(clientEtag) &&
|
||||
(clientEtag.replaceFirst("^W/", "").equals(etag.replaceFirst("^W/", "")) ||
|
||||
clientEtag.equals("*"))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
String ifNoneMatch = getHeader(HEADER_IF_NONE_MATCH);
|
||||
// compare weak/strong ETag as per https://tools.ietf.org/html/rfc7232#section-2.3
|
||||
String serverETag = etag.replaceFirst("^W/", "");
|
||||
Matcher eTagMatcher = ETAG_HEADER_VALUE_PATTERN.matcher(ifNoneMatch);
|
||||
while (eTagMatcher.find()) {
|
||||
if ("*".equals(eTagMatcher.group())
|
||||
|| serverETag.equals(eTagMatcher.group(3))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
@ -367,7 +373,6 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
|
|||
return etag;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getDescription(boolean includeClientInfo) {
|
||||
HttpServletRequest request = getRequest();
|
||||
|
|
|
|||
|
|
@ -144,6 +144,18 @@ public class ServletWebRequestHttpMethodsTests {
|
|||
assertEquals(eTag, servletResponse.getHeader("ETag"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkNotModifiedETagWithSeparatorChars() {
|
||||
String eTag = "\"Foo, Bar\"";
|
||||
servletRequest.addHeader("If-None-Match", eTag);
|
||||
|
||||
assertTrue(request.checkNotModified(eTag));
|
||||
|
||||
assertEquals(304, servletResponse.getStatus());
|
||||
assertEquals(eTag, servletResponse.getHeader("ETag"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void checkModifiedETag() {
|
||||
String currentETag = "\"Foo\"";
|
||||
|
|
|
|||
Loading…
Reference in New Issue