diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java index 5b8b33dc..3abba6ee 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java @@ -133,11 +133,11 @@ public class GatewayAutoConfiguration { } @Bean - public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, List globalFilters, + public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, List webFilterFactories, List predicates, RouteDefinitionLocator routeDefinitionLocator) { - return new CachingRouteLocator(new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, globalFilters, webFilterFactories, properties)); + return new CachingRouteLocator(new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, webFilterFactories, properties)); } @Bean @@ -148,9 +148,8 @@ public class GatewayAutoConfiguration { } @Bean - public FilteringWebHandler filteringWebHandler(GatewayProperties properties, - RouteLocator routeLocator) { - return new FilteringWebHandler(properties, routeLocator); + public FilteringWebHandler filteringWebHandler(List globalFilters) { + return new FilteringWebHandler(globalFilters); } @Bean diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/OrderedWebFilter.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/OrderedWebFilter.java new file mode 100644 index 00000000..caf29c98 --- /dev/null +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/OrderedWebFilter.java @@ -0,0 +1,57 @@ +/* + * Copyright 2013-2017 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.gateway.filter; + +import org.springframework.core.Ordered; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; +import reactor.core.publisher.Mono; + +/** + * @author Spencer Gibb + */ +public class OrderedWebFilter implements WebFilter, Ordered { + + private final WebFilter delegate; + private final int order; + + public OrderedWebFilter(WebFilter delegate, int order) { + this.delegate = delegate; + this.order = order; + } + + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + return this.delegate.filter(exchange, chain); + } + + @Override + public int getOrder() { + return this.order; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("OrderedWebFilter{"); + sb.append("delegate=").append(delegate); + sb.append(", order=").append(order); + sb.append('}'); + return sb.toString(); + } +} diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java index 8ca6da7f..fb2f81e6 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/FilteringWebHandler.java @@ -17,16 +17,21 @@ package org.springframework.cloud.gateway.handler; +import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.cloud.gateway.filter.OrderedWebFilter; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.config.GatewayProperties; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.factory.WebFilterFactory; import org.springframework.cloud.gateway.route.Route; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; @@ -48,18 +53,27 @@ import reactor.core.publisher.Mono; public class FilteringWebHandler extends WebHandlerDecorator { protected final Log logger = LogFactory.getLog(getClass()); - private final GatewayProperties gatewayProperties; - private final RouteLocator routeLocator; + private final List globalFilters; - public FilteringWebHandler(GatewayProperties gatewayProperties, RouteLocator routeLocator) { - this(new EmptyWebHandler(), gatewayProperties, routeLocator); + public FilteringWebHandler(List globalFilters) { + this(new EmptyWebHandler(), globalFilters); } - public FilteringWebHandler(WebHandler targetHandler, GatewayProperties gatewayProperties, - RouteLocator routeLocator) { + public FilteringWebHandler(WebHandler targetHandler, List globalFilters) { super(targetHandler); - this.gatewayProperties = gatewayProperties; - this.routeLocator = routeLocator; + this.globalFilters = loadFilters(globalFilters); + } + + private static List loadFilters(List filters) { + return filters.stream() + .map(filter -> { + WebFilterAdapter webFilter = new WebFilterAdapter(filter); + if (filter instanceof Ordered) { + int order = ((Ordered) filter).getOrder(); + return new OrderedWebFilter(webFilter, order); + } + return webFilter; + }).collect(Collectors.toList()); } /* TODO: relocate @EventListener(RefreshRoutesEvent.class) @@ -72,9 +86,14 @@ public class FilteringWebHandler extends WebHandlerDecorator { Optional route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); List webFilters = route.get().getWebFilters(); - logger.debug("Sorted webFilterFactories: "+ webFilters); + List combined = new ArrayList<>(this.globalFilters); + combined.addAll(webFilters); + //TODO: needed or cached? + AnnotationAwareOrderComparator.sort(combined); - return new DefaultWebFilterChain(webFilters, getDelegate()).filter(exchange); + logger.debug("Sorted webFilterFactories: "+ combined); + + return new DefaultWebFilterChain(combined, getDelegate()).filter(exchange); } private static class DefaultWebFilterChain implements WebFilterChain { @@ -100,6 +119,29 @@ public class FilteringWebHandler extends WebHandlerDecorator { } } + private static class WebFilterAdapter implements WebFilter { + + private final GlobalFilter delegate; + + public WebFilterAdapter(GlobalFilter delegate) { + this.delegate = delegate; + } + + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + return this.delegate.filter(exchange, chain); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("WebFilterAdapter{"); + sb.append("delegate=").append(delegate); + sb.append('}'); + return sb.toString(); + } + } + + private static class EmptyWebHandler implements WebHandler { @Override public Mono handle(ServerWebExchange exchange) { diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteLocator.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteLocator.java index 0dc49ce4..a17936c4 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteLocator.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/route/RouteLocator.java @@ -17,7 +17,6 @@ package org.springframework.cloud.gateway.route; -import org.springframework.cloud.gateway.route.Route; import reactor.core.publisher.Flux; /** diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/RouteDefinitionRouteLocator.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/RouteDefinitionRouteLocator.java index 70d9d3b7..ac7b7ad9 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/RouteDefinitionRouteLocator.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/support/RouteDefinitionRouteLocator.java @@ -17,38 +17,32 @@ package org.springframework.cloud.gateway.support; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.cloud.gateway.route.RouteDefinitionLocator; -import org.springframework.cloud.gateway.route.RouteLocator; -import org.springframework.cloud.gateway.config.GatewayProperties; -import org.springframework.cloud.gateway.filter.GlobalFilter; -import org.springframework.cloud.gateway.filter.factory.WebFilterFactory; -import org.springframework.cloud.gateway.handler.predicate.RequestPredicateFactory; -import org.springframework.cloud.gateway.filter.FilterDefinition; -import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; -import org.springframework.cloud.gateway.route.Route; -import org.springframework.cloud.gateway.route.RouteDefinition; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.AnnotationAwareOrderComparator; -import org.springframework.tuple.Tuple; -import org.springframework.tuple.TupleBuilder; -import org.springframework.web.reactive.function.server.RequestPredicate; -import org.springframework.web.server.ServerWebExchange; -import org.springframework.web.server.WebFilter; -import org.springframework.web.server.WebFilterChain; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import static java.util.Collections.emptyList; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.cloud.gateway.config.GatewayProperties; +import org.springframework.cloud.gateway.filter.FilterDefinition; +import org.springframework.cloud.gateway.filter.OrderedWebFilter; +import org.springframework.cloud.gateway.filter.factory.WebFilterFactory; +import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; +import org.springframework.cloud.gateway.handler.predicate.RequestPredicateFactory; +import org.springframework.cloud.gateway.route.Route; +import org.springframework.cloud.gateway.route.RouteDefinition; +import org.springframework.cloud.gateway.route.RouteDefinitionLocator; +import org.springframework.cloud.gateway.route.RouteLocator; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; +import org.springframework.tuple.Tuple; +import org.springframework.tuple.TupleBuilder; +import org.springframework.web.reactive.function.server.RequestPredicate; +import org.springframework.web.server.WebFilter; + +import reactor.core.publisher.Flux; /** * {@link RouteLocator} that loads routes from a {@link RouteDefinitionLocator} @@ -59,26 +53,19 @@ public class RouteDefinitionRouteLocator implements RouteLocator { private final RouteDefinitionLocator routeDefinitionLocator; private final Map requestPredicates = new LinkedHashMap<>(); - private final List globalFilters; private final Map webFilterFactories = new HashMap<>(); private final GatewayProperties gatewayProperties; public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator, List requestPredicates, - List globalFilters, List webFilterFactories, GatewayProperties gatewayProperties) { this.routeDefinitionLocator = routeDefinitionLocator; initFactories(requestPredicates); - this.globalFilters = initList(globalFilters); webFilterFactories.forEach(factory -> this.webFilterFactories.put(factory.name(), factory)); this.gatewayProperties = gatewayProperties; } - private static List initList(List list) { - return (list != null ? list : emptyList()); - } - private void initFactories(List requestPredicates) { requestPredicates.forEach(factory -> { String key = factory.name(); @@ -124,17 +111,7 @@ public class RouteDefinitionRouteLocator implements RouteLocator { .build(); } - public static Collection loadFilters(List filters) { - return filters.stream() - .map(filter -> { - WebFilterAdapter webFilter = new WebFilterAdapter(filter); - if (filter instanceof Ordered) { - int order = ((Ordered) filter).getOrder(); - return new OrderedWebFilter(webFilter, order); - } - return webFilter; - }).collect(Collectors.toList()); - } + private List loadWebFilters(String id, List filterDefinitions) { List filters = filterDefinitions.stream() @@ -202,21 +179,20 @@ public class RouteDefinitionRouteLocator implements RouteLocator { } private List getFilters(RouteDefinition routeDefinition) { - //TODO: probably a java 8 stream way of doing this - List combined = new ArrayList<>(loadFilters(this.globalFilters)); + List filters = new ArrayList<>(); //TODO: support option to apply defaults after route specific filters? if (!this.gatewayProperties.getDefaultFilters().isEmpty()) { - combined.addAll(loadWebFilters("defaultFilters", + filters.addAll(loadWebFilters("defaultFilters", this.gatewayProperties.getDefaultFilters())); } if (!routeDefinition.getFilters().isEmpty()) { - combined.addAll(loadWebFilters(routeDefinition.getId(), routeDefinition.getFilters())); + filters.addAll(loadWebFilters(routeDefinition.getId(), routeDefinition.getFilters())); } - AnnotationAwareOrderComparator.sort(combined); - return combined; + AnnotationAwareOrderComparator.sort(filters); + return filters; } private RequestPredicate combinePredicates(RouteDefinition routeDefinition) { @@ -247,56 +223,4 @@ public class RouteDefinitionRouteLocator implements RouteLocator { return found.apply(tuple); } - - private static class WebFilterAdapter implements WebFilter { - - private final GlobalFilter delegate; - - public WebFilterAdapter(GlobalFilter delegate) { - this.delegate = delegate; - } - - @Override - public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { - return this.delegate.filter(exchange, chain); - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("WebFilterAdapter{"); - sb.append("delegate=").append(delegate); - sb.append('}'); - return sb.toString(); - } - } - - public static class OrderedWebFilter implements WebFilter, Ordered { - - private final WebFilter delegate; - private final int order; - - public OrderedWebFilter(WebFilter delegate, int order) { - this.delegate = delegate; - this.order = order; - } - - @Override - public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { - return this.delegate.filter(exchange, chain); - } - - @Override - public int getOrder() { - return this.order; - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("OrderedWebFilter{"); - sb.append("delegate=").append(delegate); - sb.append(", order=").append(order); - sb.append('}'); - return sb.toString(); - } - } } diff --git a/spring-cloud-gateway-core/src/test/resources/application-removenonproxyheaders.yml b/spring-cloud-gateway-core/src/test/resources/application-removenonproxyheaders.yml index c489e002..2a9cc127 100644 --- a/spring-cloud-gateway-core/src/test/resources/application-removenonproxyheaders.yml +++ b/spring-cloud-gateway-core/src/test/resources/application-removenonproxyheaders.yml @@ -4,10 +4,12 @@ spring: gateway: default-filters: - RemoveNonProxyHeaders + - PrefixPath=/httpbin routes: # ===================================== - id: remove_request_header_test - uri: http://httpbin.org:80 +# uri: http://httpbin.org:80 + uri: lb://testservice predicates: - Host=**.removenonproxyheaders.org - Path=/headers diff --git a/spring-cloud-gateway-sample/src/main/java/org/springframework/cloud/gateway/sample/GatewaySampleApplication.java b/spring-cloud-gateway-sample/src/main/java/org/springframework/cloud/gateway/sample/GatewaySampleApplication.java index 60474302..5b689b6c 100644 --- a/spring-cloud-gateway-sample/src/main/java/org/springframework/cloud/gateway/sample/GatewaySampleApplication.java +++ b/spring-cloud-gateway-sample/src/main/java/org/springframework/cloud/gateway/sample/GatewaySampleApplication.java @@ -17,22 +17,17 @@ package org.springframework.cloud.gateway.sample; -import java.util.List; - import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.cloud.gateway.EnableGateway; -import org.springframework.cloud.gateway.route.RouteLocator; -import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.factory.AddResponseHeaderWebFilterFactory; import org.springframework.cloud.gateway.route.Route; +import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.context.annotation.Bean; import org.springframework.tuple.TupleBuilder; import org.springframework.web.reactive.function.server.RequestPredicates; -import static org.springframework.cloud.gateway.support.RouteDefinitionRouteLocator.loadFilters; - import reactor.core.publisher.Flux; /** @@ -44,13 +39,12 @@ import reactor.core.publisher.Flux; public class GatewaySampleApplication { @Bean - public RouteLocator customRouteLocator(List globalFilters) { + public RouteLocator customRouteLocator() { return () -> { Route route = Route.builder() .id("test") .uri("http://httpbin.org:80") .requestPredicate(RequestPredicates.path("/image/png")) - .addAll(loadFilters(globalFilters)) .add(new AddResponseHeaderWebFilterFactory() .apply(TupleBuilder.tuple().of("name", "X-TestHeader", "value", "foobar"))) .build();