Adds toString() to filter factories and predicate factories. (#785)

This allows for easier debugging and visibility into configuration.

Predicates needed custom implementations of and() or() and not() so
that toString() could function.

Actuator endpoint was updated to use the new toString(). To enable
set `spring.cloud.gateway.actuator.verbose.enabled=true`.

fixes gh-784
This commit is contained in:
Spencer Gibb
2019-07-23 11:31:00 -04:00
committed by GitHub
parent 19494b2c7d
commit 1cf449c870
86 changed files with 2054 additions and 559 deletions

View File

@@ -1400,6 +1400,38 @@ management.endpoint.gateway.enabled=true # default value
management.endpoints.web.exposure.include=gateway
----
=== Verbose Actuator Format
A new, more verbose format has been added to Gateway. This adds more detail to each route allowing to view the predicates and filters associated to each route along with any configuration that is available.
`/actuator/gateway/routes`
[source,json]
----
[
{
"predicate": "(Hosts: [**.addrequestheader.org] && Paths: [/headers], match trailing slash: true)",
"route_id": "add_request_header_test",
"filters": [
"[[AddResponseHeader X-Response-Default-Foo = 'Default-Bar'], order = 1]",
"[[AddRequestHeader X-Request-Foo = 'Bar'], order = 1]",
"[[PrefixPath prefix = '/httpbin'], order = 2]"
],
"uri": "lb://testservice",
"order": 0
}
]
----
To enable this feature, set the following property:
.application.properties
[source,properties]
----
spring.cloud.gateway.actuator.verbose.enabled=true
----
This will default to true in a future release.
=== Retrieving route filters
==== Global Filters
To retrieve the <<global-filters,global filters>> applied to all routes, make a `GET` request to `/actuator/gateway/globalfilters`. The resulting response is similar to the following:

View File

@@ -0,0 +1,145 @@
/*
* Copyright 2013-2019 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
*
* https://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.actuate;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.core.Ordered;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* @author Spencer Gibb
*/
public class AbstractGatewayControllerEndpoint implements ApplicationEventPublisherAware {
private static final Log log = LogFactory.getLog(GatewayControllerEndpoint.class);
protected RouteDefinitionLocator routeDefinitionLocator;
protected List<GlobalFilter> globalFilters;
protected List<GatewayFilterFactory> GatewayFilters;
protected RouteDefinitionWriter routeDefinitionWriter;
protected RouteLocator routeLocator;
protected ApplicationEventPublisher publisher;
public AbstractGatewayControllerEndpoint(
RouteDefinitionLocator routeDefinitionLocator,
List<GlobalFilter> globalFilters, List<GatewayFilterFactory> GatewayFilters,
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
this.routeDefinitionLocator = routeDefinitionLocator;
this.globalFilters = globalFilters;
this.GatewayFilters = GatewayFilters;
this.routeDefinitionWriter = routeDefinitionWriter;
this.routeLocator = routeLocator;
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
// TODO: Add uncommited or new but not active routes endpoint
@PostMapping("/refresh")
public Mono<Void> refresh() {
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return Mono.empty();
}
@GetMapping("/globalfilters")
public Mono<HashMap<String, Object>> globalfilters() {
return getNamesToOrders(this.globalFilters);
}
@GetMapping("/routefilters")
public Mono<HashMap<String, Object>> routefilers() {
return getNamesToOrders(this.GatewayFilters);
}
private <T> Mono<HashMap<String, Object>> getNamesToOrders(List<T> list) {
return Flux.fromIterable(list).reduce(new HashMap<>(), this::putItem);
}
private HashMap<String, Object> putItem(HashMap<String, Object> map, Object o) {
Integer order = null;
if (o instanceof Ordered) {
order = ((Ordered) o).getOrder();
}
// filters.put(o.getClass().getName(), order);
map.put(o.toString(), order);
return map;
}
/*
* http POST :8080/admin/gateway/routes/apiaddreqhead uri=http://httpbin.org:80
* predicates:='["Host=**.apiaddrequestheader.org", "Path=/headers"]'
* filters:='["AddRequestHeader=X-Request-ApiFoo, ApiBar"]'
*/
@PostMapping("/routes/{id}")
@SuppressWarnings("unchecked")
public Mono<ResponseEntity<Void>> save(@PathVariable String id,
@RequestBody Mono<RouteDefinition> route) {
return this.routeDefinitionWriter.save(route.map(r -> {
r.setId(id);
log.debug("Saving route: " + route);
return r;
})).then(Mono.defer(() -> Mono
.just(ResponseEntity.created(URI.create("/routes/" + id)).build())));
}
@DeleteMapping("/routes/{id}")
public Mono<ResponseEntity<Object>> delete(@PathVariable String id) {
return this.routeDefinitionWriter.delete(Mono.just(id))
.then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
.onErrorResume(t -> t instanceof NotFoundException,
t -> Mono.just(ResponseEntity.notFound().build()));
}
@GetMapping("/routes/{id}/combinedfilters")
public Mono<HashMap<String, Object>> combinedfilters(@PathVariable String id) {
// TODO: missing global filters
return this.routeLocator.getRoutes().filter(route -> route.getId().equals(id))
.reduce(new HashMap<>(), this::putItem);
}
}

View File

@@ -16,188 +16,71 @@
package org.springframework.cloud.gateway.actuate;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.core.Ordered;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* @author Spencer Gibb
*/
@RestControllerEndpoint(id = "gateway")
public class GatewayControllerEndpoint implements ApplicationEventPublisherAware {
public class GatewayControllerEndpoint extends AbstractGatewayControllerEndpoint {
private static final Log log = LogFactory.getLog(GatewayControllerEndpoint.class);
private RouteDefinitionLocator routeDefinitionLocator;
private List<GlobalFilter> globalFilters;
private List<GatewayFilterFactory> GatewayFilters;
private RouteDefinitionWriter routeDefinitionWriter;
private RouteLocator routeLocator;
private ApplicationEventPublisher publisher;
public GatewayControllerEndpoint(RouteDefinitionLocator routeDefinitionLocator,
List<GlobalFilter> globalFilters, List<GatewayFilterFactory> GatewayFilters,
public GatewayControllerEndpoint(List<GlobalFilter> globalFilters,
List<GatewayFilterFactory> gatewayFilters,
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
this.routeDefinitionLocator = routeDefinitionLocator;
this.globalFilters = globalFilters;
this.GatewayFilters = GatewayFilters;
this.routeDefinitionWriter = routeDefinitionWriter;
this.routeLocator = routeLocator;
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
// TODO: Add uncommited or new but not active routes endpoint
@PostMapping("/refresh")
public Mono<Void> refresh() {
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return Mono.empty();
}
@GetMapping("/globalfilters")
public Mono<HashMap<String, Object>> globalfilters() {
return getNamesToOrders(this.globalFilters);
}
@GetMapping("/routefilters")
public Mono<HashMap<String, Object>> routefilers() {
return getNamesToOrders(this.GatewayFilters);
}
private <T> Mono<HashMap<String, Object>> getNamesToOrders(List<T> list) {
return Flux.fromIterable(list).reduce(new HashMap<>(), this::putItem);
}
private HashMap<String, Object> putItem(HashMap<String, Object> map, Object o) {
Integer order = null;
if (o instanceof Ordered) {
order = ((Ordered) o).getOrder();
}
// filters.put(o.getClass().getName(), order);
map.put(o.toString(), order);
return map;
super(null, globalFilters, gatewayFilters, routeDefinitionWriter, routeLocator);
}
// TODO: Flush out routes without a definition
@GetMapping("/routes")
public Mono<List<Map<String, Object>>> routes() {
Mono<Map<String, RouteDefinition>> routeDefs = this.routeDefinitionLocator
.getRouteDefinitions().collectMap(RouteDefinition::getId);
Mono<List<Route>> routes = this.routeLocator.getRoutes().collectList();
return Mono.zip(routeDefs, routes).map(tuple -> {
Map<String, RouteDefinition> defs = tuple.getT1();
List<Route> routeList = tuple.getT2();
List<Map<String, Object>> allRoutes = new ArrayList<>();
routeList.forEach(route -> {
HashMap<String, Object> r = new HashMap<>();
r.put("route_id", route.getId());
r.put("order", route.getOrder());
if (defs.containsKey(route.getId())) {
r.put("route_definition", defs.get(route.getId()));
}
else {
HashMap<String, Object> obj = new HashMap<>();
obj.put("predicate", route.getPredicate().toString());
if (!route.getFilters().isEmpty()) {
ArrayList<String> filters = new ArrayList<>();
for (GatewayFilter filter : route.getFilters()) {
filters.add(filter.toString());
}
obj.put("filters", filters);
}
if (!obj.isEmpty()) {
r.put("route_object", obj);
}
}
allRoutes.add(r);
});
return allRoutes;
});
public Flux<Map<String, Object>> routes() {
return this.routeLocator.getRoutes().map(this::serialize);
}
/*
* http POST :8080/admin/gateway/routes/apiaddreqhead uri=http://httpbin.org:80
* predicates:='["Host=**.apiaddrequestheader.org", "Path=/headers"]'
* filters:='["AddRequestHeader=X-Request-ApiFoo, ApiBar"]'
*/
@PostMapping("/routes/{id}")
@SuppressWarnings("unchecked")
public Mono<ResponseEntity<Void>> save(@PathVariable String id,
@RequestBody Mono<RouteDefinition> route) {
return this.routeDefinitionWriter.save(route.map(r -> {
r.setId(id);
log.debug("Saving route: " + route);
return r;
})).then(Mono.defer(() -> Mono
.just(ResponseEntity.created(URI.create("/routes/" + id)).build())));
}
Map<String, Object> serialize(Route route) {
HashMap<String, Object> r = new HashMap<>();
r.put("route_id", route.getId());
r.put("uri", route.getUri().toString());
r.put("order", route.getOrder());
r.put("predicate", route.getPredicate().toString());
@DeleteMapping("/routes/{id}")
public Mono<ResponseEntity<Object>> delete(@PathVariable String id) {
return this.routeDefinitionWriter.delete(Mono.just(id))
.then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
.onErrorResume(t -> t instanceof NotFoundException,
t -> Mono.just(ResponseEntity.notFound().build()));
ArrayList<String> filters = new ArrayList<>();
for (int i = 0; i < route.getFilters().size(); i++) {
GatewayFilter gatewayFilter = route.getFilters().get(i);
filters.add(gatewayFilter.toString());
}
r.put("filters", filters);
return r;
}
@GetMapping("/routes/{id}")
public Mono<ResponseEntity<RouteDefinition>> route(@PathVariable String id) {
// TODO: missing RouteLocator
return this.routeDefinitionLocator.getRouteDefinitions()
.filter(route -> route.getId().equals(id)).singleOrEmpty()
public Mono<ResponseEntity<Map<String, Object>>> route(@PathVariable String id) {
// @formatter:off
return this.routeLocator.getRoutes()
.filter(route -> route.getId().equals(id))
.singleOrEmpty()
.map(this::serialize)
.map(ResponseEntity::ok)
.switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
}
@GetMapping("/routes/{id}/combinedfilters")
public Mono<HashMap<String, Object>> combinedfilters(@PathVariable String id) {
// TODO: missing global filters
return this.routeLocator.getRoutes().filter(route -> route.getId().equals(id))
.reduce(new HashMap<>(), this::putItem);
// @formatter:on
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright 2013-2019 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
*
* https://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.actuate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import reactor.core.publisher.Mono;
import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @author Spencer Gibb
*/
@RestControllerEndpoint(id = "gateway")
public class GatewayLegacyControllerEndpoint extends AbstractGatewayControllerEndpoint {
public GatewayLegacyControllerEndpoint(RouteDefinitionLocator routeDefinitionLocator,
List<GlobalFilter> globalFilters, List<GatewayFilterFactory> GatewayFilters,
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
super(routeDefinitionLocator, globalFilters, GatewayFilters,
routeDefinitionWriter, routeLocator);
}
@GetMapping("/routes")
public Mono<List<Map<String, Object>>> routes() {
Mono<Map<String, RouteDefinition>> routeDefs = this.routeDefinitionLocator
.getRouteDefinitions().collectMap(RouteDefinition::getId);
Mono<List<Route>> routes = this.routeLocator.getRoutes().collectList();
return Mono.zip(routeDefs, routes).map(tuple -> {
Map<String, RouteDefinition> defs = tuple.getT1();
List<Route> routeList = tuple.getT2();
List<Map<String, Object>> allRoutes = new ArrayList<>();
routeList.forEach(route -> {
HashMap<String, Object> r = new HashMap<>();
r.put("route_id", route.getId());
r.put("order", route.getOrder());
if (defs.containsKey(route.getId())) {
r.put("route_definition", defs.get(route.getId()));
}
else {
HashMap<String, Object> obj = new HashMap<>();
obj.put("predicate", route.getPredicate().toString());
if (!route.getFilters().isEmpty()) {
ArrayList<String> filters = new ArrayList<>();
for (GatewayFilter filter : route.getFilters()) {
filters.add(filter.toString());
}
obj.put("filters", filters);
}
if (!obj.isEmpty()) {
r.put("route_object", obj);
}
}
allRoutes.add(r);
});
return allRoutes;
});
}
@GetMapping("/routes/{id}")
public Mono<ResponseEntity<RouteDefinition>> route(@PathVariable String id) {
// TODO: missing RouteLocator
return this.routeDefinitionLocator.getRouteDefinitions()
.filter(route -> route.getId().equals(id)).singleOrEmpty()
.map(ResponseEntity::ok)
.switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
}
}

View File

@@ -39,11 +39,13 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.NoneNestedConditions;
import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint;
import org.springframework.cloud.gateway.actuate.GatewayLegacyControllerEndpoint;
import org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter;
import org.springframework.cloud.gateway.filter.ForwardPathFilter;
import org.springframework.cloud.gateway.filter.ForwardRoutingFilter;
@@ -120,6 +122,7 @@ import org.springframework.cloud.gateway.support.StringToZonedDateTimeConverter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
@@ -645,14 +648,39 @@ public class GatewayAutoConfiguration {
protected static class GatewayActuatorConfiguration {
@Bean
@ConditionalOnProperty("spring.cloud.gateway.actuator.verbose.enabled")
@ConditionalOnEnabledEndpoint
public GatewayControllerEndpoint gatewayControllerEndpoint(
List<GlobalFilter> globalFilters,
List<GatewayFilterFactory> gatewayFilters,
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
return new GatewayControllerEndpoint(globalFilters, gatewayFilters,
routeDefinitionWriter, routeLocator);
}
@Bean
@Conditional(OnVerboseDisabledCondition.class)
@ConditionalOnEnabledEndpoint
public GatewayLegacyControllerEndpoint gatewayLegacyControllerEndpoint(
RouteDefinitionLocator routeDefinitionLocator,
List<GlobalFilter> globalFilters,
List<GatewayFilterFactory> GatewayFilters,
List<GatewayFilterFactory> gatewayFilters,
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
return new GatewayControllerEndpoint(routeDefinitionLocator, globalFilters,
GatewayFilters, routeDefinitionWriter, routeLocator);
return new GatewayLegacyControllerEndpoint(routeDefinitionLocator,
globalFilters, gatewayFilters, routeDefinitionWriter, routeLocator);
}
}
private static class OnVerboseDisabledCondition extends NoneNestedConditions {
OnVerboseDisabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnProperty("spring.cloud.gateway.actuator.verbose.enabled")
static class VerboseDisabled {
}
}

View File

@@ -51,11 +51,8 @@ public class OrderedGatewayFilter implements GatewayFilter, Ordered {
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("OrderedGatewayFilter{");
sb.append("delegate=").append(delegate);
sb.append(", order=").append(order);
sb.append('}');
return sb.toString();
return new StringBuilder("[").append(delegate).append(", order = ").append(order)
.append("]").toString();
}
}

View File

@@ -16,8 +16,14 @@
package org.springframework.cloud.gateway.filter.factory;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
/**
* @author Spencer Gibb
@@ -27,11 +33,21 @@ public class AddRequestHeaderGatewayFilterFactory
@Override
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest().mutate()
.header(config.getName(), config.getValue()).build();
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest().mutate()
.header(config.getName(), config.getValue()).build();
return chain.filter(exchange.mutate().request(request).build());
return chain.filter(exchange.mutate().request(request).build());
}
@Override
public String toString() {
return filterToStringCreator(AddRequestHeaderGatewayFilterFactory.this)
.append(config.getName(), config.getValue()).toString();
}
};
}

View File

@@ -18,11 +18,17 @@ package org.springframework.cloud.gateway.filter.factory;
import java.net.URI;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
/**
* @author Spencer Gibb
*/
@@ -31,35 +37,45 @@ public class AddRequestParameterGatewayFilterFactory
@Override
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> {
URI uri = exchange.getRequest().getURI();
StringBuilder query = new StringBuilder();
String originalQuery = uri.getRawQuery();
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
URI uri = exchange.getRequest().getURI();
StringBuilder query = new StringBuilder();
String originalQuery = uri.getRawQuery();
if (StringUtils.hasText(originalQuery)) {
query.append(originalQuery);
if (originalQuery.charAt(originalQuery.length() - 1) != '&') {
query.append('&');
if (StringUtils.hasText(originalQuery)) {
query.append(originalQuery);
if (originalQuery.charAt(originalQuery.length() - 1) != '&') {
query.append('&');
}
}
// TODO urlencode?
query.append(config.getName());
query.append('=');
query.append(config.getValue());
try {
URI newUri = UriComponentsBuilder.fromUri(uri)
.replaceQuery(query.toString()).build(true).toUri();
ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri)
.build();
return chain.filter(exchange.mutate().request(request).build());
}
catch (RuntimeException ex) {
throw new IllegalStateException(
"Invalid URI query: \"" + query.toString() + "\"");
}
}
// TODO urlencode?
query.append(config.getName());
query.append('=');
query.append(config.getValue());
try {
URI newUri = UriComponentsBuilder.fromUri(uri)
.replaceQuery(query.toString()).build(true).toUri();
ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri)
.build();
return chain.filter(exchange.mutate().request(request).build());
}
catch (RuntimeException ex) {
throw new IllegalStateException(
"Invalid URI query: \"" + query.toString() + "\"");
@Override
public String toString() {
return filterToStringCreator(AddRequestParameterGatewayFilterFactory.this)
.append(config.getName(), config.getValue()).toString();
}
};
}

View File

@@ -16,7 +16,13 @@
package org.springframework.cloud.gateway.filter.factory;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
/**
* @author Spencer Gibb
@@ -26,10 +32,21 @@ public class AddResponseHeaderGatewayFilterFactory
@Override
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> {
exchange.getResponse().getHeaders().add(config.getName(), config.getValue());
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
exchange.getResponse().getHeaders().add(config.getName(),
config.getValue());
return chain.filter(exchange);
return chain.filter(exchange);
}
@Override
public String toString() {
return filterToStringCreator(AddResponseHeaderGatewayFilterFactory.this)
.append(config.getName(), config.getValue()).toString();
}
};
}

View File

@@ -23,7 +23,11 @@ import java.util.stream.Collectors;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpHeaders;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
/*
Use case: Both your legacy backend and your API gateway add CORS header values. So, your consumer ends up with
@@ -81,9 +85,22 @@ public class DedupeResponseHeaderGatewayFilterFactory extends
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> chain.filter(exchange).then(Mono.fromRunnable(() -> {
dedupe(exchange.getResponse().getHeaders(), config);
}));
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
return chain.filter(exchange).then(Mono.fromRunnable(
() -> dedupe(exchange.getResponse().getHeaders(), config)));
}
@Override
public String toString() {
return filterToStringCreator(
DedupeResponseHeaderGatewayFilterFactory.this)
.append(config.getName(), config.getStrategy())
.toString();
}
};
}
public enum Strategy {

View File

@@ -47,6 +47,7 @@ import org.springframework.web.util.UriComponentsBuilder;
import static java.util.Collections.singletonList;
import static java.util.Optional.ofNullable;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.containsEncodedParts;
@@ -113,42 +114,55 @@ public class HystrixGatewayFilterFactory
config.setter = Setter.withGroupKey(groupKey).andCommandKey(commandKey);
}
return (exchange, chain) -> {
RouteHystrixCommand command = new RouteHystrixCommand(config.setter,
config.fallbackUri, exchange, chain);
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
RouteHystrixCommand command = new RouteHystrixCommand(config.setter,
config.fallbackUri, exchange, chain);
return Mono.create(s -> {
Subscription sub = command.toObservable().subscribe(s::success, s::error,
s::success);
s.onCancel(sub::unsubscribe);
}).onErrorResume((Function<Throwable, Mono<Void>>) throwable -> {
if (throwable instanceof HystrixRuntimeException) {
HystrixRuntimeException e = (HystrixRuntimeException) throwable;
HystrixRuntimeException.FailureType failureType = e.getFailureType();
return Mono.create(s -> {
Subscription sub = command.toObservable().subscribe(s::success,
s::error, s::success);
s.onCancel(sub::unsubscribe);
}).onErrorResume((Function<Throwable, Mono<Void>>) throwable -> {
if (throwable instanceof HystrixRuntimeException) {
HystrixRuntimeException e = (HystrixRuntimeException) throwable;
HystrixRuntimeException.FailureType failureType = e
.getFailureType();
switch (failureType) {
case TIMEOUT:
return Mono.error(new TimeoutException());
case COMMAND_EXCEPTION: {
Throwable cause = e.getCause();
switch (failureType) {
case TIMEOUT:
return Mono.error(new TimeoutException());
case COMMAND_EXCEPTION: {
Throwable cause = e.getCause();
/*
* We forsake here the null check for cause as
* HystrixRuntimeException will always have a cause if the failure
* type is COMMAND_EXCEPTION.
*/
if (cause instanceof ResponseStatusException
|| AnnotatedElementUtils.findMergedAnnotation(
cause.getClass(), ResponseStatus.class) != null) {
return Mono.error(cause);
/*
* We forsake here the null check for cause as
* HystrixRuntimeException will always have a cause if the
* failure type is COMMAND_EXCEPTION.
*/
if (cause instanceof ResponseStatusException
|| AnnotatedElementUtils.findMergedAnnotation(
cause.getClass(),
ResponseStatus.class) != null) {
return Mono.error(cause);
}
}
default:
break;
}
}
default:
break;
}
}
return Mono.error(throwable);
}).then();
return Mono.error(throwable);
}).then();
}
@Override
public String toString() {
return filterToStringCreator(HystrixGatewayFilterFactory.this)
.append("name", config.getName())
.append("fallback", config.fallbackUri).toString();
}
};
}

