Skip to content

Commit 6fba829

Browse files
committed
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
1 parent d550ffb commit 6fba829

File tree

2 files changed

+24
-3
lines changed

2 files changed

+24
-3
lines changed

spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import javax.servlet.http.HttpServletResponse;
2929
import javax.servlet.http.HttpServletResponseWrapper;
3030

31+
import org.springframework.http.HttpMethod;
3132
import org.springframework.util.Assert;
3233
import org.springframework.util.DigestUtils;
3334
import org.springframework.util.StreamUtils;
@@ -51,6 +52,10 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter {
5152

5253
private static final String HEADER_IF_NONE_MATCH = "If-None-Match";
5354

55+
private static final String HEADER_CACHE_CONTROL = "Cache-Control";
56+
57+
private static final String DIRECTIVE_NO_STORE = "no-store";
58+
5459

5560
/**
5661
* The default value is "false" so that the filter may delay the generation of
@@ -122,7 +127,13 @@ private void copyBodyToResponse(byte[] body, HttpServletResponse response) throw
122127

123128
/**
124129
* Indicates whether the given request and response are eligible for ETag generation.
125-
* <p>The default implementation returns {@code true} for response status codes in the {@code 2xx} series.
130+
* <p>The default implementation returns {@code true} if all conditions match:
131+
* <ul>
132+
* <li>response status codes in the {@code 2xx} series</li>
133+
* <li>request method is a GET</li>
134+
* <li>response Cache-Control header is null or does not contain a "no-store" directive</li>
135+
* </ul>
136+
*
126137
* @param request the HTTP request
127138
* @param response the HTTP response
128139
* @param responseStatusCode the HTTP response status code
@@ -132,7 +143,10 @@ private void copyBodyToResponse(byte[] body, HttpServletResponse response) throw
132143
protected boolean isEligibleForEtag(HttpServletRequest request, HttpServletResponse response,
133144
int responseStatusCode, byte[] responseBody) {
134145

135-
return (responseStatusCode >= 200 && responseStatusCode < 300);
146+
return (responseStatusCode >= 200 && responseStatusCode < 300)
147+
&& HttpMethod.GET.name().equals(request.getMethod())
148+
&& (response.getHeader(HEADER_CACHE_CONTROL) == null
149+
|| !response.getHeader(HEADER_CACHE_CONTROL).contains(DIRECTIVE_NO_STORE));
136150
}
137151

138152
/**

spring-web/src/test/java/org/springframework/web/filter/ShallowEtagHeaderFilterTests.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -48,6 +48,13 @@ public void isEligibleForEtag() {
4848

4949
assertTrue(filter.isEligibleForEtag(request, response, 200, new byte[0]));
5050
assertFalse(filter.isEligibleForEtag(request, response, 300, new byte[0]));
51+
52+
request = new MockHttpServletRequest("POST", "/hotels");
53+
assertFalse(filter.isEligibleForEtag(request, response, 200, new byte[0]));
54+
55+
request = new MockHttpServletRequest("POST", "/hotels");
56+
request.addHeader("Cache-Control","must-revalidate, no-store");
57+
assertFalse(filter.isEligibleForEtag(request, response, 200, new byte[0]));
5158
}
5259

5360
@Test

0 commit comments

Comments
 (0)