From 6fba8292f5fa28cb99ee567f039ab21fa340cedf Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Thu, 13 Feb 2014 22:08:44 +0100 Subject: [PATCH] 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 --- .../web/filter/ShallowEtagHeaderFilter.java | 18 ++++++++++++++++-- .../filter/ShallowEtagHeaderFilterTests.java | 9 ++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java b/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java index 620b326f0f7..6b5e688848d 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java @@ -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. - *

The default implementation returns {@code true} for response status codes in the {@code 2xx} series. + *

The default implementation returns {@code true} if all conditions match: + *

+ * * @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)); } /** diff --git a/spring-web/src/test/java/org/springframework/web/filter/ShallowEtagHeaderFilterTests.java b/spring-web/src/test/java/org/springframework/web/filter/ShallowEtagHeaderFilterTests.java index 8ff31eb224a..8c0318312c3 100644 --- a/spring-web/src/test/java/org/springframework/web/filter/ShallowEtagHeaderFilterTests.java +++ b/spring-web/src/test/java/org/springframework/web/filter/ShallowEtagHeaderFilterTests.java @@ -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