View File

@@ -21,10 +21,14 @@ import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ALREADY_PREFIXED_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl;
@@ -54,29 +58,38 @@ public class PrefixPathGatewayFilterFactory
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
boolean alreadyPrefixed = exchange
.getAttributeOrDefault(GATEWAY_ALREADY_PREFIXED_ATTR, false);
if (alreadyPrefixed) {
return chain.filter(exchange);
}
exchange.getAttributes().put(GATEWAY_ALREADY_PREFIXED_ATTR, true);
boolean alreadyPrefixed = exchange
.getAttributeOrDefault(GATEWAY_ALREADY_PREFIXED_ATTR, false);
if (alreadyPrefixed) {
return chain.filter(exchange);
}
exchange.getAttributes().put(GATEWAY_ALREADY_PREFIXED_ATTR, true);
ServerHttpRequest req = exchange.getRequest();
addOriginalRequestUrl(exchange, req.getURI());
String newPath = config.prefix + req.getURI().getRawPath();
ServerHttpRequest req = exchange.getRequest();
addOriginalRequestUrl(exchange, req.getURI());
String newPath = config.prefix + req.getURI().getRawPath();
ServerHttpRequest request = req.mutate().path(newPath).build();
ServerHttpRequest request = req.mutate().path(newPath).build();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, request.getURI());
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, request.getURI());
if (log.isTraceEnabled()) {
log.trace("Prefixed URI with: " + config.prefix + " -> "
+ request.getURI());
}
if (log.isTraceEnabled()) {
log.trace("Prefixed URI with: " + config.prefix + " -> "
+ request.getURI());
return chain.filter(exchange.mutate().request(request).build());
}
return chain.filter(exchange.mutate().request(request).build());
@Override
public String toString() {
return filterToStringCreator(PrefixPathGatewayFilterFactory.this)
.append("prefix", config.getPrefix()).toString();
}
};
}

View File

