diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteLocator.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteLocator.java index 478cf02d..4ef774ee 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteLocator.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteLocator.java @@ -18,7 +18,6 @@ package org.springframework.cloud.gateway.discovery; import java.net.URI; -import java.util.Collections; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.gateway.api.RouteLocator; @@ -28,6 +27,8 @@ import org.springframework.cloud.gateway.model.FilterDefinition; import org.springframework.cloud.gateway.model.PredicateDefinition; import org.springframework.cloud.gateway.model.Route; +import static org.springframework.cloud.gateway.filter.factory.RewritePathWebFilterFactory.REGEXP_KEY; +import static org.springframework.cloud.gateway.filter.factory.RewritePathWebFilterFactory.REPLACEMENT_KEY; import static org.springframework.cloud.gateway.handler.predicate.PathRequestPredicateFactory.PATTERN_KEY; import static org.springframework.cloud.gateway.support.NameUtils.normalizeFilterName; import static org.springframework.cloud.gateway.support.NameUtils.normalizePredicateName; @@ -59,7 +60,7 @@ public class DiscoveryClientRouteLocator implements RouteLocator { // add a predicate that matches the url at /serviceId/** PredicateDefinition predicate = new PredicateDefinition(); predicate.setName(normalizePredicateName(PathRequestPredicateFactory.class)); - predicate.setArgs(Collections.singletonMap(PATTERN_KEY, "/" + serviceId + "/**")); + predicate.addArg(PATTERN_KEY, "/" + serviceId + "/**"); route.getPredicates().add(predicate); //TODO: support for other default predicates @@ -69,7 +70,8 @@ public class DiscoveryClientRouteLocator implements RouteLocator { filter.setName(normalizeFilterName(RewritePathWebFilterFactory.class)); String regex = "/" + serviceId + "/(?.*)"; String replacement = "/${remaining}"; - filter.setArgs(regex, replacement); + filter.addArg(REGEXP_KEY, regex); + filter.addArg(REPLACEMENT_KEY, replacement); route.getFilters().add(filter); //TODO: support for default filters diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddRequestHeaderWebFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddRequestHeaderWebFilterFactory.java index 767879b3..36e835cb 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddRequestHeaderWebFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddRequestHeaderWebFilterFactory.java @@ -17,21 +17,31 @@ package org.springframework.cloud.gateway.filter.factory; +import org.springframework.tuple.Tuple; import org.springframework.web.server.WebFilter; import org.springframework.http.server.reactive.ServerHttpRequest; +import java.util.Arrays; +import java.util.List; + /** * @author Spencer Gibb */ public class AddRequestHeaderWebFilterFactory implements WebFilterFactory { @Override - public WebFilter apply(String... args) { - validate(2, args); + public List argNames() { + return Arrays.asList(NAME_KEY, VALUE_KEY); + } + + @Override + public WebFilter apply(Tuple args) { + String name = args.getString(NAME_KEY); + String value = args.getString(VALUE_KEY); return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest().mutate() - .header(args[0], args[1]) + .header(name, value) .build(); return chain.filter(exchange.mutate().request(request).build()); diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddRequestParameterWebFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddRequestParameterWebFilterFactory.java index 3181aa5b..8f4d87b8 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddRequestParameterWebFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddRequestParameterWebFilterFactory.java @@ -19,11 +19,14 @@ package org.springframework.cloud.gateway.filter.factory; import java.net.URI; import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.List; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequestDecorator; +import org.springframework.tuple.Tuple; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.web.server.WebFilter; @@ -34,10 +37,14 @@ import org.springframework.web.server.WebFilter; public class AddRequestParameterWebFilterFactory implements WebFilterFactory { @Override - public WebFilter apply(String... args) { - validate(2, args); - String parameter = args[0]; - String value = args[1]; + public List argNames() { + return Arrays.asList(NAME_KEY, VALUE_KEY); + } + + @Override + public WebFilter apply(Tuple args) { + String parameter = args.getString(NAME_KEY); + String value = args.getString(VALUE_KEY); return (exchange, chain) -> { diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddResponseHeaderWebFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddResponseHeaderWebFilterFactory.java index 484cb27d..78e8c127 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddResponseHeaderWebFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/AddResponseHeaderWebFilterFactory.java @@ -17,18 +17,26 @@ package org.springframework.cloud.gateway.filter.factory; +import org.springframework.tuple.Tuple; import org.springframework.web.server.WebFilter; +import java.util.Arrays; +import java.util.List; + /** * @author Spencer Gibb */ public class AddResponseHeaderWebFilterFactory implements WebFilterFactory { @Override - public WebFilter apply(String... args) { - validate(2, args); - final String header = args[0]; - final String value = args[1]; + public List argNames() { + return Arrays.asList(NAME_KEY, VALUE_KEY); + } + + @Override + public WebFilter apply(Tuple args) { + final String header = args.getString(NAME_KEY); + final String value = args.getString(VALUE_KEY); return (exchange, chain) -> { exchange.getResponse().getHeaders().add(header, value); diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/HystrixWebFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/HystrixWebFilterFactory.java index a3169994..b0ea9a30 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/HystrixWebFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/HystrixWebFilterFactory.java @@ -17,6 +17,7 @@ package org.springframework.cloud.gateway.filter.factory; +import org.springframework.tuple.Tuple; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; @@ -30,15 +31,22 @@ import rx.Observable; import rx.RxReactiveStreams; import rx.Subscription; +import java.util.Arrays; +import java.util.List; + /** * @author Spencer Gibb */ public class HystrixWebFilterFactory implements WebFilterFactory { @Override - public WebFilter apply(String... args) { - validate(1, args); - final String commandName = args[0]; + public List argNames() { + return Arrays.asList(NAME_KEY); + } + + @Override + public WebFilter apply(Tuple args) { + final String commandName = args.getString(NAME_KEY); final HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey(getClass().getSimpleName()); final HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey(commandName); diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/PrefixPathWebFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/PrefixPathWebFilterFactory.java index 0b8f8e23..6cec28da 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/PrefixPathWebFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/PrefixPathWebFilterFactory.java @@ -18,18 +18,28 @@ package org.springframework.cloud.gateway.filter.factory; import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.tuple.Tuple; import org.springframework.web.server.WebFilter; +import java.util.Arrays; +import java.util.List; + /** * @author Spencer Gibb */ public class PrefixPathWebFilterFactory implements WebFilterFactory { + public static final String PREFIX_KEY = "prefix"; + + @Override + public List argNames() { + return Arrays.asList(PREFIX_KEY); + } + @Override @SuppressWarnings("unchecked") - public WebFilter apply(String... args) { - validate(1, args); - final String prefix = args[0]; + public WebFilter apply(Tuple args) { + final String prefix = args.getString(PREFIX_KEY); return (exchange, chain) -> { ServerHttpRequest req = exchange.getRequest(); diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RedirectToWebFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RedirectToWebFilterFactory.java index 54903035..a88bc824 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RedirectToWebFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RedirectToWebFilterFactory.java @@ -20,10 +20,13 @@ package org.springframework.cloud.gateway.filter.factory; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; +import java.util.Arrays; +import java.util.List; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.tuple.Tuple; import org.springframework.web.server.WebFilter; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.parse; @@ -35,18 +38,26 @@ import reactor.core.publisher.Mono; * @author Spencer Gibb */ public class RedirectToWebFilterFactory implements WebFilterFactory { + + public static final String STATUS_KEY = "status"; + public static final String URL_KEY = "url"; + @Override - public WebFilter apply(String... args) { - validate(2, args); - final String statusString = args[0]; - final String uri = args[1]; + public List argNames() { + return Arrays.asList(STATUS_KEY, URL_KEY); + } + + @Override + public WebFilter apply(Tuple args) { + String statusString = args.getRawString(STATUS_KEY); + String urlString = args.getString(URL_KEY); final HttpStatus httpStatus = parse(statusString); final URL url; try { - url = URI.create(uri).toURL(); + url = URI.create(urlString).toURL(); } catch (MalformedURLException e) { - throw new IllegalArgumentException("Invalid url " + uri, e); + throw new IllegalArgumentException("Invalid url " + urlString, e); } return (exchange, chain) -> diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveNonProxyHeadersWebFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveNonProxyHeadersWebFilterFactory.java index 8428394f..72560afe 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveNonProxyHeadersWebFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveNonProxyHeadersWebFilterFactory.java @@ -19,6 +19,7 @@ package org.springframework.cloud.gateway.filter.factory; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.tuple.Tuple; import org.springframework.web.server.WebFilter; import java.util.Arrays; @@ -61,7 +62,7 @@ public class RemoveNonProxyHeadersWebFilterFactory implements WebFilterFactory { } @Override - public WebFilter apply(String... args) { + public WebFilter apply(Tuple args) { //TODO: support filter args return (exchange, chain) -> { diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestHeaderWebFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestHeaderWebFilterFactory.java index d0d199f9..580fd834 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestHeaderWebFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestHeaderWebFilterFactory.java @@ -17,9 +17,13 @@ package org.springframework.cloud.gateway.filter.factory; +import org.springframework.tuple.Tuple; import org.springframework.web.server.WebFilter; import org.springframework.http.server.reactive.ServerHttpRequest; +import java.util.Arrays; +import java.util.List; + /** * @author Spencer Gibb */ @@ -28,9 +32,13 @@ public class RemoveRequestHeaderWebFilterFactory implements WebFilterFactory { private static final String FAKE_HEADER = "_______force_______"; @Override - public WebFilter apply(String... args) { - validate(1, args); - final String header = args[0]; + public List argNames() { + return Arrays.asList(NAME_KEY); + } + + @Override + public WebFilter apply(Tuple args) { + final String header = args.getString(NAME_KEY); return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest().mutate() diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveResponseHeaderWebFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveResponseHeaderWebFilterFactory.java index f252b38d..6f751431 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveResponseHeaderWebFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveResponseHeaderWebFilterFactory.java @@ -17,17 +17,25 @@ package org.springframework.cloud.gateway.filter.factory; +import org.springframework.tuple.Tuple; import org.springframework.web.server.WebFilter; +import java.util.Arrays; +import java.util.List; + /** * @author Spencer Gibb */ public class RemoveResponseHeaderWebFilterFactory implements WebFilterFactory { @Override - public WebFilter apply(String... args) { - validate(1, args); - final String header = args[0]; + public List argNames() { + return Arrays.asList(NAME_KEY); + } + + @Override + public WebFilter apply(Tuple args) { + final String header = args.getString(NAME_KEY); return (exchange, chain) -> { exchange.getResponse().getHeaders().remove(header); diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RewritePathWebFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RewritePathWebFilterFactory.java index f6310f78..d5d4dd38 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RewritePathWebFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/RewritePathWebFilterFactory.java @@ -17,19 +17,30 @@ package org.springframework.cloud.gateway.filter.factory; +import org.springframework.tuple.Tuple; import org.springframework.web.server.WebFilter; import org.springframework.http.server.reactive.ServerHttpRequest; +import java.util.Arrays; +import java.util.List; + /** * @author Spencer Gibb */ public class RewritePathWebFilterFactory implements WebFilterFactory { + public static final String REGEXP_KEY = "regexp"; + public static final String REPLACEMENT_KEY = "replacement"; + @Override - public WebFilter apply(String... args) { - validate(2, args); - final String regex = args[0]; - String replacement = args[1].replace("$\\", "$"); + public List argNames() { + return Arrays.asList(REGEXP_KEY, REPLACEMENT_KEY); + } + + @Override + public WebFilter apply(Tuple args) { + final String regex = args.getString(REGEXP_KEY); + String replacement = args.getString(REPLACEMENT_KEY).replace("$\\", "$"); return (exchange, chain) -> { ServerHttpRequest req = exchange.getRequest(); diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersWebFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersWebFilterFactory.java index 72aa1b2d..1b2dcaeb 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersWebFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SecureHeadersWebFilterFactory.java @@ -18,6 +18,7 @@ package org.springframework.cloud.gateway.filter.factory; import org.springframework.http.HttpHeaders; +import org.springframework.tuple.Tuple; import org.springframework.web.server.WebFilter; /** @@ -42,7 +43,7 @@ public class SecureHeadersWebFilterFactory implements WebFilterFactory { } @Override - public WebFilter apply(String... args) { + public WebFilter apply(Tuple args) { //TODO: allow args to override properties return (exchange, chain) -> { diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetPathWebFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetPathWebFilterFactory.java index 9b572ec7..bc766b87 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetPathWebFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetPathWebFilterFactory.java @@ -18,9 +18,12 @@ package org.springframework.cloud.gateway.filter.factory; import java.net.URI; +import java.util.Arrays; +import java.util.List; import java.util.Map; import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.tuple.Tuple; import org.springframework.web.server.WebFilter; import org.springframework.web.util.UriTemplate; @@ -32,11 +35,18 @@ import static org.springframework.web.reactive.function.server.RouterFunctions.U */ public class SetPathWebFilterFactory implements WebFilterFactory { + public static final String TEMPLATE_KEY = "template"; + + @Override + public List argNames() { + return Arrays.asList(TEMPLATE_KEY); + } + @Override @SuppressWarnings("unchecked") - public WebFilter apply(String... args) { - validate(1, args); - UriTemplate uriTemplate = new UriTemplate(args[0]); + public WebFilter apply(Tuple args) { + String template = args.getString(TEMPLATE_KEY); + UriTemplate uriTemplate = new UriTemplate(template); return (exchange, chain) -> { Map variables = getAttribute(exchange, URI_TEMPLATE_VARIABLES_ATTRIBUTE, Map.class); diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetResponseHeaderWebFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetResponseHeaderWebFilterFactory.java index f9d84d32..1ecc2c67 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetResponseHeaderWebFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetResponseHeaderWebFilterFactory.java @@ -17,18 +17,26 @@ package org.springframework.cloud.gateway.filter.factory; +import org.springframework.tuple.Tuple; import org.springframework.web.server.WebFilter; +import java.util.Arrays; +import java.util.List; + /** * @author Spencer Gibb */ public class SetResponseHeaderWebFilterFactory implements WebFilterFactory { @Override - public WebFilter apply(String... args) { - validate(2, args); - final String header = args[0]; - final String value = args[1]; + public List argNames() { + return Arrays.asList(NAME_KEY, VALUE_KEY); + } + + @Override + public WebFilter apply(Tuple args) { + final String header = args.getString(NAME_KEY); + final String value = args.getString(VALUE_KEY); return (exchange, chain) -> { exchange.getResponse().getHeaders().set(header, value); diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetStatusWebFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetStatusWebFilterFactory.java index 0d19f0d7..c4efefce 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetStatusWebFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/SetStatusWebFilterFactory.java @@ -19,21 +19,32 @@ package org.springframework.cloud.gateway.filter.factory; import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; import org.springframework.http.HttpStatus; +import org.springframework.tuple.Tuple; import org.springframework.web.server.WebFilter; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.setResponseStatus; import reactor.core.publisher.Mono; +import java.util.Arrays; +import java.util.List; + /** * @author Spencer Gibb */ public class SetStatusWebFilterFactory implements WebFilterFactory { + public static final String STATUS_KEY = "status"; + @Override - public WebFilter apply(String... args) { - validate(1, args); - final HttpStatus httpStatus = ServerWebExchangeUtils.parse(args[0]); + public List argNames() { + return Arrays.asList(STATUS_KEY); + } + + @Override + public WebFilter apply(Tuple args) { + String status = args.getRawString(STATUS_KEY); + final HttpStatus httpStatus = ServerWebExchangeUtils.parse(status); return (exchange, chain) -> { diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/WebFilterFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/WebFilterFactory.java index 793d3b06..f8777a4f 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/WebFilterFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/WebFilterFactory.java @@ -18,24 +18,48 @@ package org.springframework.cloud.gateway.filter.factory; import org.springframework.cloud.gateway.support.NameUtils; +import org.springframework.tuple.Tuple; import org.springframework.util.Assert; import org.springframework.web.server.WebFilter; +import java.util.Collections; +import java.util.List; + /** * @author Spencer Gibb */ @FunctionalInterface public interface WebFilterFactory { + String NAME_KEY = "name"; + String VALUE_KEY = "value"; + //TODO: move from String... to Tuple - WebFilter apply(String... args); + WebFilter apply(Tuple args); default String name() { return NameUtils.normalizeFilterName(getClass()); } - default void validate(int requiredSize, String... args) { - Assert.isTrue(args != null && args.length == requiredSize, + /** + * Returns hints about the number of args and the order for shortcut parsing. + * @return + */ + default List argNames() { + return Collections.emptyList(); + } + + /** + * Validate supplied argument size against {@see #argNames} size. + * Useful for variable arg predicates. + * @return + */ + default boolean validateArgs() { + return true; + } + + default void validate(int requiredSize, Tuple args) { + Assert.isTrue(args != null && args.size() == requiredSize, "args must have "+ requiredSize +" entry(s)"); } } 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 f65a806d..84712720 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 @@ -18,7 +18,6 @@ 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; @@ -36,10 +35,13 @@ import org.springframework.cloud.gateway.model.FilterDefinition; import org.springframework.cloud.gateway.model.Route; import org.springframework.cloud.gateway.config.GatewayProperties; import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.cloud.gateway.support.NameUtils; import org.springframework.cloud.gateway.support.RefreshRoutesEvent; import org.springframework.context.event.EventListener; 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.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; @@ -153,16 +155,49 @@ public class FilteringWebHandler extends WebHandlerDecorator { if (filter == null) { throw new IllegalArgumentException("Unable to find WebFilterFactory with name " + definition.getName()); } + Map args = definition.getArgs(); if (logger.isDebugEnabled()) { - List args; - if (definition.getArgs() != null) { - args = Arrays.asList(definition.getArgs()); - } else { - args = Collections.emptyList(); - } logger.debug("Route " + id + " applying filter " + args + " to " + definition.getName()); } - return filter.apply(definition.getArgs()); + + //TODO: move Tuple building to common class, see RequestPredicateFactory.lookup + TupleBuilder builder = TupleBuilder.tuple(); + + List argNames = filter.argNames(); + if (!argNames.isEmpty()) { + // ensure size is the same for key replacement later + if (filter.validateArgs() && args.size() != argNames.size()) { + throw new IllegalArgumentException("Wrong number of arguments. Expected " + argNames + + " " + argNames + ". Found " + args.size() + " " + args + "'"); + } + } + + int entryIdx = 0; + for (Map.Entry entry : args.entrySet()) { + String key = entry.getKey(); + + // RequestPredicateFactory has name hints and this has a fake key name + // replace with the matching key hint + if (key.startsWith(NameUtils.GENERATED_NAME_PREFIX) && !argNames.isEmpty() + && entryIdx < args.size()) { + key = argNames.get(entryIdx); + } + + builder.put(key, entry.getValue()); + entryIdx++; + } + + Tuple tuple = builder.build(); + + if (filter.validateArgs()) { + for (String name : argNames) { + if (!tuple.hasFieldName(name)) { + throw new IllegalArgumentException("Missing argument '" + name + "'. Given " + tuple); + } + } + } + + return filter.apply(tuple); }) .collect(Collectors.toList()); diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/RequestPredicateHandlerMapping.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/RequestPredicateHandlerMapping.java index 907fbd31..cdb307a7 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/RequestPredicateHandlerMapping.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/RequestPredicateHandlerMapping.java @@ -170,10 +170,12 @@ public class RequestPredicateHandlerMapping extends AbstractHandlerMapping { List argNames = found.argNames(); if (!argNames.isEmpty()) { - // ensure size is the same for key replacement later - if (found.validateArgSize() && args.size() != argNames.size()) { - throw new IllegalArgumentException("Wrong number of arguments. Expected " + argNames - + " " + argNames + ". Found "+ args.size() +" " + args +"'"); + if (!argNames.isEmpty()) { + // ensure size is the same for key replacement later + if (found.validateArgs() && args.size() != argNames.size()) { + throw new IllegalArgumentException("Wrong number of arguments. Expected " + argNames + + " " + argNames + ". Found " + args.size() + " " + args + "'"); + } } } @@ -193,6 +195,15 @@ public class RequestPredicateHandlerMapping extends AbstractHandlerMapping { } Tuple tuple = builder.build(); + + if (found.validateArgs()) { + for (String name : argNames) { + if (!tuple.hasFieldName(name)) { + throw new IllegalArgumentException("Missing argument '" + name + "'. Given " + tuple); + } + } + } + return found.apply(tuple); } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRequestPredicateFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRequestPredicateFactory.java index 20d642ad..392733a3 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRequestPredicateFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/QueryRequestPredicateFactory.java @@ -39,7 +39,7 @@ public class QueryRequestPredicateFactory implements RequestPredicateFactory { } @Override - public boolean validateArgSize() { + public boolean validateArgs() { return false; } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/RequestPredicateFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/RequestPredicateFactory.java index 4740ccc4..2ec259e5 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/RequestPredicateFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/RequestPredicateFactory.java @@ -46,11 +46,11 @@ public interface RequestPredicateFactory { } /** - * Validate supplied argument size against {@see #argNames} size. + * Auto validate supplied argument size against {@see #argNames} size and that an arg for each key exists. * Useful for variable arg predicates. * @return */ - default boolean validateArgSize() { + default boolean validateArgs() { return true; } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/model/FilterDefinition.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/model/FilterDefinition.java index 83028eb5..76afe6d1 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/model/FilterDefinition.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/model/FilterDefinition.java @@ -17,11 +17,14 @@ package org.springframework.cloud.gateway.model; -import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.Objects; import javax.validation.constraints.NotNull; +import org.springframework.cloud.gateway.support.NameUtils; + import static org.springframework.util.StringUtils.tokenizeToStringArray; /** @@ -30,7 +33,7 @@ import static org.springframework.util.StringUtils.tokenizeToStringArray; public class FilterDefinition { @NotNull private String name; - private String[] args; + private Map args = new LinkedHashMap<>(); public FilterDefinition() { } @@ -45,7 +48,9 @@ public class FilterDefinition { String[] args = tokenizeToStringArray(text.substring(eqIdx+1), ","); - setArgs(args); + for (int i=0; i < args.length; i++) { + this.args.put(NameUtils.generateName(i), args[i]); + } } public String getName() { @@ -56,21 +61,25 @@ public class FilterDefinition { this.name = name; } - public String[] getArgs() { + public Map getArgs() { return args; } - public void setArgs(String... args) { + public void setArgs(Map args) { this.args = args; } + public void addArg(String key, String value) { + this.args.put(key, value); + } + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; FilterDefinition that = (FilterDefinition) o; return Objects.equals(name, that.name) && - Arrays.equals(args, that.args); + Objects.equals(args, that.args); } @Override @@ -82,7 +91,7 @@ public class FilterDefinition { public String toString() { final StringBuilder sb = new StringBuilder("FilterDefinition{"); sb.append("name='").append(name).append('\''); - sb.append(", args=").append(Arrays.toString(args)); + sb.append(", args=").append(args); sb.append('}'); return sb.toString(); } diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/model/PredicateDefinition.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/model/PredicateDefinition.java index 9c95c4c2..086d839a 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/model/PredicateDefinition.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/model/PredicateDefinition.java @@ -70,6 +70,10 @@ public class PredicateDefinition { this.args = args; } + public void addArg(String key, String value) { + this.args.put(key, value); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewritePathWebFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewritePathWebFilterFactoryTests.java index e8db6811..47ff3422 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewritePathWebFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/RewritePathWebFilterFactoryTests.java @@ -20,16 +20,20 @@ package org.springframework.cloud.gateway.filter.factory; import org.assertj.core.api.Assertions; import org.junit.Test; import org.mockito.ArgumentCaptor; -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; +import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; import org.springframework.web.server.adapter.DefaultServerWebExchange; -import reactor.core.publisher.Mono; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.springframework.cloud.gateway.filter.factory.RewritePathWebFilterFactory.REGEXP_KEY; +import static org.springframework.cloud.gateway.filter.factory.RewritePathWebFilterFactory.REPLACEMENT_KEY; +import static org.springframework.tuple.TupleBuilder.tuple; + +import reactor.core.publisher.Mono; /** * @author Spencer Gibb @@ -47,7 +51,7 @@ public class RewritePathWebFilterFactoryTests { } private void testRewriteFilter(String regex, String replacement, String actualPath, String expectedPath) { - WebFilter filter = new RewritePathWebFilterFactory().apply(regex, replacement); + WebFilter filter = new RewritePathWebFilterFactory().apply(tuple().of(REGEXP_KEY, regex, REPLACEMENT_KEY, replacement)); MockServerHttpRequest request = MockServerHttpRequest .get("http://localhost"+ actualPath) diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathWebFilterFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathWebFilterFactoryTests.java index 43542d14..3ff191fd 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathWebFilterFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory/SetPathWebFilterFactoryTests.java @@ -32,6 +32,8 @@ import org.springframework.web.server.adapter.DefaultServerWebExchange; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.springframework.cloud.gateway.filter.factory.SetPathWebFilterFactory.TEMPLATE_KEY; +import static org.springframework.tuple.TupleBuilder.tuple; import reactor.core.publisher.Mono; @@ -54,7 +56,7 @@ public class SetPathWebFilterFactoryTests { } private void testRewriteFilter(String template, String actualPath, String expectedPath, HashMap variables) { - WebFilter filter = new SetPathWebFilterFactory().apply(new String[]{template}); + WebFilter filter = new SetPathWebFilterFactory().apply(tuple().of(TEMPLATE_KEY, template)); MockServerHttpRequest request = MockServerHttpRequest .get("http://localhost"+ actualPath) diff --git a/spring-cloud-gateway-core/src/test/resources/application.yml b/spring-cloud-gateway-core/src/test/resources/application.yml index 598de504..826790bb 100644 --- a/spring-cloud-gateway-core/src/test/resources/application.yml +++ b/spring-cloud-gateway-core/src/test/resources/application.yml @@ -151,7 +151,8 @@ spring: - Path=/headers filters: - name: SetStatus - args: 401 + args: + status: 401 # ===================================== - id: set_status_string_test