Restrict ETag generation in ShallowEtagHeaderFilter

Prior to this commit, all 2xx HTTP responses were eligible for ETag
generation in ShallowEtagHeaderFilter. In some cases, this would use
CPU resources for no reason since HTTP clients would not use ETags.

This commit is an optimization and restricts ETags generation in cases
where (all conditions must be met):
- response has a 2xx status
- request is a GET
- response does not contain "no-store" in its "Cache-Control" header

Issue: SPR-11110
This commit is contained in:
Brian Clozel 2014-02-13 22:08:44 +01:00
parent d550ffb37f
commit 6fba8292f5
2 changed files with 24 additions and 3 deletions

View File

@ -28,6 +28,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
import org.springframework.util.DigestUtils;
import org.springframework.util.StreamUtils;
@ -51,6 +52,10 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter {
private static final String HEADER_IF_NONE_MATCH = "If-None-Match";
private static final String HEADER_CACHE_CONTROL = "Cache-Control";
private static final String DIRECTIVE_NO_STORE = "no-store";
/**
* The default value is "false" so that the filter may delay the generation of
@ -122,7 +127,13 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter {
/**
* Indicates whether the given request and response are eligible for ETag generation.
* <p>The default implementation returns {@code true} for response status codes in the {@code 2xx} series.
* <p>The default implementation returns {@code true} if all conditions match:
* <ul>
* <li>response status codes in the {@code 2xx} series</li>
* <li>request method is a GET</li>
* <li>response Cache-Control header is null or does not contain a "no-store" directive</li>
* </ul>
*
* @param request the HTTP request
* @param response the HTTP response
* @param responseStatusCode the HTTP response status code
@ -132,7 +143,10 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter {
protected boolean isEligibleForEtag(HttpServletRequest request, HttpServletResponse response,
int responseStatusCode, byte[] responseBody) {
return (responseStatusCode >= 200 && responseStatusCode < 300);
return (responseStatusCode >= 200 && responseStatusCode < 300)
&& HttpMethod.GET.name().equals(request.getMethod())
&& (response.getHeader(HEADER_CACHE_CONTROL) == null
|| !response.getHeader(HEADER_CACHE_CONTROL).contains(DIRECTIVE_NO_STORE));
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -48,6 +48,13 @@ public class ShallowEtagHeaderFilterTests {
assertTrue(filter.isEligibleForEtag(request, response, 200, new byte[0]));
assertFalse(filter.isEligibleForEtag(request, response, 300, new byte[0]));
request = new MockHttpServletRequest("POST", "/hotels");
assertFalse(filter.isEligibleForEtag(request, response, 200, new byte[0]));
request = new MockHttpServletRequest("POST", "/hotels");
request.addHeader("Cache-Control","must-revalidate, no-store");
assertFalse(filter.isEligibleForEtag(request, response, 200, new byte[0]));
}
@Test