Browse Source

Flush of underlying response in ContentCachingResponseWrapper

Prior to this commit, when adding a ShallowEtagHeaderFilter to an
application, the ServletResponse would be wrapped by a
ContentCachingResponseWrapper. When any part of the Spring
infrastructure calls `flushBuffer` on the wrapped response, the call is
delegated to the actual response, which is committed. It's not possible
to alter the response (headers, content) anymore - the ETag filter can't
act.

This change prevents the `flushBuffer` call to be delegated and only
commits the underlying response once the cached content is copied to the
actual response stream.

Issue: SPR-13717
pull/929/head
Brian Clozel 9 years ago
parent
commit
9d9433a6eb
  1. 9
      spring-web/src/main/java/org/springframework/web/util/ContentCachingResponseWrapper.java
  2. 21
      spring-web/src/test/java/org/springframework/web/filter/ShallowEtagHeaderFilterTests.java

9
spring-web/src/main/java/org/springframework/web/util/ContentCachingResponseWrapper.java

@ -21,6 +21,7 @@ import java.io.InputStream; @@ -21,6 +21,7 @@ import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
@ -121,6 +122,11 @@ public class ContentCachingResponseWrapper extends HttpServletResponseWrapper { @@ -121,6 +122,11 @@ public class ContentCachingResponseWrapper extends HttpServletResponseWrapper {
return this.writer;
}
@Override
public void flushBuffer() throws IOException {
// do not flush the underlying response as the content as not been copied to it yet
}
@Override
public void setContentLength(int len) {
if (len > this.content.size()) {
@ -213,6 +219,9 @@ public class ContentCachingResponseWrapper extends HttpServletResponseWrapper { @@ -213,6 +219,9 @@ public class ContentCachingResponseWrapper extends HttpServletResponseWrapper {
}
this.content.writeTo(rawResponse.getOutputStream());
this.content.reset();
if (complete) {
super.flushBuffer();
}
}
}

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

@ -197,4 +197,25 @@ public class ShallowEtagHeaderFilterTests { @@ -197,4 +197,25 @@ public class ShallowEtagHeaderFilterTests {
assertEquals("Invalid redirect URL", "http://www.google.com", response.getRedirectedUrl());
}
// SPR-13717
@Test
public void filterFlushResponse() throws Exception {
final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels");
MockHttpServletResponse response = new MockHttpServletResponse();
final byte[] responseBody = "Hello World".getBytes("UTF-8");
FilterChain filterChain = (filterRequest, filterResponse) -> {
assertEquals("Invalid request passed", request, filterRequest);
((HttpServletResponse) filterResponse).setStatus(HttpServletResponse.SC_OK);
FileCopyUtils.copy(responseBody, filterResponse.getOutputStream());
filterResponse.flushBuffer();
};
filter.doFilter(request, response, filterChain);
assertEquals("Invalid status", 200, response.getStatus());
assertEquals("Invalid ETag header", "\"0b10a8db164e0754105b7a99be72e3fe5\"", response.getHeader("ETag"));
assertTrue("Invalid Content-Length header", response.getContentLength() > 0);
assertArrayEquals("Invalid content", responseBody, response.getContentAsByteArray());
}
}

Loading…
Cancel
Save