diff --git a/docs/src/main/asciidoc/spring-cloud-netflix.adoc b/docs/src/main/asciidoc/spring-cloud-netflix.adoc index 4d735c2f..9052cc3d 100644 --- a/docs/src/main/asciidoc/spring-cloud-netflix.adoc +++ b/docs/src/main/asciidoc/spring-cloud-netflix.adoc @@ -1700,11 +1700,17 @@ Security. The assumption in this case is that the downstream services might add these headers too, and we want the values from the proxy. To not discard these well known security headers in case Spring Security is on the classpath you can set `zuul.ignoreSecurityHeaders` to `false`. This can be useful if you disabled the HTTP Security response headers in Spring Security and want the values provided by downstream services -=== The Routes Endpoint +=== Management Endpoints -If you are using `@EnableZuulProxy` with tha Spring Boot Actuator you -will enable (by default) an additional endpoint, available via HTTP as -`/routes`. A GET to this endpoint will return a list of the mapped +If you are using `@EnableZuulProxy` with the Spring Boot Actuator you +will enable (by default) two additional endpoints: + +* Routes +* Filters + +==== Routes Endpoint + +A GET to the routes endpoint at `/routes` will return a list of the mapped routes: .GET /routes @@ -1743,6 +1749,12 @@ NOTE: the routes should respond automatically to changes in the service catalog, but the POST to /routes is a way to force the change to happen immediately. +==== Filters Endpoint + +A GET to the filters endpoint at `/filters` will return a map of Zuul +filters by type. For each filter type in the map, you will find a list +of all the filters of that type, along with their details. + === Strangulation Patterns and Local Forwards A common pattern when migrating an existing application or API is to diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/FiltersEndpoint.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/FiltersEndpoint.java new file mode 100644 index 00000000..a57d98d0 --- /dev/null +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/FiltersEndpoint.java @@ -0,0 +1,57 @@ +package org.springframework.cloud.netflix.zuul; + +import com.netflix.zuul.ZuulFilter; +import com.netflix.zuul.filters.FilterRegistry; +import org.springframework.boot.actuate.endpoint.AbstractEndpoint; +import org.springframework.jmx.export.annotation.ManagedAttribute; +import org.springframework.jmx.export.annotation.ManagedResource; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * Endpoint for listing Zuul filters. + * + * @author Daryl Robbins + * @author Gregor Zurowski + */ +@ManagedResource(description = "List Zuul filters") +public class FiltersEndpoint extends AbstractEndpoint>>> { + + private static final String ID = "filters"; + + private final FilterRegistry filterRegistry; + + public FiltersEndpoint(FilterRegistry filterRegistry) { + super(ID, true); + this.filterRegistry = filterRegistry; + } + + @ManagedAttribute + @Override + public Map>> invoke() { + // Map of filters by type + final Map>> filterMap = new TreeMap<>(); + + for (ZuulFilter filter : this.filterRegistry.getAllFilters()) { + // Ensure that we have a list to store filters of each type + if (!filterMap.containsKey(filter.filterType())) { + filterMap.put(filter.filterType(), new ArrayList>()); + } + + final Map filterInfo = new LinkedHashMap<>(); + filterInfo.put("class", filter.getClass().getName()); + filterInfo.put("order", filter.filterOrder()); + filterInfo.put("disabled", filter.isFilterDisabled()); + filterInfo.put("static", filter.isStaticFilter()); + + filterMap.get(filter.filterType()).add(filterInfo); + } + + return filterMap; + } + +} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulProxyAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulProxyAutoConfiguration.java index 40dee955..dbdc4770 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulProxyAutoConfiguration.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/ZuulProxyAutoConfiguration.java @@ -19,8 +19,10 @@ package org.springframework.cloud.netflix.zuul; import java.util.Collections; import java.util.List; +import com.netflix.zuul.filters.FilterRegistry; import org.apache.http.impl.client.CloseableHttpClient; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint; import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.trace.TraceRepository; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -158,22 +160,31 @@ public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration { @Configuration @ConditionalOnClass(Endpoint.class) - protected static class RoutesEndpointConfiguration { + protected static class EndpointConfiguration { @Autowired(required = false) private TraceRepository traces; + @ConditionalOnEnabledEndpoint("routes") @Bean - public RoutesEndpoint zuulEndpoint(RouteLocator routeLocator) { + public RoutesEndpoint routesEndpoint(RouteLocator routeLocator) { return new RoutesEndpoint(routeLocator); } + @ConditionalOnEnabledEndpoint("routes") @Bean - public RoutesMvcEndpoint zuulMvcEndpoint(RouteLocator routeLocator, + public RoutesMvcEndpoint routesMvcEndpoint(RouteLocator routeLocator, RoutesEndpoint endpoint) { return new RoutesMvcEndpoint(endpoint, routeLocator); } + @ConditionalOnEnabledEndpoint("filters") + @Bean + public FiltersEndpoint filtersEndpoint() { + FilterRegistry filterRegistry = FilterRegistry.instance(); + return new FiltersEndpoint(filterRegistry); + } + @Bean public ProxyRequestHelper proxyRequestHelper(ZuulProperties zuulProperties) { TraceProxyRequestHelper helper = new TraceProxyRequestHelper(); diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/FiltersEndpointTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/FiltersEndpointTests.java new file mode 100644 index 00000000..6fc98430 --- /dev/null +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/FiltersEndpointTests.java @@ -0,0 +1,89 @@ +package org.springframework.cloud.netflix.zuul; + +import com.netflix.zuul.ZuulFilter; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Map; + +import static org.hibernate.validator.internal.util.Contracts.assertTrue; +import static org.junit.Assert.assertEquals; + +/** + * Tests for Filters endpoint + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = FiltersEndpointApplication.class, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + value = { "server.contextPath: /app" }) +public class FiltersEndpointTests { + + @Autowired + private FiltersEndpoint endpoint; + + @Test + public void getFilters() { + final Map>> filters = endpoint.invoke(); + + boolean foundFilter = false; + + if (filters.containsKey("sample")) { + for (Map filterInfo : filters.get("sample")) { + if (TestFilter.class.getName().equals(filterInfo.get("class"))) { + foundFilter = true; + + // Verify filter's attributes + assertEquals(0, filterInfo.get("order")); + + break; // the search is over + } + } + } + + assertTrue(foundFilter, "Could not find expected sample filter from filters endpoint"); + } + +} + +@Configuration +@EnableAutoConfiguration +@RestController +@EnableZuulProxy +class FiltersEndpointApplication { + + @Bean + public ZuulFilter sampleFilter() { + return new TestFilter(); + } + +} + +class TestFilter extends ZuulFilter { + @Override + public String filterType() { + return "sample"; + } + + @Override + public boolean shouldFilter() { + return true; + } + + @Override + public Object run() { + return null; + } + + @Override + public int filterOrder() { + return 0; + } +} \ No newline at end of file diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/RoutesEndpointIntegrationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/RoutesEndpointIntegrationTests.java index d2386d23..be11e19c 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/RoutesEndpointIntegrationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/RoutesEndpointIntegrationTests.java @@ -25,6 +25,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.cloud.netflix.zuul.EnableZuulProxy; +import org.springframework.cloud.netflix.zuul.RoutesEndpoint; +import org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Configuration; import org.springframework.core.ParameterizedTypeReference; diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/RoutesEndpointTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/RoutesEndpointTests.java index 53a90fbc..ab0d20bf 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/RoutesEndpointTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/RoutesEndpointTests.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Map; import org.junit.Before; import org.junit.Test; +import org.springframework.cloud.netflix.zuul.RoutesEndpoint; import org.springframework.cloud.netflix.zuul.filters.Route; import org.springframework.cloud.netflix.zuul.filters.RouteLocator; diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/RoutesMvcEndpointTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/RoutesMvcEndpointTests.java index 248ecad6..7ea13129 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/RoutesMvcEndpointTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/RoutesMvcEndpointTests.java @@ -30,6 +30,9 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.netflix.zuul.RoutesEndpoint; +import org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent; +import org.springframework.cloud.netflix.zuul.RoutesMvcEndpoint; import org.springframework.cloud.netflix.zuul.filters.Route; import org.springframework.cloud.netflix.zuul.filters.RouteLocator; import org.springframework.context.ApplicationEventPublisher; diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/SimpleZuulProxyApplicationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/SimpleZuulProxyApplicationTests.java index 8221655c..cefa9f1d 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/SimpleZuulProxyApplicationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/SimpleZuulProxyApplicationTests.java @@ -25,7 +25,6 @@ import com.netflix.zuul.context.RequestContext; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/CustomHostRoutingFilterTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/CustomHostRoutingFilterTests.java index 413f64c1..b25e5a97 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/CustomHostRoutingFilterTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/CustomHostRoutingFilterTests.java @@ -23,12 +23,9 @@ import com.netflix.zuul.context.RequestContext; import org.apache.http.client.config.CookieSpecs; import org.apache.http.client.config.RequestConfig; -import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -42,7 +39,6 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory; import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory; import org.springframework.cloud.commons.httpclient.DefaultApacheHttpClientFactory; import org.springframework.cloud.netflix.feign.ribbon.FeignRibbonClientAutoConfiguration;