@@ -16,8 +16,13 @@
package org.springframework.cloud.gateway.filter.factory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.PRESERVE_HOST_HEADER_ATTRIBUTE;
/**
@@ -31,9 +36,19 @@ public class PreserveHostHeaderGatewayFilterFactory extends AbstractGatewayFilte
}
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
exchange.getAttributes().put(PRESERVE_HOST_HEADER_ATTRIBUTE, true);
return chain.filter(exchange);
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
exchange.getAttributes().put(PRESERVE_HOST_HEADER_ATTRIBUTE, true);
return chain.filter(exchange);
}
@Override
public String toString() {
return filterToStringCreator(PreserveHostHeaderGatewayFilterFactory.this)
.toString();
}
};
}

View File

@@ -23,12 +23,15 @@ import java.util.List;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.support.HttpStatusHolder;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.Assert;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.setResponseStatus;
/**
@@ -74,16 +77,33 @@ public class RedirectToGatewayFilterFactory
}
public GatewayFilter apply(HttpStatusHolder httpStatus, URI uri) {
return (exchange, chain) -> chain.filter(exchange).then(Mono.defer(() -> {
if (!exchange.getResponse().isCommitted()) {
setResponseStatus(exchange, httpStatus);
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
if (!exchange.getResponse().isCommitted()) {
setResponseStatus(exchange, httpStatus);
final ServerHttpResponse response = exchange.getResponse();
response.getHeaders().set(HttpHeaders.LOCATION, uri.toString());
return response.setComplete();
final ServerHttpResponse response = exchange.getResponse();
response.getHeaders().set(HttpHeaders.LOCATION, uri.toString());
return response.setComplete();
}
return Mono.empty();
}
return Mono.empty();
}));
@Override
public String toString() {
String status;
if (httpStatus.getHttpStatus() != null) {
status = String.valueOf(httpStatus.getHttpStatus().value());
}
else {
status = httpStatus.getStatus().toString();
}
return filterToStringCreator(RedirectToGatewayFilterFactory.this)
.append(status, uri).toString();
}
};
}
public static class Config {

View File

@@ -19,8 +19,14 @@ package org.springframework.cloud.gateway.filter.factory;
import java.util.Arrays;
import java.util.List;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
/**
* @author Spencer Gibb
@@ -39,11 +45,22 @@ public class RemoveRequestHeaderGatewayFilterFactory
@Override
public GatewayFilter apply(NameConfig config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest().mutate()
.headers(httpHeaders -> httpHeaders.remove(config.getName())).build();
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest().mutate()
.headers(httpHeaders -> httpHeaders.remove(config.getName()))
.build();
return chain.filter(exchange.mutate().request(request).build());
return chain.filter(exchange.mutate().request(request).build());
}
@Override
public String toString() {
return filterToStringCreator(RemoveRequestHeaderGatewayFilterFactory.this)
.append("name", config.getName()).toString();
}
};
}

View File

@@ -22,6 +22,10 @@ import java.util.List;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
/**
* @author Spencer Gibb
@@ -40,9 +44,21 @@ public class RemoveResponseHeaderGatewayFilterFactory
@Override
public GatewayFilter apply(NameConfig config) {
return (exchange, chain) -> chain.filter(exchange).then(Mono.fromRunnable(() -> {
exchange.getResponse().getHeaders().remove(config.getName());
}));
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
return chain.filter(exchange).then(Mono.fromRunnable(() -> exchange
.getResponse().getHeaders().remove(config.getName())));
}
@Override
public String toString() {
return filterToStringCreator(
RemoveResponseHeaderGatewayFilterFactory.this)
.append("name", config.getName()).toString();
}
};
}
}

View File

@@ -27,8 +27,12 @@ import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
/**
* This filter changes the request uri by a request header.
*
@@ -49,6 +53,21 @@ public class RequestHeaderToRequestUriGatewayFilterFactory extends
return Arrays.asList(NAME_KEY);
}
@Override
public GatewayFilter apply(NameConfig config) {
// AbstractChangeRequestUriGatewayFilterFactory.apply() returns
// OrderedGatewayFilter
OrderedGatewayFilter gatewayFilter = (OrderedGatewayFilter) super.apply(config);
return new OrderedGatewayFilter(gatewayFilter, gatewayFilter.getOrder()) {
@Override
public String toString() {
return filterToStringCreator(
RequestHeaderToRequestUriGatewayFilterFactory.this)
.append("name", config.getName()).toString();
}
};
}
@Override
protected Optional<URI> determineRequestUri(ServerWebExchange exchange,
NameConfig config) {

View File

@@ -16,11 +16,17 @@
package org.springframework.cloud.gateway.filter.factory;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
/**
* This filter blocks the request, if the request size is more than the permissible size.
@@ -59,27 +65,39 @@ public class RequestSizeGatewayFilterFactory extends
public GatewayFilter apply(
RequestSizeGatewayFilterFactory.RequestSizeConfig requestSizeConfig) {
requestSizeConfig.validate();
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
String contentLength = request.getHeaders().getFirst("content-length");
if (!StringUtils.isEmpty(contentLength)) {
Long currentRequestSize = Long.valueOf(contentLength);
if (currentRequestSize > requestSizeConfig.getMaxSize()) {
exchange.getResponse().setStatusCode(HttpStatus.PAYLOAD_TOO_LARGE);
if (!exchange.getResponse().isCommitted()) {
exchange.getResponse().getHeaders().add("errorMessage",
getErrorMessage(currentRequestSize,
requestSizeConfig.getMaxSize()));
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String contentLength = request.getHeaders().getFirst("content-length");
if (!StringUtils.isEmpty(contentLength)) {
Long currentRequestSize = Long.valueOf(contentLength);
if (currentRequestSize > requestSizeConfig.getMaxSize()) {
exchange.getResponse()
.setStatusCode(HttpStatus.PAYLOAD_TOO_LARGE);
if (!exchange.getResponse().isCommitted()) {
exchange.getResponse().getHeaders().add("errorMessage",
getErrorMessage(currentRequestSize,
requestSizeConfig.getMaxSize()));
}
return exchange.getResponse().setComplete();
}
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public String toString() {
return filterToStringCreator(RequestSizeGatewayFilterFactory.this)
.append("max", requestSizeConfig.getMaxSize()).toString();
}
return chain.filter(exchange);
};
}
public static class RequestSizeConfig {
// TODO: use boot data size type
private Long maxSize = 5000000L;
public Long getMaxSize() {
@@ -92,6 +110,7 @@ public class RequestSizeGatewayFilterFactory extends
return this;
}
// TODO: use validator annotation
public void validate() {
Assert.isTrue(this.maxSize != null && this.maxSize > 0,
"maxSize must be greater than 0");

View File

@@ -35,6 +35,7 @@ import reactor.retry.RetryContext;
import org.springframework.cloud.gateway.event.EnableBodyCachingEvent;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.support.HasRouteId;
import org.springframework.cloud.gateway.support.TimeoutException;
import org.springframework.http.HttpMethod;
@@ -43,6 +44,7 @@ import org.springframework.http.HttpStatus.Series;
import org.springframework.util.Assert;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.CLIENT_RESPONSE_HEADER_NAMES;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ALREADY_ROUTED_ATTR;
@@ -132,7 +134,25 @@ public class RetryGatewayFilterFactory
.retryMax(retryConfig.getRetries());
}
return apply(retryConfig.getRouteId(), statusCodeRepeat, exceptionRetry);
GatewayFilter gatewayFilter = apply(retryConfig.getRouteId(), statusCodeRepeat,
exceptionRetry);
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
return gatewayFilter.filter(exchange, chain);
}
@Override
public String toString() {
return filterToStringCreator(RetryGatewayFilterFactory.this)
.append("retries", retryConfig.getRetries())
.append("series", retryConfig.getSeries())
.append("statuses", retryConfig.getStatuses())
.append("methods", retryConfig.getMethods())
.append("exceptions", retryConfig.getExceptions()).toString();
}
};
}
public boolean exceedsMaxIterations(ServerWebExchange exchange,

View File

@@ -19,9 +19,14 @@ package org.springframework.cloud.gateway.filter.factory;
import java.util.Arrays;
import java.util.List;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.http.server.reactive.ServerHttpRequest;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl;
@@ -53,17 +58,27 @@ public class RewritePathGatewayFilterFactory
@Override
public GatewayFilter apply(Config config) {
String replacement = config.replacement.replace("$\\", "$");
return (exchange, chain) -> {
ServerHttpRequest req = exchange.getRequest();
addOriginalRequestUrl(exchange, req.getURI());
String path = req.getURI().getRawPath();
String newPath = path.replaceAll(config.regexp, replacement);
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
ServerHttpRequest req = exchange.getRequest();
addOriginalRequestUrl(exchange, req.getURI());
String path = req.getURI().getRawPath();
String newPath = path.replaceAll(config.regexp, replacement);
ServerHttpRequest request = req.mutate().path(newPath).build();
ServerHttpRequest request = req.mutate().path(newPath).build();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, request.getURI());
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, request.getURI());
return chain.filter(exchange.mutate().request(request).build());
return chain.filter(exchange.mutate().request(request).build());
}
@Override
public String toString() {
return filterToStringCreator(RewritePathGatewayFilterFactory.this)
.append(config.getRegexp(), replacement).toString();
}
};
}

View File

@@ -22,8 +22,11 @@ import java.util.List;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
/**
* @author Vitaliy Pavlyuk
*/
@@ -51,9 +54,24 @@ public class RewriteResponseHeaderGatewayFilterFactory extends
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> chain.filter(exchange).then(Mono.fromRunnable(() -> {
rewriteHeader(exchange, config);
}));
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
return chain.filter(exchange)
.then(Mono.fromRunnable(() -> rewriteHeader(exchange, config)));
}
@Override
public String toString() {
return filterToStringCreator(
RewriteResponseHeaderGatewayFilterFactory.this)
.append("name", config.getName())
.append("regexp", config.getRegexp())
.append("replacement", config.getReplacement())
.toString();
}
};
}
protected void rewriteHeader(ServerWebExchange exchange, Config config) {

View File

@@ -16,9 +16,15 @@
package org.springframework.cloud.gateway.filter.factory;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebSession;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
/**
* Save the current {@link WebSession} before executing the rest of the
* {@link org.springframework.cloud.gateway.filter.GatewayFilterChain}.
@@ -33,8 +39,20 @@ public class SaveSessionGatewayFilterFactory extends AbstractGatewayFilterFactor
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> exchange.getSession().map(WebSession::save)
.then(chain.filter(exchange));
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
return exchange.getSession().map(WebSession::save)
.then(chain.filter(exchange));
}
@Override
public String toString() {
return filterToStringCreator(SaveSessionGatewayFilterFactory.this)
.toString();
}
};
}
}

View File

@@ -18,8 +18,14 @@ package org.springframework.cloud.gateway.filter.factory;
import java.util.List;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpHeaders;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
/**
* https://blog.appcanary.com/2017/http-security-headers.html.
@@ -78,48 +84,60 @@ public class SecureHeadersGatewayFilterFactory extends AbstractGatewayFilterFact
public GatewayFilter apply(Object config) {
// TODO: allow args to override properties
return (exchange, chain) -> {
HttpHeaders headers = exchange.getResponse().getHeaders();
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
HttpHeaders headers = exchange.getResponse().getHeaders();
List<String> disabled = properties.getDisable();
List<String> disabled = properties.getDisable();
if (isEnabled(disabled, X_XSS_PROTECTION_HEADER)) {
headers.add(X_XSS_PROTECTION_HEADER, properties.getXssProtectionHeader());
if (isEnabled(disabled, X_XSS_PROTECTION_HEADER)) {
headers.add(X_XSS_PROTECTION_HEADER,
properties.getXssProtectionHeader());
}
if (isEnabled(disabled, STRICT_TRANSPORT_SECURITY_HEADER)) {
headers.add(STRICT_TRANSPORT_SECURITY_HEADER,
properties.getStrictTransportSecurity());
}
if (isEnabled(disabled, X_FRAME_OPTIONS_HEADER)) {
headers.add(X_FRAME_OPTIONS_HEADER, properties.getFrameOptions());
}
if (isEnabled(disabled, X_CONTENT_TYPE_OPTIONS_HEADER)) {
headers.add(X_CONTENT_TYPE_OPTIONS_HEADER,
properties.getContentTypeOptions());
}
if (isEnabled(disabled, REFERRER_POLICY_HEADER)) {
headers.add(REFERRER_POLICY_HEADER, properties.getReferrerPolicy());
}
if (isEnabled(disabled, CONTENT_SECURITY_POLICY_HEADER)) {
headers.add(CONTENT_SECURITY_POLICY_HEADER,
properties.getContentSecurityPolicy());
}
if (isEnabled(disabled, X_DOWNLOAD_OPTIONS_HEADER)) {
headers.add(X_DOWNLOAD_OPTIONS_HEADER,
properties.getDownloadOptions());
}
if (isEnabled(disabled, X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER)) {
headers.add(X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER,
properties.getPermittedCrossDomainPolicies());
}
return chain.filter(exchange);
}
if (isEnabled(disabled, STRICT_TRANSPORT_SECURITY_HEADER)) {
headers.add(STRICT_TRANSPORT_SECURITY_HEADER,
properties.getStrictTransportSecurity());
@Override
public String toString() {
return filterToStringCreator(SecureHeadersGatewayFilterFactory.this)
.toString();
}
if (isEnabled(disabled, X_FRAME_OPTIONS_HEADER)) {
headers.add(X_FRAME_OPTIONS_HEADER, properties.getFrameOptions());
}
if (isEnabled(disabled, X_CONTENT_TYPE_OPTIONS_HEADER)) {
headers.add(X_CONTENT_TYPE_OPTIONS_HEADER,
properties.getContentTypeOptions());
}
if (isEnabled(disabled, REFERRER_POLICY_HEADER)) {
headers.add(REFERRER_POLICY_HEADER, properties.getReferrerPolicy());
}
if (isEnabled(disabled, CONTENT_SECURITY_POLICY_HEADER)) {
headers.add(CONTENT_SECURITY_POLICY_HEADER,
properties.getContentSecurityPolicy());
}
if (isEnabled(disabled, X_DOWNLOAD_OPTIONS_HEADER)) {
headers.add(X_DOWNLOAD_OPTIONS_HEADER, properties.getDownloadOptions());
}
if (isEnabled(disabled, X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER)) {
headers.add(X_PERMITTED_CROSS_DOMAIN_POLICIES_HEADER,
properties.getPermittedCrossDomainPolicies());
}
return chain.filter(exchange);
};
}

View File

@@ -21,10 +21,15 @@ import java.util.Arrays;
import java.util.List;
import java.util.Map;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriTemplate;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.getUriTemplateVariables;
@@ -53,20 +58,30 @@ public class SetPathGatewayFilterFactory
public GatewayFilter apply(Config config) {
UriTemplate uriTemplate = new UriTemplate(config.template);
return (exchange, chain) -> {
ServerHttpRequest req = exchange.getRequest();
addOriginalRequestUrl(exchange, req.getURI());
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
ServerHttpRequest req = exchange.getRequest();
addOriginalRequestUrl(exchange, req.getURI());
Map<String, String> uriVariables = getUriTemplateVariables(exchange);
Map<String, String> uriVariables = getUriTemplateVariables(exchange);
URI uri = uriTemplate.expand(uriVariables);
String newPath = uri.getRawPath();
URI uri = uriTemplate.expand(uriVariables);
String newPath = uri.getRawPath();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, uri);
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, uri);
ServerHttpRequest request = req.mutate().path(newPath).build();
ServerHttpRequest request = req.mutate().path(newPath).build();
return chain.filter(exchange.mutate().request(request).build());
return chain.filter(exchange.mutate().request(request).build());
}
@Override
public String toString() {
return filterToStringCreator(SetPathGatewayFilterFactory.this)
.append("template", config.getTemplate()).toString();
}
};
}

View File

@@ -16,8 +16,14 @@
package org.springframework.cloud.gateway.filter.factory;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
/**
* @author Spencer Gibb
@@ -27,12 +33,23 @@ public class SetRequestHeaderGatewayFilterFactory
@Override
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest().mutate()
.headers(httpHeaders -> httpHeaders.set(config.name, config.value))
.build();
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest().mutate()
.headers(
httpHeaders -> httpHeaders.set(config.name, config.value))
.build();
return chain.filter(exchange.mutate().request(request).build());
return chain.filter(exchange.mutate().request(request).build());
}
@Override
public String toString() {
return filterToStringCreator(SetRequestHeaderGatewayFilterFactory.this)
.append(config.getName(), config.getValue()).toString();
}
};
}

View File

@@ -19,6 +19,10 @@ package org.springframework.cloud.gateway.filter.factory;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
/**
* @author Spencer Gibb
@@ -28,9 +32,20 @@ public class SetResponseHeaderGatewayFilterFactory
@Override
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> chain.filter(exchange).then(Mono.fromRunnable(() -> {
exchange.getResponse().getHeaders().set(config.name, config.value);
}));
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
return chain.filter(exchange).then(Mono.fromRunnable(() -> exchange
.getResponse().getHeaders().set(config.name, config.value)));
}
@Override
public String toString() {
return filterToStringCreator(SetResponseHeaderGatewayFilterFactory.this)
.append(config.getName(), config.getValue()).toString();
}
};
}
}

View File

@@ -22,8 +22,11 @@ import java.util.List;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.support.HttpStatusHolder;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.setResponseStatus;
/**
@@ -49,21 +52,31 @@ public class SetStatusGatewayFilterFactory
@Override
public GatewayFilter apply(Config config) {
HttpStatusHolder statusHolder = HttpStatusHolder.parse(config.status);
return (exchange, chain) -> {
// option 1 (runs in filter order)
/*
* exchange.getResponse().beforeCommit(() -> {
* exchange.getResponse().setStatusCode(finalStatus); return Mono.empty(); });
* return chain.filter(exchange);
*/
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
// option 1 (runs in filter order)
/*
* exchange.getResponse().beforeCommit(() -> {
* exchange.getResponse().setStatusCode(finalStatus); return Mono.empty();
* }); return chain.filter(exchange);
*/
// option 2 (runs in reverse filter order)
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
// check not really needed, since it is guarded in setStatusCode,
// but it's a good example
setResponseStatus(exchange, statusHolder);
}));
// option 2 (runs in reverse filter order)
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
// check not really needed, since it is guarded in setStatusCode,
// but it's a good example
setResponseStatus(exchange, statusHolder);
}));
}
@Override
public String toString() {
return filterToStringCreator(SetStatusGatewayFilterFactory.this)
.append("status", config.getStatus()).toString();
}
};
}

