Migrate Routes statics to RouteLocatorBuilder bean.
This allows flexibility with RoutePredicateFactorys and GatewayFilterFactories that require dependencies to be injected.
This commit is contained in:
@@ -80,6 +80,8 @@ import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
@@ -141,6 +143,11 @@ public class GatewayAutoConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RouteLocatorBuilder routeLocatorBuilder(ConfigurableApplicationContext context) {
|
||||
return new RouteLocatorBuilder(context);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {
|
||||
@@ -223,7 +230,7 @@ public class GatewayAutoConfiguration {
|
||||
//TODO: default over netty? configurable
|
||||
public WebClientHttpRoutingFilter webClientHttpRoutingFilter() {
|
||||
//TODO: WebClient bean
|
||||
return new WebClientHttpRoutingFilter(WebClient.builder().build());
|
||||
return new WebClientHttpRoutingFilter(WebClient.routes().build());
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -30,8 +30,10 @@ import com.netflix.hystrix.HystrixObservableCommand;
|
||||
import static org.springframework.tuple.TupleBuilder.tuple;
|
||||
|
||||
/**
|
||||
* @deprecated inject {@link org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder} bean instead
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Deprecated
|
||||
public class GatewayFilters {
|
||||
|
||||
public static final Tuple EMPTY_TUPLE = tuple().build();
|
||||
|
||||
@@ -63,11 +63,14 @@ public class RemoveNonProxyHeadersGatewayFilterFactory implements GatewayFilterF
|
||||
@Override
|
||||
public GatewayFilter apply(Tuple args) {
|
||||
//TODO: support filter args
|
||||
return apply(this.headers);
|
||||
}
|
||||
|
||||
public GatewayFilter apply(List<String> headersToRemove) {
|
||||
return (exchange, chain) -> {
|
||||
ServerHttpRequest request = exchange.getRequest().mutate()
|
||||
.headers(httpHeaders -> {
|
||||
for (String header : this.headers) {
|
||||
for (String header : headersToRemove) {
|
||||
httpHeaders.remove(header);
|
||||
}
|
||||
})
|
||||
|
||||
@@ -39,6 +39,10 @@ public class RequestRateLimiterGatewayFilterFactory implements GatewayFilterFact
|
||||
this.defaultKeyResolver = defaultKeyResolver;
|
||||
}
|
||||
|
||||
public KeyResolver getDefaultKeyResolver() {
|
||||
return defaultKeyResolver;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public GatewayFilter apply(Tuple args) {
|
||||
|
||||
@@ -18,25 +18,17 @@
|
||||
package org.springframework.cloud.gateway.handler.predicate;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
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.cloud.gateway.handler.predicate.MethodRoutePredicateFactory.METHOD_KEY;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory.PATTERN_KEY;
|
||||
import static org.springframework.tuple.TupleBuilder.tuple;
|
||||
|
||||
/**
|
||||
* @deprecated inject {@link org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder} bean instead
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Deprecated
|
||||
public class RoutePredicates {
|
||||
|
||||
public static Predicate<ServerWebExchange> after(ZonedDateTime datetime) {
|
||||
|
||||
@@ -102,6 +102,23 @@ public class Route implements Ordered {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder and(Predicate<ServerWebExchange> predicate) {
|
||||
Assert.notNull(this.predicate, "can not call and() on null predicate");
|
||||
this.predicate = this.predicate.and(predicate);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder or(Predicate<ServerWebExchange> predicate) {
|
||||
Assert.notNull(this.predicate, "can not call or() on null predicate");
|
||||
this.predicate = this.predicate.or(predicate);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder negate() {
|
||||
Assert.notNull(this.predicate, "can not call negate() on null predicate");
|
||||
this.predicate = this.predicate.negate();
|
||||
return this;
|
||||
}
|
||||
public Builder gatewayFilters(List<GatewayFilter> gatewayFilters) {
|
||||
this.gatewayFilters = gatewayFilters;
|
||||
return this;
|
||||
@@ -120,7 +137,7 @@ public class Route implements Ordered {
|
||||
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.predicate, "predicate can not be null");
|
||||
Assert.notNull(this.predicate, "predicate can not be null");
|
||||
|
||||
return new Route(this.id, this.uri, this.order, this.predicate, this.gatewayFilters);
|
||||
}
|
||||
|
||||
@@ -31,8 +31,10 @@ import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @deprecated inject {@link org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder} bean instead
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Deprecated
|
||||
public class Routes {
|
||||
|
||||
public static LocatorBuilder locator() {
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.route.builder;
|
||||
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static org.springframework.cloud.gateway.route.builder.BooleanSpec.Operator.*;
|
||||
|
||||
public class BooleanSpec extends GatewayFilterSpec {
|
||||
|
||||
enum Operator { AND, OR, NEGATE }
|
||||
|
||||
public BooleanSpec(Route.Builder routeBuilder, RouteLocatorBuilder.Builder builder) {
|
||||
super(routeBuilder, builder);
|
||||
}
|
||||
|
||||
public BooleanOpSpec and() {
|
||||
return new BooleanOpSpec(routeBuilder, builder, AND);
|
||||
}
|
||||
|
||||
public BooleanOpSpec or() {
|
||||
return new BooleanOpSpec(routeBuilder, builder, OR);
|
||||
}
|
||||
|
||||
public BooleanOpSpec negate() {
|
||||
return new BooleanOpSpec(routeBuilder, builder, NEGATE);
|
||||
}
|
||||
|
||||
public static class BooleanOpSpec extends PredicateSpec {
|
||||
|
||||
private Operator operator;
|
||||
|
||||
BooleanOpSpec(Route.Builder routeBuilder, RouteLocatorBuilder.Builder builder, Operator operator) {
|
||||
super(routeBuilder, builder);
|
||||
Assert.notNull(operator, "operator may not be null");
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BooleanSpec predicate(Predicate<ServerWebExchange> predicate) {
|
||||
switch (this.operator) {
|
||||
case AND:
|
||||
this.routeBuilder.and(predicate);
|
||||
break;
|
||||
case OR:
|
||||
this.routeBuilder.or(predicate);
|
||||
break;
|
||||
case NEGATE:
|
||||
this.routeBuilder.negate();
|
||||
}
|
||||
return gatewayFilterBuilder();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* 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.route.builder;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.AddRequestHeaderGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.AddRequestParameterGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.AddResponseHeaderGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.HystrixGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.PrefixPathGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.RedirectToGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.RemoveNonProxyHeadersGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.RemoveRequestHeaderGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.RemoveResponseHeaderGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.SecureHeadersGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.SetPathGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.SetResponseHeaderGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.SetStatusGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.tuple.Tuple;
|
||||
|
||||
import com.netflix.hystrix.HystrixObservableCommand;
|
||||
|
||||
import static org.springframework.tuple.TupleBuilder.tuple;
|
||||
|
||||
public class GatewayFilterSpec extends UriSpec {
|
||||
|
||||
static final Tuple EMPTY_TUPLE = tuple().build();
|
||||
|
||||
public GatewayFilterSpec(Route.Builder routeBuilder, RouteLocatorBuilder.Builder builder) {
|
||||
super(routeBuilder, builder);
|
||||
}
|
||||
|
||||
public GatewayFilterSpec gatewayFilters(List<GatewayFilter> gatewayFilters) {
|
||||
this.addAll(gatewayFilters);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GatewayFilterSpec add(GatewayFilter gatewayFilter) {
|
||||
return this.filter(gatewayFilter);
|
||||
}
|
||||
|
||||
public GatewayFilterSpec filter(GatewayFilter gatewayFilter) {
|
||||
return this.filter(gatewayFilter, 0);
|
||||
}
|
||||
|
||||
public GatewayFilterSpec filter(GatewayFilter gatewayFilter, int order) {
|
||||
this.routeBuilder.add(new OrderedGatewayFilter(gatewayFilter, order));
|
||||
return this;
|
||||
}
|
||||
|
||||
public GatewayFilterSpec addAll(Collection<GatewayFilter> gatewayFilters) {
|
||||
this.routeBuilder.addAll(gatewayFilters);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public GatewayFilterSpec addRequestHeader(String headerName, String headerValue) {
|
||||
return filter(getBean(AddRequestHeaderGatewayFilterFactory.class).apply(headerName, headerValue));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec addRequestParameter(String param, String value) {
|
||||
return filter(getBean(AddRequestParameterGatewayFilterFactory.class).apply(param, value));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec addResponseHeader(String headerName, String headerValue) {
|
||||
return filter(getBean(AddResponseHeaderGatewayFilterFactory.class).apply(headerName, headerValue));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec hystrix(String commandName) {
|
||||
return filter(getBean(HystrixGatewayFilterFactory.class).apply(commandName));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec hystrix(HystrixObservableCommand.Setter setter) {
|
||||
return filter(getBean(HystrixGatewayFilterFactory.class).apply(setter));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec prefixPath(String prefix) {
|
||||
return filter(getBean(PrefixPathGatewayFilterFactory.class).apply(prefix));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec redirect(int status, URI url) {
|
||||
return redirect(String.valueOf(status), url.toString());
|
||||
}
|
||||
|
||||
public GatewayFilterSpec redirect(int status, String url) {
|
||||
return redirect(String.valueOf(status), url);
|
||||
}
|
||||
|
||||
public GatewayFilterSpec redirect(String status, URI url) {
|
||||
return redirect(status, url.toString());
|
||||
}
|
||||
|
||||
public GatewayFilterSpec redirect(String status, String url) {
|
||||
return filter(getBean(RedirectToGatewayFilterFactory.class).apply(status, url));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec redirect(HttpStatus status, URL url) {
|
||||
return filter(getBean(RedirectToGatewayFilterFactory.class).apply(status, url));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec removeNonProxyHeaders() {
|
||||
return filter(getBean(RemoveNonProxyHeadersGatewayFilterFactory.class).apply(EMPTY_TUPLE));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec removeNonProxyHeaders(String... headersToRemove) {
|
||||
return filter(getBean(RemoveNonProxyHeadersGatewayFilterFactory.class).apply(Arrays.asList(headersToRemove)));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec removeRequestHeader(String headerName) {
|
||||
return filter(getBean(RemoveRequestHeaderGatewayFilterFactory.class).apply(headerName));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec removeResponseHeader(String headerName) {
|
||||
return filter(getBean(RemoveResponseHeaderGatewayFilterFactory.class).apply(headerName));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec requestRateLimiter(Tuple args) {
|
||||
RequestRateLimiterGatewayFilterFactory factory = getBean(RequestRateLimiterGatewayFilterFactory.class);
|
||||
KeyResolver keyResolver;
|
||||
try {
|
||||
keyResolver = getBean(KeyResolver.class);
|
||||
} catch (NoSuchBeanDefinitionException e) {
|
||||
keyResolver = factory.getDefaultKeyResolver();
|
||||
}
|
||||
return filter(factory.apply(keyResolver, args));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec rewritePath(String regex, String replacement) {
|
||||
return filter(getBean(RewritePathGatewayFilterFactory.class).apply(regex, replacement));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec secureHeaders() {
|
||||
return filter(getBean(SecureHeadersGatewayFilterFactory.class).apply(EMPTY_TUPLE));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec setPath(String template) {
|
||||
return filter(getBean(SetPathGatewayFilterFactory.class).apply(template));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec setResponseHeader(String headerName, String headerValue) {
|
||||
return filter(getBean(SetResponseHeaderGatewayFilterFactory.class).apply(headerName, headerValue));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec setStatus(int status) {
|
||||
return setStatus(String.valueOf(status));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec setStatus(String status) {
|
||||
return filter(getBean(SetStatusGatewayFilterFactory.class).apply(status));
|
||||
}
|
||||
|
||||
public GatewayFilterSpec setStatus(HttpStatus status) {
|
||||
return filter(getBean(SetStatusGatewayFilterFactory.class).apply(status));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.route.builder;
|
||||
|
||||
import org.springframework.cloud.gateway.handler.predicate.AfterRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.BeforeRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.CookieRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.HeaderRoutePredicateFactory;
|
||||
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.QueryRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicateFactory;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class PredicateSpec extends UriSpec {
|
||||
|
||||
PredicateSpec(Route.Builder routeBuilder, RouteLocatorBuilder.Builder builder) {
|
||||
super(routeBuilder, builder);
|
||||
}
|
||||
|
||||
public PredicateSpec order(int order) {
|
||||
this.routeBuilder.order(order);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BooleanSpec predicate(Predicate<ServerWebExchange> predicate) {
|
||||
this.routeBuilder.predicate(predicate);
|
||||
return gatewayFilterBuilder();
|
||||
}
|
||||
|
||||
protected BooleanSpec gatewayFilterBuilder() {
|
||||
return new BooleanSpec(this.routeBuilder, this.builder);
|
||||
}
|
||||
|
||||
public BooleanSpec after(ZonedDateTime datetime) {
|
||||
return predicate(getBean(AfterRoutePredicateFactory.class).apply(datetime));
|
||||
}
|
||||
|
||||
public BooleanSpec before(ZonedDateTime datetime) {
|
||||
return predicate(getBean(BeforeRoutePredicateFactory.class).apply(datetime));
|
||||
}
|
||||
|
||||
public BooleanSpec between(ZonedDateTime datetime1, ZonedDateTime datetime2) {
|
||||
return predicate(getBean(BetweenRoutePredicateFactory.class).apply(datetime1, datetime2));
|
||||
}
|
||||
|
||||
public BooleanSpec cookie(String name, String regex) {
|
||||
return predicate(getBean(CookieRoutePredicateFactory.class).apply(name, regex));
|
||||
}
|
||||
|
||||
public BooleanSpec header(String header, String regex) {
|
||||
return predicate(getBean(HeaderRoutePredicateFactory.class).apply(header, regex));
|
||||
}
|
||||
|
||||
public BooleanSpec host(String pattern) {
|
||||
return predicate(getBean(HostRoutePredicateFactory.class).apply(pattern));
|
||||
}
|
||||
|
||||
public BooleanSpec method(String method) {
|
||||
return predicate(getBean(MethodRoutePredicateFactory.class).apply(method));
|
||||
}
|
||||
|
||||
public BooleanSpec method(HttpMethod method) {
|
||||
return predicate(getBean(MethodRoutePredicateFactory.class).apply(method));
|
||||
}
|
||||
|
||||
public BooleanSpec path(String pattern) {
|
||||
return predicate(getBean(PathRoutePredicateFactory.class).apply(pattern));
|
||||
}
|
||||
|
||||
public BooleanSpec query(String param, String regex) {
|
||||
return predicate(getBean(QueryRoutePredicateFactory.class).apply(param, regex));
|
||||
}
|
||||
|
||||
public BooleanSpec query(String param) {
|
||||
return predicate(getBean(QueryRoutePredicateFactory.class).apply(param, null));
|
||||
}
|
||||
|
||||
public BooleanSpec remoteAddr(String... addrs) {
|
||||
return predicate(getBean(RemoteAddrRoutePredicateFactory.class).apply(addrs));
|
||||
}
|
||||
|
||||
public BooleanSpec alwaysTrue() {
|
||||
return predicate(exchange -> true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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.route.builder;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
public class RouteLocatorBuilder {
|
||||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
public RouteLocatorBuilder(ConfigurableApplicationContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public Builder routes() {
|
||||
return new Builder(context);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private List<Route> routes = new ArrayList<>();
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
public Builder(ConfigurableApplicationContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public PredicateSpec route(String id) {
|
||||
return new RouteSpec(this).id(id);
|
||||
}
|
||||
|
||||
private void add(Route route) {
|
||||
this.routes.add(route);
|
||||
}
|
||||
|
||||
Builder uri(Route.Builder builder, String uri) {
|
||||
Route route = builder.uri(uri).build();
|
||||
routes.add(route);
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder uri(Route.Builder builder, URI uri) {
|
||||
Route route = builder.uri(uri).build();
|
||||
routes.add(route);
|
||||
return this;
|
||||
}
|
||||
|
||||
public RouteLocator build() {
|
||||
return () -> Flux.fromIterable(this.routes);
|
||||
}
|
||||
|
||||
ConfigurableApplicationContext getContext() {
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class RouteSpec {
|
||||
private final Route.Builder routeBuilder = Route.builder();
|
||||
private final Builder builder;
|
||||
|
||||
private RouteSpec(Builder builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
public PredicateSpec id(String id) {
|
||||
this.routeBuilder.id(id);
|
||||
return predicateBuilder();
|
||||
}
|
||||
|
||||
private PredicateSpec predicateBuilder() {
|
||||
return new PredicateSpec(this.routeBuilder, this.builder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.route.builder;
|
||||
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
public class UriSpec {
|
||||
final Route.Builder routeBuilder;
|
||||
final RouteLocatorBuilder.Builder builder;
|
||||
|
||||
UriSpec(Route.Builder routeBuilder, RouteLocatorBuilder.Builder builder) {
|
||||
this.routeBuilder = routeBuilder;
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
public RouteLocatorBuilder.Builder uri(String uri) {
|
||||
return this.builder.uri(this.routeBuilder, uri);
|
||||
}
|
||||
|
||||
public RouteLocatorBuilder.Builder uri(URI uri) {
|
||||
return this.builder.uri(this.routeBuilder, uri);
|
||||
}
|
||||
|
||||
|
||||
<T> T getBean(Class<T> type) {
|
||||
return this.builder.getContext().getBean(type);
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,11 @@ class RouteLocatorDsl {
|
||||
* A helper to return a composed [Predicate] that tests against this [Predicate] OR the [other] predicate
|
||||
*/
|
||||
infix fun <T> Predicate<T>.or(other: Predicate<T>) = this.or(other)
|
||||
|
||||
/**
|
||||
* A helper to return a composed [Predicate] that negates this [Predicate]
|
||||
*/
|
||||
fun <T> Predicate<T>.negate() = this.negate()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -14,9 +14,8 @@ import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.route.Routes;
|
||||
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
|
||||
@@ -32,10 +31,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT;
|
||||
import static org.springframework.cloud.gateway.filter.factory.GatewayFilters.prefixPath;
|
||||
import static org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter.BURST_CAPACITY_KEY;
|
||||
import static org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter.REPLENISH_RATE_KEY;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.RoutePredicates.path;
|
||||
import static org.springframework.tuple.TupleBuilder.tuple;
|
||||
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;
|
||||
|
||||
@@ -96,12 +91,12 @@ public class PrincipalNameKeyResolverIntegrationTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RouteLocator customRouteLocator(RequestRateLimiterGatewayFilterFactory rateLimiterFactory) {
|
||||
return Routes.locator()
|
||||
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
|
||||
return builder.routes()
|
||||
.route("protected-throttled")
|
||||
.predicate(path("/myapi/**"))
|
||||
.filter(rateLimiterFactory.apply(tuple().of(REPLENISH_RATE_KEY, 1, BURST_CAPACITY_KEY, 1)))
|
||||
.filter(prefixPath("/downstream"))
|
||||
.path("/myapi/**")
|
||||
.requestRateLimiter(tuple().build())
|
||||
.prefixPath("/downstream")
|
||||
.uri("http://localhost:"+port)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.route.Routes;
|
||||
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
|
||||
import org.springframework.cloud.gateway.test.PermitAllSecurityConfiguration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.Lifecycle;
|
||||
@@ -285,11 +285,11 @@ public class WebSocketIntegrationTests {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: workaround for suspected RxNetty WebSocket client issue
|
||||
// https://github.com/ReactiveX/RxNetty/issues/560
|
||||
|
||||
private static Mono<Void> doSend(WebSocketSession session, Publisher<WebSocketMessage> output) {
|
||||
return session.send(Mono.delay(Duration.ofMillis(100)).thenMany(output));
|
||||
return session.send(output);
|
||||
// workaround for suspected RxNetty WebSocket client issue
|
||||
// https://github.com/ReactiveX/RxNetty/issues/560
|
||||
// return session.send(Mono.delay(Duration.ofMillis(100)).thenMany(output));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -301,10 +301,10 @@ public class WebSocketIntegrationTests {
|
||||
private int wsPort;
|
||||
|
||||
@Bean
|
||||
public RouteLocator wsRouteLocator() {
|
||||
return Routes.locator()
|
||||
public RouteLocator wsRouteLocator(RouteLocatorBuilder builder) {
|
||||
return builder.routes()
|
||||
.route("testws")
|
||||
.predicate(alwaysTrue())
|
||||
.alwaysTrue()
|
||||
.uri("ws://localhost:"+this.wsPort)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -17,11 +17,13 @@
|
||||
|
||||
package org.springframework.cloud.gateway.sample;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.route.Routes;
|
||||
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
@@ -30,10 +32,8 @@ import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
|
||||
import static org.springframework.cloud.gateway.filter.factory.GatewayFilters.addResponseHeader;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.RoutePredicates.host;
|
||||
import static org.springframework.cloud.gateway.handler.predicate.RoutePredicates.path;
|
||||
import static org.springframework.tuple.TupleBuilder.tuple;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
@@ -44,24 +44,24 @@ import static org.springframework.tuple.TupleBuilder.tuple;
|
||||
public class GatewaySampleApplication {
|
||||
|
||||
@Bean
|
||||
public RouteLocator customRouteLocator(ThrottleGatewayFilterFactory throttle) {
|
||||
public RouteLocator customRouteLocator(RouteLocatorBuilder builder, ThrottleGatewayFilterFactory throttle) {
|
||||
//@formatter:off
|
||||
return Routes.locator()
|
||||
return builder.routes()
|
||||
.route("test")
|
||||
.predicate(host("**.abc.org").and(path("/image/png")))
|
||||
.host("**.abc.org").and().path("/image/png")
|
||||
.addResponseHeader("X-TestHeader", "foobar")
|
||||
.uri("http://httpbin.org:80")
|
||||
.route("test2")
|
||||
.predicate(path("/image/webp"))
|
||||
.add(addResponseHeader("X-AnotherHeader", "baz"))
|
||||
.path("/image/webp")
|
||||
.addResponseHeader("X-AnotherHeader", "baz")
|
||||
.uri("http://httpbin.org:80")
|
||||
.route("test3")
|
||||
.order(-1)
|
||||
.predicate(host("**.throttle.org").and(path("/get")))
|
||||
.add(throttle.apply(tuple().of("capacity", 1,
|
||||
"refillTokens", 1,
|
||||
"refillPeriod", 10,
|
||||
"refillUnit", "SECONDS")))
|
||||
.add(throttle.apply(1,
|
||||
1,
|
||||
10,
|
||||
TimeUnit.SECONDS))
|
||||
.uri("http://httpbin.org:80")
|
||||
.build();
|
||||
////@formatter:on
|
||||
|
||||
@@ -41,6 +41,10 @@ public class ThrottleGatewayFilterFactory implements GatewayFilterFactory {
|
||||
int refillTokens = args.getInt("refillTokens");
|
||||
int refillPeriod = args.getInt("refillPeriod");
|
||||
TimeUnit refillUnit = TimeUnit.valueOf(args.getString("refillUnit"));
|
||||
return apply(capacity, refillTokens, refillPeriod, refillUnit);
|
||||
}
|
||||
|
||||
public GatewayFilter apply(int capacity, int refillTokens, int refillPeriod, TimeUnit refillUnit) {
|
||||
|
||||
final TokenBucket tokenBucket = TokenBuckets.builder()
|
||||
.withCapacity(capacity)
|
||||
|
||||
@@ -22,17 +22,9 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
/**
|
||||
@@ -45,28 +37,31 @@ public class GatewaySampleApplicationTests {
|
||||
@LocalServerPort
|
||||
protected int port = 0;
|
||||
|
||||
protected WebClient webClient;
|
||||
protected WebTestClient webClient;
|
||||
protected String baseUri;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
baseUri = "http://localhost:" + port;
|
||||
this.webClient = WebClient.create(baseUri);
|
||||
this.webClient = WebTestClient.bindToServer().baseUrl(baseUri).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
Mono<ClientResponse> result = webClient.get()
|
||||
webClient.get()
|
||||
.uri("/get")
|
||||
.exchange();
|
||||
.exchange()
|
||||
.expectStatus().isOk();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void complexPredicate() {
|
||||
webClient.get()
|
||||
.uri("/image/png")
|
||||
.header("Host", "www.abc.org")
|
||||
.exchange()
|
||||
.expectHeader().valueEquals("X-TestHeader", "foobar")
|
||||
.expectStatus().isOk();
|
||||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(
|
||||
response -> {
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
HttpHeaders httpHeaders = response.headers().asHttpHeaders();
|
||||
})
|
||||
.expectComplete()
|
||||
.verify(Duration.ofSeconds(5));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user