diff --git a/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/ZuulServerAutoConfiguration.java b/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/ZuulServerAutoConfiguration.java index 44547a7a..6fbfb494 100644 --- a/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/ZuulServerAutoConfiguration.java +++ b/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/ZuulServerAutoConfiguration.java @@ -160,8 +160,8 @@ public class ZuulServerAutoConfiguration { // post filters @Bean - public SendResponseFilter sendResponseFilter() { - return new SendResponseFilter(); + public SendResponseFilter sendResponseFilter(ZuulProperties properties) { + return new SendResponseFilter(zuulProperties); } @Bean diff --git a/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java b/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java index 0de4884a..604e7d36 100644 --- a/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java +++ b/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/filters/ZuulProperties.java @@ -160,7 +160,22 @@ public class ZuulProperties { private HystrixSemaphore semaphore = new HystrixSemaphore(); private HystrixThreadPool threadPool = new HystrixThreadPool(); - + + /** + * Setting for SendResponseFilter to conditionally set Content-Length header. + */ + private boolean setContentLength = false; + + /** + * Setting for SendResponseFilter to conditionally include X-Zuul-Debug-Header header. + */ + private boolean includeDebugHeader = false; + + /** + * Setting for SendResponseFilter for the initial stream buffer size. + */ + private int initialStreamBufferSize = 8192; + public Set getIgnoredHeaders() { Set ignoredHeaders = new LinkedHashSet<>(this.ignoredHeaders); if (ClassUtils.isPresent( @@ -774,6 +789,30 @@ public class ZuulProperties { this.threadPool = threadPool; } + public boolean isSetContentLength() { + return setContentLength; + } + + public void setSetContentLength(boolean setContentLength) { + this.setContentLength = setContentLength; + } + + public boolean isIncludeDebugHeader() { + return includeDebugHeader; + } + + public void setIncludeDebugHeader(boolean includeDebugHeader) { + this.includeDebugHeader = includeDebugHeader; + } + + public int getInitialStreamBufferSize() { + return initialStreamBufferSize; + } + + public void setInitialStreamBufferSize(int initialStreamBufferSize) { + this.initialStreamBufferSize = initialStreamBufferSize; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -798,6 +837,9 @@ public class ZuulProperties { Objects.equals(servletPath, that.servletPath) && sslHostnameValidationEnabled == that.sslHostnameValidationEnabled && stripPrefix == that.stripPrefix && + setContentLength == that.setContentLength && + includeDebugHeader == that.includeDebugHeader && + initialStreamBufferSize == that.initialStreamBufferSize && Objects.equals(threadPool, that.threadPool) && traceRequestBody == that.traceRequestBody; } @@ -808,7 +850,8 @@ public class ZuulProperties { host, ignoredHeaders, ignoredPatterns, ignoredServices, ignoreLocalService, ignoreSecurityHeaders, prefix, removeSemicolonContent, retryable, ribbonIsolationStrategy, routes, semaphore, sensitiveHeaders, servletPath, - sslHostnameValidationEnabled, stripPrefix, threadPool, traceRequestBody); + sslHostnameValidationEnabled, stripPrefix, threadPool, traceRequestBody, + setContentLength, includeDebugHeader, initialStreamBufferSize); } @Override @@ -835,6 +878,9 @@ public class ZuulProperties { .append("ribbonIsolationStrategy=").append(ribbonIsolationStrategy).append(", ") .append("semaphore=").append(semaphore).append(", ") .append("threadPool=").append(threadPool).append(", ") + .append("setContentLength=").append(setContentLength).append(", ") + .append("includeDebugHeader=").append(includeDebugHeader).append(", ") + .append("initialStreamBufferSize=").append(initialStreamBufferSize).append(", ") .append("}").toString(); } 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 19b547c9..f5ed174b 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 @@ -28,14 +28,11 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.cloud.netflix.zuul.filters.ZuulProperties; import org.springframework.util.ReflectionUtils; -import com.netflix.config.DynamicBooleanProperty; -import com.netflix.config.DynamicIntProperty; -import com.netflix.config.DynamicPropertyFactory; import com.netflix.util.Pair; import com.netflix.zuul.ZuulFilter; -import com.netflix.zuul.constants.ZuulConstants; import com.netflix.zuul.constants.ZuulHeaders; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.util.HTTPRequestUtils; @@ -56,21 +53,18 @@ public class SendResponseFilter extends ZuulFilter { private static final Log log = LogFactory.getLog(SendResponseFilter.class); - private static DynamicBooleanProperty INCLUDE_DEBUG_HEADER = DynamicPropertyFactory - .getInstance() - .getBooleanProperty(ZuulConstants.ZUUL_INCLUDE_DEBUG_HEADER, false); - - private static DynamicIntProperty INITIAL_STREAM_BUFFER_SIZE = DynamicPropertyFactory - .getInstance() - .getIntProperty(ZuulConstants.ZUUL_INITIAL_STREAM_BUFFER_SIZE, 8192); - - private static DynamicBooleanProperty SET_CONTENT_LENGTH = DynamicPropertyFactory - .getInstance() - .getBooleanProperty(ZuulConstants.ZUUL_SET_CONTENT_LENGTH, false); private boolean useServlet31 = true; + private ZuulProperties zuulProperties; + private ThreadLocal buffers; + + @Deprecated public SendResponseFilter() { - super(); + this(new ZuulProperties()); + } + + public SendResponseFilter(ZuulProperties zuulProperties) { + this.zuulProperties = zuulProperties; // To support Servlet API 3.1 we need to check if setContentLengthLong exists // minimum support in Spring 5 is 3.0 so we need to keep tihs try { @@ -78,19 +72,13 @@ public class SendResponseFilter extends ZuulFilter { } catch(NoSuchMethodException e) { useServlet31 = false; } + buffers = ThreadLocal.withInitial(() -> new byte[zuulProperties.getInitialStreamBufferSize()]); } /* for testing */ boolean isUseServlet31() { return useServlet31; } - private ThreadLocal buffers = new ThreadLocal() { - @Override - protected byte[] initialValue() { - return new byte[INITIAL_STREAM_BUFFER_SIZE.get()]; - } - }; - @Override public String filterType() { return POST_TYPE; @@ -223,7 +211,7 @@ public class SendResponseFilter extends ZuulFilter { private void addResponseHeaders() { RequestContext context = RequestContext.getCurrentContext(); HttpServletResponse servletResponse = context.getResponse(); - if (INCLUDE_DEBUG_HEADER.get()) { + if (this.zuulProperties.isIncludeDebugHeader()) { @SuppressWarnings("unchecked") List rd = (List) context.get(ROUTING_DEBUG_KEY); if (rd != null) { @@ -242,7 +230,7 @@ public class SendResponseFilter extends ZuulFilter { } // Only inserts Content-Length if origin provides it and origin response is not // gzipped - if (SET_CONTENT_LENGTH.get()) { + if (this.zuulProperties.isSetContentLength()) { Long contentLength = context.getOriginContentLength(); if ( contentLength != null && !context.getResponseGZipped()) { if(useServlet31) { 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 871514bb..c64d71a8 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 @@ -28,14 +28,13 @@ import javax.servlet.http.HttpServletResponse; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.springframework.cloud.netflix.zuul.filters.ZuulProperties; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.web.util.WebUtils; -import com.netflix.config.ConfigurationManager; -import com.netflix.zuul.constants.ZuulConstants; import com.netflix.zuul.context.Debug; import com.netflix.zuul.context.RequestContext; @@ -89,9 +88,10 @@ public class SendResponseFilterTests { @Test public void runWithDebugHeader() throws Exception { - ConfigurationManager.getConfigInstance().setProperty(ZuulConstants.ZUUL_INCLUDE_DEBUG_HEADER, true); + ZuulProperties properties = new ZuulProperties(); + properties.setIncludeDebugHeader(true); - SendResponseFilter filter = createFilter("hello", null, new MockHttpServletResponse(), false); + SendResponseFilter filter = createFilter(properties, "hello", null, new MockHttpServletResponse(), false); Debug.addRoutingDebug("test"); filter.run(); @@ -102,9 +102,10 @@ public class SendResponseFilterTests { @Test public void runWithOriginContentLength() throws Exception { - ConfigurationManager.getConfigInstance().setProperty(ZuulConstants.ZUUL_SET_CONTENT_LENGTH, true); + ZuulProperties properties = new ZuulProperties(); + properties.setSetContentLength(true); - SendResponseFilter filter = createFilter("hello", null, new MockHttpServletResponse(), false); + SendResponseFilter filter = createFilter(properties, "hello", null, new MockHttpServletResponse(), false); RequestContext.getCurrentContext().setOriginContentLength(6L); // for test RequestContext.getCurrentContext().setResponseGZipped(false); filter.run(); @@ -154,6 +155,10 @@ public class SendResponseFilterTests { } private SendResponseFilter createFilter(String content, String characterEncoding, MockHttpServletResponse response, boolean streamContent) throws Exception { + return createFilter(new ZuulProperties(), content, characterEncoding, response, streamContent); + } + + private SendResponseFilter createFilter(ZuulProperties properties, String content, String characterEncoding, MockHttpServletResponse response, boolean streamContent) throws Exception { HttpServletRequest request = new MockHttpServletRequest(); RequestContext context = new RequestContext(); context.setRequest(request); @@ -173,7 +178,7 @@ public class SendResponseFilterTests { context.set("error.status_code", HttpStatus.NOT_FOUND.value()); RequestContext.testSetCurrentContext(context); - SendResponseFilter filter = new SendResponseFilter(); + SendResponseFilter filter = new SendResponseFilter(properties); return filter; } diff --git a/spring-cloud-netflix-zuul/src/test/java/org/springframework/cloud/netflix/zuul/test/AdhocTestSuite.java b/spring-cloud-netflix-zuul/src/test/java/org/springframework/cloud/netflix/zuul/test/AdhocTestSuite.java new file mode 100644 index 00000000..359b7863 --- /dev/null +++ b/spring-cloud-netflix-zuul/src/test/java/org/springframework/cloud/netflix/zuul/test/AdhocTestSuite.java @@ -0,0 +1,92 @@ +/* + * Copyright 2012-2013 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.netflix.zuul.test; + +import org.junit.Ignore; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +/** + * A test suite for probing weird ordering problems in the zuul tests. + * + * @author Spencer Gibb + */ +@RunWith(Suite.class) +@SuiteClasses({ + org.springframework.cloud.netflix.zuul.ZuulServerAutoConfigurationTests.class, + org.springframework.cloud.netflix.zuul.RoutesEndpointTests.class, + org.springframework.cloud.netflix.zuul.FormZuulServletProxyApplicationTests.class, + org.springframework.cloud.netflix.zuul.metrics.DefaultCounterFactoryTests.class, + org.springframework.cloud.netflix.zuul.metrics.ZuulEmptyMetricsApplicationTests.class, + org.springframework.cloud.netflix.zuul.metrics.ZuulMetricsApplicationTests.class, + org.springframework.cloud.netflix.zuul.web.ZuulHandlerMappingTests.class, + org.springframework.cloud.netflix.zuul.ZuulProxyAutoConfigurationTests.class, + org.springframework.cloud.netflix.zuul.RetryableZuulProxyApplicationTests.class, + org.springframework.cloud.netflix.zuul.ServletPathZuulProxyApplicationTests.class, + org.springframework.cloud.netflix.zuul.FormZuulProxyApplicationTests.class, + org.springframework.cloud.netflix.zuul.filters.CompositeRouteLocatorTests.class, + org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommandIntegrationTests.class, + org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommandFallbackTests.class, + org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommandFactoryTest.class, + org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonRetryIntegrationTests.class, + org.springframework.cloud.netflix.zuul.filters.route.LazyLoadOfZuulConfigurationTests.class, + org.springframework.cloud.netflix.zuul.filters.route.EagerLoadOfZuulConfigurationTests.class, + org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilterTests.class, + org.springframework.cloud.netflix.zuul.filters.route.SendForwardFilterTests.class, + org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommandTests.class, + org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilterLoadBalancerKeyIntegrationTests.class, + org.springframework.cloud.netflix.zuul.filters.route.okhttp.OkHttpRibbonCommandFallbackTests.class, + org.springframework.cloud.netflix.zuul.filters.route.okhttp.OkHttpRibbonRetryIntegrationTests.class, + org.springframework.cloud.netflix.zuul.filters.route.okhttp.OkHttpRibbonCommandIntegrationTests.class, + org.springframework.cloud.netflix.zuul.filters.route.okhttp.OkHttpRibbonCommandFactoryTest.class, + org.springframework.cloud.netflix.zuul.filters.route.restclient.RestClientRibbonCommandIntegrationTests.class, + org.springframework.cloud.netflix.zuul.filters.route.restclient.RestClientRibbonCommandFallbackTests.class, + org.springframework.cloud.netflix.zuul.filters.route.support.RibbonCommandCauseFallbackPropagationTest.class, + org.springframework.cloud.netflix.zuul.filters.route.support.RibbonCommandHystrixThreadPoolKeyTests.class, + org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilterTests.class, + org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelperTests.class, + org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocatorTests.class, + org.springframework.cloud.netflix.zuul.filters.ZuulPropertiesTests.class, + org.springframework.cloud.netflix.zuul.filters.CustomHostRoutingFilterTests.class, + org.springframework.cloud.netflix.zuul.filters.discovery.PatternServiceRouteMapperIntegrationTests.class, + org.springframework.cloud.netflix.zuul.filters.discovery.PatternServiceRouteMapperTests.class, + org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocatorTests.class, + org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilterTests.class, + org.springframework.cloud.netflix.zuul.filters.pre.FormBodyWrapperFilterTests.class, + org.springframework.cloud.netflix.zuul.filters.post.LocationRewriteFilterTests.class, + org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilterTests.class, + org.springframework.cloud.netflix.zuul.filters.post.SendErrorFilterIntegrationTests.class, + org.springframework.cloud.netflix.zuul.filters.post.LocationRewriteFilterIntegrationTests.class, + org.springframework.cloud.netflix.zuul.filters.post.SendErrorFilterTests.class, + org.springframework.cloud.netflix.zuul.RoutesEndpointDetailsTests.class, + org.springframework.cloud.netflix.zuul.ZuulProxyConfigurationTests.class, + org.springframework.cloud.netflix.zuul.ZuulProxyApplicationTests.class, + org.springframework.cloud.netflix.zuul.SimpleZuulProxyApplicationTests.class, + org.springframework.cloud.netflix.zuul.ZuulFilterInitializerTests.class, + org.springframework.cloud.netflix.zuul.RoutesEndpointIntegrationTests.class, + org.springframework.cloud.netflix.zuul.test.ZuulApacheHttpClientConfigurationTests.class, + org.springframework.cloud.netflix.zuul.test.ZuulOkHttpClientConfigurationTests.class, + org.springframework.cloud.netflix.zuul.ContextPathZuulProxyApplicationTests.class, + org.springframework.cloud.netflix.zuul.SimpleZuulServerApplicationTests.class, + org.springframework.cloud.netflix.zuul.FiltersEndpointTests.class, + +}) +@Ignore +public class AdhocTestSuite { + +}