View File

@@ -20,10 +20,15 @@ import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl;
@@ -52,19 +57,30 @@ public class StripPrefixGatewayFilterFactory
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
addOriginalRequestUrl(exchange, request.getURI());
String path = request.getURI().getRawPath();
String newPath = "/"
+ Arrays.stream(StringUtils.tokenizeToStringArray(path, "/"))
.skip(config.parts).collect(Collectors.joining("/"));
newPath += (newPath.length() > 1 && path.endsWith("/") ? "/" : "");
ServerHttpRequest newRequest = request.mutate().path(newPath).build();
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
addOriginalRequestUrl(exchange, request.getURI());
String path = request.getURI().getRawPath();
String newPath = "/"
+ Arrays.stream(StringUtils.tokenizeToStringArray(path, "/"))
.skip(config.parts).collect(Collectors.joining("/"));
newPath += (newPath.length() > 1 && path.endsWith("/") ? "/" : "");
ServerHttpRequest newRequest = request.mutate().path(newPath).build();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR,
newRequest.getURI());
return chain.filter(exchange.mutate().request(newRequest).build());
return chain.filter(exchange.mutate().request(newRequest).build());
}
@Override
public String toString() {
return filterToStringCreator(StripPrefixGatewayFilterFactory.this)
.append("parts", config.getParts()).toString();
}
};
}

View File

@@ -23,6 +23,7 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.core.io.buffer.DataBuffer;
@@ -37,8 +38,10 @@ import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
/**
* This filter is BETA and may be subject to change in a future release.
* GatewayFilter that modifies the request body.
*/
public class ModifyRequestBodyGatewayFilterFactory extends
AbstractGatewayFilterFactory<ModifyRequestBodyGatewayFilterFactory.Config> {
@@ -58,40 +61,52 @@ public class ModifyRequestBodyGatewayFilterFactory extends
@Override
@SuppressWarnings("unchecked")
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
Class inClass = config.getInClass();
ServerRequest serverRequest = ServerRequest.create(exchange,
this.messageReaders);
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
Class inClass = config.getInClass();
ServerRequest serverRequest = ServerRequest.create(exchange,
messageReaders);
// TODO: flux or mono
Mono<?> modifiedBody = serverRequest.bodyToMono(inClass)
// .log("modify_request_mono", Level.INFO)
.flatMap(o -> config.rewriteFunction.apply(exchange, o));
// TODO: flux or mono
Mono<?> modifiedBody = serverRequest.bodyToMono(inClass)
// .log("modify_request_mono", Level.INFO)
.flatMap(o -> config.rewriteFunction.apply(exchange, o));
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody,
config.getOutClass());
HttpHeaders headers = new HttpHeaders();
headers.putAll(exchange.getRequest().getHeaders());
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody,
config.getOutClass());
HttpHeaders headers = new HttpHeaders();
headers.putAll(exchange.getRequest().getHeaders());
// the new content type will be computed by bodyInserter
// and then set in the request decorator
headers.remove(HttpHeaders.CONTENT_LENGTH);
// the new content type will be computed by bodyInserter
// and then set in the request decorator
headers.remove(HttpHeaders.CONTENT_LENGTH);
// if the body is changing content types, set it here, to the bodyInserter
// will know about it
if (config.getContentType() != null) {
headers.set(HttpHeaders.CONTENT_TYPE, config.getContentType());
// if the body is changing content types, set it here, to the bodyInserter
// will know about it
if (config.getContentType() != null) {
headers.set(HttpHeaders.CONTENT_TYPE, config.getContentType());
}
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(
exchange, headers);
return bodyInserter.insert(outputMessage, new BodyInserterContext())
// .log("modify_request", Level.INFO)
.then(Mono.defer(() -> {
ServerHttpRequest decorator = decorate(exchange, headers,
outputMessage);
return chain
.filter(exchange.mutate().request(decorator).build());
}));
}
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange,
headers);
return bodyInserter.insert(outputMessage, new BodyInserterContext())
// .log("modify_request", Level.INFO)
.then(Mono.defer(() -> {
ServerHttpRequest decorator = decorate(exchange, headers,
outputMessage);
return chain.filter(exchange.mutate().request(decorator).build());
}));
@Override
public String toString() {
return filterToStringCreator(ModifyRequestBodyGatewayFilterFactory.this)
.append("Content type", config.getContentType())
.append("In class", config.getInClass())
.append("Out class", config.getOutClass()).toString();
}
};
}

View File

@@ -26,6 +26,7 @@ import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
@@ -38,10 +39,11 @@ import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR;
/**
* This filter is BETA and may be subject to change in a future release.
* GatewayFilter that modifies the respons body.
*/
public class ModifyResponseBodyGatewayFilterFactory extends
AbstractGatewayFilterFactory<ModifyResponseBodyGatewayFilterFactory.Config> {
@@ -57,7 +59,10 @@ public class ModifyResponseBodyGatewayFilterFactory extends
@Override
public GatewayFilter apply(Config config) {
return new ModifyResponseGatewayFilter(config);
ModifyResponseGatewayFilter gatewayFilter = new ModifyResponseGatewayFilter(
config);
gatewayFilter.setFactory(this);
return gatewayFilter;
}
public static class Config {
@@ -142,12 +147,13 @@ public class ModifyResponseBodyGatewayFilterFactory extends
private final Config config;
private GatewayFilterFactory<Config> gatewayFilterFactory;
public ModifyResponseGatewayFilter(Config config) {
this.config = config;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange.mutate().response(decorate(exchange)).build());
}
@@ -212,6 +218,20 @@ public class ModifyResponseBodyGatewayFilterFactory extends
return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
}
@Override
public String toString() {
Object obj = (this.gatewayFilterFactory != null) ? this.gatewayFilterFactory
: this;
return filterToStringCreator(obj)
.append("New content type", config.getNewContentType())
.append("In class", config.getInClass())
.append("Out class", config.getOutClass()).toString();
}
public void setFactory(GatewayFilterFactory<Config> gatewayFilterFactory) {
this.gatewayFilterFactory = gatewayFilterFactory;
}
}
}

View File

