Merge branch '2.1.x'

# Conflicts:
#	spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpointTests.java
This commit is contained in:
Olga Maciaszek-Sharma
2019-09-18 12:45:32 +02:00
7 changed files with 185 additions and 14 deletions

View File

@@ -847,7 +847,7 @@ spring:
predicates:
- Path=/foo/**
filters:
- RewritePath=/foo/(?<segment>.*), /$\{segment}
- RewritePath=/foo(?<segment>/?.*), $\{segment}
----
For a request path of `/foo/bar`, this will set the path to `/bar` before making the downstream request. Notice the `$` Should be replaced with `$\` because of the YAML spec.

View File

@@ -28,6 +28,7 @@ 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.handler.predicate.RoutePredicateFactory;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
@@ -56,6 +57,8 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
protected List<GatewayFilterFactory> GatewayFilters;
protected List<RoutePredicateFactory> routePredicates;
protected RouteDefinitionWriter routeDefinitionWriter;
protected RouteLocator routeLocator;
@@ -65,10 +68,12 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
public AbstractGatewayControllerEndpoint(
RouteDefinitionLocator routeDefinitionLocator,
List<GlobalFilter> globalFilters, List<GatewayFilterFactory> GatewayFilters,
List<RoutePredicateFactory> routePredicates,
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
this.routeDefinitionLocator = routeDefinitionLocator;
this.globalFilters = globalFilters;
this.GatewayFilters = GatewayFilters;
this.routePredicates = routePredicates;
this.routeDefinitionWriter = routeDefinitionWriter;
this.routeLocator = routeLocator;
}
@@ -96,6 +101,11 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
return getNamesToOrders(this.GatewayFilters);
}
@GetMapping("/routepredicates")
public Mono<HashMap<String, Object>> routepredicates() {
return getNamesToOrders(this.routePredicates);
}
private <T> Mono<HashMap<String, Object>> getNamesToOrders(List<T> list) {
return Flux.fromIterable(list).reduce(new HashMap<>(), this::putItem);
}
@@ -117,14 +127,35 @@ public class AbstractGatewayControllerEndpoint implements ApplicationEventPublis
*/
@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())));
public Mono<ResponseEntity<Object>> save(@PathVariable String id,
@RequestBody RouteDefinition route) {
return Mono.just(route).filter(this::validateRouteDefinition)
.flatMap(routeDefinition -> this.routeDefinitionWriter
.save(Mono.just(routeDefinition).map(r -> {
r.setId(id);
log.debug("Saving route: " + route);
return r;
}))
.then(Mono.defer(() -> Mono.just(ResponseEntity
.created(URI.create("/routes/" + id)).build()))))
.switchIfEmpty(
Mono.defer(() -> Mono.just(ResponseEntity.badRequest().build())));
}
private boolean validateRouteDefinition(RouteDefinition routeDefinition) {
boolean hasValidFilterDefinitions = routeDefinition.getFilters().stream()
.allMatch(filterDefinition -> GatewayFilters.stream()
.anyMatch(gatewayFilterFactory -> filterDefinition.getName()
.equals(gatewayFilterFactory.name())));
boolean hasValidPredicateDefinitions = routeDefinition.getPredicates().stream()
.allMatch(predicateDefinition -> routePredicates.stream()
.anyMatch(routePredicate -> predicateDefinition.getName()
.equals(routePredicate.name())));
log.debug("FilterDefinitions valid: " + hasValidFilterDefinitions);
log.debug("PredicateDefinitions valid: " + hasValidPredicateDefinitions);
return hasValidFilterDefinitions && hasValidPredicateDefinitions;
}
@DeleteMapping("/routes/{id}")

View File

@@ -28,6 +28,7 @@ import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEn
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.handler.predicate.RoutePredicateFactory;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.cloud.gateway.route.RouteLocator;
@@ -44,8 +45,10 @@ public class GatewayControllerEndpoint extends AbstractGatewayControllerEndpoint
public GatewayControllerEndpoint(List<GlobalFilter> globalFilters,
List<GatewayFilterFactory> gatewayFilters,
List<RoutePredicateFactory> routePredicates,
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
super(null, globalFilters, gatewayFilters, routeDefinitionWriter, routeLocator);
super(null, globalFilters, gatewayFilters, routePredicates, routeDefinitionWriter,
routeLocator);
}
// TODO: Flush out routes without a definition

View File

@@ -27,6 +27,7 @@ import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEn
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.handler.predicate.RoutePredicateFactory;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
@@ -45,8 +46,9 @@ public class GatewayLegacyControllerEndpoint extends AbstractGatewayControllerEn
public GatewayLegacyControllerEndpoint(RouteDefinitionLocator routeDefinitionLocator,
List<GlobalFilter> globalFilters, List<GatewayFilterFactory> GatewayFilters,
List<RoutePredicateFactory> routePredicates,
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
super(routeDefinitionLocator, globalFilters, GatewayFilters,
super(routeDefinitionLocator, globalFilters, GatewayFilters, routePredicates,
routeDefinitionWriter, routeLocator);
}

View File

@@ -727,9 +727,10 @@ public class GatewayAutoConfiguration {
public GatewayControllerEndpoint gatewayControllerEndpoint(
List<GlobalFilter> globalFilters,
List<GatewayFilterFactory> gatewayFilters,
List<RoutePredicateFactory> routePredicates,
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
return new GatewayControllerEndpoint(globalFilters, gatewayFilters,
routeDefinitionWriter, routeLocator);
routePredicates, routeDefinitionWriter, routeLocator);
}
@Bean
@@ -739,9 +740,11 @@ public class GatewayAutoConfiguration {
RouteDefinitionLocator routeDefinitionLocator,
List<GlobalFilter> globalFilters,
List<GatewayFilterFactory> gatewayFilters,
List<RoutePredicateFactory> routePredicates,
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
return new GatewayLegacyControllerEndpoint(routeDefinitionLocator,
globalFilters, gatewayFilters, routeDefinitionWriter, routeLocator);
globalFilters, gatewayFilters, routePredicates, routeDefinitionWriter,
routeLocator);
}
}

