diff --git a/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java b/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java index 35f06a62..1a7c7fb4 100644 --- a/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java +++ b/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java @@ -84,9 +84,9 @@ public class GatewayAutoConfiguration { } @Bean - public FilteringWebHandler filteringWebHandler(List globalFilters, + public FilteringWebHandler filteringWebHandler(GatewayProperties properties, List globalFilters, Map routeFilters) { - return new FilteringWebHandler(globalFilters, routeFilters); + return new FilteringWebHandler(properties, globalFilters, routeFilters); } @Bean diff --git a/src/main/java/org/springframework/cloud/gateway/config/GatewayProperties.java b/src/main/java/org/springframework/cloud/gateway/config/GatewayProperties.java index b61abb06..114ea116 100644 --- a/src/main/java/org/springframework/cloud/gateway/config/GatewayProperties.java +++ b/src/main/java/org/springframework/cloud/gateway/config/GatewayProperties.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.gateway.api.FilterDefinition; import org.springframework.cloud.gateway.api.Route; import javax.validation.Valid; @@ -16,12 +17,17 @@ import javax.validation.constraints.NotNull; public class GatewayProperties { /** - * Map of route names to properties. + * List of Routes */ @NotNull @Valid private List routes = new ArrayList<>(); + /** + * List of filter definitions that are applied to every route. + */ + private List defaultFilters = new ArrayList<>(); + public List getRoutes() { return routes; } @@ -30,4 +36,11 @@ public class GatewayProperties { this.routes = routes; } + public List getDefaultFilters() { + return defaultFilters; + } + + public void setDefaultFilters(List defaultFilters) { + this.defaultFilters = defaultFilters; + } } diff --git a/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java b/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java index 7d07b95d..f704d0ab 100644 --- a/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java +++ b/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java @@ -28,7 +28,9 @@ import java.util.stream.Collectors; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.cloud.gateway.api.FilterDefinition; import org.springframework.cloud.gateway.api.Route; +import org.springframework.cloud.gateway.config.GatewayProperties; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.route.RouteFilter; import org.springframework.core.Ordered; @@ -55,19 +57,21 @@ import reactor.core.publisher.Mono; public class FilteringWebHandler extends WebHandlerDecorator { protected final Log logger = LogFactory.getLog(getClass()); + private final GatewayProperties gatewayProperties; private final List globalFilters; private final Map routeFilters = new HashMap<>(); private final Map> combinedFiltersForRoute = new HashMap<>(); - public FilteringWebHandler(List globalFilters, + public FilteringWebHandler(GatewayProperties gatewayProperties, List globalFilters, Map routeFilters) { - this(new EmptyWebHandler(), globalFilters, routeFilters); + this(new EmptyWebHandler(), gatewayProperties, globalFilters, routeFilters); } - public FilteringWebHandler(WebHandler targetHandler, List globalFilters, + public FilteringWebHandler(WebHandler targetHandler, GatewayProperties gatewayProperties, List globalFilters, Map routeFilters) { super(targetHandler); + this.gatewayProperties = gatewayProperties; this.globalFilters = initList(globalFilters); routeFilters.forEach((name, def) -> this.routeFilters.put(normalizeName(name), def)); } @@ -107,8 +111,14 @@ public class FilteringWebHandler extends WebHandlerDecorator { //TODO: probably a java 8 stream way of doing this combinedFilters = new ArrayList<>(loadFilters(this.globalFilters)); + //TODO: support option to apply defaults after route specific filters? + if (!this.gatewayProperties.getDefaultFilters().isEmpty()) { + combinedFilters.addAll(loadWebFilters("defaultFilters", + this.gatewayProperties.getDefaultFilters())); + } + if (route.isPresent() && !route.get().getFilters().isEmpty()) { - combinedFilters.addAll(loadRouteFilters(route.get())); + combinedFilters.addAll(loadWebFilters(route.get().getId(), route.get().getFilters())); } AnnotationAwareOrderComparator.sort(combinedFilters); @@ -129,8 +139,8 @@ public class FilteringWebHandler extends WebHandlerDecorator { }).collect(Collectors.toList()); } - private List loadRouteFilters(Route route) { - List filters = route.getFilters().stream() + private List loadWebFilters(String id, List filterDefinitions) { + List filters = filterDefinitions.stream() .map(definition -> { RouteFilter filter = this.routeFilters.get(definition.getName()); if (filter == null) { @@ -143,7 +153,7 @@ public class FilteringWebHandler extends WebHandlerDecorator { } else { args = Collections.emptyList(); } - logger.debug("Route " + route.getId() + " applying filter " + args + " to " + definition.getName()); + logger.debug("Route " + id + " applying filter " + args + " to " + definition.getName()); } return filter.apply(definition.getArgs()); }) diff --git a/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java b/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java index 555d7935..0354b59a 100644 --- a/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java +++ b/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java @@ -9,11 +9,13 @@ import org.apache.commons.logging.LogFactory; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.context.embedded.LocalServerPort; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.gateway.api.Route; +import org.springframework.cloud.gateway.config.GatewayProperties; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.route.SecureHeadersProperties; import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping; @@ -73,6 +75,9 @@ public class GatewayIntegrationTests { private WebClient webClient; private String baseUri; + @Autowired + private GatewayProperties properties; + @Before public void setup() { //TODO: how to set new ReactorClientHttpConnector() @@ -187,6 +192,27 @@ public class GatewayIntegrationTests { .verify(); } + @Test + public void defaultFiltersWorks() { + assertThat(this.properties.getDefaultFilters()).isNotEmpty(); + + Mono result = webClient.get() + .uri("/headers") + .header("Host", "www.addresponseheader.org") + .exchange(); + + StepVerifier.create(result) + .consumeNextWith( + response -> { + HttpHeaders httpHeaders = response.headers().asHttpHeaders(); + assertThat(httpHeaders.getFirst("X-Response-Default-Foo")) + .isEqualTo("Default-Bar"); + assertThat(httpHeaders.get("X-Response-Default-Foo")).hasSize(1); + }) + .expectComplete() + .verify(DURATION); + } + @Test public void hostRouteWorks() { Mono result = webClient.get() diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 17c2d022..356d4ed3 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -2,6 +2,9 @@ spring: cloud: gateway: + default-filters: + - AddResponseHeader=X-Response-Default-Foo, Default-Bar + routes: # ===================================== - host_example_to_httpbin=http://httpbin.org:80, Host=**.example.org