@@ -17,12 +17,15 @@
package org.springframework.cloud.gateway.handler;
import java.util.function.Function;
import java.util.function.Predicate;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.util.Assert;
import org.springframework.web.server.ServerWebExchange;
/**
* @author Ben Hale
@@ -30,21 +33,115 @@ import org.springframework.util.Assert;
public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {
default AsyncPredicate<T> and(AsyncPredicate<? super T> other) {
Assert.notNull(other, "other must not be null");
return t -> Flux.zip(apply(t), other.apply(t))
.map(tuple -> tuple.getT1() && tuple.getT2());
return new AndAsyncPredicate<>(this, other);
}
default AsyncPredicate<T> negate() {
return t -> Mono.from(apply(t)).map(b -> !b);
return new NegateAsyncPredicate<>(this);
}
default AsyncPredicate<T> or(AsyncPredicate<? super T> other) {
Assert.notNull(other, "other must not be null");
return new OrAsyncPredicate<>(this, other);
}
static AsyncPredicate<ServerWebExchange> from(
Predicate<? super ServerWebExchange> predicate) {
return new DefaultAsyncPredicate<>(GatewayPredicate.wrapIfNeeded(predicate));
}
class DefaultAsyncPredicate<T> implements AsyncPredicate<T> {
private final Predicate<T> delegate;
public DefaultAsyncPredicate(Predicate<T> delegate) {
this.delegate = delegate;
}
@Override
public Publisher<Boolean> apply(T t) {
return Mono.just(delegate.test(t));
}
@Override
public String toString() {
return this.delegate.toString();
}
}
class NegateAsyncPredicate<T> implements AsyncPredicate<T> {
private final AsyncPredicate<? super T> predicate;
public NegateAsyncPredicate(AsyncPredicate<? super T> predicate) {
Assert.notNull(predicate, "predicate AsyncPredicate must not be null");
this.predicate = predicate;
}
@Override
public Publisher<Boolean> apply(T t) {
return Mono.from(predicate.apply(t)).map(b -> !b);
}
@Override
public String toString() {
return String.format("!%s", this.predicate);
}
}
class AndAsyncPredicate<T> implements AsyncPredicate<T> {
private final AsyncPredicate<? super T> left;
private final AsyncPredicate<? super T> right;
public AndAsyncPredicate(AsyncPredicate<? super T> left,
AsyncPredicate<? super T> right) {
Assert.notNull(left, "Left AsyncPredicate must not be null");
Assert.notNull(right, "Right AsyncPredicate must not be null");
this.left = left;
this.right = right;
}
@Override
public Publisher<Boolean> apply(T t) {
return Flux.zip(left.apply(t), right.apply(t))
.map(tuple -> tuple.getT1() && tuple.getT2());
}
@Override
public String toString() {
return String.format("(%s && %s)", this.left, this.right);
}
}
class OrAsyncPredicate<T> implements AsyncPredicate<T> {
private final AsyncPredicate<? super T> left;
private final AsyncPredicate<? super T> right;
public OrAsyncPredicate(AsyncPredicate<? super T> left,
AsyncPredicate<? super T> right) {
Assert.notNull(left, "Left AsyncPredicate must not be null");
Assert.notNull(right, "Right AsyncPredicate must not be null");
this.left = left;
this.right = right;
}
@Override
public Publisher<Boolean> apply(T t) {
return Flux.zip(left.apply(t), right.apply(t))
.map(tuple -> tuple.getT1() || tuple.getT2());
}
@Override
public String toString() {
return String.format("(%s || %s)", this.left, this.right);
}
return t -> Flux.zip(apply(t), other.apply(t))
.map(tuple -> tuple.getT1() || tuple.getT2());
}
}

View File

@@ -47,10 +47,17 @@ public class AfterRoutePredicateFactory
@Override
public Predicate<ServerWebExchange> apply(Config config) {
ZonedDateTime datetime = config.getDatetime();
return exchange -> {
final ZonedDateTime now = ZonedDateTime.now();
return now.isAfter(datetime);
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
final ZonedDateTime now = ZonedDateTime.now();
return now.isAfter(config.getDatetime());
}
@Override
public String toString() {
return String.format("After: %s", config.getDatetime());
}
};
}

View File

@@ -45,10 +45,17 @@ public class BeforeRoutePredicateFactory
@Override
public Predicate<ServerWebExchange> apply(Config config) {
ZonedDateTime datetime = config.getDatetime();
return exchange -> {
final ZonedDateTime now = ZonedDateTime.now();
return now.isBefore(datetime);
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
final ZonedDateTime now = ZonedDateTime.now();
return now.isBefore(config.getDatetime());
}
@Override
public String toString() {
return String.format("Before: %s", config.getDatetime());
}
};
}

View File

@@ -54,14 +54,22 @@ public class BetweenRoutePredicateFactory
@Override
public Predicate<ServerWebExchange> apply(Config config) {
ZonedDateTime datetime1 = config.datetime1;
ZonedDateTime datetime2 = config.datetime2;
Assert.isTrue(datetime1.isBefore(datetime2),
config.datetime1 + " must be before " + config.datetime2);
Assert.isTrue(config.getDatetime1().isBefore(config.getDatetime2()),
config.getDatetime1() + " must be before " + config.getDatetime2());
return exchange -> {
final ZonedDateTime now = ZonedDateTime.now();
return now.isAfter(datetime1) && now.isBefore(datetime2);
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
final ZonedDateTime now = ZonedDateTime.now();
return now.isAfter(config.getDatetime1())
&& now.isBefore(config.getDatetime2());
}
@Override
public String toString() {
return String.format("Between: %s and %s", config.getDatetime1(),
config.getDatetime2());
}
};
}

View File

@@ -53,18 +53,27 @@ public class CookieRoutePredicateFactory
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
List<HttpCookie> cookies = exchange.getRequest().getCookies()
.get(config.name);
if (cookies == null) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
List<HttpCookie> cookies = exchange.getRequest().getCookies()
.get(config.name);
if (cookies == null) {
return false;
}
for (HttpCookie cookie : cookies) {
if (cookie.getValue().matches(config.regexp)) {
return true;
}
}
return false;
}
for (HttpCookie cookie : cookies) {
if (cookie.getValue().matches(config.regexp)) {
return true;
}
@Override
public String toString() {
return String.format("Cookie: name=%s regexp=%s", config.name,
config.regexp);
}
return false;
};
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright 2013-2019 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.gateway.handler.predicate;
import java.util.function.Predicate;
import org.springframework.util.Assert;
import org.springframework.web.server.ServerWebExchange;
public interface GatewayPredicate extends Predicate<ServerWebExchange> {
@Override
default Predicate<ServerWebExchange> and(Predicate<? super ServerWebExchange> other) {
return new AndGatewayPredicate(this, wrapIfNeeded(other));
}
@Override
default Predicate<ServerWebExchange> negate() {
return new NegateGatewayPredicate(this);
}
@Override
default Predicate<ServerWebExchange> or(Predicate<? super ServerWebExchange> other) {
return new OrGatewayPredicate(this, wrapIfNeeded(other));
}
static GatewayPredicate wrapIfNeeded(Predicate<? super ServerWebExchange> other) {
GatewayPredicate right;
if (other instanceof GatewayPredicate) {
right = (GatewayPredicate) other;
}
else {
right = new GatewayPredicateWrapper(other);
}
return right;
}
class GatewayPredicateWrapper implements GatewayPredicate {
private final Predicate<? super ServerWebExchange> delegate;
public GatewayPredicateWrapper(Predicate<? super ServerWebExchange> delegate) {
Assert.notNull(delegate, "delegate GatewayPredicate must not be null");
this.delegate = delegate;
}
@Override
public boolean test(ServerWebExchange exchange) {
return this.delegate.test(exchange);
}
@Override
public String toString() {
return this.delegate.getClass().getSimpleName();
}
}
class NegateGatewayPredicate implements GatewayPredicate {
private final GatewayPredicate predicate;
public NegateGatewayPredicate(GatewayPredicate predicate) {
Assert.notNull(predicate, "predicate GatewayPredicate must not be null");
this.predicate = predicate;
}
@Override
public boolean test(ServerWebExchange t) {
return !this.predicate.test(t);
}
@Override
public String toString() {
return String.format("!%s", this.predicate);
}
}
class AndGatewayPredicate implements GatewayPredicate {
private final GatewayPredicate left;
private final GatewayPredicate right;
public AndGatewayPredicate(GatewayPredicate left, GatewayPredicate right) {
Assert.notNull(left, "Left GatewayPredicate must not be null");
Assert.notNull(right, "Right GatewayPredicate must not be null");
this.left = left;
this.right = right;
}
@Override
public boolean test(ServerWebExchange t) {
return (this.left.test(t) && this.right.test(t));
}
@Override
public String toString() {
return String.format("(%s && %s)", this.left, this.right);
}
}
class OrGatewayPredicate implements GatewayPredicate {
private final GatewayPredicate left;
private final GatewayPredicate right;
public OrGatewayPredicate(GatewayPredicate left, GatewayPredicate right) {
Assert.notNull(left, "Left GatewayPredicate must not be null");
Assert.notNull(right, "Right GatewayPredicate must not be null");
this.left = left;
this.right = right;
}
@Override
public boolean test(ServerWebExchange t) {
return (this.left.test(t) || this.right.test(t));
}
@Override
public String toString() {
return String.format("(%s || %s)", this.left, this.right);
}
}
}

View File

@@ -56,20 +56,30 @@ public class HeaderRoutePredicateFactory
public Predicate<ServerWebExchange> apply(Config config) {
boolean hasRegex = !StringUtils.isEmpty(config.regexp);
return exchange -> {
List<String> values = exchange.getRequest().getHeaders()
.getOrDefault(config.header, Collections.emptyList());
if (values.isEmpty()) {
return false;
}
// values is now guaranteed to not be empty
if (hasRegex) {
// check if a header value matches
return values.stream().anyMatch(value -> value.matches(config.regexp));
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
List<String> values = exchange.getRequest().getHeaders()
.getOrDefault(config.header, Collections.emptyList());
if (values.isEmpty()) {
return false;
}
// values is now guaranteed to not be empty
if (hasRegex) {
// check if a header value matches
return values.stream()
.anyMatch(value -> value.matches(config.regexp));
}
// there is a value and since regexp is empty, we only check existence.
return true;
}
// there is a value and since regexp is empty, we only check existence.
return true;
@Override
public String toString() {
return String.format("Header: %s regexp=%s", config.header,
config.regexp);
}
};
}

View File

@@ -59,19 +59,27 @@ public class HostRoutePredicateFactory
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
String host = exchange.getRequest().getHeaders().getFirst("Host");
Optional<String> optionalPattern = config.getPatterns().stream()
.filter(pattern -> this.pathMatcher.match(pattern, host)).findFirst();
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
String host = exchange.getRequest().getHeaders().getFirst("Host");
Optional<String> optionalPattern = config.getPatterns().stream()
.filter(pattern -> pathMatcher.match(pattern, host)).findFirst();
if (optionalPattern.isPresent()) {
Map<String, String> variables = this.pathMatcher
.extractUriTemplateVariables(optionalPattern.get(), host);
ServerWebExchangeUtils.putUriTemplateVariables(exchange, variables);
return true;
if (optionalPattern.isPresent()) {
Map<String, String> variables = pathMatcher
.extractUriTemplateVariables(optionalPattern.get(), host);
ServerWebExchangeUtils.putUriTemplateVariables(exchange, variables);
return true;
}
return false;
}
return false;
@Override
public String toString() {
return String.format("Hosts: %s", config.getPatterns());
}
};
}
@@ -99,8 +107,9 @@ public class HostRoutePredicateFactory
return patterns;
}
public void setPatterns(List<String> patterns) {
public Config setPatterns(List<String> patterns) {
this.patterns = patterns;
return this;
}
@Override

View File

@@ -45,9 +45,17 @@ public class MethodRoutePredicateFactory
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
HttpMethod requestMethod = exchange.getRequest().getMethod();
return requestMethod == config.getMethod();
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
HttpMethod requestMethod = exchange.getRequest().getMethod();
return requestMethod == config.getMethod();
}
@Override
public String toString() {
return String.format("Method: %s", config.getMethod());
}
};
}

View File

@@ -87,22 +87,32 @@ public class PathRoutePredicateFactory
pathPatterns.add(pathPattern);
});
}
return exchange -> {
PathContainer path = parsePath(exchange.getRequest().getURI().getRawPath());
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
PathContainer path = parsePath(
exchange.getRequest().getURI().getRawPath());
Optional<PathPattern> optionalPathPattern = pathPatterns.stream()
.filter(pattern -> pattern.matches(path)).findFirst();
Optional<PathPattern> optionalPathPattern = pathPatterns.stream()
.filter(pattern -> pattern.matches(path)).findFirst();
if (optionalPathPattern.isPresent()) {
PathPattern pathPattern = optionalPathPattern.get();
traceMatch("Pattern", pathPattern.getPatternString(), path, true);
PathMatchInfo pathMatchInfo = pathPattern.matchAndExtract(path);
putUriTemplateVariables(exchange, pathMatchInfo.getUriVariables());
return true;
if (optionalPathPattern.isPresent()) {
PathPattern pathPattern = optionalPathPattern.get();
traceMatch("Pattern", pathPattern.getPatternString(), path, true);
PathMatchInfo pathMatchInfo = pathPattern.matchAndExtract(path);
putUriTemplateVariables(exchange, pathMatchInfo.getUriVariables());
return true;
}
else {
traceMatch("Pattern", config.getPatterns(), path, false);
return false;
}
}
else {
traceMatch("Pattern", config.getPatterns(), path, false);
return false;
@Override
public String toString() {
return String.format("Paths: %s, match trailing slash: %b",
config.getPatterns(), config.isMatchOptionalTrailingSeparator());
}
};
}

View File

@@ -53,23 +53,33 @@ public class QueryRoutePredicateFactory
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
if (!StringUtils.hasText(config.regexp)) {
// check existence of header
return exchange.getRequest().getQueryParams().containsKey(config.param);
}
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
if (!StringUtils.hasText(config.regexp)) {
// check existence of header
return exchange.getRequest().getQueryParams()
.containsKey(config.param);
}
List<String> values = exchange.getRequest().getQueryParams()
.get(config.param);
if (values == null) {
List<String> values = exchange.getRequest().getQueryParams()
.get(config.param);
if (values == null) {
return false;
}
for (String value : values) {
if (value != null && value.matches(config.regexp)) {
return true;
}
}
return false;
}
for (String value : values) {
if (value != null && value.matches(config.regexp)) {
return true;
}
@Override
public String toString() {
return String.format("Query: param=%s regexp=%s", config.getParam(),
config.getRegexp());
}
return false;
};
}

View File

@@ -22,6 +22,7 @@ import java.util.function.Predicate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.handler.AsyncPredicate;
@@ -55,40 +56,49 @@ public class ReadBodyPredicateFactory
@Override
@SuppressWarnings("unchecked")
public AsyncPredicate<ServerWebExchange> applyAsync(Config config) {
return exchange -> {
Class inClass = config.getInClass();
return new AsyncPredicate<ServerWebExchange>() {
@Override
public Publisher<Boolean> apply(ServerWebExchange exchange) {
Class inClass = config.getInClass();
Object cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY);
Mono<?> modifiedBody;
// We can only read the body from the request once, once that happens if we
// try to read the body again an exception will be thrown. The below if/else
// caches the body object as a request attribute in the ServerWebExchange
// so if this filter is run more than once (due to more than one route
// using it) we do not try to read the request body multiple times
if (cachedBody != null) {
try {
boolean test = config.predicate.test(cachedBody);
exchange.getAttributes().put(TEST_ATTRIBUTE, test);
return Mono.just(test);
}
catch (ClassCastException e) {
if (log.isDebugEnabled()) {
log.debug("Predicate test failed because class in predicate "
+ "does not match the cached body object", e);
Object cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY);
Mono<?> modifiedBody;
// We can only read the body from the request once, once that happens if
// we try to read the body again an exception will be thrown. The below
// if/else caches the body object as a request attribute in the
// ServerWebExchange so if this filter is run more than once (due to more
// than one route using it) we do not try to read the request body
// multiple times
if (cachedBody != null) {
try {
boolean test = config.predicate.test(cachedBody);
exchange.getAttributes().put(TEST_ATTRIBUTE, test);
return Mono.just(test);
}
catch (ClassCastException e) {
if (log.isDebugEnabled()) {
log.debug("Predicate test failed because class in predicate "
+ "does not match the cached body object", e);
}
}
return Mono.just(false);
}
else {
return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange,
(serverHttpRequest) -> ServerRequest
.create(exchange.mutate().request(serverHttpRequest)
.build(), messageReaders)
.bodyToMono(inClass)
.doOnNext(objectValue -> exchange.getAttributes().put(
CACHE_REQUEST_BODY_OBJECT_KEY, objectValue))
.map(objectValue -> config.getPredicate()
.test(objectValue)));
}
return Mono.just(false);
}
else {
return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange,
(serverHttpRequest) -> ServerRequest
.create(exchange.mutate().request(serverHttpRequest)
.build(), messageReaders)
.bodyToMono(inClass)
.doOnNext(objectValue -> exchange.getAttributes()
.put(CACHE_REQUEST_BODY_OBJECT_KEY, objectValue))
.map(objectValue -> config.getPredicate()
.test(objectValue)));
@Override
public String toString() {
return String.format("ReadBody: %s", config.getInClass());
}
};
}

View File

@@ -73,26 +73,34 @@ public class RemoteAddrRoutePredicateFactory
public Predicate<ServerWebExchange> apply(Config config) {
List<IpSubnetFilterRule> sources = convert(config.sources);
return exchange -> {
InetSocketAddress remoteAddress = config.remoteAddressResolver
.resolve(exchange);
if (remoteAddress != null && remoteAddress.getAddress() != null) {
String hostAddress = remoteAddress.getAddress().getHostAddress();
String host = exchange.getRequest().getURI().getHost();
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
InetSocketAddress remoteAddress = config.remoteAddressResolver
.resolve(exchange);
if (remoteAddress != null && remoteAddress.getAddress() != null) {
String hostAddress = remoteAddress.getAddress().getHostAddress();
String host = exchange.getRequest().getURI().getHost();
if (log.isDebugEnabled() && !hostAddress.equals(host)) {
log.debug("Remote addresses didn't match " + hostAddress + " != "
+ host);
}
if (log.isDebugEnabled() && !hostAddress.equals(host)) {
log.debug("Remote addresses didn't match " + hostAddress + " != "
+ host);
}
for (IpSubnetFilterRule source : sources) {
if (source.matches(remoteAddress)) {
return true;
for (IpSubnetFilterRule source : sources) {
if (source.matches(remoteAddress)) {
return true;
}
}
}
return false;
}
return false;
@Override
public String toString() {
return String.format("RemoteAddrs: %s", config.getSources());
}
};
}

View File

@@ -84,31 +84,40 @@ public class WeightRoutePredicateFactory
@Override
public Predicate<ServerWebExchange> apply(WeightConfig config) {
return exchange -> {
Map<String, String> weights = exchange.getAttributeOrDefault(WEIGHT_ATTR,
Collections.emptyMap());
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
Map<String, String> weights = exchange.getAttributeOrDefault(WEIGHT_ATTR,
Collections.emptyMap());
String routeId = exchange.getAttribute(GATEWAY_PREDICATE_ROUTE_ATTR);
String routeId = exchange.getAttribute(GATEWAY_PREDICATE_ROUTE_ATTR);
// all calculations and comparison against random num happened in
// WeightCalculatorWebFilter
String group = config.getGroup();
if (weights.containsKey(group)) {
// all calculations and comparison against random num happened in
// WeightCalculatorWebFilter
String group = config.getGroup();
if (weights.containsKey(group)) {
String chosenRoute = weights.get(group);
if (log.isTraceEnabled()) {
log.trace("in group weight: " + group + ", current route: " + routeId
+ ", chosen route: " + chosenRoute);
String chosenRoute = weights.get(group);
if (log.isTraceEnabled()) {
log.trace("in group weight: " + group + ", current route: "
+ routeId + ", chosen route: " + chosenRoute);
}
return routeId.equals(chosenRoute);
}
else if (log.isTraceEnabled()) {
log.trace("no weights found for group: " + group + ", current route: "
+ routeId);
}
return routeId.equals(chosenRoute);
}
else if (log.isTraceEnabled()) {
log.trace("no weights found for group: " + group + ", current route: "
+ routeId);
return false;
}
return false;
@Override
public String toString() {
return String.format("Weight: %s %s", config.getGroup(),
config.getWeight());
}
};
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2013-2019 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
*
* https://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.function.Function;
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
import org.springframework.core.style.DefaultToStringStyler;
import org.springframework.core.style.DefaultValueStyler;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.ClassUtils;
public class GatewayToStringStyler extends DefaultToStringStyler {
private static final GatewayToStringStyler FILTER_INSTANCE = new GatewayToStringStyler(
GatewayFilterFactory.class, NameUtils::normalizeFilterFactoryName);
private final Function<Class, String> classNameFormatter;
private final Class instanceClass;
public static ToStringCreator filterToStringCreator(Object obj) {
return new ToStringCreator(obj, FILTER_INSTANCE);
}
public GatewayToStringStyler(Class instanceClass,
Function<Class, String> classNameFormatter) {
super(new DefaultValueStyler());
this.classNameFormatter = classNameFormatter;
this.instanceClass = instanceClass;
}
@Override
public void styleStart(StringBuilder buffer, Object obj) {
if (!obj.getClass().isArray()) {
String shortName;
if (instanceClass.isInstance(obj)) {
shortName = classNameFormatter.apply(obj.getClass());
}
else {
shortName = ClassUtils.getShortName(obj.getClass());
}
buffer.append('[').append(shortName);
}
else {
buffer.append('[');
styleValue(buffer, obj);
}
}
}

View File

@@ -246,7 +246,7 @@ public final class ServerWebExchangeUtils {
public static AsyncPredicate<ServerWebExchange> toAsyncPredicate(
Predicate<? super ServerWebExchange> predicate) {
Assert.notNull(predicate, "predicate must not be null");
return t -> Mono.just(predicate.test(t));
return AsyncPredicate.from(predicate);
}
@SuppressWarnings("unchecked")

View File

@@ -27,6 +27,8 @@ import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint;
import org.springframework.cloud.gateway.actuate.GatewayLegacyControllerEndpoint;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.filter.reactive.HiddenHttpMethodFilter;
@@ -36,9 +38,8 @@ public class GatewayAutoConfigurationTests {
@Test
public void noHiddenHttpMethodFilter() {
try (ConfigurableApplicationContext ctx = SpringApplication.run(
NoHiddenHttpMethodFilterConfig.class, "--spring.jmx.enabled=false",
"--server.port=0")) {
try (ConfigurableApplicationContext ctx = SpringApplication.run(Config.class,
"--spring.jmx.enabled=false", "--server.port=0")) {
assertThat(ctx.getEnvironment()
.getProperty("spring.webflux.hiddenmethod.filter.enabled"))
.isEqualTo("false");
@@ -108,9 +109,30 @@ public class GatewayAutoConfigurationTests {
});
}
@Test
public void legacyActuatorEnabledByDefault() {
try (ConfigurableApplicationContext ctx = SpringApplication.run(Config.class,
"--spring.jmx.enabled=false", "--server.port=0")) {
assertThat(ctx.getBeanNamesForType(GatewayControllerEndpoint.class))
.isEmpty();
assertThat(ctx.getBeanNamesForType(GatewayLegacyControllerEndpoint.class))
.hasSize(1);
}
}
@Test
public void verboseActuatorEnabled() {
try (ConfigurableApplicationContext ctx = SpringApplication.run(Config.class,
"--spring.jmx.enabled=false", "--server.port=0",
"--spring.cloud.gateway.actuator.verbose.enabled=true")) {
assertThat(ctx.getBeanNamesForType(GatewayControllerEndpoint.class))
.hasSize(1);
}
}
@EnableAutoConfiguration
@SpringBootConfiguration
protected static class NoHiddenHttpMethodFilterConfig {
protected static class Config {
}

View File

@@ -25,6 +25,8 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory.NameValueConfig;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
@@ -68,6 +70,14 @@ public class AddRequestHeaderGatewayFilterFactoryTests extends BaseWebClientTest
});
}
@Test
public void toStringFormat() {
NameValueConfig config = new NameValueConfig().setName("myname")
.setValue("myvalue");
GatewayFilter filter = new AddRequestHeaderGatewayFilterFactory().apply(config);
assertThat(filter.toString()).contains("myname").contains("myvalue");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)

View File

@@ -28,6 +28,8 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory.NameValueConfig;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
@@ -107,6 +109,15 @@ public class AddRequestParameterGatewayFilterFactoryTests extends BaseWebClientT
});
}
@Test
public void toStringFormat() {
NameValueConfig config = new NameValueConfig().setName("myname")
.setValue("myvalue");
GatewayFilter filter = new AddRequestParameterGatewayFilterFactory()
.apply(config);
assertThat(filter.toString()).contains("myname").contains("myvalue");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)

View File

@@ -25,6 +25,8 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory.NameValueConfig;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
@@ -34,23 +36,42 @@ import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.util.UriComponentsBuilder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT)
@DirtiesContext
public class AddResponseParameterGatewayFilterFactoryTests extends BaseWebClientTests {
public class AddResponseHeaderGatewayFilterFactoryTests extends BaseWebClientTests {
@Test
public void testResposneParameterFilter() {
public void testResposneHeaderFilter() {
URI uri = UriComponentsBuilder.fromUriString(this.baseUri + "/headers")
.build(true).toUri();
String host = "www.addresponseheader.org";
String expectedValue = "Bar";
testClient.get().uri(uri).header("Host", host).exchange().expectHeader()
.valueEquals("X-Request-Foo", expectedValue);
}
@Test
public void testResposneHeaderFilterJavaDsl() {
URI uri = UriComponentsBuilder.fromUriString(this.baseUri + "/get").build(true)
.toUri();
String host = "www.addresponseparamjava.org";
String host = "www.addresponseheaderjava.org";
String expectedValue = "myresponsevalue";
testClient.get().uri(uri).header("Host", host).exchange().expectHeader()
.valueEquals("example", expectedValue);
}
@Test
public void toStringFormat() {
NameValueConfig config = new NameValueConfig().setName("myname")
.setValue("myvalue");
GatewayFilter filter = new AddResponseHeaderGatewayFilterFactory().apply(config);
assertThat(filter.toString()).contains("myname").contains("myvalue");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)
@@ -61,8 +82,8 @@ public class AddResponseParameterGatewayFilterFactoryTests extends BaseWebClient
@Bean
public RouteLocator testRouteLocator(RouteLocatorBuilder builder) {
return builder.routes().route("add_response_param_java_test",
r -> r.path("/get").and().host("**.addresponseparamjava.org")
return builder.routes().route("add_response_header_java_test",
r -> r.path("/get").and().host("**.addresponseheaderjava.org")
.filters(f -> f.prefixPath("/httpbin")
.addResponseHeader("example", "myresponsevalue"))
.uri(uri))

View File

@@ -23,8 +23,13 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.http.HttpHeaders;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.gateway.filter.factory.DedupeResponseHeaderGatewayFilterFactory.Config;
import static org.springframework.cloud.gateway.filter.factory.DedupeResponseHeaderGatewayFilterFactory.Strategy;
public class DedupeResponseHeaderGatewayFilterFactoryUnitTests {
private static final String NAME_1 = HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN;
@@ -33,14 +38,14 @@ public class DedupeResponseHeaderGatewayFilterFactoryUnitTests {
private HttpHeaders headers;
private DedupeResponseHeaderGatewayFilterFactory.Config config;
private Config config;
private DedupeResponseHeaderGatewayFilterFactory filter;
@Before
public void setUp() {
headers = Mockito.mock(HttpHeaders.class);
config = new DedupeResponseHeaderGatewayFilterFactory.Config();
config = new Config();
filter = new DedupeResponseHeaderGatewayFilterFactory();
}
@@ -99,7 +104,7 @@ public class DedupeResponseHeaderGatewayFilterFactoryUnitTests {
@Test
public void dedupMultipleValuesRetainLast() {
config.setName(NAME_1);
config.setStrategy(DedupeResponseHeaderGatewayFilterFactory.Strategy.RETAIN_LAST);
config.setStrategy(Strategy.RETAIN_LAST);
Mockito.when(headers.get(NAME_1)).thenReturn(Arrays.asList("2", "3", "3", "4"));
filter.dedupe(headers, config);
Mockito.verify(headers).get(NAME_1);
@@ -110,8 +115,7 @@ public class DedupeResponseHeaderGatewayFilterFactoryUnitTests {
@Test
public void dedupMultipleValuesRetainUnique() {
config.setName(NAME_1);
config.setStrategy(
DedupeResponseHeaderGatewayFilterFactory.Strategy.RETAIN_UNIQUE);
config.setStrategy(Strategy.RETAIN_UNIQUE);
Mockito.when(headers.get(NAME_1)).thenReturn(Arrays.asList("2", "3", "3", "4"));
filter.dedupe(headers, config);
Mockito.verify(headers).get(NAME_1);
@@ -120,4 +124,15 @@ public class DedupeResponseHeaderGatewayFilterFactoryUnitTests {
Mockito.verify(headers).put(Mockito.anyString(), Mockito.anyList());
}
@Test
public void toStringFormat() {
Config config = new Config();
config.setName("myname");
config.setStrategy(Strategy.RETAIN_LAST);
GatewayFilter filter = new DedupeResponseHeaderGatewayFilterFactory()
.apply(config);
assertThat(filter.toString()).contains("myname")
.contains(Strategy.RETAIN_LAST.toString());
}
}

View File

@@ -20,6 +20,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
import org.springframework.http.HttpStatus;
import org.springframework.test.annotation.DirtiesContext;
@@ -28,6 +29,7 @@ import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.Assert;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.core.StringContains.containsString;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
import static org.springframework.cloud.gateway.filter.factory.ExceptionFallbackHandler.RETRIEVED_EXCEPTION;
@@ -117,4 +119,12 @@ public class HystrixGatewayFilterFactoryTests extends BaseWebClientTests {
});
}
@Test
public void toStringFormat() {
HystrixGatewayFilterFactory.Config config = new HystrixGatewayFilterFactory.Config()
.setName("myname").setFallbackUri("forward:/myfallback");
GatewayFilter filter = new HystrixGatewayFilterFactory(null).apply(config);
assertThat(filter.toString()).contains("myname").contains("forward:/myfallback");
}
}

View File

@@ -25,6 +25,7 @@ import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.PrefixPathGatewayFilterFactory.Config;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.server.ServerWebExchange;
@@ -69,4 +70,12 @@ public class PrefixPathGatewayFilterFactoryTest {
assertThat(uris).contains(request.getURI());
}
@Test
public void toStringFormat() {
Config config = new Config();
config.setPrefix("myprefix");
GatewayFilter filter = new PrefixPathGatewayFilterFactory().apply(config);
assertThat(filter.toString()).contains("myprefix");
}
}

View File

@@ -25,6 +25,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
@@ -56,6 +57,12 @@ public class PreserveHostHeaderGatewayFilterFactoryTests extends BaseWebClientTe
});
}
@Test
public void toStringFormat() {
GatewayFilter filter = new PreserveHostHeaderGatewayFilterFactory().apply();
assertThat(filter.toString()).contains("PreserveHostHeader");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)

View File

@@ -22,6 +22,8 @@ import org.junit.runner.RunWith;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.RedirectToGatewayFilterFactory.Config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
@@ -32,6 +34,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@RunWith(SpringRunner.class)
@@ -53,6 +56,15 @@ public class RedirectToGatewayFilterFactoryTests extends BaseWebClientTests {
.valueEquals(HttpHeaders.LOCATION, "/index.html#/customers");
}
@Test
public void toStringFormat() {
Config config = new Config();
config.setStatus("301");
config.setUrl("http://newurl");
GatewayFilter filter = new RedirectToGatewayFilterFactory().apply(config);
assertThat(filter.toString()).contains("301").contains("http://newurl");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)

View File

@@ -24,6 +24,8 @@ import org.junit.runner.RunWith;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory.NameConfig;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
@@ -49,6 +51,15 @@ public class RemoveRequestHeaderGatewayFilterFactoryTests extends BaseWebClientT
});
}
@Test
public void toStringFormat() {
NameConfig config = new NameConfig();
config.setName("myname");
GatewayFilter filter = new RemoveRequestHeaderGatewayFilterFactory()
.apply(config);
assertThat(filter.toString()).contains("myname");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)

View File

@@ -22,11 +22,14 @@ import org.junit.runner.RunWith;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory.NameConfig;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@RunWith(SpringRunner.class)
@@ -41,6 +44,15 @@ public class RemoveResponseHeaderGatewayFilterFactoryTests extends BaseWebClient
.doesNotExist("X-Request-Foo");
}
@Test
public void toStringFormat() {
NameConfig config = new NameConfig();
config.setName("myname");
GatewayFilter filter = new RemoveResponseHeaderGatewayFilterFactory()
.apply(config);
assertThat(filter.toString()).contains("myname");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)

View File

@@ -24,6 +24,7 @@ import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory.NameConfig;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.server.ServerWebExchange;
@@ -98,4 +99,13 @@ public class RequestHeaderToRequestUriGatewayFilterFactoryTests {
assertThat(uri.toURL().toString()).isEqualTo("http://localhost");
}
@Test
public void toStringFormat() {
NameConfig config = new NameConfig();
config.setName("myname");
GatewayFilter filter = new RequestHeaderToRequestUriGatewayFilterFactory()
.apply(config);
assertThat(filter.toString()).contains("myname");
}
}

View File

@@ -23,6 +23,8 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.RequestSizeGatewayFilterFactory.RequestSizeConfig;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
@@ -32,6 +34,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
@@ -54,6 +57,14 @@ public class RequestSizeGatewayFilterFactoryTest extends BaseWebClientTests {
.valueMatches("errorMessage", responseMesssage);
}
@Test
public void toStringFormat() {
RequestSizeConfig config = new RequestSizeConfig();
config.setMaxSize(1000L);
GatewayFilter filter = new RequestSizeGatewayFilterFactory().apply(config);
assertThat(filter.toString()).contains("max").contains("1000");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)

View File

@@ -16,6 +16,7 @@
package org.springframework.cloud.gateway.filter.factory;
import java.io.IOException;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -33,6 +34,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.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.RetryGatewayFilterFactory.RetryConfig;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
@@ -111,6 +114,18 @@ public class RetryGatewayFilterFactoryIntegrationTests extends BaseWebClientTest
});
}
@Test
public void toStringFormat() {
RetryConfig config = new RetryConfig();
config.setRetries(4);
config.setMethods(HttpMethod.GET);
config.setSeries(HttpStatus.Series.SERVER_ERROR);
config.setExceptions(IOException.class);
GatewayFilter filter = new RetryGatewayFilterFactory().apply(config);
assertThat(filter.toString()).contains("4").contains("[GET]")
.contains("[SERVER_ERROR]").contains("[IOException]");
}
@RestController
@EnableAutoConfiguration
@SpringBootConfiguration

View File

@@ -25,6 +25,7 @@ import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory.Config;
import org.springframework.http.HttpMethod;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
@@ -101,4 +102,11 @@ public class RewritePathGatewayFilterFactoryTests {
assertThat(uri.getRawQuery()).isEqualTo("name=%E6%89%8E%E6%A0%B9");
}
@Test
public void toStringFormat() {
Config config = new Config().setRegexp("regexp1").setReplacement("replacement1");
GatewayFilter filter = new RewritePathGatewayFilterFactory().apply(config);
assertThat(filter.toString()).contains("regexp1").contains("replacement1");
}
}

View File

@@ -19,6 +19,9 @@ package org.springframework.cloud.gateway.filter.factory;
import org.junit.Before;
import org.junit.Test;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.RewriteResponseHeaderGatewayFilterFactory.Config;
import static org.assertj.core.api.Assertions.assertThat;
public class RewriteResponseHeaderGatewayFilterFactoryUnitTests {
@@ -42,4 +45,16 @@ public class RewriteResponseHeaderGatewayFilterFactoryUnitTests {
.isEqualTo("/foo/cafe/wat/cafe");
}
@Test
public void toStringFormat() {
Config config = new Config();
config.setName("myname");
config.setRegexp("myregexp");
config.setReplacement("myreplacement");
GatewayFilter filter = new RewriteResponseHeaderGatewayFilterFactory()
.apply(config);
assertThat(filter.toString()).contains("myname").contains("myregexp")
.contains("myreplacement");
}
}

View File

@@ -28,6 +28,7 @@ import reactor.test.StepVerifier;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
@@ -37,6 +38,7 @@ import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.server.WebSession;
import org.springframework.web.server.session.WebSessionManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -70,6 +72,12 @@ public class SaveSessionGatewayFilterFactoryTests extends BaseWebClientTests {
verify(mockWebSession).save();
}
@Test
public void toStringFormat() {
GatewayFilter filter = new SaveSessionGatewayFilterFactory().apply("");
assertThat(filter.toString()).contains("SaveSession");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)

View File

@@ -16,6 +16,7 @@
package org.springframework.cloud.gateway.filter.factory;
import org.assertj.core.api.Assertions;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -109,4 +110,11 @@ public class SecureHeadersGatewayFilterFactoryUnitTests {
}
@Test
public void toStringFormat() {
GatewayFilter filter = new SecureHeadersGatewayFilterFactory(
new SecureHeadersProperties()).apply("");
Assertions.assertThat(filter.toString()).contains("SecureHeaders");
}
}

View File

@@ -26,6 +26,7 @@ import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.SetPathGatewayFilterFactory.Config;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
@@ -102,4 +103,12 @@ public class SetPathGatewayFilterFactoryTests {
assertThat(uris).contains(request.getURI());
}
@Test
public void toStringFormat() {
Config config = new Config();
config.setTemplate("mytemplate");
GatewayFilter filter = new SetPathGatewayFilterFactory().apply(config);
assertThat(filter.toString()).contains("mytemplate");
}
}

View File

@@ -25,6 +25,8 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory.NameValueConfig;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
@@ -57,6 +59,14 @@ public class SetRequestHeaderGatewayFilterFactoryTests extends BaseWebClientTest
});
}
@Test
public void toStringFormat() {
NameValueConfig config = new NameValueConfig().setName("myname")
.setValue("myvalue");
GatewayFilter filter = new SetRequestHeaderGatewayFilterFactory().apply(config);
assertThat(filter.toString()).contains("myname").contains("myvalue");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)

View File

@@ -22,11 +22,14 @@ import org.junit.runner.RunWith;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory.NameValueConfig;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@RunWith(SpringRunner.class)
@@ -41,6 +44,14 @@ public class SetResponseHeaderGatewayFilterFactoryTests extends BaseWebClientTes
.valueEquals("X-Request-Foo", "Bar");
}
@Test
public void toStringFormat() {
NameValueConfig config = new NameValueConfig().setName("myname")
.setValue("myvalue");
GatewayFilter filter = new SetResponseHeaderGatewayFilterFactory().apply(config);
assertThat(filter.toString()).contains("myname").contains("myvalue");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)

View File

@@ -24,6 +24,8 @@ import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.SetStatusGatewayFilterFactory.Config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
@@ -76,6 +78,14 @@ public class SetStatusGatewayFilterFactoryTests extends BaseWebClientTests {
*/
}
@Test
public void toStringFormat() {
Config config = new Config();
config.setStatus("401");
GatewayFilter filter = new SetStatusGatewayFilterFactory().apply(config);
assertThat(filter.toString()).contains("401");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)