View File

@@ -16,8 +16,12 @@
package org.springframework.cloud.gateway.actuate;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import org.assertj.core.util.Maps;
import org.junit.Test;
@@ -28,13 +32,23 @@ 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.FilterDefinition;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.test.PermitAllSecurityConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
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;
@@ -93,6 +107,92 @@ public class GatewayControllerEndpointTests {
Maps.newHashMap("name", "value")));
}
@Test
public void testRouteFilters() {
testClient.get()
.uri("http://localhost:" + port + "/actuator/gateway/routefilters")
.exchange().expectStatus().isOk().expectBody(Map.class)
.consumeWith(result -> {
Map<?, ?> responseBody = result.getResponseBody();
assertThat(responseBody).isNotEmpty();
});
}
@Test
public void testRoutePredicates() {
testClient.get()
.uri("http://localhost:" + port + "/actuator/gateway/routepredicates")
.exchange().expectStatus().isOk().expectBody(Map.class)
.consumeWith(result -> {
Map<?, ?> responseBody = result.getResponseBody();
assertThat(responseBody).isNotEmpty();
});
}
@Test
public void testPostValidRouteDefinition() {
RouteDefinition testRouteDefinition = new RouteDefinition();
testRouteDefinition.setUri(URI.create("http://example.org"));
FilterDefinition prefixPathFilterDefinition = new FilterDefinition(
"PrefixPath=/test-path");
FilterDefinition redirectToFilterDefinition = new FilterDefinition(
"RemoveResponseHeader=Sensitive-Header");
FilterDefinition testFilterDefinition = new FilterDefinition("TestFilter");
testRouteDefinition.setFilters(Arrays.asList(prefixPathFilterDefinition,
redirectToFilterDefinition, testFilterDefinition));
PredicateDefinition hostRoutePredicateDefinition = new PredicateDefinition(
"Host=myhost.org");
PredicateDefinition methodRoutePredicateDefinition = new PredicateDefinition(
"Method=GET");
PredicateDefinition testPredicateDefinition = new PredicateDefinition(
"Test=value");
testRouteDefinition.setPredicates(Arrays.asList(hostRoutePredicateDefinition,
methodRoutePredicateDefinition, testPredicateDefinition));
testClient.post()
.uri("http://localhost:" + port + "/actuator/gateway/routes/test-route")
.accept(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(testRouteDefinition)).exchange()
.expectStatus().isCreated();
}
@Test
public void testPostRouteWithNotExistingFilter() {
RouteDefinition testRouteDefinition = new RouteDefinition();
testRouteDefinition.setUri(URI.create("http://example.org"));
FilterDefinition filterDefinition = new FilterDefinition(
"NotExistingFilter=test-config");
testRouteDefinition.setFilters(Collections.singletonList(filterDefinition));
testClient.post()
.uri("http://localhost:" + port + "/actuator/gateway/routes/test-route")
.accept(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(testRouteDefinition)).exchange()
.expectStatus().isBadRequest();
}
@Test
public void testPostRouteWithNotExistingPredicate() {
RouteDefinition testRouteDefinition = new RouteDefinition();
testRouteDefinition.setUri(URI.create("http://example.org"));
PredicateDefinition predicateDefinition = new PredicateDefinition(
"NotExistingPredicate=test-config");
testRouteDefinition.setPredicates(Collections.singletonList(predicateDefinition));
testClient.post()
.uri("http://localhost:" + port + "/actuator/gateway/routes/test-route")
.accept(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(testRouteDefinition)).exchange()
.expectStatus().isBadRequest();
}
@SpringBootConfiguration
@EnableAutoConfiguration
@Import(PermitAllSecurityConfiguration.class)
@@ -106,6 +206,39 @@ public class GatewayControllerEndpointTests {
.build();
}
@Bean
public TestFilterGatewayFilterFactory customGatewayFilterFactory() {
return new TestFilterGatewayFilterFactory();
}
@Bean
public TestRoutePredicateFactory customGatewayPredicateFactory() {
return new TestRoutePredicateFactory(Object.class);
}
}
private static class TestFilterGatewayFilterFactory
extends AbstractGatewayFilterFactory {
@Override
public GatewayFilter apply(Object config) {
return null;
}
}
private static class TestRoutePredicateFactory extends AbstractRoutePredicateFactory {
TestRoutePredicateFactory(Class configClass) {
super(configClass);
}
@Override
public Predicate<ServerWebExchange> apply(Object config) {
return (GatewayPredicate) serverWebExchange -> true;
}
}
}

View File

@@ -1 +0,0 @@
provides: spring-cloud-gateway-core