Move back to Predicate<ServerWebExchange>

Stick to webflux class as this project is currently built on webflux.

fixes gh-35
This commit is contained in:
Spencer Gibb
2017-03-22 12:01:27 -06:00
parent 34c35fbcc9
commit b9bf99451f
37 changed files with 417 additions and 585 deletions

View File

@@ -30,21 +30,21 @@ TODO: document the meaning of terms to follow, like Route, Predicate and Filter
TODO: give an overview of how the gateway works with maybe a ascii diagram
[[gateway-request-predicates-factories]]
== Request Predicate Factories
== Route Predicate Factories
Spring Cloud Gateway matches routes as part of the Spring WebFlux `HandlerMapping` infrastructure. Spring Cloud Gateway includes many built-in Request Predicate Factorys. All of these predicates match on different attributes of the HTTP request. Multiple Request Predicate Factorys can be combined and are combined via logical `and`.
Spring Cloud Gateway matches routes as part of the Spring WebFlux `HandlerMapping` infrastructure. Spring Cloud Gateway includes many built-in Route Predicate Factorys. All of these predicates match on different attributes of the HTTP request. Multiple Route Predicate Factorys can be combined and are combined via logical `and`.
=== After Request Predicate Factory
TODO: document After Request Predicate Factory
=== After Route Predicate Factory
TODO: document After Route Predicate Factory
=== Before Request Predicate Factory
TODO: document Before Request Predicate Factory
=== Before Route Predicate Factory
TODO: document Before Route Predicate Factory
=== Between Request Predicate Factory
TODO: document Between Request Predicate Factory
=== Between Route Predicate Factory
TODO: document Between Route Predicate Factory
=== Cookie Request Predicate Factory
The Cookie Request Predicate Factory takes two parameters, the cookie name and a regular expression. This predicate matches cookies that have the given name and the value matches the regular expression.
=== Cookie Route Predicate Factory
The Cookie Route Predicate Factory takes two parameters, the cookie name and a regular expression. This predicate matches cookies that have the given name and the value matches the regular expression.
.application.yml
[source,yaml]
@@ -62,8 +62,8 @@ spring:
This route matches the request has a cookie named `chocolate` who's value matches the `ch.p` regular expression.
=== Header Request Predicate Factory
The Header Request Predicate Factory takes two parameters, the header name and a regular expression. This predicate matches with a header that has the given name and the value matches the regular expression.
=== Header Route Predicate Factory
The Header Route Predicate Factory takes two parameters, the header name and a regular expression. This predicate matches with a header that has the given name and the value matches the regular expression.
.application.yml
[source,yaml]
@@ -81,8 +81,8 @@ spring:
This route matches if the request has a header named `X-Request-Id` whos value matches the `\d+` regular expression (has a value of one or more digits).
=== Host Request Predicate Factory
The Host Request Predicate Factory takes one parameter: the host name pattern. The pattern is an Ant style pattern with `.` as the separator. This predicates matches the `Host` header that matches the pattern.
=== Host Route Predicate Factory
The Host Route Predicate Factory takes one parameter: the host name pattern. The pattern is an Ant style pattern with `.` as the separator. This predicates matches the `Host` header that matches the pattern.
.application.yml
[source,yaml]
@@ -101,8 +101,8 @@ spring:
This route would match if the request has a `Host` header has the value `www.somehost.org` or `beta.somehost.org`.
=== Method Request Predicate Factory
The Method Request Predicate Factory takes one parameter: the HTTP method to match.
=== Method Route Predicate Factory
The Method Route Predicate Factory takes one parameter: the HTTP method to match.
.application.yml
[source,yaml]
@@ -120,8 +120,8 @@ spring:
This route would match if the request method was a `GET`.
=== Path Request Predicate Factory
The Path Request Predicate Factory takes one parameter: a Spring `PathMatcher` pattern.
=== Path Route Predicate Factory
The Path Route Predicate Factory takes one parameter: a Spring `PathMatcher` pattern.
.application.yml
[source,yaml]
@@ -141,11 +141,11 @@ This route would match if the request path was, for example: `/foo/1` or `/foo/b
This predicate extracts the URI template variables (like `segment` defined in the example above) as a map of names and values and places it in the `ServerWebExchange.getAttributes()` with a key defined in `PathRoutePredicate.URL_PREDICATE_VARS_ATTR`. Those values are then available for use by <<gateway-route-filters,WebFilter Factorys>>
=== Query Request Predicate Factory
TODO: document Query Request Predicate Factory
=== Query Route Predicate Factory
TODO: document Query Route Predicate Factory
=== RemoteAddr Request Predicate Factory
TODO: document RemoteAddr Request Predicate Factory
=== RemoteAddr Route Predicate Factory
TODO: document RemoteAddr Route Predicate Factory
[[gateway-route-filters]]
== WebFilter Factorys
@@ -210,9 +210,9 @@ TODO: document the `/gateway` actuator endpoint
TODO: overview of writing custom integrations
=== Writing Custom Request Predicate Factorys
=== Writing Custom Route Predicate Factorys
TODO: document writing Custom Request Predicate Factorys
TODO: document writing Custom Route Predicate Factorys
=== Writing Custom WebFilter Factorys

View File

@@ -28,6 +28,12 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.actuate.GatewayEndpoint;
import org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.HostRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
@@ -54,18 +60,12 @@ import org.springframework.cloud.gateway.filter.factory.SetStatusWebFilterFactor
import org.springframework.cloud.gateway.filter.factory.WebFilterFactory;
import org.springframework.cloud.gateway.handler.FilteringWebHandler;
import org.springframework.cloud.gateway.handler.NettyRoutingWebHandler;
import org.springframework.cloud.gateway.handler.RequestPredicateHandlerMapping;
import org.springframework.cloud.gateway.handler.predicate.AfterRequestPredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.BeforeRequestPredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.BetweenRequestPredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.CookieRequestPredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.HeaderRequestPredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.HostRequestPredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.MethodRequestPredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.PathRequestPredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.QueryRequestPredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.RemoteAddrRequestPredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.RequestPredicateFactory;
import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping;
import org.springframework.cloud.gateway.handler.predicate.AfterRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.BeforeRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.CookieRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.HeaderRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactory;
import org.springframework.cloud.gateway.route.CachingRouteLocator;
import org.springframework.cloud.gateway.route.CompositeRouteDefinitionLocator;
import org.springframework.cloud.gateway.route.CompositeRouteLocator;
@@ -135,7 +135,7 @@ public class GatewayAutoConfiguration {
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
List<WebFilterFactory> webFilterFactories,
List<RequestPredicateFactory> predicates,
List<RoutePredicateFactory> predicates,
RouteDefinitionLocator routeDefinitionLocator) {
return new CachingRouteLocator(new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, webFilterFactories, properties));
}
@@ -153,9 +153,9 @@ public class GatewayAutoConfiguration {
}
@Bean
public RequestPredicateHandlerMapping requestPredicateHandlerMapping(FilteringWebHandler webHandler,
public RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler,
RouteLocator routeLocator) {
return new RequestPredicateHandlerMapping(webHandler, routeLocator);
return new RoutePredicateHandlerMapping(webHandler, routeLocator);
}
// ConfigurationProperty beans
@@ -185,53 +185,53 @@ public class GatewayAutoConfiguration {
// Request Predicate beans
@Bean
public AfterRequestPredicateFactory afterRequestPredicateFactory() {
return new AfterRequestPredicateFactory();
public AfterRoutePredicateFactory afterRoutePredicateFactory() {
return new AfterRoutePredicateFactory();
}
@Bean
public BeforeRequestPredicateFactory beforeRequestPredicateFactory() {
return new BeforeRequestPredicateFactory();
public BeforeRoutePredicateFactory beforeRoutePredicateFactory() {
return new BeforeRoutePredicateFactory();
}
@Bean
public BetweenRequestPredicateFactory betweenRequestPredicateFactory() {
return new BetweenRequestPredicateFactory();
public BetweenRoutePredicateFactory betweenRoutePredicateFactory() {
return new BetweenRoutePredicateFactory();
}
@Bean
public CookieRequestPredicateFactory cookieRequestPredicateFactory() {
return new CookieRequestPredicateFactory();
public CookieRoutePredicateFactory cookieRoutePredicateFactory() {
return new CookieRoutePredicateFactory();
}
@Bean
public HeaderRequestPredicateFactory headerRequestPredicateFactory() {
return new HeaderRequestPredicateFactory();
public HeaderRoutePredicateFactory headerRoutePredicateFactory() {
return new HeaderRoutePredicateFactory();
}
@Bean
public HostRequestPredicateFactory hostRequestPredicateFactory() {
return new HostRequestPredicateFactory();
public HostRoutePredicateFactory hostRoutePredicateFactory() {
return new HostRoutePredicateFactory();
}
@Bean
public MethodRequestPredicateFactory methodRequestPredicateFactory() {
return new MethodRequestPredicateFactory();
public MethodRoutePredicateFactory methodRoutePredicateFactory() {
return new MethodRoutePredicateFactory();
}
@Bean
public PathRequestPredicateFactory pathRequestPredicateFactory() {
return new PathRequestPredicateFactory();
public PathRoutePredicateFactory pathRoutePredicateFactory() {
return new PathRoutePredicateFactory();
}
@Bean
public QueryRequestPredicateFactory queryRequestPredicateFactory() {
return new QueryRequestPredicateFactory();
public QueryRoutePredicateFactory queryRoutePredicateFactory() {
return new QueryRoutePredicateFactory();
}
@Bean
public RemoteAddrRequestPredicateFactory remoteAddrRequestPredicateFactory() {
return new RemoteAddrRequestPredicateFactory();
public RemoteAddrRoutePredicateFactory remoteAddrRoutePredicateFactory() {
return new RemoteAddrRoutePredicateFactory();
}
// Filter Factory beans

View File

@@ -22,14 +22,14 @@ import java.net.URI;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.gateway.filter.factory.RewritePathWebFilterFactory;
import org.springframework.cloud.gateway.handler.predicate.PathRequestPredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
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.handler.predicate.PathRoutePredicateFactory.PATTERN_KEY;
import static org.springframework.cloud.gateway.support.NameUtils.normalizeFilterName;
import static org.springframework.cloud.gateway.support.NameUtils.normalizePredicateName;
@@ -59,7 +59,7 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc
// add a predicate that matches the url at /serviceId*
PredicateDefinition predicate = new PredicateDefinition();
predicate.setName(normalizePredicateName(PathRequestPredicateFactory.class));
predicate.setName(normalizePredicateName(PathRoutePredicateFactory.class));
predicate.addArg(PATTERN_KEY, "/" + serviceId + "*");
routeDefinition.getPredicates().add(predicate);

View File

@@ -27,8 +27,8 @@ import org.springframework.tuple.Tuple;
import org.springframework.web.server.WebFilter;
import org.springframework.web.util.UriTemplate;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.getAttribute;
import static org.springframework.web.reactive.function.server.RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
/**
* @author Spencer Gibb

View File

@@ -20,7 +20,6 @@ package org.springframework.cloud.gateway.handler;
import java.util.function.Function;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.handler.support.ExchangeServerRequest;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.web.reactive.handler.AbstractHandlerMapping;
import org.springframework.web.server.ServerWebExchange;
@@ -34,12 +33,12 @@ import reactor.core.publisher.Mono;
/**
* @author Spencer Gibb
*/
public class RequestPredicateHandlerMapping extends AbstractHandlerMapping {
public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
private final WebHandler webHandler;
private final RouteLocator routeLocator;
public RequestPredicateHandlerMapping(WebHandler webHandler, RouteLocator routeLocator) {
public RoutePredicateHandlerMapping(WebHandler webHandler, RouteLocator routeLocator) {
this.webHandler = webHandler;
this.routeLocator = routeLocator;
@@ -80,7 +79,7 @@ public class RequestPredicateHandlerMapping extends AbstractHandlerMapping {
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
return this.routeLocator.getRoutes()
.filter(route -> route.getRequestPredicate().test(new ExchangeServerRequest(exchange)))
.filter(route -> route.getPredicate().test(exchange))
.next()
//TODO: error handling
.map(route -> {

View File

@@ -20,16 +20,17 @@ package org.springframework.cloud.gateway.handler.predicate;
import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import org.springframework.tuple.Tuple;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRequestPredicateFactory.parseZonedDateTime;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactory.parseZonedDateTime;
/**
* @author Spencer Gibb
*/
public class AfterRequestPredicateFactory implements RequestPredicateFactory {
public class AfterRoutePredicateFactory implements RoutePredicateFactory {
public static final String DATETIME_KEY = "datetime";
@@ -39,10 +40,10 @@ public class AfterRequestPredicateFactory implements RequestPredicateFactory {
}
@Override
public RequestPredicate apply(Tuple args) {
public Predicate<ServerWebExchange> apply(Tuple args) {
final ZonedDateTime dateTime = parseZonedDateTime(args.getString(DATETIME_KEY));
return request -> {
return exchange -> {
final ZonedDateTime now = ZonedDateTime.now();
return now.isAfter(dateTime);
};

View File

@@ -20,16 +20,17 @@ package org.springframework.cloud.gateway.handler.predicate;
import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import org.springframework.tuple.Tuple;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRequestPredicateFactory.parseZonedDateTime;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactory.parseZonedDateTime;
/**
* @author Spencer Gibb
*/
public class BeforeRequestPredicateFactory implements RequestPredicateFactory {
public class BeforeRoutePredicateFactory implements RoutePredicateFactory {
public static final String DATETIME_KEY = "datetime";
@@ -39,10 +40,10 @@ public class BeforeRequestPredicateFactory implements RequestPredicateFactory {
}
@Override
public RequestPredicate apply(Tuple args) {
public Predicate<ServerWebExchange> apply(Tuple args) {
final ZonedDateTime dateTime = parseZonedDateTime(args.getString(DATETIME_KEY));
return request -> {
return exchange -> {
final ZonedDateTime now = ZonedDateTime.now();
return now.isBefore(dateTime);
};

View File

@@ -20,28 +20,29 @@ package org.springframework.cloud.gateway.handler.predicate;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.function.Predicate;
import org.springframework.tuple.Tuple;
import org.springframework.util.Assert;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.server.ServerWebExchange;
/**
* @author Spencer Gibb
*/
public class BetweenRequestPredicateFactory implements RequestPredicateFactory {
public class BetweenRoutePredicateFactory implements RoutePredicateFactory {
public static final String DATETIME1_KEY = "datetime1";
public static final String DATETIME2_KEY = "datetime2";
@Override
public RequestPredicate apply(Tuple args) {
public Predicate<ServerWebExchange> apply(Tuple args) {
//TODO: is ZonedDateTime the right thing to use?
final ZonedDateTime dateTime1 = parseZonedDateTime(args.getString(DATETIME1_KEY));
final ZonedDateTime dateTime2 = parseZonedDateTime(args.getString(DATETIME2_KEY));
Assert.isTrue(dateTime1.isBefore(dateTime2), args.getString(DATETIME1_KEY) +
" must be before " + args.getString(DATETIME2_KEY));
return request -> {
return exchange -> {
final ZonedDateTime now = ZonedDateTime.now();
return now.isAfter(dateTime1) && now.isBefore(dateTime2);
};

View File

@@ -19,17 +19,16 @@ package org.springframework.cloud.gateway.handler.predicate;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import org.springframework.http.HttpCookie;
import org.springframework.tuple.Tuple;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.server.ServerWebExchange;
/**
* @author Spencer Gibb
*/
public class CookieRequestPredicateFactory implements RequestPredicateFactory {
public class CookieRoutePredicateFactory implements RoutePredicateFactory {
public static final String NAME_KEY = "name";
public static final String REGEXP_KEY = "regexp";
@@ -40,13 +39,12 @@ public class CookieRequestPredicateFactory implements RequestPredicateFactory {
}
@Override
public RequestPredicate apply(Tuple args) {
public Predicate<ServerWebExchange> apply(Tuple args) {
String name = args.getString(NAME_KEY);
String regexp = args.getString(REGEXP_KEY);
return request -> {
Optional<ServerWebExchange> exchange = request.attribute("exchange");
List<HttpCookie> cookies = exchange.get().getRequest().getCookies().get(name);
return exchange -> {
List<HttpCookie> cookies = exchange.getRequest().getCookies().get(name);
for (HttpCookie cookie : cookies) {
if (cookie.getValue().matches(regexp)) {
return true;

View File

@@ -1,58 +0,0 @@
/*
* 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.handler.predicate;
import org.springframework.web.reactive.function.server.RequestPredicate;
import static org.springframework.cloud.gateway.handler.predicate.RequestPredicateFactory.PATTERN_KEY;
import static org.springframework.tuple.TupleBuilder.tuple;
/**
* @author Spencer Gibb
*/
public class GatewayRequestPredicates {
//TODO: add support for AfterRequestPredicateFactory
//TODO: add support for BeforeRequestPredicateFactory
//TODO: add support for BetweenRequestPredicateFactory
//TODO: add support for CookieRequestPredicateFactory
//TODO: add support for GatewayRequestPredicates
//TODO: add support for HeaderRequestPredicateFactory
public static RequestPredicate host(String pattern) {
return new HostRequestPredicateFactory().apply(tuple().of(PATTERN_KEY, pattern));
}
//TODO: add support for MethodRequestPredicateFactory
public static RequestPredicate path(String pattern) {
return new PathRequestPredicateFactory().apply(tuple().of(PATTERN_KEY, pattern));
}
//TODO: add support for PredicateDefinition
//TODO: add support for QueryRequestPredicateFactory
//TODO: add support for RemoteAddrRequestPredicateFactory
}

View File

@@ -19,15 +19,15 @@ package org.springframework.cloud.gateway.handler.predicate;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import org.springframework.tuple.Tuple;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.server.ServerWebExchange;
/**
* @author Spencer Gibb
*/
public class HeaderRequestPredicateFactory implements RequestPredicateFactory {
public class HeaderRoutePredicateFactory implements RoutePredicateFactory {
public static final String HEADER_KEY = "header";
public static final String REGEXP_KEY = "regexp";
@@ -38,18 +38,18 @@ public class HeaderRequestPredicateFactory implements RequestPredicateFactory {
}
@Override
public RequestPredicate apply(Tuple args) {
public Predicate<ServerWebExchange> apply(Tuple args) {
String header = args.getString(HEADER_KEY);
String regexp = args.getString(REGEXP_KEY);
return RequestPredicates.headers(headers -> {
List<String> values = headers.asHttpHeaders().get(header);
return exchange -> {
List<String> values = exchange.getRequest().getHeaders().get(header);
for (String value : values) {
if (value.matches(regexp)) {
return true;
}
}
return false;
});
};
}
}

View File

@@ -19,17 +19,17 @@ package org.springframework.cloud.gateway.handler.predicate;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import org.springframework.tuple.Tuple;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.server.ServerWebExchange;
/**
* @author Spencer Gibb
*/
public class HostRequestPredicateFactory implements RequestPredicateFactory {
public class HostRoutePredicateFactory implements RoutePredicateFactory {
private PathMatcher pathMatcher = new AntPathMatcher(".");
@@ -43,12 +43,12 @@ public class HostRequestPredicateFactory implements RequestPredicateFactory {
}
@Override
public RequestPredicate apply(Tuple args) {
public Predicate<ServerWebExchange> apply(Tuple args) {
String pattern = args.getString(PATTERN_KEY);
return RequestPredicates.headers(headers -> {
String host = headers.asHttpHeaders().getFirst("Host");
return exchange -> {
String host = exchange.getRequest().getHeaders().getFirst("Host");
return this.pathMatcher.match(pattern, host);
});
};
}
}

View File

@@ -17,18 +17,18 @@
package org.springframework.cloud.gateway.handler.predicate;
import org.springframework.http.HttpMethod;
import org.springframework.tuple.Tuple;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.reactive.function.server.RequestPredicates;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import org.springframework.http.HttpMethod;
import org.springframework.tuple.Tuple;
import org.springframework.web.server.ServerWebExchange;
/**
* @author Spencer Gibb
*/
public class MethodRequestPredicateFactory implements RequestPredicateFactory {
public class MethodRoutePredicateFactory implements RoutePredicateFactory {
public static final String METHOD_KEY = "method";
@@ -38,8 +38,11 @@ public class MethodRequestPredicateFactory implements RequestPredicateFactory {
}
@Override
public RequestPredicate apply(Tuple args) {
public Predicate<ServerWebExchange> apply(Tuple args) {
String method = args.getString(METHOD_KEY);
return RequestPredicates.method(HttpMethod.resolve(method));
return exchange -> {
HttpMethod requestMethod = exchange.getRequest().getMethod();
return requestMethod.matches(method);
};
}
}

View File

@@ -1,54 +0,0 @@
/*
* 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.handler.predicate;
import org.springframework.tuple.Tuple;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.util.patterns.PathPatternParser;
import java.util.Collections;
import java.util.List;
/**
* @author Spencer Gibb
*/
public class PathRequestPredicateFactory implements RequestPredicateFactory {
private PathPatternParser pathPatternParser;
public void setPathPatternParser(PathPatternParser pathPatternParser) {
this.pathPatternParser = pathPatternParser;
}
@Override
public List<String> argNames() {
return Collections.singletonList(PATTERN_KEY);
}
@Override
public RequestPredicate apply(Tuple args) {
String pattern = args.getString(PATTERN_KEY);
if (this.pathPatternParser != null) {
return RequestPredicates.pathPredicates(this.pathPatternParser).apply(pattern);
}
return RequestPredicates.path(pattern);
}
}

View File

@@ -0,0 +1,71 @@
/*
* 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.handler.predicate;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import org.springframework.tuple.Tuple;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.patterns.PathPattern;
import org.springframework.web.util.patterns.PathPatternParser;
import static org.springframework.cloud.gateway.handler.support.RoutePredicateFactoryUtils.traceMatch;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
/**
* @author Spencer Gibb
*/
public class PathRoutePredicateFactory implements RoutePredicateFactory {
private PathPatternParser pathPatternParser = new PathPatternParser();
public void setPathPatternParser(PathPatternParser pathPatternParser) {
this.pathPatternParser = pathPatternParser;
}
@Override
public List<String> argNames() {
return Collections.singletonList(PATTERN_KEY);
}
@Override
public Predicate<ServerWebExchange> apply(Tuple args) {
String unparsedPattern = args.getString(PATTERN_KEY);
PathPattern pattern;
synchronized (this.pathPatternParser) {
pattern = this.pathPatternParser.parse(unparsedPattern);
}
return exchange -> {
String path = exchange.getRequest().getURI().getPath();
boolean match = pattern.matches(path);
traceMatch("Pattern", pattern.getPatternString(), path, match);
if (match) {
Map<String, String> uriTemplateVariables = pattern.matchAndExtract(path);
exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
return true;
}
else {
return false;
}
};
}
}

View File

@@ -19,17 +19,15 @@ package org.springframework.cloud.gateway.handler.predicate;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import org.springframework.tuple.Tuple;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.server.ServerWebExchange;
/**
* @author Spencer Gibb
*/
public class QueryRequestPredicateFactory implements RequestPredicateFactory {
public class QueryRoutePredicateFactory implements RoutePredicateFactory {
public static final String PARAM_KEY = "param";
public static final String REGEXP_KEY = "regexp";
@@ -45,20 +43,25 @@ public class QueryRequestPredicateFactory implements RequestPredicateFactory {
}
@Override
public RequestPredicate apply(Tuple args) {
public Predicate<ServerWebExchange> apply(Tuple args) {
validateMin(1, args);
String param = args.getString(PARAM_KEY);
if (!args.hasFieldName(REGEXP_KEY)) {
return req -> {
//TODO: ServerRequest support for query params with no value
Optional<ServerWebExchange> exchange = req.attribute("exchange");
return exchange.get().getRequest().getQueryParams().containsKey(param);
};
}
return exchange -> {
if (!args.hasFieldName(REGEXP_KEY)) {
// check existence of header
return exchange.getRequest().getQueryParams().containsKey(param);
}
String regexp = args.getString(REGEXP_KEY);
String regexp = args.getString(REGEXP_KEY);
return RequestPredicates.queryParam(param, value -> value.matches(regexp));
List<String> values = exchange.getRequest().getQueryParams().get(param);
for (String value : values) {
if (value.matches(regexp)) {
return true;
}
}
return false;
};
}
}

View File

@@ -21,23 +21,23 @@ import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.gateway.support.SubnetUtils;
import org.springframework.tuple.Tuple;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.server.ServerWebExchange;
/**
* @author Spencer Gibb
*/
public class RemoteAddrRequestPredicateFactory implements RequestPredicateFactory {
public class RemoteAddrRoutePredicateFactory implements RoutePredicateFactory {
private static final Log log = LogFactory.getLog(RemoteAddrRequestPredicateFactory.class);
private static final Log log = LogFactory.getLog(RemoteAddrRoutePredicateFactory.class);
@Override
public RequestPredicate apply(Tuple args) {
public Predicate<ServerWebExchange> apply(Tuple args) {
validate(1, args);
List<SubnetUtils> sources = new ArrayList<>();
@@ -47,12 +47,11 @@ public class RemoteAddrRequestPredicateFactory implements RequestPredicateFactor
}
}
return request -> {
Optional<ServerWebExchange> exchange = request.attribute("exchange");
Optional<InetSocketAddress> remoteAddress = exchange.get().getRequest().getRemoteAddress();
return exchange -> {
Optional<InetSocketAddress> remoteAddress = exchange.getRequest().getRemoteAddress();
if (remoteAddress.isPresent()) {
String hostAddress = remoteAddress.get().getAddress().getHostAddress();
String host = request.uri().getHost();
String host = exchange.getRequest().getURI().getHost();
if (!hostAddress.equals(host)) {
log.warn("Remote addresses didn't match " + hostAddress + " != " + host);

View File

@@ -17,19 +17,21 @@
package org.springframework.cloud.gateway.handler.predicate;
import java.util.function.Predicate;
import org.springframework.cloud.gateway.support.ArgumentHints;
import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.tuple.Tuple;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.server.ServerWebExchange;
/**
* @author Spencer Gibb
*/
@FunctionalInterface
public interface RequestPredicateFactory extends ArgumentHints {
public interface RoutePredicateFactory extends ArgumentHints {
String PATTERN_KEY = "pattern";
RequestPredicate apply(Tuple args);
Predicate<ServerWebExchange> apply(Tuple args);
default String name() {
return NameUtils.normalizePredicateName(getClass());

View File

@@ -0,0 +1,60 @@
/*
* 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.handler.predicate;
import java.util.function.Predicate;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory.PATTERN_KEY;
import static org.springframework.tuple.TupleBuilder.tuple;
/**
* @author Spencer Gibb
*/
public class RoutePredicates {
//TODO: add support for AfterRoutePredicateFactory
//TODO: add support for BeforeRoutePredicateFactory
//TODO: add support for BetweenRoutePredicateFactory
//TODO: add support for CookieRoutePredicateFactory
//TODO: add support for RoutePredicates
//TODO: add support for HeaderRoutePredicateFactory
public static Predicate<ServerWebExchange> host(String pattern) {
return new HostRoutePredicateFactory().apply(tuple().of(PATTERN_KEY, pattern));
}
//TODO: add support for MethodRoutePredicateFactory
public static Predicate<ServerWebExchange> path(String pattern) {
return new PathRoutePredicateFactory().apply(tuple().of(PATTERN_KEY, pattern));
}
//TODO: add support for PredicateDefinition
//TODO: add support for QueryRoutePredicateFactory
//TODO: add support for RemoteAddrRoutePredicateFactory
}

View File

@@ -1,233 +0,0 @@
/*
* 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.handler.support;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRange;
import org.springframework.http.MediaType;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.Assert;
import org.springframework.web.reactive.function.BodyExtractor;
import org.springframework.web.reactive.function.BodyExtractors;
import org.springframework.web.reactive.function.UnsupportedMediaTypeException;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.UnsupportedMediaTypeStatusException;
import org.springframework.web.server.WebSession;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* {@code ServerRequest} implementation based on a {@link ServerWebExchange}.
*
* @author Arjen Poutsma
* @since 1.0
*/
public class ExchangeServerRequest implements ServerRequest {
private static final Function<UnsupportedMediaTypeException, UnsupportedMediaTypeStatusException> ERROR_MAPPER =
ex -> ex.getContentType()
.map(contentType -> new UnsupportedMediaTypeStatusException(contentType,
ex.getSupportedMediaTypes()))
.orElseGet(() -> new UnsupportedMediaTypeStatusException(ex.getMessage()));
private final ServerWebExchange exchange;
private final Headers headers;
private final HandlerStrategies strategies;
public ExchangeServerRequest(ServerWebExchange exchange) {
this(exchange, HandlerStrategies.withDefaults());
}
public ExchangeServerRequest(ServerWebExchange exchange, HandlerStrategies strategies) {
this.exchange = exchange;
this.strategies = strategies;
this.headers = new ExchangeServerRequest.DefaultHeaders();
this.exchange.getAttributes().put("exchange", this.exchange);
}
@Override
public HttpMethod method() {
return request().getMethod();
}
@Override
public URI uri() {
return request().getURI();
}
@Override
public Headers headers() {
return this.headers;
}
@Override
public <T> T body(BodyExtractor<T, ? super ServerHttpRequest> extractor) {
return body(extractor, Collections.emptyMap());
}
@Override
public <T> T body(BodyExtractor<T, ? super ServerHttpRequest> extractor, Map<String, Object> hints) {
Assert.notNull(extractor, "'extractor' must not be null");
return extractor.extract(request(),
new BodyExtractor.Context() {
@Override
public Supplier<Stream<HttpMessageReader<?>>> messageReaders() {
return ExchangeServerRequest.this.strategies.messageReaders();
}
@Override
public Map<String, Object> hints() {
return hints;
}
});
}
@Override
public <T> Mono<T> bodyToMono(Class<? extends T> elementClass) {
Mono<T> mono = body(BodyExtractors.toMono(elementClass));
return mono.mapError(UnsupportedMediaTypeException.class, ERROR_MAPPER);
}
@Override
public <T> Flux<T> bodyToFlux(Class<? extends T> elementClass) {
Flux<T> flux = body(BodyExtractors.toFlux(elementClass));
return flux.mapError(UnsupportedMediaTypeException.class, ERROR_MAPPER);
}
@Override
public <T> Optional<T> attribute(String name) {
return this.exchange.getAttribute(name);
}
@Override
public Map<String, Object> attributes() {
return this.exchange.getAttributes();
}
@Override
public List<String> queryParams(String name) {
List<String> queryParams = request().getQueryParams().get(name);
return queryParams != null ? queryParams : Collections.emptyList();
}
@Override
public Map<String, String> pathVariables() {
return this.exchange.<Map<String, String>>getAttribute(RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE).
orElseGet(Collections::emptyMap);
}
@Override
public Mono<WebSession> session() {
return this.exchange.getSession();
}
private ServerHttpRequest request() {
return this.exchange.getRequest();
}
public ServerWebExchange exchange() {
return this.exchange;
}
@Override
public String toString() {
return String.format("%s %s", method(), path());
}
private class DefaultHeaders implements Headers {
private HttpHeaders delegate() {
return request().getHeaders();
}
@Override
public List<MediaType> accept() {
return delegate().getAccept();
}
@Override
public List<Charset> acceptCharset() {
return delegate().getAcceptCharset();
}
@Override
public List<Locale.LanguageRange> acceptLanguage() {
return delegate().getAcceptLanguage();
}
@Override
public OptionalLong contentLength() {
long value = delegate().getContentLength();
return (value != -1 ? OptionalLong.of(value) : OptionalLong.empty());
}
@Override
public Optional<MediaType> contentType() {
return Optional.ofNullable(delegate().getContentType());
}
@Override
public InetSocketAddress host() {
return delegate().getHost();
}
@Override
public List<HttpRange> range() {
return delegate().getRange();
}
@Override
public List<String> header(String headerName) {
List<String> headerValues = delegate().get(headerName);
return (headerValues != null ? headerValues : Collections.emptyList());
}
@Override
public HttpHeaders asHttpHeaders() {
return HttpHeaders.readOnlyHttpHeaders(delegate());
}
@Override
public String toString() {
return delegate().toString();
}
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.handler.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
/**
* @author Spencer Gibb
*/
public class RoutePredicateFactoryUtils {
private static final Log logger = LogFactory.getLog(RoutePredicateFactory.class);
public static void traceMatch(String prefix, Object desired, Object actual, boolean match) {
if (logger.isTraceEnabled()) {
String message = String.format("%s \"%s\" %s against value \"%s\"",
prefix, desired, match ? "matches" : "does not match", actual);
logger.trace(message);
}
}
}

View File

@@ -23,9 +23,10 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import org.springframework.util.Assert;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
/**
@@ -37,7 +38,7 @@ public class Route {
private final URI uri;
private final RequestPredicate requestPredicate;
private final Predicate<ServerWebExchange> predicate;
private final List<WebFilter> webFilters;
@@ -51,10 +52,10 @@ public class Route {
.uri(routeDefinition.getUri());
}
public Route(String id, URI uri, RequestPredicate requestPredicate, List<WebFilter> webFilters) {
public Route(String id, URI uri, Predicate<ServerWebExchange> predicate, List<WebFilter> webFilters) {
this.id = id;
this.uri = uri;
this.requestPredicate = requestPredicate;
this.predicate = predicate;
this.webFilters = webFilters;
}
@@ -63,7 +64,7 @@ public class Route {
private URI uri;
private RequestPredicate requestPredicate;
private Predicate<ServerWebExchange> predicate;
private List<WebFilter> webFilters = new ArrayList<>();
@@ -84,8 +85,8 @@ public class Route {
return this;
}
public Builder requestPredicate(RequestPredicate requestPredicate) {
this.requestPredicate = requestPredicate;
public Builder predicate(Predicate<ServerWebExchange> predicate) {
this.predicate = predicate;
return this;
}
@@ -107,9 +108,9 @@ public class Route {
public Route build() {
Assert.notNull(this.id, "id can not be null");
Assert.notNull(this.uri, "uri can not be null");
//TODO: Assert.notNull(this.requestPredicate, "requestPredicates can not be null");
//TODO: Assert.notNull(this.predicate, "predicate can not be null");
return new Route(this.id, this.uri, this.requestPredicate, this.webFilters);
return new Route(this.id, this.uri, this.predicate, this.webFilters);
}
}
@@ -121,8 +122,8 @@ public class Route {
return this.uri;
}
public RequestPredicate getRequestPredicate() {
return this.requestPredicate;
public Predicate<ServerWebExchange> getPredicate() {
return this.predicate;
}
public List<WebFilter> getWebFilters() {
@@ -136,13 +137,13 @@ public class Route {
Route route = (Route) o;
return Objects.equals(id, route.id) &&
Objects.equals(uri, route.uri) &&
Objects.equals(requestPredicate, route.requestPredicate) &&
Objects.equals(predicate, route.predicate) &&
Objects.equals(webFilters, route.webFilters);
}
@Override
public int hashCode() {
return Objects.hash(id, uri, requestPredicate, webFilters);
return Objects.hash(id, uri, predicate, webFilters);
}
@Override
@@ -150,7 +151,7 @@ public class Route {
final StringBuffer sb = new StringBuffer("Route{");
sb.append("id='").append(id).append('\'');
sb.append(", uri=").append(uri);
sb.append(", requestPredicates=").append(requestPredicate);
sb.append(", predicate=").append(predicate);
sb.append(", webFilters=").append(webFilters);
sb.append('}');
return sb.toString();

View File

@@ -22,6 +22,7 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
@@ -31,17 +32,13 @@ 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.cloud.gateway.handler.predicate.RoutePredicateFactory;
import org.springframework.cloud.gateway.support.ArgumentHints;
import org.springframework.cloud.gateway.support.NameUtils;
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 reactor.core.publisher.Flux;
@@ -54,31 +51,31 @@ public class RouteDefinitionRouteLocator implements RouteLocator {
protected final Log logger = LogFactory.getLog(getClass());
private final RouteDefinitionLocator routeDefinitionLocator;
private final Map<String, RequestPredicateFactory> requestPredicates = new LinkedHashMap<>();
private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>();
private final Map<String, WebFilterFactory> webFilterFactories = new HashMap<>();
private final GatewayProperties gatewayProperties;
public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
List<RequestPredicateFactory> requestPredicates,
List<RoutePredicateFactory> predicates,
List<WebFilterFactory> webFilterFactories,
GatewayProperties gatewayProperties) {
this.routeDefinitionLocator = routeDefinitionLocator;
initFactories(requestPredicates);
initFactories(predicates);
webFilterFactories.forEach(factory -> this.webFilterFactories.put(factory.name(), factory));
this.gatewayProperties = gatewayProperties;
}
private void initFactories(List<RequestPredicateFactory> requestPredicates) {
requestPredicates.forEach(factory -> {
private void initFactories(List<RoutePredicateFactory> predicates) {
predicates.forEach(factory -> {
String key = factory.name();
if (this.requestPredicates.containsKey(key)) {
this.logger.warn("A RequestPredicateFactory named "+ key
+ " already exists, class: " + this.requestPredicates.get(key)
if (this.predicates.containsKey(key)) {
this.logger.warn("A RoutePredicateFactory named "+ key
+ " already exists, class: " + this.predicates.get(key)
+ ". It will be overwritten.");
}
this.requestPredicates.put(key, factory);
this.predicates.put(key, factory);
if (logger.isInfoEnabled()) {
logger.info("Loaded RequestPredicateFactory [" + key + "]");
logger.info("Loaded RoutePredicateFactory [" + key + "]");
}
});
}
@@ -102,19 +99,16 @@ public class RouteDefinitionRouteLocator implements RouteLocator {
}*/
}
private Route convertToRoute(RouteDefinition routeDefinition) {
RequestPredicate requestPredicate = combinePredicates(routeDefinition);
Predicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
List<WebFilter> webFilters = getFilters(routeDefinition);
return Route.builder(routeDefinition)
.requestPredicate(requestPredicate)
.predicate(predicate)
.webFilters(webFilters)
.build();
}
private List<WebFilter> loadWebFilters(String id, List<FilterDefinition> filterDefinitions) {
List<WebFilter> filters = filterDefinitions.stream()
.map(definition -> {
@@ -157,7 +151,7 @@ public class RouteDefinitionRouteLocator implements RouteLocator {
for (Map.Entry<String, String> entry : args.entrySet()) {
String key = entry.getKey();
// RequestPredicateFactory has name hints and this has a fake key name
// RoutePredicateFactory 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()) {
@@ -197,22 +191,22 @@ public class RouteDefinitionRouteLocator implements RouteLocator {
return filters;
}
private RequestPredicate combinePredicates(RouteDefinition routeDefinition) {
private Predicate<ServerWebExchange> combinePredicates(RouteDefinition routeDefinition) {
List<PredicateDefinition> predicates = routeDefinition.getPredicates();
RequestPredicate predicate = lookup(routeDefinition, predicates.get(0));
Predicate<ServerWebExchange> predicate = lookup(routeDefinition, predicates.get(0));
for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) {
RequestPredicate found = lookup(routeDefinition, andPredicate);
Predicate<ServerWebExchange> found = lookup(routeDefinition, andPredicate);
predicate = predicate.and(found);
}
return predicate;
}
private RequestPredicate lookup(RouteDefinition routeDefinition, PredicateDefinition predicate) {
RequestPredicateFactory found = this.requestPredicates.get(predicate.getName());
private Predicate<ServerWebExchange> lookup(RouteDefinition routeDefinition, PredicateDefinition predicate) {
RoutePredicateFactory found = this.predicates.get(predicate.getName());
if (found == null) {
throw new IllegalArgumentException("Unable to find RequestPredicateFactory with name " + predicate.getName());
throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName());
}
Map<String, String> args = predicate.getArgs();
if (logger.isDebugEnabled()) {

View File

@@ -21,9 +21,10 @@ import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;
import org.springframework.cloud.gateway.filter.factory.WebFilterFactories;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import reactor.core.publisher.Flux;
@@ -94,13 +95,16 @@ public class Routes {
this.locatorBuilder = locatorBuilder;
}
/* TODO: has and, or & negate of RequestPredicate with terminal filters()?
public RequestPredicateBuilder host(String pattern) {
RequestPredicate requestPredicate = GatewayRequestPredicates.host(pattern);
/* TODO: has and, or & negate of Predicate with terminal andFilters()?
public RoutePredicateBuilder predicate() {
}
// this goes in new class
public RoutePredicateBuilder host(String pattern) {
Predicate<ServerWebExchange> predicate = RoutePredicates.host(pattern);
}*/
public WebFilterSpec predicate(RequestPredicate predicate) {
this.routeBuilder.requestPredicate(predicate);
public WebFilterSpec predicate(Predicate<ServerWebExchange> predicate) {
this.routeBuilder.predicate(predicate);
return webFilterBuilder();
}
@@ -138,6 +142,7 @@ public class Routes {
return add(WebFilterFactories.addResponseHeader(headerName, headerValue));
}
// TODO: build()?
public LocatorBuilder and() {
Route route = this.builder.build();
this.locatorBuilder.add(route);

View File

@@ -18,7 +18,7 @@
package org.springframework.cloud.gateway.support;
import org.springframework.cloud.gateway.filter.factory.WebFilterFactory;
import org.springframework.cloud.gateway.handler.predicate.RequestPredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
/**
* @author Spencer Gibb
@@ -30,8 +30,8 @@ public class NameUtils {
return GENERATED_NAME_PREFIX + i;
}
public static String normalizePredicateName(Class<? extends RequestPredicateFactory> clazz) {
return clazz.getSimpleName().replace(RequestPredicateFactory.class.getSimpleName(), "");
public static String normalizePredicateName(Class<? extends RoutePredicateFactory> clazz) {
return clazz.getSimpleName().replace(RoutePredicateFactory.class.getSimpleName(), "");
}
public static String normalizeFilterName(Class<? extends WebFilterFactory> clazz) {

View File

@@ -26,8 +26,12 @@ import org.springframework.web.server.ServerWebExchange;
* @author Spencer Gibb
*/
public class ServerWebExchangeUtils {
private static final Log logger = LogFactory.getLog(ServerWebExchangeUtils.class);
public static final String URI_TEMPLATE_VARIABLES_ATTRIBUTE =
ServerWebExchangeUtils.class.getName() + ".uriTemplateVariables";
public static final String CLIENT_RESPONSE_ATTR = "webHandlerClientResponse";
public static final String GATEWAY_ROUTE_ATTR = "gatewayRoute";
public static final String GATEWAY_REQUEST_URL_ATTR = "gatewayRequestUrl";

View File

@@ -24,7 +24,6 @@ import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.http.server.reactive.MockServerWebExchange;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
@@ -32,6 +31,7 @@ import org.springframework.web.server.WebFilterChain;
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.cloud.gateway.support.ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
import static org.springframework.tuple.TupleBuilder.tuple;
import reactor.core.publisher.Mono;
@@ -62,7 +62,7 @@ public class SetPathWebFilterFactoryTests {
.build();
ServerWebExchange exchange = new MockServerWebExchange(request);
exchange.getAttributes().put(RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE, variables);
exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, variables);
WebFilterChain filterChain = mock(WebFilterChain.class);

View File

@@ -20,18 +20,18 @@ package org.springframework.cloud.gateway.handler.predicate;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.gateway.handler.predicate.AfterRequestPredicateFactory.DATETIME_KEY;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRequestPredicateFactoryTests.getRequest;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRequestPredicateFactoryTests.minusHours;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRequestPredicateFactoryTests.minusHoursMillis;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRequestPredicateFactoryTests.plusHours;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRequestPredicateFactoryTests.plusHoursMillis;
import static org.springframework.cloud.gateway.handler.predicate.AfterRoutePredicateFactory.DATETIME_KEY;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.getExchange;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.minusHours;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.minusHoursMillis;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.plusHours;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.plusHoursMillis;
import static org.springframework.tuple.TupleBuilder.tuple;
/**
* @author Spencer Gibb
*/
public class AfterRequestPredicateFactoryTests {
public class AfterRoutePredicateFactoryTests {
@Test
public void beforeStringWorks() {
@@ -70,6 +70,6 @@ public class AfterRequestPredicateFactoryTests {
}
private boolean runPredicate(String dateString) {
return new AfterRequestPredicateFactory().apply(tuple().of(DATETIME_KEY, dateString)).test(getRequest());
return new AfterRoutePredicateFactory().apply(tuple().of(DATETIME_KEY, dateString)).test(getExchange());
}
}

View File

@@ -20,18 +20,18 @@ package org.springframework.cloud.gateway.handler.predicate;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.gateway.handler.predicate.BeforeRequestPredicateFactory.DATETIME_KEY;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRequestPredicateFactoryTests.getRequest;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRequestPredicateFactoryTests.minusHours;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRequestPredicateFactoryTests.minusHoursMillis;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRequestPredicateFactoryTests.plusHours;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRequestPredicateFactoryTests.plusHoursMillis;
import static org.springframework.cloud.gateway.handler.predicate.BeforeRoutePredicateFactory.DATETIME_KEY;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.getExchange;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.minusHours;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.minusHoursMillis;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.plusHours;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.plusHoursMillis;
import static org.springframework.tuple.TupleBuilder.tuple;
/**
* @author Spencer Gibb
*/
public class BeforeRequestPredicateFactoryTests {
public class BeforeRoutePredicateFactoryTests {
@Test
public void beforeStringWorks() {
@@ -70,6 +70,6 @@ public class BeforeRequestPredicateFactoryTests {
}
private boolean runPredicate(String dateString) {
return new BeforeRequestPredicateFactory().apply(tuple().of(DATETIME_KEY, dateString)).test(getRequest());
return new BeforeRoutePredicateFactory().apply(tuple().of(DATETIME_KEY, dateString)).test(getExchange());
}
}

View File

@@ -21,20 +21,19 @@ import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import org.junit.Test;
import org.springframework.cloud.gateway.handler.support.ExchangeServerRequest;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.http.server.reactive.MockServerWebExchange;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRequestPredicateFactory.DATETIME1_KEY;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRequestPredicateFactory.DATETIME2_KEY;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactory.DATETIME1_KEY;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactory.DATETIME2_KEY;
import static org.springframework.tuple.TupleBuilder.tuple;
/**
* @author Spencer Gibb
*/
public class BetweenRequestPredicateFactoryTests {
public class BetweenRoutePredicateFactoryTests {
@Test
public void beforeStringWorks() {
@@ -97,8 +96,8 @@ public class BetweenRequestPredicateFactoryTests {
}
boolean runPredicate(String dateString1, String dateString2) {
return new BetweenRequestPredicateFactory().apply(tuple()
.of(DATETIME1_KEY, dateString1, DATETIME2_KEY, dateString2)).test(getRequest());
return new BetweenRoutePredicateFactory().apply(tuple()
.of(DATETIME1_KEY, dateString1, DATETIME2_KEY, dateString2)).test(getExchange());
}
static String minusHoursMillis(int hours) {
@@ -119,8 +118,8 @@ public class BetweenRequestPredicateFactoryTests {
return ZonedDateTime.now().plusHours(hours).format(DateTimeFormatter.ISO_ZONED_DATE_TIME);
}
static ServerRequest getRequest() {
static ServerWebExchange getExchange() {
MockServerHttpRequest request = MockServerHttpRequest.get("http://example.com").build();
return new ExchangeServerRequest(new MockServerWebExchange(request));
return new MockServerWebExchange(request);
}
}

View File

@@ -22,7 +22,7 @@ import org.junit.runner.RunWith;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.handler.RequestPredicateHandlerMapping;
import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpHeaders;
@@ -41,7 +41,7 @@ import reactor.test.StepVerifier;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT)
@DirtiesContext
public class HostRequestPredicateFactoryTests extends BaseWebClientTests {
public class HostRoutePredicateFactoryTests extends BaseWebClientTests {
@Test
public void hostRouteWorks() {
@@ -56,7 +56,7 @@ public class HostRequestPredicateFactoryTests extends BaseWebClientTests {
assertStatus(response, HttpStatus.OK);
HttpHeaders httpHeaders = response.headers().asHttpHeaders();
assertThat(httpHeaders.getFirst(HANDLER_MAPPER_HEADER))
.isEqualTo(RequestPredicateHandlerMapping.class.getSimpleName());
.isEqualTo(RoutePredicateHandlerMapping.class.getSimpleName());
assertThat(httpHeaders.getFirst(ROUTE_ID_HEADER))
.isEqualTo("host_example_to_httpbin");
})

View File

@@ -22,7 +22,7 @@ import org.junit.runner.RunWith;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.handler.RequestPredicateHandlerMapping;
import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpHeaders;
@@ -41,7 +41,7 @@ import reactor.test.StepVerifier;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT)
@DirtiesContext
public class MethodRequestPredicateFactoryTests extends BaseWebClientTests {
public class MethodRoutePredicateFactoryTests extends BaseWebClientTests {
@Test
public void hostRouteWorks() {
@@ -56,7 +56,7 @@ public class MethodRequestPredicateFactoryTests extends BaseWebClientTests {
assertStatus(response, HttpStatus.OK);
HttpHeaders httpHeaders = response.headers().asHttpHeaders();
assertThat(httpHeaders.getFirst(HANDLER_MAPPER_HEADER))
.isEqualTo(RequestPredicateHandlerMapping.class.getSimpleName());
.isEqualTo(RoutePredicateHandlerMapping.class.getSimpleName());
assertThat(httpHeaders.getFirst(ROUTE_ID_HEADER))
.isEqualTo("method_test");
})

View File

@@ -22,7 +22,7 @@ import org.junit.runner.RunWith;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.handler.RequestPredicateHandlerMapping;
import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpHeaders;
@@ -41,7 +41,7 @@ import reactor.test.StepVerifier;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT)
@DirtiesContext
public class PathRequestPredicateFactoryTests extends BaseWebClientTests {
public class PathRoutePredicateFactoryTests extends BaseWebClientTests {
@Test
public void pathRouteWorks() {
@@ -55,7 +55,7 @@ public class PathRequestPredicateFactoryTests extends BaseWebClientTests {
assertStatus(response, HttpStatus.OK);
HttpHeaders httpHeaders = response.headers().asHttpHeaders();
assertThat(httpHeaders.getFirst(HANDLER_MAPPER_HEADER))
.isEqualTo(RequestPredicateHandlerMapping.class.getSimpleName());
.isEqualTo(RoutePredicateHandlerMapping.class.getSimpleName());
assertThat(httpHeaders.getFirst(ROUTE_ID_HEADER))
.isEqualTo("default_path_to_httpbin");
})

View File

@@ -34,12 +34,12 @@ import org.springframework.cloud.gateway.filter.factory.SetPathWebFilterFactoryI
import org.springframework.cloud.gateway.filter.factory.SetPathWebFilterFactoryTests;
import org.springframework.cloud.gateway.filter.factory.SetResponseWebFilterFactoryTests;
import org.springframework.cloud.gateway.filter.factory.SetStatusWebFilterFactoryTests;
import org.springframework.cloud.gateway.handler.predicate.AfterRequestPredicateFactoryTests;
import org.springframework.cloud.gateway.handler.predicate.BeforeRequestPredicateFactoryTests;
import org.springframework.cloud.gateway.handler.predicate.BetweenRequestPredicateFactoryTests;
import org.springframework.cloud.gateway.handler.predicate.HostRequestPredicateFactoryTests;
import org.springframework.cloud.gateway.handler.predicate.MethodRequestPredicateFactoryTests;
import org.springframework.cloud.gateway.handler.predicate.PathRequestPredicateFactoryTests;
import org.springframework.cloud.gateway.handler.predicate.AfterRoutePredicateFactoryTests;
import org.springframework.cloud.gateway.handler.predicate.BeforeRoutePredicateFactoryTests;
import org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests;
import org.springframework.cloud.gateway.handler.predicate.HostRoutePredicateFactoryTests;
import org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactoryTests;
import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactoryTests;
/**
* @author Spencer Gibb
@@ -62,13 +62,13 @@ import org.springframework.cloud.gateway.handler.predicate.PathRequestPredicateF
SetResponseWebFilterFactoryTests.class,
SetStatusWebFilterFactoryTests.class,
RewritePathWebFilterFactoryTests.class,
// RequestPredicateFactory tests
AfterRequestPredicateFactoryTests.class,
BeforeRequestPredicateFactoryTests.class,
BetweenRequestPredicateFactoryTests.class,
HostRequestPredicateFactoryTests.class,
MethodRequestPredicateFactoryTests.class,
PathRequestPredicateFactoryTests.class,
// RoutePredicateFactory tests
AfterRoutePredicateFactoryTests.class,
BeforeRoutePredicateFactoryTests.class,
BetweenRoutePredicateFactoryTests.class,
HostRoutePredicateFactoryTests.class,
MethodRoutePredicateFactoryTests.class,
PathRoutePredicateFactoryTests.class,
})
public class AdhocTestSuite {
}

View File

@@ -29,7 +29,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.handler.RequestPredicateHandlerMapping;
import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
@@ -93,7 +93,7 @@ public class GatewayIntegrationTests extends BaseWebClientTests {
assertStatus(response, HttpStatus.OK);
HttpHeaders httpHeaders = response.headers().asHttpHeaders();
assertThat(httpHeaders.getFirst(HANDLER_MAPPER_HEADER))
.isEqualTo(RequestPredicateHandlerMapping.class.getSimpleName());
.isEqualTo(RoutePredicateHandlerMapping.class.getSimpleName());
assertThat(httpHeaders.getFirst(ROUTE_ID_HEADER))
.isEqualTo("host_foo_path_headers_to_httpbin");
assertThat(httpHeaders.getFirst("X-Response-Foo"))

View File

View File

@@ -26,9 +26,8 @@ import org.springframework.cloud.gateway.route.Routes;
import org.springframework.context.annotation.Bean;
import static org.springframework.cloud.gateway.filter.factory.WebFilterFactories.addResponseHeader;
import static org.springframework.cloud.gateway.handler.predicate.GatewayRequestPredicates.host;
import static org.springframework.cloud.gateway.handler.predicate.GatewayRequestPredicates.path;
// import static org.springframework.web.reactive.function.server.RequestPredicates.path;
import static org.springframework.cloud.gateway.handler.predicate.RoutePredicates.host;
import static org.springframework.cloud.gateway.handler.predicate.RoutePredicates.path;
/**
* @author Spencer Gibb