View File

@@ -25,6 +25,7 @@ import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.StripPrefixGatewayFilterFactory.Config;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.server.ServerWebExchange;
@@ -85,4 +86,12 @@ public class StripPrefixGatewayFilterFactoryTests {
assertThat(uris).contains(request.getURI());
}
@Test
public void toStringFormat() {
Config config = new Config();
config.setParts(2);
GatewayFilter filter = new StripPrefixGatewayFilterFactory().apply(config);
assertThat(filter.toString()).contains("2");
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2013-2019 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
*
* https://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.filter.factory.rewrite;
import org.junit.Test;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory.Config;
import static org.assertj.core.api.Assertions.assertThat;
public class ModifyRequestBodyGatewayFilterFactoryUnitTests {
@Test
public void toStringFormat() {
Config config = new Config();
config.setInClass(String.class);
config.setOutClass(Integer.class);
config.setContentType("mycontenttype");
GatewayFilter filter = new ModifyRequestBodyGatewayFilterFactory().apply(config);
assertThat(filter.toString()).contains("String").contains("Integer")
.contains("mycontenttype");
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2013-2019 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
*
* https://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.filter.factory.rewrite;
import org.junit.Test;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyResponseBodyGatewayFilterFactory.Config;
import static org.assertj.core.api.Assertions.assertThat;
public class ModifyResponseBodyGatewayFilterFactoryUnitTests {
@Test
public void toStringFormat() {
Config config = new Config();
config.setInClass(String.class);
config.setOutClass(Integer.class);
config.setNewContentType("mycontenttype");
GatewayFilter filter = new ModifyResponseBodyGatewayFilterFactory().apply(config);
assertThat(filter.toString()).contains("String").contains("Integer")
.contains("mycontenttype");
}
}

View File

@@ -18,9 +18,12 @@ package org.springframework.cloud.gateway.handler.predicate;
import java.time.ZonedDateTime;
import java.util.HashMap;
import java.util.function.Predicate;
import org.junit.Test;
import org.springframework.cloud.gateway.handler.predicate.AfterRoutePredicateFactory.Config;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.gateway.handler.predicate.AfterRoutePredicateFactory.DATETIME_KEY;
import static org.springframework.cloud.gateway.handler.predicate.BetweenRoutePredicateFactoryTests.bindConfig;
@@ -84,9 +87,17 @@ public class AfterRoutePredicateFactoryTests {
map.put(DATETIME_KEY, dateString);
AfterRoutePredicateFactory factory = new AfterRoutePredicateFactory();
AfterRoutePredicateFactory.Config config = bindConfig(map, factory);
Config config = bindConfig(map, factory);
return factory.apply(config).test(getExchange());
}
@Test
public void toStringFormat() {
Config config = new Config();
config.setDatetime(ZonedDateTime.now());
Predicate predicate = new AfterRoutePredicateFactory().apply(config);
assertThat(predicate.toString()).contains("After: " + config.getDatetime());
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.cloud.gateway.handler.predicate;
import java.time.ZonedDateTime;
import java.util.HashMap;
import java.util.function.Predicate;
import org.junit.Test;
@@ -90,4 +91,12 @@ public class BeforeRoutePredicateFactoryTests {
return factory.apply(config).test(getExchange());
}
@Test
public void toStringFormat() {
BeforeRoutePredicateFactory.Config config = new BeforeRoutePredicateFactory.Config();
config.setDatetime(ZonedDateTime.now());
Predicate predicate = new BeforeRoutePredicateFactory().apply(config);
assertThat(predicate.toString()).contains("Before: " + config.getDatetime());
}
}

View File

@@ -19,6 +19,7 @@ package org.springframework.cloud.gateway.handler.predicate;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.function.Predicate;
import org.junit.Test;
@@ -160,4 +161,14 @@ public class BetweenRoutePredicateFactoryTests {
return factory.apply(config).test(getExchange());
}
@Test
public void toStringFormat() {
BetweenRoutePredicateFactory.Config config = new BetweenRoutePredicateFactory.Config();
config.setDatetime1(ZonedDateTime.now());
config.setDatetime2(ZonedDateTime.now().plusHours(1));
Predicate predicate = new BetweenRoutePredicateFactory().apply(config);
assertThat(predicate.toString()).contains(
"Between: " + config.getDatetime1() + " and " + config.getDatetime2());
}
}

View File

@@ -57,4 +57,14 @@ public class CookieRoutePredicateFactoryTests extends BaseWebClientTests {
assertThat(predicate.test(exchange)).isTrue();
}
@Test
public void toStringFormat() {
Config config = new Config();
config.setName("mycookie");
config.setRegexp("myregexp");
Predicate predicate = new CookieRoutePredicateFactory().apply(config);
assertThat(predicate.toString())
.contains("Cookie: name=mycookie regexp=myregexp");
}
}

View File

@@ -16,6 +16,8 @@
package org.springframework.cloud.gateway.handler.predicate;
import java.util.function.Predicate;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -24,6 +26,7 @@ import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping;
import org.springframework.cloud.gateway.handler.predicate.HeaderRoutePredicateFactory.Config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
@@ -32,6 +35,7 @@ import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@RunWith(SpringRunner.class)
@@ -68,6 +72,15 @@ public class HeaderRoutePredicateFactoryTests extends BaseWebClientTests {
.expectHeader().valueEquals(ROUTE_ID_HEADER, "header_exists_dsl");
}
@Test
public void toStringFormat() {
Config config = new Config();
config.setHeader("myheader");
config.setRegexp("myregexp");
Predicate predicate = new HeaderRoutePredicateFactory().apply(config);
assertThat(predicate.toString()).contains("Header: myheader regexp=myregexp");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)

View File

@@ -16,6 +16,9 @@
package org.springframework.cloud.gateway.handler.predicate;
import java.util.Arrays;
import java.util.function.Predicate;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -24,6 +27,7 @@ import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping;
import org.springframework.cloud.gateway.handler.predicate.HostRoutePredicateFactory.Config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
@@ -32,6 +36,7 @@ import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@RunWith(SpringRunner.class)
@@ -75,6 +80,13 @@ public class HostRoutePredicateFactoryTests extends BaseWebClientTests {
expectHostRoute("www.hostmultidsl2.org", "host_multi_dsl");
}
@Test
public void toStringFormat() {
Config config = new Config().setPatterns(Arrays.asList("pattern1", "pattern2"));
Predicate predicate = new HostRoutePredicateFactory().apply(config);
assertThat(predicate.toString()).contains("pattern1").contains("pattern2");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)

View File

@@ -16,6 +16,8 @@
package org.springframework.cloud.gateway.handler.predicate;
import java.util.function.Predicate;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -23,11 +25,14 @@ import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping;
import org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactory.Config;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpMethod;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@RunWith(SpringRunner.class)
@@ -44,6 +49,14 @@ public class MethodRoutePredicateFactoryTests extends BaseWebClientTests {
.expectHeader().valueEquals(ROUTE_ID_HEADER, "method_test");
}
@Test
public void toStringFormat() {
Config config = new Config();
config.setMethod(HttpMethod.GET);
Predicate predicate = new MethodRoutePredicateFactory().apply(config);
assertThat(predicate.toString()).contains("Method: " + config.getMethod());
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)

View File

@@ -16,6 +16,9 @@
package org.springframework.cloud.gateway.handler.predicate;
import java.util.Arrays;
import java.util.function.Predicate;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -24,6 +27,7 @@ import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping;
import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory.Config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
@@ -33,6 +37,7 @@ import org.springframework.http.HttpHeaders;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@RunWith(SpringRunner.class)
@@ -93,6 +98,15 @@ public class PathRoutePredicateFactoryTests extends BaseWebClientTests {
.expectHeader().valueEquals(ROUTE_ID_HEADER, "path_test");
}
@Test
public void toStringFormat() {
Config config = new Config().setPatterns(Arrays.asList("patternA", "patternB"))
.setMatchOptionalTrailingSeparator(false);
Predicate predicate = new PathRoutePredicateFactory().apply(config);
assertThat(predicate.toString()).contains("patternA").contains("patternB")
.contains("false");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)

View File

@@ -16,6 +16,8 @@
package org.springframework.cloud.gateway.handler.predicate;
import java.util.function.Predicate;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -25,6 +27,7 @@ import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.rule.OutputCapture;
import org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactory.Config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
@@ -33,6 +36,7 @@ import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@@ -67,6 +71,15 @@ public class QueryRoutePredicateFactoryTests extends BaseWebClientTests {
containsString("Error applying predicate for route: foo_query_param")));
}
@Test
public void toStringFormat() {
Config config = new Config();
config.setParam("myparam");
config.setRegexp("myregexp");
Predicate predicate = new QueryRoutePredicateFactory().apply(config);
assertThat(predicate.toString()).contains("Query: param=myparam regexp=myregexp");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)

