diff --git a/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/filters/post/SendResponseFilter.java b/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/filters/post/SendResponseFilter.java index af5ee1102..a90f373cc 100644 --- a/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/filters/post/SendResponseFilter.java +++ b/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/filters/post/SendResponseFilter.java @@ -240,18 +240,14 @@ public class SendResponseFilter extends ZuulFilter { servletResponse.addHeader(it.first(), it.second()); } } - // Only inserts Content-Length if origin provides it and origin response is not - // gzipped - if (this.zuulProperties.isSetContentLength()) { + if (includeContentLengthHeader(context)) { Long contentLength = context.getOriginContentLength(); - if ( contentLength != null && !context.getResponseGZipped()) { - if(useServlet31) { - servletResponse.setContentLengthLong(contentLength); - } else { - //Try and set some kind of content length if we can safely convert the Long to an int - if (isLongSafe(contentLength)) { - servletResponse.setContentLength(contentLength.intValue()); - } + if(useServlet31) { + servletResponse.setContentLengthLong(contentLength); + } else { + //Try and set some kind of content length if we can safely convert the Long to an int + if (isLongSafe(contentLength)) { + servletResponse.setContentLength(contentLength.intValue()); } } } @@ -261,4 +257,23 @@ public class SendResponseFilter extends ZuulFilter { return value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE; } + protected boolean includeContentLengthHeader(RequestContext context) { + // Not configured to forward the header + if (!this.zuulProperties.isSetContentLength()) { + return false; + } + + // Only if Content-Length is provided + if (context.getOriginContentLength() == null) { + return false; + } + + // If response is compressed, include header only if we are not about to decompress it + if (context.getResponseGZipped()) { + return context.isGzipRequested(); + } + + // Forward it in all other cases + return true; + } } diff --git a/spring-cloud-netflix-zuul/src/test/java/org/springframework/cloud/netflix/zuul/filters/post/SendResponseFilterTests.java b/spring-cloud-netflix-zuul/src/test/java/org/springframework/cloud/netflix/zuul/filters/post/SendResponseFilterTests.java index 5e133c50e..ce05b5c33 100644 --- a/spring-cloud-netflix-zuul/src/test/java/org/springframework/cloud/netflix/zuul/filters/post/SendResponseFilterTests.java +++ b/spring-cloud-netflix-zuul/src/test/java/org/springframework/cloud/netflix/zuul/filters/post/SendResponseFilterTests.java @@ -36,6 +36,7 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.web.util.WebUtils; +import com.netflix.zuul.constants.ZuulHeaders; import com.netflix.zuul.context.Debug; import com.netflix.zuul.context.RequestContext; @@ -61,6 +62,7 @@ public class SendResponseFilterTests { @Before public void setTestRequestcontext() { RequestContext context = new RequestContext(); + context.setResponseGZipped(false); RequestContext.testSetCurrentContext(context); } @@ -102,6 +104,9 @@ public class SendResponseFilterTests { assertThat("wrong debug header", debugHeader, equalTo("[[[test]]]")); } + /* + * GZip NOT requested and NOT a GZip response -> Content-Length forwarded asis + */ @Test public void runWithOriginContentLength() throws Exception { ZuulProperties properties = new ZuulProperties(); @@ -117,6 +122,42 @@ public class SendResponseFilterTests { assertThat("wrong origin content length", contentLength, equalTo("6")); } + /* + * GZip requested and GZip response -> Content-Length forwarded asis + */ + @Test + public void runWithOriginContentLength_gzipRequested_gzipResponse() throws Exception { + ZuulProperties properties = new ZuulProperties(); + properties.setSetContentLength(true); + + SendResponseFilter filter = createFilter(properties, "hello", "UTF-8", new MockHttpServletResponse(), true); + RequestContext.getCurrentContext().setOriginContentLength(6L); // for test + RequestContext.getCurrentContext().setResponseGZipped(true); + ((MockHttpServletRequest) RequestContext.getCurrentContext().getRequest()).addHeader(ZuulHeaders.ACCEPT_ENCODING, "gzip"); + + filter.run(); + + String contentLength = RequestContext.getCurrentContext().getResponse().getHeader("Content-Length"); + assertThat("wrong origin content length", contentLength, equalTo("6")); + } + + /* + * GZip NOT requested and GZip response -> Content-Length discarded + */ + @Test + public void runWithOriginContentLength_gzipNotRequested_gzipResponse() throws Exception { + ZuulProperties properties = new ZuulProperties(); + properties.setSetContentLength(true); + + SendResponseFilter filter = createFilter(properties, "hello", "UTF-8", new MockHttpServletResponse(), true); + RequestContext.getCurrentContext().setOriginContentLength(6L); // for test + RequestContext.getCurrentContext().setResponseGZipped(true); + + filter.run(); + + assertThat(RequestContext.getCurrentContext().getResponse().getHeader("Content-Length")).isNull(); + } + @Test public void closeResponseOutputStreamError() throws Exception { HttpServletResponse response = mock(HttpServletResponse.class); @@ -126,6 +167,7 @@ public class SendResponseFilterTests { context.setRequest(new MockHttpServletRequest()); context.setResponse(response); context.setResponseDataStream(mockStream); + context.setResponseGZipped(false); Closeable zuulResponse = mock(Closeable.class); context.set("zuulResponse", zuulResponse); RequestContext.testSetCurrentContext(context); @@ -156,6 +198,7 @@ public class SendResponseFilterTests { context.setRequest(new MockHttpServletRequest()); context.setResponse(response); context.setResponseDataStream(mockStream); + context.setResponseGZipped(false); Closeable zuulResponse = mock(Closeable.class); context.set("zuulResponse", zuulResponse); RequestContext.testSetCurrentContext(context); @@ -200,8 +243,7 @@ public class SendResponseFilterTests { context.setResponseBody(content); } - context.addZuulResponseHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(content.length())); - + context.setResponseGZipped(false); context.set("error.status_code", HttpStatus.NOT_FOUND.value()); RequestContext.testSetCurrentContext(context); SendResponseFilter filter = new SendResponseFilter(properties);