From 444378c426128d72870971564131014d3cdff77c Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Mon, 15 Jun 2009 10:58:45 +0000 Subject: [PATCH] SPR-5825 - ShallowEtagHeaderFilter doesn't work: response body is empty --- .../web/filter/ShallowEtagHeaderFilter.java | 69 ++++++++++++++----- .../filter/ShallowEtagHeaderFilterTest.java | 28 +++++++- 2 files changed, 78 insertions(+), 19 deletions(-) diff --git a/org.springframework.web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java b/org.springframework.web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java index dfe1318193..3888435784 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java +++ b/org.springframework.web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java @@ -20,7 +20,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; -import java.io.Writer; +import java.io.UnsupportedEncodingException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; @@ -32,12 +32,12 @@ import org.springframework.util.FileCopyUtils; import org.springframework.util.Md5HashUtils; /** - * {@link javax.servlet.Filter} that generates an ETag value based on the content - * on the response. This ETag is compared to the If-None-Match header of the request. - * If these headers are equal, the resonse content is not sent, but rather a 304 "Not Modified" status. + * {@link javax.servlet.Filter} that generates an ETag value based on the content on the response. This + * ETag is compared to the If-None-Match header of the request. If these headers are equal, the resonse + * content is not sent, but rather a 304 "Not Modified" status. * - *

Since the ETag is based on the response content, the response (or {@link org.springframework.web.servlet.View}) - * is still rendered. As such, this filter only saves bandwidth, not server performance. + *

Since the ETag is based on the response content, the response (or {@link org.springframework.web.servlet.View}) is + * still rendered. As such, this filter only saves bandwidth, not server performance. * * @author Arjen Poutsma * @since 3.0 @@ -48,7 +48,6 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter { private static String HEADER_IF_NONE_MATCH = "If-None-Match"; - @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { @@ -78,8 +77,9 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter { } /** - * Generate the ETag header value from the given response body byte array. - *

The default implementation generates an MD5 hash. + * Generate the ETag header value from the given response body byte array.

The default implementation generates an + * MD5 hash. + * * @param bytes the response bdoy as byte array * @return the ETag header value * @see Md5HashUtils @@ -91,11 +91,10 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter { return builder.toString(); } - /** - * {@link HttpServletRequest} wrapper that buffers all content written to the - * {@linkplain #getOutputStream() output stream} and {@linkplain #getWriter() writer}, - * and allows this content to be retrieved via a {@link #toByteArray() byte array}. + * {@link HttpServletRequest} wrapper that buffers all content written to the {@linkplain #getOutputStream() output + * stream} and {@linkplain #getWriter() writer}, and allows this content to be retrieved via a {@link #toByteArray() + * byte array}. */ private static class ShallowEtagResponseWrapper extends HttpServletResponseWrapper { @@ -118,10 +117,8 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter { public PrintWriter getWriter() throws IOException { if (this.writer == null) { String characterEncoding = getCharacterEncoding(); - Writer targetWriter = (characterEncoding != null ? - new OutputStreamWriter(this.outputStream, characterEncoding) : - new OutputStreamWriter(this.outputStream)); - this.writer = new PrintWriter(targetWriter); + this.writer = (characterEncoding != null ? new ResponsePrintWriter(characterEncoding) : + new ResponsePrintWriter()); } return this.writer; } @@ -141,14 +138,50 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter { return this.content.toByteArray(); } - private class ResponseServletOutputStream extends ServletOutputStream { @Override public void write(int b) throws IOException { content.write(b); } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + content.write(b, off, len); + } + + } + + private class ResponsePrintWriter extends PrintWriter { + + private ResponsePrintWriter() { + super(new OutputStreamWriter(content)); + } + + private ResponsePrintWriter(String characterEncoding) throws UnsupportedEncodingException { + super(new OutputStreamWriter(content, characterEncoding)); + } + + @Override + public void write(char buf[], int off, int len) { + super.write(buf, off, len); + super.flush(); + } + + @Override + public void write(String s, int off, int len) { + super.write(s, off, len); + super.flush(); + } + + @Override + public void write(int c) { + super.write(c); + super.flush(); + } + } + } } diff --git a/org.springframework.web/src/test/java/org/springframework/web/filter/ShallowEtagHeaderFilterTest.java b/org.springframework.web/src/test/java/org/springframework/web/filter/ShallowEtagHeaderFilterTest.java index 35f4f45f27..a088908e66 100644 --- a/org.springframework.web/src/test/java/org/springframework/web/filter/ShallowEtagHeaderFilterTest.java +++ b/org.springframework.web/src/test/java/org/springframework/web/filter/ShallowEtagHeaderFilterTest.java @@ -1,5 +1,5 @@ /* - * Copyright ${YEAR} the original author or authors. + * Copyright 2002-2009 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. @@ -90,4 +90,30 @@ public class ShallowEtagHeaderFilterTest { assertArrayEquals("Invalid content", new byte[0], response.getContentAsByteArray()); } + @Test + public void filterWriter() throws Exception { + final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels"); + String etag = "\"0b10a8db164e0754105b7a99be72e3fe5\""; + request.addHeader("If-None-Match", etag); + MockHttpServletResponse response = new MockHttpServletResponse(); + + FilterChain filterChain = new FilterChain() { + + public void doFilter(ServletRequest filterRequest, ServletResponse filterResponse) + throws IOException, ServletException { + assertEquals("Invalid request passed", request, filterRequest); + ((HttpServletResponse) filterResponse).setStatus(HttpServletResponse.SC_OK); + String responseBody = "Hello World"; + FileCopyUtils.copy(responseBody, filterResponse.getWriter()); + } + }; + + filter.doFilter(request, response, filterChain); + + assertEquals("Invalid status", 304, response.getStatus()); + assertEquals("Invalid ETag header", "\"0b10a8db164e0754105b7a99be72e3fe5\"", response.getHeader("ETag")); + assertEquals("Invalid Content-Length header", 0, response.getContentLength()); + assertArrayEquals("Invalid content", new byte[0], response.getContentAsByteArray()); + } + } \ No newline at end of file -- GitLab