View File

@@ -28,6 +28,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.handler.AsyncPredicate;
import org.springframework.cloud.gateway.handler.predicate.ReadBodyPredicateFactory.Config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.test.PermitAllSecurityConfiguration;
@@ -45,7 +47,9 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.server.ServerWebExchange;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
@@ -76,6 +80,15 @@ public class ReadBodyPredicateFactoryTest {
}
@Test
public void toStringFormat() {
Config config = new Config();
config.setInClass(String.class);
AsyncPredicate<ServerWebExchange> predicate = new ReadBodyPredicateFactory()
.applyAsync(config);
assertThat(predicate.toString()).contains("ReadBody: " + config.getInClass());
}
@EnableAutoConfiguration
@SpringBootConfiguration
@RibbonClients({

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.gateway.handler.predicate;
import java.time.Duration;
import java.util.function.Predicate;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -27,6 +28,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.handler.predicate.RemoteAddrRoutePredicateFactory.Config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.support.ipresolver.XForwardedRemoteAddressResolver;
@@ -39,6 +41,7 @@ import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.reactive.function.client.ClientResponse;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
import static org.springframework.cloud.gateway.test.TestUtils.assertStatus;
@@ -76,6 +79,14 @@ public class RemoteAddrRoutePredicateFactoryTests extends BaseWebClientTests {
.expectComplete().verify(Duration.ofSeconds(20));
}
@Test
public void toStringFormat() {
Config config = new Config();
config.setSources("1.2.3.4", "5.6.7.8");
Predicate predicate = new RemoteAddrRoutePredicateFactory().apply(config);
assertThat(predicate.toString()).contains("RemoteAddrs: [1.2.3.4, 5.6.7.8]");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.gateway.handler.predicate;
import java.util.Random;
import java.util.function.Predicate;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -29,6 +30,7 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.support.WeightConfig;
import org.springframework.cloud.gateway.test.BaseWebClientTests;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
@@ -36,6 +38,7 @@ import org.springframework.http.HttpHeaders;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@@ -72,6 +75,13 @@ public class WeightRoutePredicateFactoryIntegrationTests extends BaseWebClientTe
.valueEquals(ROUTE_ID_HEADER, "weight_low_test");
}
@Test
public void toStringFormat() {
WeightConfig config = new WeightConfig("mygroup", "myroute", 5);
Predicate predicate = new WeightRoutePredicateFactory().apply(config);
assertThat(predicate.toString()).contains("Weight: mygroup 5");
}
@EnableAutoConfiguration
@SpringBootConfiguration
@Import(DefaultTestConfig.class)

View File

@@ -35,6 +35,7 @@ import org.springframework.cloud.gateway.handler.predicate.HostRoutePredicateFac
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat;
@@ -74,10 +75,10 @@ public class RouteDefinitionRouteLocatorTests {
.block();
List<GatewayFilter> filters = routes.get(0).getFilters();
assertThat(filters).hasSize(3);
assertThat(getFilterClassName(filters.get(0))).startsWith("RemoveResponseHeader");
assertThat(getFilterClassName(filters.get(1))).startsWith("AddResponseHeader");
assertThat(getFilterClassName(filters.get(0))).contains("RemoveResponseHeader");
assertThat(getFilterClassName(filters.get(1))).contains("AddResponseHeader");
assertThat(getFilterClassName(filters.get(2)))
.startsWith("RouteDefinitionRouteLocatorTests$TestOrderedGateway");
.contains("RouteDefinitionRouteLocatorTests$TestOrderedGateway");
}
private String getFilterClassName(GatewayFilter target) {
@@ -85,7 +86,12 @@ public class RouteDefinitionRouteLocatorTests {
return getFilterClassName(((OrderedGatewayFilter) target).getDelegate());
}
else {
return target.getClass().getSimpleName();
String simpleName = target.getClass().getSimpleName();
if (StringUtils.isEmpty(simpleName)) {
// maybe a lambda using new toString methods
simpleName = target.toString();
}
return simpleName;
}
}

View File

@@ -25,10 +25,12 @@ import org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitio
import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile;
@SpringBootConfiguration
@EnableAutoConfiguration
@Import(PermitAllSecurityConfiguration.class)
public class GatewayTestApplication {
public static void main(String[] args) {

View File

@@ -384,3 +384,8 @@ eureka:
client:
enabled: false
management:
endpoints:
web:
exposure:
include: "*"