Commit 406e112a by Bertrand Renuart Committed by Ryan Baxter

Always forward Content-Length header from origin when set (#2833)

* Always forward Content-Length from origin when set unless response is compressed and client does not support it Fix for #2832 * Add test cases to assert behaviour with regards to the Content-Length header
parent 2118d5b4
...@@ -240,18 +240,14 @@ public class SendResponseFilter extends ZuulFilter { ...@@ -240,18 +240,14 @@ public class SendResponseFilter extends ZuulFilter {
servletResponse.addHeader(it.first(), it.second()); servletResponse.addHeader(it.first(), it.second());
} }
} }
// Only inserts Content-Length if origin provides it and origin response is not if (includeContentLengthHeader(context)) {
// gzipped
if (this.zuulProperties.isSetContentLength()) {
Long contentLength = context.getOriginContentLength(); Long contentLength = context.getOriginContentLength();
if ( contentLength != null && !context.getResponseGZipped()) { if(useServlet31) {
if(useServlet31) { servletResponse.setContentLengthLong(contentLength);
servletResponse.setContentLengthLong(contentLength); } else {
} else { //Try and set some kind of content length if we can safely convert the Long to an int
//Try and set some kind of content length if we can safely convert the Long to an int if (isLongSafe(contentLength)) {
if (isLongSafe(contentLength)) { servletResponse.setContentLength(contentLength.intValue());
servletResponse.setContentLength(contentLength.intValue());
}
} }
} }
} }
...@@ -261,4 +257,23 @@ public class SendResponseFilter extends ZuulFilter { ...@@ -261,4 +257,23 @@ public class SendResponseFilter extends ZuulFilter {
return value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE; 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;
}
} }
...@@ -36,6 +36,7 @@ import org.springframework.mock.web.MockHttpServletRequest; ...@@ -36,6 +36,7 @@ import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.util.WebUtils; import org.springframework.web.util.WebUtils;
import com.netflix.zuul.constants.ZuulHeaders;
import com.netflix.zuul.context.Debug; import com.netflix.zuul.context.Debug;
import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.context.RequestContext;
...@@ -61,6 +62,7 @@ public class SendResponseFilterTests { ...@@ -61,6 +62,7 @@ public class SendResponseFilterTests {
@Before @Before
public void setTestRequestcontext() { public void setTestRequestcontext() {
RequestContext context = new RequestContext(); RequestContext context = new RequestContext();
context.setResponseGZipped(false);
RequestContext.testSetCurrentContext(context); RequestContext.testSetCurrentContext(context);
} }
...@@ -102,6 +104,9 @@ public class SendResponseFilterTests { ...@@ -102,6 +104,9 @@ public class SendResponseFilterTests {
assertThat("wrong debug header", debugHeader, equalTo("[[[test]]]")); assertThat("wrong debug header", debugHeader, equalTo("[[[test]]]"));
} }
/*
* GZip NOT requested and NOT a GZip response -> Content-Length forwarded asis
*/
@Test @Test
public void runWithOriginContentLength() throws Exception { public void runWithOriginContentLength() throws Exception {
ZuulProperties properties = new ZuulProperties(); ZuulProperties properties = new ZuulProperties();
...@@ -117,6 +122,42 @@ public class SendResponseFilterTests { ...@@ -117,6 +122,42 @@ public class SendResponseFilterTests {
assertThat("wrong origin content length", contentLength, equalTo("6")); 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 @Test
public void closeResponseOutputStreamError() throws Exception { public void closeResponseOutputStreamError() throws Exception {
HttpServletResponse response = mock(HttpServletResponse.class); HttpServletResponse response = mock(HttpServletResponse.class);
...@@ -126,6 +167,7 @@ public class SendResponseFilterTests { ...@@ -126,6 +167,7 @@ public class SendResponseFilterTests {
context.setRequest(new MockHttpServletRequest()); context.setRequest(new MockHttpServletRequest());
context.setResponse(response); context.setResponse(response);
context.setResponseDataStream(mockStream); context.setResponseDataStream(mockStream);
context.setResponseGZipped(false);
Closeable zuulResponse = mock(Closeable.class); Closeable zuulResponse = mock(Closeable.class);
context.set("zuulResponse", zuulResponse); context.set("zuulResponse", zuulResponse);
RequestContext.testSetCurrentContext(context); RequestContext.testSetCurrentContext(context);
...@@ -156,6 +198,7 @@ public class SendResponseFilterTests { ...@@ -156,6 +198,7 @@ public class SendResponseFilterTests {
context.setRequest(new MockHttpServletRequest()); context.setRequest(new MockHttpServletRequest());
context.setResponse(response); context.setResponse(response);
context.setResponseDataStream(mockStream); context.setResponseDataStream(mockStream);
context.setResponseGZipped(false);
Closeable zuulResponse = mock(Closeable.class); Closeable zuulResponse = mock(Closeable.class);
context.set("zuulResponse", zuulResponse); context.set("zuulResponse", zuulResponse);
RequestContext.testSetCurrentContext(context); RequestContext.testSetCurrentContext(context);
...@@ -200,8 +243,7 @@ public class SendResponseFilterTests { ...@@ -200,8 +243,7 @@ public class SendResponseFilterTests {
context.setResponseBody(content); context.setResponseBody(content);
} }
context.addZuulResponseHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(content.length())); context.setResponseGZipped(false);
context.set("error.status_code", HttpStatus.NOT_FOUND.value()); context.set("error.status_code", HttpStatus.NOT_FOUND.value());
RequestContext.testSetCurrentContext(context); RequestContext.testSetCurrentContext(context);
SendResponseFilter filter = new SendResponseFilter(properties); SendResponseFilter filter = new SendResponseFilter(properties);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment