Move resolution of Factories to RouteLocator
This commit is contained in:
@@ -25,8 +25,8 @@ import java.util.Optional;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.gateway.api.RouteLocator;
|
||||
import org.springframework.cloud.gateway.api.RouteWriter;
|
||||
import org.springframework.cloud.gateway.api.RouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.api.RouteDefinitionWriter;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.WebFilterFactory;
|
||||
import org.springframework.cloud.gateway.handler.FilteringWebHandler;
|
||||
@@ -58,22 +58,22 @@ public class GatewayEndpoint implements ApplicationEventPublisherAware {/*extend
|
||||
|
||||
private static final Log log = LogFactory.getLog(GatewayEndpoint.class);
|
||||
|
||||
private RouteLocator routeLocator;
|
||||
private RouteDefinitionLocator routeDefinitionLocator;
|
||||
private List<GlobalFilter> globalFilters;
|
||||
private List<WebFilterFactory> webFilterFactories;
|
||||
private FilteringWebHandler filteringWebHandler;
|
||||
private RouteWriter routeWriter;
|
||||
private RouteDefinitionWriter routeDefinitionWriter;
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
public GatewayEndpoint(RouteLocator routeLocator, List<GlobalFilter> globalFilters,
|
||||
public GatewayEndpoint(RouteDefinitionLocator routeDefinitionLocator, List<GlobalFilter> globalFilters,
|
||||
List<WebFilterFactory> webFilterFactories, FilteringWebHandler filteringWebHandler,
|
||||
RouteWriter routeWriter) {
|
||||
RouteDefinitionWriter routeDefinitionWriter) {
|
||||
//super("gateway");
|
||||
this.routeLocator = routeLocator;
|
||||
this.routeDefinitionLocator = routeDefinitionLocator;
|
||||
this.globalFilters = globalFilters;
|
||||
this.webFilterFactories = webFilterFactories;
|
||||
this.filteringWebHandler = filteringWebHandler;
|
||||
this.routeWriter = routeWriter;
|
||||
this.routeDefinitionWriter = routeDefinitionWriter;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,7 +119,7 @@ public class GatewayEndpoint implements ApplicationEventPublisherAware {/*extend
|
||||
|
||||
@GetMapping("/routes")
|
||||
public Mono<List<RouteDefinition>> routes() {
|
||||
return this.routeLocator.getRoutes().collectList();
|
||||
return this.routeDefinitionLocator.getRouteDefinitions().collectList();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -127,7 +127,7 @@ http POST :8080/admin/gateway/routes/apiaddreqhead uri=http://httpbin.org:80 pre
|
||||
*/
|
||||
@PostMapping("/routes/{id}")
|
||||
public Mono<ResponseEntity<Void>> save(@PathVariable String id, @RequestBody Mono<RouteDefinition> route) {
|
||||
return this.routeWriter.save(route.map(r -> {
|
||||
return this.routeDefinitionWriter.save(route.map(r -> {
|
||||
r.setId(id);
|
||||
log.debug("Saving route: " + route);
|
||||
return r;
|
||||
@@ -138,14 +138,14 @@ http POST :8080/admin/gateway/routes/apiaddreqhead uri=http://httpbin.org:80 pre
|
||||
|
||||
@DeleteMapping("/routes/{id}")
|
||||
public Mono<ResponseEntity<Object>> delete(@PathVariable String id) {
|
||||
return this.routeWriter .delete(Mono.just(id))
|
||||
return this.routeDefinitionWriter.delete(Mono.just(id))
|
||||
.then(() -> Mono.just(ResponseEntity.ok().build()))
|
||||
.otherwise(t -> t instanceof NotFoundException, t -> Mono.just(ResponseEntity.notFound().build()));
|
||||
}
|
||||
|
||||
@GetMapping("/routes/{id}")
|
||||
public Mono<ResponseEntity<RouteDefinition>> route(@PathVariable String id) {
|
||||
return this.routeLocator.getRoutes()
|
||||
return this.routeDefinitionLocator.getRouteDefinitions()
|
||||
.filter(route -> route.getId().equals(id))
|
||||
.singleOrEmpty()
|
||||
.map(route -> ResponseEntity.ok(route))
|
||||
@@ -154,10 +154,11 @@ http POST :8080/admin/gateway/routes/apiaddreqhead uri=http://httpbin.org:80 pre
|
||||
|
||||
@GetMapping("/routes/{id}/combinedfilters")
|
||||
public Map<String, Object> combinedfilters(@PathVariable String id) {
|
||||
Mono<RouteDefinition> route = this.routeLocator.getRoutes()
|
||||
Mono<RouteDefinition> route = this.routeDefinitionLocator.getRouteDefinitions()
|
||||
.filter(r -> r.getId().equals(id))
|
||||
.singleOrEmpty();
|
||||
Optional<RouteDefinition> optional = Optional.ofNullable(route.block()); //TODO: remove block();
|
||||
return getNamesToOrders(this.filteringWebHandler.combineFiltersForRoute(optional));
|
||||
//FIXME: return getNamesToOrders(this.filteringWebHandler.combineFiltersForRoute(optional));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.api;
|
||||
|
||||
import org.springframework.cloud.gateway.model.RouteDefinition;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public interface RouteDefinitionLocator {
|
||||
|
||||
Flux<RouteDefinition> getRouteDefinitions();
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import reactor.core.publisher.Mono;
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public interface RouteWriter {
|
||||
public interface RouteDefinitionWriter {
|
||||
|
||||
Mono<Void> save(Mono<RouteDefinition> route);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
package org.springframework.cloud.gateway.api;
|
||||
|
||||
import org.springframework.cloud.gateway.model.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.model.Route;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
@@ -25,5 +25,5 @@ import reactor.core.publisher.Flux;
|
||||
*/
|
||||
public interface RouteLocator {
|
||||
|
||||
Flux<RouteDefinition> getRoutes();
|
||||
Flux<Route> getRoutes();
|
||||
}
|
||||
|
||||
@@ -26,8 +26,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.gateway.actuate.GatewayEndpoint;
|
||||
import org.springframework.cloud.gateway.api.RouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.api.RouteDefinitionWriter;
|
||||
import org.springframework.cloud.gateway.api.RouteLocator;
|
||||
import org.springframework.cloud.gateway.api.RouteWriter;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter;
|
||||
import org.springframework.cloud.gateway.filter.NettyRoutingFilter;
|
||||
@@ -64,7 +65,8 @@ import org.springframework.cloud.gateway.handler.predicate.QueryRequestPredicate
|
||||
import org.springframework.cloud.gateway.handler.predicate.RemoteAddrRequestPredicateFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.RequestPredicateFactory;
|
||||
import org.springframework.cloud.gateway.support.CachingRouteLocator;
|
||||
import org.springframework.cloud.gateway.support.InMemoryRouteRepository;
|
||||
import org.springframework.cloud.gateway.support.DefaultRouteLocator;
|
||||
import org.springframework.cloud.gateway.support.InMemoryRouteDefinitionRepository;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@@ -105,23 +107,31 @@ public class GatewayAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(RouteLocator.class)
|
||||
public RouteLocator routeLocator(GatewayProperties properties) {
|
||||
//TODO: how to automatically apply CachingRouteLocator
|
||||
return new CachingRouteLocator(new PropertiesRouteLocator(properties));
|
||||
// @ConditionalOnMissingBean(RouteDefinitionLocator.class)
|
||||
public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {
|
||||
return new PropertiesRouteDefinitionLocator(properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilteringWebHandler filteringWebHandler(GatewayProperties properties, List<GlobalFilter> globalFilters,
|
||||
List<WebFilterFactory> webFilterFactories) {
|
||||
return new FilteringWebHandler(properties, globalFilters, webFilterFactories);
|
||||
@ConditionalOnMissingBean
|
||||
public RouteLocator defaultRouteLocator(GatewayProperties properties, List<GlobalFilter> globalFilters,
|
||||
List<WebFilterFactory> webFilterFactories,
|
||||
List<RequestPredicateFactory> predicates,
|
||||
RouteDefinitionLocator routeDefinitionLocator) {
|
||||
return new CachingRouteLocator(new DefaultRouteLocator(properties, routeDefinitionLocator,
|
||||
predicates, globalFilters, webFilterFactories));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilteringWebHandler filteringWebHandler(GatewayProperties properties,
|
||||
RouteLocator routeLocator) {
|
||||
return new FilteringWebHandler(properties, routeLocator);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RequestPredicateHandlerMapping requestPredicateHandlerMapping(FilteringWebHandler webHandler,
|
||||
List<RequestPredicateFactory> predicates,
|
||||
RouteLocator routeLocator) {
|
||||
return new RequestPredicateHandlerMapping(webHandler, predicates, routeLocator);
|
||||
return new RequestPredicateHandlerMapping(webHandler, routeLocator);
|
||||
}
|
||||
|
||||
// ConfigurationProperty beans
|
||||
@@ -288,8 +298,8 @@ public class GatewayAutoConfiguration {
|
||||
|
||||
//TODO: control creation
|
||||
@Bean
|
||||
public InMemoryRouteRepository inMemoryRouteRepository() {
|
||||
return new InMemoryRouteRepository();
|
||||
public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {
|
||||
return new InMemoryRouteDefinitionRepository();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -297,10 +307,10 @@ public class GatewayAutoConfiguration {
|
||||
protected static class GatewayActuatorConfiguration {
|
||||
|
||||
@Bean
|
||||
public GatewayEndpoint gatewayEndpoint(RouteLocator routeLocator, List<GlobalFilter> globalFilters,
|
||||
public GatewayEndpoint gatewayEndpoint(RouteDefinitionLocator routeDefinitionLocator, List<GlobalFilter> globalFilters,
|
||||
List<WebFilterFactory> webFilterFactories, FilteringWebHandler filteringWebHandler,
|
||||
RouteWriter routeWriter) {
|
||||
return new GatewayEndpoint(routeLocator, globalFilters, webFilterFactories, filteringWebHandler, routeWriter);
|
||||
RouteDefinitionWriter routeDefinitionWriter) {
|
||||
return new GatewayEndpoint(routeDefinitionLocator, globalFilters, webFilterFactories, filteringWebHandler, routeDefinitionWriter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,23 +18,23 @@
|
||||
package org.springframework.cloud.gateway.config;
|
||||
|
||||
import org.springframework.cloud.gateway.model.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.api.RouteLocator;
|
||||
import org.springframework.cloud.gateway.api.RouteDefinitionLocator;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class PropertiesRouteLocator implements RouteLocator {
|
||||
public class PropertiesRouteDefinitionLocator implements RouteDefinitionLocator {
|
||||
|
||||
private final GatewayProperties properties;
|
||||
|
||||
public PropertiesRouteLocator(GatewayProperties properties) {
|
||||
public PropertiesRouteDefinitionLocator(GatewayProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<RouteDefinition> getRoutes() {
|
||||
public Flux<RouteDefinition> getRouteDefinitions() {
|
||||
return Flux.fromIterable(this.properties.getRoutes());
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ package org.springframework.cloud.gateway.discovery;
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.gateway.api.RouteLocator;
|
||||
import org.springframework.cloud.gateway.api.RouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.filter.factory.RewritePathWebFilterFactory;
|
||||
import org.springframework.cloud.gateway.handler.predicate.PathRequestPredicateFactory;
|
||||
import org.springframework.cloud.gateway.model.FilterDefinition;
|
||||
@@ -39,18 +39,18 @@ import reactor.core.publisher.Flux;
|
||||
* TODO: developer configuration, in zuul, this was opt out, should be opt in
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class DiscoveryClientRouteLocator implements RouteLocator {
|
||||
public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator {
|
||||
|
||||
private final DiscoveryClient discoveryClient;
|
||||
private final String routeIdPrefix;
|
||||
|
||||
public DiscoveryClientRouteLocator(DiscoveryClient discoveryClient) {
|
||||
public DiscoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient) {
|
||||
this.discoveryClient = discoveryClient;
|
||||
this.routeIdPrefix = this.discoveryClient.getClass().getSimpleName() + "_";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<RouteDefinition> getRoutes() {
|
||||
public Flux<RouteDefinition> getRouteDefinitions() {
|
||||
return Flux.fromIterable(discoveryClient.getServices())
|
||||
.map(serviceId -> {
|
||||
RouteDefinition routeDefinition = new RouteDefinition();
|
||||
@@ -21,6 +21,7 @@ import java.net.URI;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.gateway.model.Route;
|
||||
import org.springframework.cloud.gateway.model.RouteDefinition;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
@@ -48,13 +49,13 @@ public class RouteToRequestUrlFilter implements GlobalFilter, Ordered {
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
RouteDefinition routeDefinition = getAttribute(exchange, GATEWAY_ROUTE_ATTR, RouteDefinition.class);
|
||||
if (routeDefinition == null) {
|
||||
Route route = getAttribute(exchange, GATEWAY_ROUTE_ATTR, Route.class);
|
||||
if (route == null) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
log.info("RouteToRequestUrlFilter start");
|
||||
URI requestUrl = UriComponentsBuilder.fromHttpRequest(exchange.getRequest())
|
||||
.uri(routeDefinition.getUri())
|
||||
.uri(route.getUri())
|
||||
.build(true)
|
||||
.toUri();
|
||||
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
|
||||
|
||||
@@ -17,38 +17,22 @@
|
||||
|
||||
package org.springframework.cloud.gateway.handler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.WebFilterFactory;
|
||||
import org.springframework.cloud.gateway.model.FilterDefinition;
|
||||
import org.springframework.cloud.gateway.model.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.api.RouteLocator;
|
||||
import org.springframework.cloud.gateway.config.GatewayProperties;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.support.NameUtils;
|
||||
import org.springframework.cloud.gateway.support.RefreshRoutesEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.tuple.TupleBuilder;
|
||||
import org.springframework.cloud.gateway.filter.factory.WebFilterFactory;
|
||||
import org.springframework.cloud.gateway.model.Route;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import org.springframework.web.server.WebHandler;
|
||||
import org.springframework.web.server.handler.WebHandlerDecorator;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -65,202 +49,34 @@ public class FilteringWebHandler extends WebHandlerDecorator {
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final GatewayProperties gatewayProperties;
|
||||
private final List<GlobalFilter> globalFilters;
|
||||
private final Map<String, WebFilterFactory> webFilterFactories = new HashMap<>();
|
||||
private final RouteLocator routeLocator;
|
||||
|
||||
private final ConcurrentMap<String, List<WebFilter>> combinedFiltersForRoute = new ConcurrentHashMap<>();
|
||||
|
||||
public FilteringWebHandler(GatewayProperties gatewayProperties, List<GlobalFilter> globalFilters,
|
||||
List<WebFilterFactory> webFilterFactories) {
|
||||
this(new EmptyWebHandler(), gatewayProperties, globalFilters, webFilterFactories);
|
||||
public FilteringWebHandler(GatewayProperties gatewayProperties, RouteLocator routeLocator) {
|
||||
this(new EmptyWebHandler(), gatewayProperties, routeLocator);
|
||||
}
|
||||
|
||||
public FilteringWebHandler(WebHandler targetHandler, GatewayProperties gatewayProperties, List<GlobalFilter> globalFilters,
|
||||
List<WebFilterFactory> webFilterFactories) {
|
||||
public FilteringWebHandler(WebHandler targetHandler, GatewayProperties gatewayProperties,
|
||||
RouteLocator routeLocator) {
|
||||
super(targetHandler);
|
||||
this.gatewayProperties = gatewayProperties;
|
||||
this.globalFilters = initList(globalFilters);
|
||||
webFilterFactories.forEach(factory -> this.webFilterFactories.put(factory.name(), factory));
|
||||
this.routeLocator = routeLocator;
|
||||
}
|
||||
|
||||
private static <T> List<T> initList(List<T> list) {
|
||||
return (list != null ? list : emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a read-only list of the configured globalFilters.
|
||||
*/
|
||||
public List<GlobalFilter> getGlobalFilters() {
|
||||
return this.globalFilters;
|
||||
}
|
||||
|
||||
|
||||
@EventListener(RefreshRoutesEvent.class)
|
||||
/* for testing */ void handleRefresh() {
|
||||
/* TODO: relocate @EventListener(RefreshRoutesEvent.class)
|
||||
void handleRefresh() {
|
||||
this.combinedFiltersForRoute.clear();
|
||||
}
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(ServerWebExchange exchange) {
|
||||
Optional<RouteDefinition> route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
|
||||
List<WebFilter> webFilters = combineFiltersForRoute(route);
|
||||
Optional<Route> route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
|
||||
List<WebFilter> webFilters = route.get().getWebFilters();
|
||||
|
||||
logger.debug("Sorted webFilterFactories: "+ webFilters);
|
||||
|
||||
return new DefaultWebFilterChain(webFilters, getDelegate()).filter(exchange);
|
||||
}
|
||||
|
||||
public List<WebFilter> combineFiltersForRoute(Optional<RouteDefinition> route) {
|
||||
if (!route.isPresent()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<WebFilter> combinedFilters = this.combinedFiltersForRoute.computeIfAbsent(
|
||||
route.get().getId(), s -> {
|
||||
|
||||
//TODO: probably a java 8 stream way of doing this
|
||||
List<WebFilter> combined = new ArrayList<>(loadFilters(this.globalFilters));
|
||||
|
||||
//TODO: support option to apply defaults after route specific filters?
|
||||
if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
|
||||
combined.addAll(loadWebFilters("defaultFilters",
|
||||
this.gatewayProperties.getDefaultFilters()));
|
||||
}
|
||||
|
||||
if (route.isPresent() && !route.get().getFilters().isEmpty()) {
|
||||
combined.addAll(loadWebFilters(route.get().getId(), route.get().getFilters()));
|
||||
}
|
||||
|
||||
AnnotationAwareOrderComparator.sort(combined);
|
||||
return combined;
|
||||
});
|
||||
return combinedFilters;
|
||||
}
|
||||
|
||||
private Collection<WebFilter> loadFilters(List<GlobalFilter> filters) {
|
||||
return filters.stream()
|
||||
.map(filter -> {
|
||||
WebFilterAdapter webFilter = new WebFilterAdapter(filter);
|
||||
if (filter instanceof Ordered) {
|
||||
int order = ((Ordered) filter).getOrder();
|
||||
return new OrderedWebFilter(webFilter, order);
|
||||
}
|
||||
return webFilter;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<WebFilter> loadWebFilters(String id, List<FilterDefinition> filterDefinitions) {
|
||||
List<WebFilter> filters = filterDefinitions.stream()
|
||||
.map(definition -> {
|
||||
WebFilterFactory filter = this.webFilterFactories.get(definition.getName());
|
||||
if (filter == null) {
|
||||
throw new IllegalArgumentException("Unable to find WebFilterFactory with name " + definition.getName());
|
||||
}
|
||||
Map<String, String> args = definition.getArgs();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("RouteDefinition " + id + " applying filter " + args + " to " + definition.getName());
|
||||
}
|
||||
|
||||
//TODO: move Tuple building to common class, see RequestPredicateFactory.lookup
|
||||
TupleBuilder builder = TupleBuilder.tuple();
|
||||
|
||||
List<String> argNames = filter.argNames();
|
||||
if (!argNames.isEmpty()) {
|
||||
// ensure size is the same for key replacement later
|
||||
if (filter.validateArgs() && args.size() != argNames.size()) {
|
||||
throw new IllegalArgumentException("Wrong number of arguments. Expected " + argNames
|
||||
+ " " + argNames + ". Found " + args.size() + " " + args + "'");
|
||||
}
|
||||
}
|
||||
|
||||
int entryIdx = 0;
|
||||
for (Map.Entry<String, String> entry : args.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
|
||||
// RequestPredicateFactory has name hints and this has a fake key name
|
||||
// replace with the matching key hint
|
||||
if (key.startsWith(NameUtils.GENERATED_NAME_PREFIX) && !argNames.isEmpty()
|
||||
&& entryIdx < args.size()) {
|
||||
key = argNames.get(entryIdx);
|
||||
}
|
||||
|
||||
builder.put(key, entry.getValue());
|
||||
entryIdx++;
|
||||
}
|
||||
|
||||
Tuple tuple = builder.build();
|
||||
|
||||
if (filter.validateArgs()) {
|
||||
for (String name : argNames) {
|
||||
if (!tuple.hasFieldName(name)) {
|
||||
throw new IllegalArgumentException("Missing argument '" + name + "'. Given " + tuple);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filter.apply(tuple);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
ArrayList<WebFilter> ordered = new ArrayList<>(filters.size());
|
||||
for (int i = 0; i < filters.size(); i++) {
|
||||
ordered.add(new OrderedWebFilter(filters.get(i), i+1));
|
||||
}
|
||||
|
||||
return ordered;
|
||||
}
|
||||
|
||||
private static class WebFilterAdapter implements WebFilter {
|
||||
|
||||
private final GlobalFilter delegate;
|
||||
|
||||
public WebFilterAdapter(GlobalFilter delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
return this.delegate.filter(exchange, chain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("WebFilterAdapter{");
|
||||
sb.append("delegate=").append(delegate);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public class OrderedWebFilter implements WebFilter, Ordered {
|
||||
|
||||
private final WebFilter delegate;
|
||||
private final int order;
|
||||
|
||||
public OrderedWebFilter(WebFilter delegate, int order) {
|
||||
this.delegate = delegate;
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
return this.delegate.filter(exchange, chain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("OrderedWebFilter{");
|
||||
sb.append("delegate=").append(delegate);
|
||||
sb.append(", order=").append(order);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static class DefaultWebFilterChain implements WebFilterChain {
|
||||
|
||||
private int index;
|
||||
|
||||
@@ -17,21 +17,11 @@
|
||||
|
||||
package org.springframework.cloud.gateway.handler;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.cloud.gateway.api.RouteLocator;
|
||||
import org.springframework.cloud.gateway.handler.predicate.RequestPredicateFactory;
|
||||
import org.springframework.cloud.gateway.model.PredicateDefinition;
|
||||
import org.springframework.cloud.gateway.model.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.support.NameUtils;
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.tuple.TupleBuilder;
|
||||
import org.springframework.cloud.gateway.handler.support.ExchangeServerRequest;
|
||||
import org.springframework.web.reactive.function.server.RequestPredicate;
|
||||
import org.springframework.cloud.gateway.model.Route;
|
||||
import org.springframework.web.reactive.handler.AbstractHandlerMapping;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebHandler;
|
||||
@@ -46,30 +36,13 @@ import reactor.core.publisher.Mono;
|
||||
*/
|
||||
public class RequestPredicateHandlerMapping extends AbstractHandlerMapping {
|
||||
|
||||
private final RouteLocator routeLocator;
|
||||
private final WebHandler webHandler;
|
||||
private final Map<String, RequestPredicateFactory> requestPredicates = new LinkedHashMap<>();
|
||||
//TODO: define semeantics for refresh (ie clearing and recalculating combinedPredicates)
|
||||
private final Map<String, RequestPredicate> combinedPredicates = new ConcurrentHashMap<>();
|
||||
private final RouteLocator routeLocator;
|
||||
|
||||
public RequestPredicateHandlerMapping(WebHandler webHandler, List<RequestPredicateFactory> requestPredicates,
|
||||
RouteLocator routeLocator) {
|
||||
public RequestPredicateHandlerMapping(WebHandler webHandler, RouteLocator routeLocator) {
|
||||
this.webHandler = webHandler;
|
||||
this.routeLocator = routeLocator;
|
||||
|
||||
requestPredicates.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)
|
||||
+ ". It will be overwritten.");
|
||||
}
|
||||
this.requestPredicates.put(key, factory);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Loaded RequestPredicateFactory [" + key + "]");
|
||||
}
|
||||
});
|
||||
|
||||
setOrder(1);
|
||||
}
|
||||
|
||||
@@ -79,7 +52,7 @@ public class RequestPredicateHandlerMapping extends AbstractHandlerMapping {
|
||||
|
||||
return lookupRoute(exchange)
|
||||
.log("TRACE")
|
||||
.then((Function<RouteDefinition, Mono<?>>) r -> {
|
||||
.then((Function<Route, Mono<?>>) r -> {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Mapping [" + getExchangeDesc(exchange) + "] to " + r);
|
||||
}
|
||||
@@ -105,18 +78,17 @@ public class RequestPredicateHandlerMapping extends AbstractHandlerMapping {
|
||||
}
|
||||
|
||||
|
||||
protected Mono<RouteDefinition> lookupRoute(ServerWebExchange exchange) {
|
||||
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
|
||||
return this.routeLocator.getRoutes()
|
||||
.map(this::getRouteCombinedPredicates)
|
||||
.filter(rcp -> rcp.combinedPredicate.test(new ExchangeServerRequest(exchange)))
|
||||
.filter(route -> route.getRequestPredicate().test(new ExchangeServerRequest(exchange)))
|
||||
.next()
|
||||
//TODO: error handling
|
||||
.map(rcp -> {
|
||||
.map(route -> {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("RouteDefinition matched: " + rcp.routeDefinition.getId());
|
||||
logger.debug("RouteDefinition matched: " + route.getId());
|
||||
}
|
||||
validateRoute(rcp.routeDefinition, exchange);
|
||||
return rcp.routeDefinition;
|
||||
validateRoute(route, exchange);
|
||||
return route;
|
||||
});
|
||||
|
||||
/* TODO: trace logging
|
||||
@@ -125,98 +97,16 @@ public class RequestPredicateHandlerMapping extends AbstractHandlerMapping {
|
||||
}*/
|
||||
}
|
||||
|
||||
private RouteCombinedPredicates getRouteCombinedPredicates(RouteDefinition routeDefinition) {
|
||||
RequestPredicate predicate = this.combinedPredicates
|
||||
.computeIfAbsent(routeDefinition.getId(), k -> combinePredicates(routeDefinition));
|
||||
|
||||
return new RouteCombinedPredicates(routeDefinition, predicate);
|
||||
}
|
||||
|
||||
private class RouteCombinedPredicates {
|
||||
private RouteDefinition routeDefinition;
|
||||
private RequestPredicate combinedPredicate;
|
||||
|
||||
public RouteCombinedPredicates(RouteDefinition routeDefinition, RequestPredicate combinedPredicate) {
|
||||
this.routeDefinition = routeDefinition;
|
||||
this.combinedPredicate = combinedPredicate;
|
||||
}
|
||||
}
|
||||
|
||||
private RequestPredicate combinePredicates(RouteDefinition routeDefinition) {
|
||||
List<PredicateDefinition> predicates = routeDefinition.getPredicates();
|
||||
RequestPredicate predicate = lookup(routeDefinition, predicates.get(0));
|
||||
|
||||
for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) {
|
||||
RequestPredicate found = lookup(routeDefinition, andPredicate);
|
||||
predicate = predicate.and(found);
|
||||
}
|
||||
|
||||
return predicate;
|
||||
}
|
||||
|
||||
//TODO: decouple from HandlerMapping?
|
||||
private RequestPredicate lookup(RouteDefinition routeDefinition, PredicateDefinition predicate) {
|
||||
RequestPredicateFactory found = this.requestPredicates.get(predicate.getName());
|
||||
if (found == null) {
|
||||
throw new IllegalArgumentException("Unable to find RequestPredicateFactory with name " + predicate.getName());
|
||||
}
|
||||
Map<String, String> args = predicate.getArgs();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("RouteDefinition " + routeDefinition.getId() + " applying "
|
||||
+ args + " to " + predicate.getName());
|
||||
}
|
||||
|
||||
TupleBuilder builder = TupleBuilder.tuple();
|
||||
|
||||
List<String> argNames = found.argNames();
|
||||
if (!argNames.isEmpty()) {
|
||||
if (!argNames.isEmpty()) {
|
||||
// ensure size is the same for key replacement later
|
||||
if (found.validateArgs() && args.size() != argNames.size()) {
|
||||
throw new IllegalArgumentException("Wrong number of arguments. Expected " + argNames
|
||||
+ " " + argNames + ". Found " + args.size() + " " + args + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int entryIdx = 0;
|
||||
for (Map.Entry<String, String> entry : args.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
|
||||
// RequestPredicateFactory has name hints and this has a fake key name
|
||||
// replace with the matching key hint
|
||||
if (key.startsWith(NameUtils.GENERATED_NAME_PREFIX) && !argNames.isEmpty()
|
||||
&& entryIdx < args.size()) {
|
||||
key = argNames.get(entryIdx);
|
||||
}
|
||||
|
||||
builder.put(key, entry.getValue());
|
||||
entryIdx++;
|
||||
}
|
||||
|
||||
Tuple tuple = builder.build();
|
||||
|
||||
if (found.validateArgs()) {
|
||||
for (String name : argNames) {
|
||||
if (!tuple.hasFieldName(name)) {
|
||||
throw new IllegalArgumentException("Missing argument '" + name + "'. Given " + tuple);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found.apply(tuple);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given handler against the current request.
|
||||
* <p>The default implementation is empty. Can be overridden in subclasses,
|
||||
* for example to enforce specific preconditions expressed in URL mappings.
|
||||
* @param routeDefinition the RouteDefinition object to validate
|
||||
* @param route the Route object to validate
|
||||
* @param exchange current exchange
|
||||
* @throws Exception if validation failed
|
||||
*/
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
protected void validateRoute(RouteDefinition routeDefinition, ServerWebExchange exchange) {
|
||||
protected void validateRoute(Route route, ServerWebExchange exchange) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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.model;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.function.server.RequestPredicate;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class Route {
|
||||
|
||||
private final String id;
|
||||
|
||||
private final URI uri;
|
||||
|
||||
private final RequestPredicate requestPredicate;
|
||||
|
||||
private final List<WebFilter> webFilters;
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static Builder builder(RouteDefinition routeDefinition) {
|
||||
return new Builder()
|
||||
.id(routeDefinition.getId())
|
||||
.uri(routeDefinition.getUri());
|
||||
}
|
||||
|
||||
public Route(String id, URI uri, RequestPredicate requestPredicate, List<WebFilter> webFilters) {
|
||||
this.id = id;
|
||||
this.uri = uri;
|
||||
this.requestPredicate = requestPredicate;
|
||||
this.webFilters = webFilters;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private String id;
|
||||
|
||||
private URI uri;
|
||||
|
||||
private RequestPredicate requestPredicate;
|
||||
|
||||
private List<WebFilter> webFilters = new ArrayList<>();
|
||||
|
||||
private Builder() {}
|
||||
|
||||
public Builder id(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder uri(URI uri) {
|
||||
this.uri = uri;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder requestPredicate(RequestPredicate requestPredicate) {
|
||||
this.requestPredicate = requestPredicate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder webFilters(List<WebFilter> webFilters) {
|
||||
this.webFilters = webFilters;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder add(WebFilter webFilter) {
|
||||
this.webFilters.add(webFilter);
|
||||
return this;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
return new Route(this.id, this.uri, this.requestPredicate, this.webFilters);
|
||||
}
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public URI getUri() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
public RequestPredicate getRequestPredicate() {
|
||||
return this.requestPredicate;
|
||||
}
|
||||
|
||||
public List<WebFilter> getWebFilters() {
|
||||
return Collections.unmodifiableList(this.webFilters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Route route = (Route) o;
|
||||
return Objects.equals(id, route.id) &&
|
||||
Objects.equals(uri, route.uri) &&
|
||||
Objects.equals(requestPredicate, route.requestPredicate) &&
|
||||
Objects.equals(webFilters, route.webFilters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, uri, requestPredicate, webFilters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuffer sb = new StringBuffer("Route{");
|
||||
sb.append("id='").append(id).append('\'');
|
||||
sb.append(", uri=").append(uri);
|
||||
sb.append(", requestPredicates=").append(requestPredicate);
|
||||
sb.append(", webFilters=").append(webFilters);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.springframework.cloud.gateway.api.RouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.model.RouteDefinition;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class CachingRouteDefinitionLocator implements RouteDefinitionLocator {
|
||||
|
||||
private final RouteDefinitionLocator delegate;
|
||||
private final AtomicReference<List<RouteDefinition>> cachedRoutes = new AtomicReference<>();
|
||||
|
||||
public CachingRouteDefinitionLocator(RouteDefinitionLocator delegate) {
|
||||
this.delegate = delegate;
|
||||
this.cachedRoutes.compareAndSet(null, collectRoutes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<RouteDefinition> getRouteDefinitions() {
|
||||
return Flux.fromIterable(this.cachedRoutes.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the new routes
|
||||
* @return old routes
|
||||
*/
|
||||
public Flux<RouteDefinition> refresh() {
|
||||
return Flux.fromIterable(this.cachedRoutes.getAndUpdate(
|
||||
routes -> CachingRouteDefinitionLocator.this.collectRoutes()));
|
||||
}
|
||||
|
||||
private List<RouteDefinition> collectRoutes() {
|
||||
return this.delegate.getRouteDefinitions().collectList().block();
|
||||
}
|
||||
|
||||
@EventListener(RefreshRoutesEvent.class)
|
||||
/* for testing */ void handleRefresh() {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
@@ -20,9 +20,12 @@ package org.springframework.cloud.gateway.support;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.springframework.cloud.gateway.model.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.api.RouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.api.RouteLocator;
|
||||
import org.springframework.cloud.gateway.model.Route;
|
||||
import org.springframework.cloud.gateway.model.RouteDefinition;
|
||||
import org.springframework.context.event.EventListener;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
@@ -31,7 +34,7 @@ import reactor.core.publisher.Flux;
|
||||
public class CachingRouteLocator implements RouteLocator {
|
||||
|
||||
private final RouteLocator delegate;
|
||||
private final AtomicReference<List<RouteDefinition>> cachedRoutes = new AtomicReference<>();
|
||||
private final AtomicReference<List<Route>> cachedRoutes = new AtomicReference<>();
|
||||
|
||||
public CachingRouteLocator(RouteLocator delegate) {
|
||||
this.delegate = delegate;
|
||||
@@ -39,7 +42,7 @@ public class CachingRouteLocator implements RouteLocator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<RouteDefinition> getRoutes() {
|
||||
public Flux<Route> getRoutes() {
|
||||
return Flux.fromIterable(this.cachedRoutes.get());
|
||||
}
|
||||
|
||||
@@ -47,12 +50,12 @@ public class CachingRouteLocator implements RouteLocator {
|
||||
* Sets the new routes
|
||||
* @return old routes
|
||||
*/
|
||||
public Flux<RouteDefinition> refresh() {
|
||||
public Flux<Route> refresh() {
|
||||
return Flux.fromIterable(this.cachedRoutes.getAndUpdate(
|
||||
routes -> CachingRouteLocator.this.collectRoutes()));
|
||||
}
|
||||
|
||||
private List<RouteDefinition> collectRoutes() {
|
||||
private List<Route> collectRoutes() {
|
||||
return this.delegate.getRoutes().collectList().block();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,23 +17,23 @@
|
||||
|
||||
package org.springframework.cloud.gateway.support;
|
||||
|
||||
import org.springframework.cloud.gateway.api.RouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.model.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.api.RouteLocator;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class CompositeRouteLocator implements RouteLocator {
|
||||
public class CompositeRouteDefinitionLocator implements RouteDefinitionLocator {
|
||||
|
||||
private final Flux<RouteLocator> delegates;
|
||||
private final Flux<RouteDefinitionLocator> delegates;
|
||||
|
||||
public CompositeRouteLocator(Flux<RouteLocator> delegates) {
|
||||
public CompositeRouteDefinitionLocator(Flux<RouteDefinitionLocator> delegates) {
|
||||
this.delegates = delegates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<RouteDefinition> getRoutes() {
|
||||
return this.delegates.flatMap(RouteLocator::getRoutes);
|
||||
public Flux<RouteDefinition> getRouteDefinitions() {
|
||||
return this.delegates.flatMap(RouteDefinitionLocator::getRouteDefinitions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.gateway.api.RouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.api.RouteLocator;
|
||||
import org.springframework.cloud.gateway.config.GatewayProperties;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.WebFilterFactory;
|
||||
import org.springframework.cloud.gateway.handler.FilteringWebHandler;
|
||||
import org.springframework.cloud.gateway.handler.predicate.RequestPredicateFactory;
|
||||
import org.springframework.cloud.gateway.model.FilterDefinition;
|
||||
import org.springframework.cloud.gateway.model.PredicateDefinition;
|
||||
import org.springframework.cloud.gateway.model.Route;
|
||||
import org.springframework.cloud.gateway.model.RouteDefinition;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.tuple.Tuple;
|
||||
import org.springframework.tuple.TupleBuilder;
|
||||
import org.springframework.web.reactive.function.server.RequestPredicate;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class DefaultRouteLocator implements RouteLocator {
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final GatewayProperties gatewayProperties;
|
||||
private final RouteDefinitionLocator routeDefinitionLocator;
|
||||
private final Map<String, RequestPredicateFactory> requestPredicates = new LinkedHashMap<>();
|
||||
private final List<GlobalFilter> globalFilters;
|
||||
private final Map<String, WebFilterFactory> webFilterFactories = new HashMap<>();
|
||||
|
||||
public DefaultRouteLocator(GatewayProperties gatewayProperties,
|
||||
RouteDefinitionLocator routeDefinitionLocator,
|
||||
List<RequestPredicateFactory> requestPredicates,
|
||||
List<GlobalFilter> globalFilters,
|
||||
List<WebFilterFactory> webFilterFactories) {
|
||||
this.gatewayProperties = gatewayProperties;
|
||||
this.routeDefinitionLocator = routeDefinitionLocator;
|
||||
this.globalFilters = initList(globalFilters);
|
||||
initFactories(requestPredicates);
|
||||
webFilterFactories.forEach(factory -> this.webFilterFactories.put(factory.name(), factory));
|
||||
}
|
||||
|
||||
private static <T> List<T> initList(List<T> list) {
|
||||
return (list != null ? list : emptyList());
|
||||
}
|
||||
|
||||
private void initFactories(List<RequestPredicateFactory> requestPredicates) {
|
||||
requestPredicates.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)
|
||||
+ ". It will be overwritten.");
|
||||
}
|
||||
this.requestPredicates.put(key, factory);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Loaded RequestPredicateFactory [" + key + "]");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Route> getRoutes() {
|
||||
return this.routeDefinitionLocator.getRouteDefinitions()
|
||||
.map(this::convertToRoute)
|
||||
//TODO: error handling
|
||||
.map(route -> {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("RouteDefinition matched: " + route.getId());
|
||||
}
|
||||
return route;
|
||||
});
|
||||
|
||||
|
||||
/* TODO: trace logging
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("RouteDefinition did not match: " + routeDefinition.getId());
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
private Route convertToRoute(RouteDefinition routeDefinition) {
|
||||
RequestPredicate requestPredicate = combinePredicates(routeDefinition);
|
||||
List<WebFilter> webFilters = getFilters(routeDefinition);
|
||||
|
||||
return Route.builder(routeDefinition)
|
||||
.requestPredicate(requestPredicate)
|
||||
.webFilters(webFilters)
|
||||
.build();
|
||||
}
|
||||
|
||||
private Collection<WebFilter> loadFilters(List<GlobalFilter> filters) {
|
||||
return filters.stream()
|
||||
.map(filter -> {
|
||||
WebFilterAdapter webFilter = new WebFilterAdapter(filter);
|
||||
if (filter instanceof Ordered) {
|
||||
int order = ((Ordered) filter).getOrder();
|
||||
return new OrderedWebFilter(webFilter, order);
|
||||
}
|
||||
return webFilter;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<WebFilter> loadWebFilters(String id, List<FilterDefinition> filterDefinitions) {
|
||||
List<WebFilter> filters = filterDefinitions.stream()
|
||||
.map(definition -> {
|
||||
WebFilterFactory filter = this.webFilterFactories.get(definition.getName());
|
||||
if (filter == null) {
|
||||
throw new IllegalArgumentException("Unable to find WebFilterFactory with name " + definition.getName());
|
||||
}
|
||||
Map<String, String> args = definition.getArgs();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("RouteDefinition " + id + " applying filter " + args + " to " + definition.getName());
|
||||
}
|
||||
|
||||
//TODO: move Tuple building to common class, see RequestPredicateFactory.lookup
|
||||
TupleBuilder builder = TupleBuilder.tuple();
|
||||
|
||||
List<String> argNames = filter.argNames();
|
||||
if (!argNames.isEmpty()) {
|
||||
// ensure size is the same for key replacement later
|
||||
if (filter.validateArgs() && args.size() != argNames.size()) {
|
||||
throw new IllegalArgumentException("Wrong number of arguments. Expected " + argNames
|
||||
+ " " + argNames + ". Found " + args.size() + " " + args + "'");
|
||||
}
|
||||
}
|
||||
|
||||
int entryIdx = 0;
|
||||
for (Map.Entry<String, String> entry : args.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
|
||||
// RequestPredicateFactory has name hints and this has a fake key name
|
||||
// replace with the matching key hint
|
||||
if (key.startsWith(NameUtils.GENERATED_NAME_PREFIX) && !argNames.isEmpty()
|
||||
&& entryIdx < args.size()) {
|
||||
key = argNames.get(entryIdx);
|
||||
}
|
||||
|
||||
builder.put(key, entry.getValue());
|
||||
entryIdx++;
|
||||
}
|
||||
|
||||
Tuple tuple = builder.build();
|
||||
|
||||
if (filter.validateArgs()) {
|
||||
for (String name : argNames) {
|
||||
if (!tuple.hasFieldName(name)) {
|
||||
throw new IllegalArgumentException("Missing argument '" + name + "'. Given " + tuple);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filter.apply(tuple);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
ArrayList<WebFilter> ordered = new ArrayList<>(filters.size());
|
||||
for (int i = 0; i < filters.size(); i++) {
|
||||
ordered.add(new OrderedWebFilter(filters.get(i), i+1));
|
||||
}
|
||||
|
||||
return ordered;
|
||||
}
|
||||
|
||||
private List<WebFilter> getFilters(RouteDefinition routeDefinition) {
|
||||
//TODO: probably a java 8 stream way of doing this
|
||||
List<WebFilter> combined = new ArrayList<>(loadFilters(this.globalFilters));
|
||||
|
||||
//TODO: support option to apply defaults after route specific filters?
|
||||
if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
|
||||
combined.addAll(loadWebFilters("defaultFilters",
|
||||
this.gatewayProperties.getDefaultFilters()));
|
||||
}
|
||||
|
||||
if (!routeDefinition.getFilters().isEmpty()) {
|
||||
combined.addAll(loadWebFilters(routeDefinition.getId(), routeDefinition.getFilters()));
|
||||
}
|
||||
|
||||
AnnotationAwareOrderComparator.sort(combined);
|
||||
return combined;
|
||||
}
|
||||
|
||||
private RequestPredicate combinePredicates(RouteDefinition routeDefinition) {
|
||||
List<PredicateDefinition> predicates = routeDefinition.getPredicates();
|
||||
RequestPredicate predicate = lookup(routeDefinition, predicates.get(0));
|
||||
|
||||
for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) {
|
||||
RequestPredicate found = lookup(routeDefinition, andPredicate);
|
||||
predicate = predicate.and(found);
|
||||
}
|
||||
|
||||
return predicate;
|
||||
}
|
||||
|
||||
private RequestPredicate lookup(RouteDefinition routeDefinition, PredicateDefinition predicate) {
|
||||
RequestPredicateFactory found = this.requestPredicates.get(predicate.getName());
|
||||
if (found == null) {
|
||||
throw new IllegalArgumentException("Unable to find RequestPredicateFactory with name " + predicate.getName());
|
||||
}
|
||||
Map<String, String> args = predicate.getArgs();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("RouteDefinition " + routeDefinition.getId() + " applying "
|
||||
+ args + " to " + predicate.getName());
|
||||
}
|
||||
|
||||
TupleBuilder builder = TupleBuilder.tuple();
|
||||
|
||||
List<String> argNames = found.argNames();
|
||||
if (!argNames.isEmpty()) {
|
||||
if (!argNames.isEmpty()) {
|
||||
// ensure size is the same for key replacement later
|
||||
if (found.validateArgs() && args.size() != argNames.size()) {
|
||||
throw new IllegalArgumentException("Wrong number of arguments. Expected " + argNames
|
||||
+ " " + argNames + ". Found " + args.size() + " " + args + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int entryIdx = 0;
|
||||
for (Map.Entry<String, String> entry : args.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
|
||||
// RequestPredicateFactory has name hints and this has a fake key name
|
||||
// replace with the matching key hint
|
||||
if (key.startsWith(NameUtils.GENERATED_NAME_PREFIX) && !argNames.isEmpty()
|
||||
&& entryIdx < args.size()) {
|
||||
key = argNames.get(entryIdx);
|
||||
}
|
||||
|
||||
builder.put(key, entry.getValue());
|
||||
entryIdx++;
|
||||
}
|
||||
|
||||
Tuple tuple = builder.build();
|
||||
|
||||
if (found.validateArgs()) {
|
||||
for (String name : argNames) {
|
||||
if (!tuple.hasFieldName(name)) {
|
||||
throw new IllegalArgumentException("Missing argument '" + name + "'. Given " + tuple);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found.apply(tuple);
|
||||
}
|
||||
|
||||
|
||||
private static class WebFilterAdapter implements WebFilter {
|
||||
|
||||
private final GlobalFilter delegate;
|
||||
|
||||
public WebFilterAdapter(GlobalFilter delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
return this.delegate.filter(exchange, chain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("WebFilterAdapter{");
|
||||
sb.append("delegate=").append(delegate);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public class OrderedWebFilter implements WebFilter, Ordered {
|
||||
|
||||
private final WebFilter delegate;
|
||||
private final int order;
|
||||
|
||||
public OrderedWebFilter(WebFilter delegate, int order) {
|
||||
this.delegate = delegate;
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
return this.delegate.filter(exchange, chain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("OrderedWebFilter{");
|
||||
sb.append("delegate=").append(delegate);
|
||||
sb.append(", order=").append(order);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,9 +20,9 @@ package org.springframework.cloud.gateway.support;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.gateway.api.RouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.model.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.api.RouteLocator;
|
||||
import org.springframework.cloud.gateway.api.RouteWriter;
|
||||
import org.springframework.cloud.gateway.api.RouteDefinitionWriter;
|
||||
|
||||
import static java.util.Collections.synchronizedMap;
|
||||
|
||||
@@ -32,7 +32,7 @@ import reactor.core.publisher.Mono;
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class InMemoryRouteRepository implements RouteLocator, RouteWriter {
|
||||
public class InMemoryRouteDefinitionRepository implements RouteDefinitionLocator, RouteDefinitionWriter {
|
||||
|
||||
private final Map<String, RouteDefinition> routes = synchronizedMap(new LinkedHashMap<String, RouteDefinition>());
|
||||
|
||||
@@ -56,7 +56,7 @@ public class InMemoryRouteRepository implements RouteLocator, RouteWriter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<RouteDefinition> getRoutes() {
|
||||
public Flux<RouteDefinition> getRouteDefinitions() {
|
||||
return Flux.fromIterable(routes.values());
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.EnableGateway;
|
||||
import org.springframework.cloud.gateway.test.BaseWebClientTests;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
@@ -69,7 +70,7 @@ public class RemoveNonProxyHeadersWebFilterFactoryTests extends BaseWebClientTes
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootConfiguration
|
||||
@EnableGateway
|
||||
@Import(DefaultTestConfig.class)
|
||||
public static class TestConfig { }
|
||||
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ import reactor.test.StepVerifier;
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public class HostRoutePredicateIntegrationTests extends BaseWebClientTests {
|
||||
public class HostRequestPredicateFactoryTests extends BaseWebClientTests {
|
||||
|
||||
@Test
|
||||
public void hostRouteWorks() {
|
||||
@@ -37,7 +37,7 @@ import org.springframework.cloud.gateway.filter.factory.SetStatusWebFilterFactor
|
||||
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.HostRoutePredicateIntegrationTests;
|
||||
import org.springframework.cloud.gateway.handler.predicate.HostRequestPredicateFactoryTests;
|
||||
import org.springframework.cloud.gateway.handler.predicate.MethodRequestPredicateFactoryTests;
|
||||
import org.springframework.cloud.gateway.handler.predicate.PathRequestPredicateFactoryTests;
|
||||
|
||||
@@ -66,7 +66,7 @@ import org.springframework.cloud.gateway.handler.predicate.PathRequestPredicateF
|
||||
AfterRequestPredicateFactoryTests.class,
|
||||
BeforeRequestPredicateFactoryTests.class,
|
||||
BetweenRequestPredicateFactoryTests.class,
|
||||
HostRoutePredicateIntegrationTests.class,
|
||||
HostRequestPredicateFactoryTests.class,
|
||||
MethodRequestPredicateFactoryTests.class,
|
||||
PathRequestPredicateFactoryTests.class,
|
||||
})
|
||||
|
||||
@@ -28,13 +28,19 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Before;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.cloud.gateway.EnableGateway;
|
||||
import org.springframework.cloud.gateway.api.RouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.config.PropertiesRouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.model.Route;
|
||||
import org.springframework.cloud.gateway.model.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.support.CompositeRouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.support.InMemoryRouteDefinitionRepository;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@@ -50,6 +56,7 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.G
|
||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
|
||||
import static org.springframework.cloud.gateway.test.TestUtils.parseMultipart;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
@@ -156,6 +163,13 @@ public class BaseWebClientTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public RouteDefinitionLocator routeDefinitionLocator(InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository,
|
||||
PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator) {
|
||||
return new CompositeRouteDefinitionLocator(Flux.just(inMemoryRouteDefinitionRepository, propertiesRouteDefinitionLocator));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(500)
|
||||
public GlobalFilter modifyResponseFilter() {
|
||||
@@ -163,9 +177,9 @@ public class BaseWebClientTests {
|
||||
log.info("modifyResponseFilter start");
|
||||
String value = (String) exchange.getAttribute(GATEWAY_HANDLER_MAPPER_ATTR).orElse("N/A");
|
||||
exchange.getResponse().getHeaders().add(HANDLER_MAPPER_HEADER, value);
|
||||
RouteDefinition routeDefinition = (RouteDefinition) exchange.getAttribute(GATEWAY_ROUTE_ATTR).orElse(null);
|
||||
if (routeDefinition != null) {
|
||||
exchange.getResponse().getHeaders().add(ROUTE_ID_HEADER, routeDefinition.getId());
|
||||
Route route = (Route) exchange.getAttribute(GATEWAY_ROUTE_ATTR).orElse(null);
|
||||
if (route != null) {
|
||||
exchange.getResponse().getHeaders().add(ROUTE_ID_HEADER, route.getId());
|
||||
}
|
||||
return chain.filter(exchange);
|
||||
};
|
||||
|
||||
@@ -23,13 +23,13 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.gateway.EnableGateway;
|
||||
import org.springframework.cloud.gateway.api.RouteLocator;
|
||||
import org.springframework.cloud.gateway.api.RouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.config.GatewayProperties;
|
||||
import org.springframework.cloud.gateway.config.PropertiesRouteLocator;
|
||||
import org.springframework.cloud.gateway.discovery.DiscoveryClientRouteLocator;
|
||||
import org.springframework.cloud.gateway.support.CachingRouteLocator;
|
||||
import org.springframework.cloud.gateway.support.CompositeRouteLocator;
|
||||
import org.springframework.cloud.gateway.support.InMemoryRouteRepository;
|
||||
import org.springframework.cloud.gateway.config.PropertiesRouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.support.CachingRouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.support.CompositeRouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.support.InMemoryRouteDefinitionRepository;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
@@ -53,22 +53,22 @@ public class GatewayTestApplication {
|
||||
protected static class GatewayDiscoveryConfiguration {
|
||||
|
||||
@Bean
|
||||
public DiscoveryClientRouteLocator discoveryClientRouteLocator(DiscoveryClient discoveryClient) {
|
||||
return new DiscoveryClientRouteLocator(discoveryClient);
|
||||
public DiscoveryClientRouteDefinitionLocator discoveryClientRouteLocator(DiscoveryClient discoveryClient) {
|
||||
return new DiscoveryClientRouteDefinitionLocator(discoveryClient);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PropertiesRouteLocator propertiesRouteLocator(GatewayProperties properties) {
|
||||
return new PropertiesRouteLocator(properties);
|
||||
public PropertiesRouteDefinitionLocator propertiesRouteLocator(GatewayProperties properties) {
|
||||
return new PropertiesRouteDefinitionLocator(properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public RouteLocator compositeRouteLocator(InMemoryRouteRepository inMemoryRouteRepository,
|
||||
DiscoveryClientRouteLocator discoveryClientRouteLocator,
|
||||
PropertiesRouteLocator propertiesRouteLocator) {
|
||||
Flux<RouteLocator> flux = Flux.just(inMemoryRouteRepository, discoveryClientRouteLocator, propertiesRouteLocator);
|
||||
return new CachingRouteLocator(new CompositeRouteLocator(flux));
|
||||
public RouteDefinitionLocator compositeRouteLocator(InMemoryRouteDefinitionRepository inMemoryRouteRepository,
|
||||
DiscoveryClientRouteDefinitionLocator discoveryClientRouteLocator,
|
||||
PropertiesRouteDefinitionLocator propertiesRouteLocator) {
|
||||
Flux<RouteDefinitionLocator> flux = Flux.just(inMemoryRouteRepository, discoveryClientRouteLocator, propertiesRouteLocator);
|
||||
return new CachingRouteDefinitionLocator(new CompositeRouteDefinitionLocator(flux));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,17 +77,17 @@ public class GatewayTestApplication {
|
||||
protected static class GatewayInMemoryConfiguration {
|
||||
|
||||
@Bean
|
||||
public PropertiesRouteLocator propertiesRouteLocator(GatewayProperties properties) {
|
||||
return new PropertiesRouteLocator(properties);
|
||||
public PropertiesRouteDefinitionLocator propertiesRouteLocator(GatewayProperties properties) {
|
||||
return new PropertiesRouteDefinitionLocator(properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public RouteLocator compositeRouteLocator(InMemoryRouteRepository inMemoryRouteRepository,
|
||||
PropertiesRouteLocator propertiesRouteLocator) {
|
||||
Flux<RouteLocator> flux = Flux.just(inMemoryRouteRepository, propertiesRouteLocator);
|
||||
CompositeRouteLocator composite = new CompositeRouteLocator(flux);
|
||||
return new CachingRouteLocator(composite);
|
||||
public RouteDefinitionLocator compositeRouteLocator(InMemoryRouteDefinitionRepository inMemoryRouteRepository,
|
||||
PropertiesRouteDefinitionLocator propertiesRouteLocator) {
|
||||
Flux<RouteDefinitionLocator> flux = Flux.just(inMemoryRouteRepository, propertiesRouteLocator);
|
||||
CompositeRouteDefinitionLocator composite = new CompositeRouteDefinitionLocator(flux);
|
||||
return new CachingRouteDefinitionLocator(composite);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,12 +21,12 @@ import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.cloud.gateway.EnableGateway;
|
||||
import org.springframework.cloud.gateway.api.RouteLocator;
|
||||
import org.springframework.cloud.gateway.api.RouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.config.GatewayProperties;
|
||||
import org.springframework.cloud.gateway.config.PropertiesRouteLocator;
|
||||
import org.springframework.cloud.gateway.support.CachingRouteLocator;
|
||||
import org.springframework.cloud.gateway.support.CompositeRouteLocator;
|
||||
import org.springframework.cloud.gateway.support.InMemoryRouteRepository;
|
||||
import org.springframework.cloud.gateway.config.PropertiesRouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.support.CachingRouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.support.CompositeRouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.support.InMemoryRouteDefinitionRepository;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -39,17 +39,17 @@ import reactor.core.publisher.Flux;
|
||||
@EnableGateway
|
||||
public class GatewaySampleApplication {
|
||||
@Bean
|
||||
public PropertiesRouteLocator propertiesRouteLocator(GatewayProperties properties) {
|
||||
return new PropertiesRouteLocator(properties);
|
||||
public PropertiesRouteDefinitionLocator propertiesRouteLocator(GatewayProperties properties) {
|
||||
return new PropertiesRouteDefinitionLocator(properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public RouteLocator compositeRouteLocator(InMemoryRouteRepository inMemoryRouteRepository,
|
||||
PropertiesRouteLocator propertiesRouteLocator) {
|
||||
Flux<RouteLocator> flux = Flux.just(inMemoryRouteRepository, propertiesRouteLocator);
|
||||
CompositeRouteLocator composite = new CompositeRouteLocator(flux);
|
||||
return new CachingRouteLocator(composite);
|
||||
public RouteDefinitionLocator compositeRouteLocator(InMemoryRouteDefinitionRepository inMemoryRouteRepository,
|
||||
PropertiesRouteDefinitionLocator propertiesRouteLocator) {
|
||||
Flux<RouteDefinitionLocator> flux = Flux.just(inMemoryRouteRepository, propertiesRouteLocator);
|
||||
CompositeRouteDefinitionLocator composite = new CompositeRouteDefinitionLocator(flux);
|
||||
return new CachingRouteDefinitionLocator(composite);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Reference in New Issue
Block a user