Rename FilterFactory to RouteFilter.

These are filters that apply only to a given Route. RouteFilter.apply() now returns WebFilter rather than GatewayFilter.

GatewayFilters are filters scoped to the gateway
This commit is contained in:
Spencer Gibb
2017-01-14 10:45:21 -07:00
parent 030798f746
commit b9daf4fd4d
18 changed files with 219 additions and 193 deletions

View File

@@ -9,24 +9,24 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.cloud.gateway.actuate.GatewayEndpoint;
import org.springframework.cloud.gateway.api.RouteReader;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AddRequestHeaderFilterFactory;
import org.springframework.cloud.gateway.filter.factory.AddResponseHeaderFilterFactory;
import org.springframework.cloud.gateway.filter.factory.FilterFactory;
import org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter;
import org.springframework.cloud.gateway.filter.factory.RemoveRequestHeaderFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RemoveResponseHeaderFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RewritePathFilterFactory;
import org.springframework.cloud.gateway.filter.factory.SetPathFilterFactory;
import org.springframework.cloud.gateway.filter.factory.SetResponseHeaderFilterFactory;
import org.springframework.cloud.gateway.filter.factory.SetStatusFilterFactory;
import org.springframework.cloud.gateway.filter.route.AddRequestHeaderRouteFilter;
import org.springframework.cloud.gateway.filter.route.AddResponseHeaderRouteFilter;
import org.springframework.cloud.gateway.filter.route.RemoveRequestHeaderRouteFilter;
import org.springframework.cloud.gateway.filter.route.RemoveResponseHeaderRouteFilter;
import org.springframework.cloud.gateway.filter.route.RewritePathRouteFilter;
import org.springframework.cloud.gateway.filter.route.RouteFilter;
import org.springframework.cloud.gateway.filter.route.SetPathRouteFilter;
import org.springframework.cloud.gateway.filter.route.SetResponseHeaderRouteFilter;
import org.springframework.cloud.gateway.filter.route.SetStatusRouteFilter;
import org.springframework.cloud.gateway.handler.GatewayFilteringWebHandler;
import org.springframework.cloud.gateway.handler.GatewayPredicateHandlerMapping;
import org.springframework.cloud.gateway.handler.GatewayWebHandler;
import org.springframework.cloud.gateway.handler.predicate.CookiePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.HeaderPredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.HostPredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.MethodPredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.PredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.HeaderPredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.QueryPredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.UrlPredicateFactory;
import org.springframework.context.annotation.Bean;
@@ -71,7 +71,7 @@ public class GatewayAutoConfiguration {
@Bean
public GatewayFilteringWebHandler gatewayFilteringWebHandler(GatewayWebHandler gatewayWebHandler,
List<GatewayFilter> filters,
List<FilterFactory> filterDefinitions) {
List<RouteFilter> filterDefinitions) {
return new GatewayFilteringWebHandler(gatewayWebHandler, filters, filterDefinitions);
}
@@ -117,43 +117,43 @@ public class GatewayAutoConfiguration {
// Filter Factory beans
@Bean
public AddRequestHeaderFilterFactory addRequestHeaderFilterFactory() {
return new AddRequestHeaderFilterFactory();
public AddRequestHeaderRouteFilter addRequestHeaderRouteFilter() {
return new AddRequestHeaderRouteFilter();
}
@Bean
public AddResponseHeaderFilterFactory addResponseHeaderFilterFactory() {
return new AddResponseHeaderFilterFactory();
public AddResponseHeaderRouteFilter addResponseHeaderRouteFilter() {
return new AddResponseHeaderRouteFilter();
}
@Bean
public RemoveRequestHeaderFilterFactory removeRequestHeaderFilterFactory() {
return new RemoveRequestHeaderFilterFactory();
public RemoveRequestHeaderRouteFilter removeRequestHeaderRouteFilter() {
return new RemoveRequestHeaderRouteFilter();
}
@Bean
public RemoveResponseHeaderFilterFactory removeResponseHeaderFilterFactory() {
return new RemoveResponseHeaderFilterFactory();
public RemoveResponseHeaderRouteFilter removeResponseHeaderRouteFilter() {
return new RemoveResponseHeaderRouteFilter();
}
@Bean
public RewritePathFilterFactory rewritePathFilterFactory() {
return new RewritePathFilterFactory();
public RewritePathRouteFilter rewritePathRouteFilter() {
return new RewritePathRouteFilter();
}
@Bean
public SetPathFilterFactory setPathFilterFactory() {
return new SetPathFilterFactory();
public SetPathRouteFilter setPathRouteFilter() {
return new SetPathRouteFilter();
}
@Bean
public SetResponseHeaderFilterFactory setResponseHeaderFilterFactory() {
return new SetResponseHeaderFilterFactory();
public SetResponseHeaderRouteFilter setResponseHeaderRouteFilter() {
return new SetResponseHeaderRouteFilter();
}
@Bean
public SetStatusFilterFactory setStatusFilterFactory() {
return new SetStatusFilterFactory();
public SetStatusRouteFilter setStatusRouteFilter() {
return new SetStatusRouteFilter();
}
@Configuration

View File

@@ -1,39 +0,0 @@
package org.springframework.cloud.gateway.filter;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
/**
* @author Spencer Gibb
*/
public class OrderedGatewayFilter implements GatewayFilter, Ordered {
private final GatewayFilter delegate;
private final int order;
public OrderedGatewayFilter(GatewayFilter delegate, int order) {
this.delegate = delegate;
this.order = order;
}
@Override
public Mono<Void> 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("OrderedGatewayFilter{");
sb.append("delegate=").append(delegate);
sb.append(", order=").append(order);
sb.append('}');
return sb.toString();
}
}

View File

@@ -1,21 +0,0 @@
package org.springframework.cloud.gateway.filter.factory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
/**
* @author Spencer Gibb
*/
public class AddResponseHeaderFilterFactory implements FilterFactory {
@Override
public GatewayFilter apply(String header, String[] args) {
validate(args, 1);
//TODO: caching can happen here
return (exchange, chain) -> {
exchange.getResponse().getHeaders().add(header, args[0]);
return chain.filter(exchange);
};
}
}

View File

@@ -1,21 +0,0 @@
package org.springframework.cloud.gateway.filter.factory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.util.Assert;
/**
* @author Spencer Gibb
*/
public interface FilterFactory {
default String getName() {
return getClass().getSimpleName().replace(FilterFactory.class.getSimpleName(), "");
}
GatewayFilter apply(String value, String[] args);
default void validate(String[] args, int requiredSize) {
Assert.isTrue(args != null && args.length == requiredSize,
"args must have "+ requiredSize +" entry(s)");
}
}

View File

@@ -1,20 +0,0 @@
package org.springframework.cloud.gateway.filter.factory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
/**
* @author Spencer Gibb
*/
public class RemoveResponseHeaderFilterFactory implements FilterFactory {
@Override
public GatewayFilter apply(String header, String[] args) {
//TODO: caching can happen here
return (exchange, chain) -> {
exchange.getResponse().getHeaders().remove(header);
return chain.filter(exchange);
};
}
}

View File

@@ -1,21 +0,0 @@
package org.springframework.cloud.gateway.filter.factory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
/**
* @author Spencer Gibb
*/
public class SetResponseHeaderFilterFactory implements FilterFactory {
@Override
public GatewayFilter apply(String header, String[] args) {
validate(args, 1);
//TODO: caching can happen here
return (exchange, chain) -> {
exchange.getResponse().getHeaders().set(header, args[0]);
return chain.filter(exchange);
};
}
}

View File

@@ -1,15 +1,15 @@
package org.springframework.cloud.gateway.filter.factory;
package org.springframework.cloud.gateway.filter.route;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.web.server.WebFilter;
import org.springframework.http.server.reactive.ServerHttpRequest;
/**
* @author Spencer Gibb
*/
public class AddRequestHeaderFilterFactory implements FilterFactory {
public class AddRequestHeaderRouteFilter implements RouteFilter {
@Override
public GatewayFilter apply(String header, String[] args) {
public WebFilter apply(String header, String[] args) {
validate(args, 1);
//TODO: caching can happen here

View File

@@ -0,0 +1,21 @@
package org.springframework.cloud.gateway.filter.route;
import org.springframework.web.server.WebFilter;
/**
* @author Spencer Gibb
*/
public class AddResponseHeaderRouteFilter implements RouteFilter {
@Override
public WebFilter apply(String header, String[] args) {
validate(args, 1);
//TODO: caching can happen here
return (exchange, chain) -> {
exchange.getResponse().getHeaders().add(header, args[0]);
return chain.filter(exchange);
};
}
}

View File

@@ -1,17 +1,17 @@
package org.springframework.cloud.gateway.filter.factory;
package org.springframework.cloud.gateway.filter.route;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.web.server.WebFilter;
import org.springframework.http.server.reactive.ServerHttpRequest;
/**
* @author Spencer Gibb
*/
public class RemoveRequestHeaderFilterFactory implements FilterFactory {
public class RemoveRequestHeaderRouteFilter implements RouteFilter {
public static final String FAKE_HEADER = "_______force_______";
@Override
public GatewayFilter apply(String header, String[] args) {
public WebFilter apply(String header, String[] args) {
//TODO: caching can happen here
return (exchange, chain) -> {

View File

@@ -0,0 +1,20 @@
package org.springframework.cloud.gateway.filter.route;
import org.springframework.web.server.WebFilter;
/**
* @author Spencer Gibb
*/
public class RemoveResponseHeaderRouteFilter implements RouteFilter {
@Override
public WebFilter apply(String header, String[] args) {
//TODO: caching can happen here
return (exchange, chain) -> {
exchange.getResponse().getHeaders().remove(header);
return chain.filter(exchange);
};
}
}

View File

@@ -1,15 +1,15 @@
package org.springframework.cloud.gateway.filter.factory;
package org.springframework.cloud.gateway.filter.route;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.web.server.WebFilter;
import org.springframework.http.server.reactive.ServerHttpRequest;
/**
* @author Spencer Gibb
*/
public class RewritePathFilterFactory implements FilterFactory {
public class RewritePathRouteFilter implements RouteFilter {
@Override
public GatewayFilter apply(String regex, String[] args) {
public WebFilter apply(String regex, String[] args) {
validate(args, 1);
String replacement = args[0].replace("$\\", "$");

View File

@@ -0,0 +1,21 @@
package org.springframework.cloud.gateway.filter.route;
import org.springframework.util.Assert;
import org.springframework.web.server.WebFilter;
/**
* @author Spencer Gibb
*/
public interface RouteFilter {
default String getName() {
return getClass().getSimpleName().replace(RouteFilter.class.getSimpleName(), "");
}
WebFilter apply(String value, String[] args);
default void validate(String[] args, int requiredSize) {
Assert.isTrue(args != null && args.length == requiredSize,
"args must have "+ requiredSize +" entry(s)");
}
}

View File

@@ -1,9 +1,9 @@
package org.springframework.cloud.gateway.filter.factory;
package org.springframework.cloud.gateway.filter.route;
import java.net.URI;
import java.util.Map;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.web.server.WebFilter;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.util.UriTemplate;
@@ -13,11 +13,11 @@ import static org.springframework.cloud.gateway.handler.predicate.UrlPredicateFa
/**
* @author Spencer Gibb
*/
public class SetPathFilterFactory implements FilterFactory {
public class SetPathRouteFilter implements RouteFilter {
@Override
@SuppressWarnings("unchecked")
public GatewayFilter apply(String template, String[] args) {
public WebFilter apply(String template, String[] args) {
UriTemplate uriTemplate = new UriTemplate(template);
//TODO: caching can happen here

View File

@@ -0,0 +1,21 @@
package org.springframework.cloud.gateway.filter.route;
import org.springframework.web.server.WebFilter;
/**
* @author Spencer Gibb
*/
public class SetResponseHeaderRouteFilter implements RouteFilter {
@Override
public WebFilter apply(String header, String[] args) {
validate(args, 1);
//TODO: caching can happen here
return (exchange, chain) -> {
exchange.getResponse().getHeaders().set(header, args[0]);
return chain.filter(exchange);
};
}
}

View File

@@ -1,6 +1,6 @@
package org.springframework.cloud.gateway.filter.factory;
package org.springframework.cloud.gateway.filter.route;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.web.server.WebFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@@ -8,10 +8,10 @@ import reactor.core.publisher.Mono;
/**
* @author Spencer Gibb
*/
public class SetStatusFilterFactory implements FilterFactory {
public class SetStatusRouteFilter implements RouteFilter {
@Override
public GatewayFilter apply(String statusString, String[] args) {
public WebFilter apply(String statusString, String[] args) {
HttpStatus httpStatus;
try {

View File

@@ -18,6 +18,7 @@ package org.springframework.cloud.gateway.handler;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -29,10 +30,11 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.gateway.config.Route;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
import org.springframework.cloud.gateway.filter.factory.FilterFactory;
import org.springframework.cloud.gateway.filter.route.RouteFilter;
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;
import org.springframework.web.server.WebHandler;
import org.springframework.web.server.handler.WebHandlerDecorator;
@@ -53,10 +55,10 @@ public class GatewayFilteringWebHandler extends WebHandlerDecorator {
protected final Log logger = LogFactory.getLog(getClass());
private final List<GatewayFilter> filters;
private final Map<String, FilterFactory> filterDefinitions = new HashMap<>();
private final Map<String, RouteFilter> filterDefinitions = new HashMap<>();
public GatewayFilteringWebHandler(WebHandler targetHandler, List<GatewayFilter> filters,
List<FilterFactory> filterDefinitions) {
List<RouteFilter> filterDefinitions) {
super(targetHandler);
this.filters = initList(filters);
initList(filterDefinitions).forEach(def -> this.filterDefinitions.put(def.getName(), def));
@@ -76,7 +78,7 @@ public class GatewayFilteringWebHandler extends WebHandlerDecorator {
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
//TODO: probably a java 8 stream way of doing this
ArrayList<GatewayFilter> routeFilters = new ArrayList<>(this.filters);
ArrayList<WebFilter> routeFilters = new ArrayList<>(loadGatewayFilters(this.filters));
Optional<Route> route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
if (route.isPresent() && !route.get().getFilters().isEmpty()) {
@@ -88,12 +90,24 @@ public class GatewayFilteringWebHandler extends WebHandlerDecorator {
return new DefaultWebFilterChain(routeFilters, getDelegate()).filter(exchange);
}
private List<GatewayFilter> loadFilters(Route route) {
List<GatewayFilter> filters = route.getFilters().stream()
private Collection<WebFilter> loadGatewayFilters(List<GatewayFilter> filters) {
return filters.stream()
.map(filter -> {
GatewayWebFilter webFilter = new GatewayWebFilter(filter);
if (filter instanceof Ordered) {
int order = ((Ordered) filter).getOrder();
return new OrderedWebFilter(webFilter, order);
}
return webFilter;
}).collect(Collectors.toList());
}
private List<WebFilter> loadFilters(Route route) {
List<WebFilter> filters = route.getFilters().stream()
.map(definition -> {
FilterFactory filter = this.filterDefinitions.get(definition.getName());
RouteFilter filter = this.filterDefinitions.get(definition.getName());
if (filter == null) {
throw new IllegalArgumentException("Unable to find FilterFactory with name " + definition.getName());
throw new IllegalArgumentException("Unable to find RouteFilter with name " + definition.getName());
}
if (logger.isDebugEnabled()) {
List<String> args;
@@ -109,22 +123,73 @@ public class GatewayFilteringWebHandler extends WebHandlerDecorator {
})
.collect(Collectors.toList());
ArrayList<GatewayFilter> ordered = new ArrayList<>(filters.size());
ArrayList<WebFilter> ordered = new ArrayList<>(filters.size());
for (int i = 0; i < filters.size(); i++) {
ordered.add(new OrderedGatewayFilter(filters.get(i), i+1));
ordered.add(new OrderedWebFilter(filters.get(i), i+1));
}
return ordered;
}
private static class GatewayWebFilter implements WebFilter {
private final GatewayFilter delegate;
public GatewayWebFilter(GatewayFilter delegate) {
this.delegate = delegate;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return this.delegate.filter(exchange, chain);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("GatewayWebFilter{");
sb.append("delegate=").append(delegate);
sb.append('}');
return sb.toString();
}
}
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<Void> 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();
}
}
private static class DefaultWebFilterChain implements WebFilterChain {
private int index;
private final List<GatewayFilter> filters;
private final List<WebFilter> filters;
private final WebHandler delegate;
public DefaultWebFilterChain(List<GatewayFilter> filters, WebHandler delegate) {
public DefaultWebFilterChain(List<WebFilter> filters, WebHandler delegate) {
this.filters = filters;
this.delegate = delegate;
}
@@ -132,7 +197,7 @@ public class GatewayFilteringWebHandler extends WebHandlerDecorator {
@Override
public Mono<Void> filter(ServerWebExchange exchange) {
if (this.index < filters.size()) {
GatewayFilter filter = filters.get(this.index++);
WebFilter filter = filters.get(this.index++);
return filter.filter(exchange, this);
}
else {

View File

@@ -1,9 +1,9 @@
package org.springframework.cloud.gateway.filter.factory;
package org.springframework.cloud.gateway.filter.route;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.web.server.WebFilter;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.http.server.reactive.MockServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
@@ -17,7 +17,7 @@ import static org.mockito.Mockito.when;
/**
* @author Spencer Gibb
*/
public class RewritePathFilterFactoryTests {
public class RewritePathRouteFilterTests {
@Test
public void rewritePathFilterWorks() {
@@ -30,7 +30,7 @@ public class RewritePathFilterFactoryTests {
}
private void testRewriteFilter(String regex, String replacement, String actualPath, String expectedPath) {
GatewayFilter filter = new RewritePathFilterFactory().apply(regex, new String[]{replacement});
WebFilter filter = new RewritePathRouteFilter().apply(regex, new String[]{replacement});
MockServerHttpRequest request = MockServerHttpRequest
.get("http://localhost"+ actualPath)

View File

@@ -1,13 +1,15 @@
package org.springframework.cloud.gateway.filter.factory;
package org.springframework.cloud.gateway.filter.route;
import java.util.HashMap;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.handler.predicate.UrlPredicateFactory;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.http.server.reactive.MockServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
@@ -16,12 +18,10 @@ import static org.mockito.Mockito.when;
import reactor.core.publisher.Mono;
import java.util.HashMap;
/**
* @author Spencer Gibb
*/
public class SetPathFilterFactoryTests {
public class SetPathRouteFilterTests {
@Test
public void rewritePathFilterWorks() {
@@ -37,7 +37,7 @@ public class SetPathFilterFactoryTests {
}
private void testRewriteFilter(String template, String actualPath, String expectedPath, HashMap<String, String> variables) {
GatewayFilter filter = new SetPathFilterFactory().apply(template, new String[]{});
WebFilter filter = new SetPathRouteFilter().apply(template, new String[]{});
MockServerHttpRequest request = MockServerHttpRequest
.get("http://localhost"+ actualPath)