From afe639d844b93565a4e9650568564b8b7a653601 Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Wed, 12 Sep 2018 14:24:03 -0400 Subject: [PATCH 1/3] Add support for optionally matching trailing / in path predicate. Fixes #511 --- .../predicate/PathRoutePredicateFactory.java | 16 +++++++++++++++- .../gateway/route/builder/PredicateSpec.java | 13 +++++++++++++ .../PathRoutePredicateFactoryTests.java | 6 ++++++ .../src/test/resources/application.yml | 2 +- 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactory.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactory.java index 507ec2d9..5003ed37 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactory.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactory.java @@ -17,6 +17,7 @@ package org.springframework.cloud.gateway.handler.predicate; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.function.Predicate; @@ -40,6 +41,7 @@ import static org.springframework.http.server.PathContainer.parsePath; */ public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory { private static final Log log = LogFactory.getLog(RoutePredicateFactory.class); + private static final String MATCH_OPTIONAL_TRAILING_SEPARATOR_KEY = "matchOptionalTrailingSeparator"; private PathPatternParser pathPatternParser = new PathPatternParser(); @@ -53,12 +55,13 @@ public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory shortcutFieldOrder() { - return Collections.singletonList(PATTERN_KEY); + return Arrays.asList(PATTERN_KEY, MATCH_OPTIONAL_TRAILING_SEPARATOR_KEY); } @Override public Predicate apply(Config config) { synchronized (this.pathPatternParser) { + pathPatternParser.setMatchOptionalTrailingSeparator(config.isMatchOptionalTrailingSeparator()); config.pathPattern = this.pathPatternParser.parse(config.pattern); } return exchange -> { @@ -88,6 +91,7 @@ public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory c.setPattern(pattern))); } + /** + * A predicate that checks if the path of the request matches the given pattern + * @param pattern the pattern to check the path against. + * The pattern is a {@link org.springframework.util.PathMatcher} pattern + * @param matchOptionalTrailingSeparator set to false if you do not want this path to match + * when there is a trailing / + * @return a {@link BooleanSpec} to be used to add logical operators + */ + public BooleanSpec path(String pattern, boolean matchOptionalTrailingSeparator) { + return asyncPredicate(getBean(PathRoutePredicateFactory.class) + .applyAsync(c -> c.setPattern(pattern).setMatchOptionalTrailingSeparator(matchOptionalTrailingSeparator))); + } + /** * This predicate is BETA and may be subject to change in a future release. * A predicate that checks the contents of the request body diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactoryTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactoryTests.java index 5603c75c..70e50843 100644 --- a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactoryTests.java +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactoryTests.java @@ -44,6 +44,12 @@ public class PathRoutePredicateFactoryTests extends BaseWebClientTests { .expectStatus().isOk() .expectHeader().valueEquals(HANDLER_MAPPER_HEADER, RoutePredicateHandlerMapping.class.getSimpleName()) .expectHeader().valueEquals(ROUTE_ID_HEADER, "path_test"); + + //since the configuration does not allow the trailing / to match this should fail + testClient.get().uri("/abc/123/function/") + .header(HttpHeaders.HOST, "www.path.org") + .exchange() + .expectStatus().is4xxClientError(); } @Test diff --git a/spring-cloud-gateway-core/src/test/resources/application.yml b/spring-cloud-gateway-core/src/test/resources/application.yml index 6ae8ed00..41e8bb5d 100644 --- a/spring-cloud-gateway-core/src/test/resources/application.yml +++ b/spring-cloud-gateway-core/src/test/resources/application.yml @@ -128,7 +128,7 @@ spring: - id: path_test uri: ${test.uri} predicates: - - Path=/{org}/{scope}/function + - Path=/{org}/{scope}/function,false - Host=**.path.org filters: - SetPath=/anything/{org}{scope} From 9019b0b70ee7f2d9b083080506ddd1e21f38021f Mon Sep 17 00:00:00 2001 From: Spencer Gibb Date: Wed, 19 Sep 2018 14:52:59 -0400 Subject: [PATCH 2/3] Bumps build to 2.0.4.BUILD-SNAPSHOT --- pom.xml | 9 ++++++++- spring-cloud-gateway-dependencies/pom.xml | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 06beb9d8..d1bf4534 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.cloud spring-cloud-build - 2.0.3.RELEASE + 2.0.4.BUILD-SNAPSHOT @@ -54,6 +54,13 @@ + + org.springframework + spring-framework-bom + 5.0.8.RELEASE + import + pom + org.springframework.cloud spring-cloud-gateway-dependencies diff --git a/spring-cloud-gateway-dependencies/pom.xml b/spring-cloud-gateway-dependencies/pom.xml index 807035c1..032b78d7 100644 --- a/spring-cloud-gateway-dependencies/pom.xml +++ b/spring-cloud-gateway-dependencies/pom.xml @@ -5,7 +5,7 @@ spring-cloud-dependencies-parent org.springframework.cloud - 2.0.3.RELEASE + 2.0.4.BUILD-SNAPSHOT From 58834daf6271c3ae379f5eb0939689ad6a14036e Mon Sep 17 00:00:00 2001 From: Spencer Gibb Date: Wed, 19 Sep 2018 15:19:16 -0400 Subject: [PATCH 3/3] temporarily disables HiddenHttpMethodFilter --- .../config/GatewayAutoConfiguration.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java index 7367455e..145970c7 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java @@ -25,6 +25,7 @@ import com.netflix.hystrix.HystrixObservableCommand; import io.netty.channel.ChannelOption; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import reactor.ipc.netty.http.client.HttpClient; import reactor.ipc.netty.http.client.HttpClientOptions; import reactor.ipc.netty.options.ClientProxyOptions; @@ -122,11 +123,14 @@ import org.springframework.core.env.Environment; import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.util.StringUtils; import org.springframework.validation.Validator; +import org.springframework.web.filter.reactive.HiddenHttpMethodFilter; import org.springframework.web.reactive.DispatcherHandler; import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient; import org.springframework.web.reactive.socket.client.WebSocketClient; import org.springframework.web.reactive.socket.server.WebSocketService; import org.springframework.web.reactive.socket.server.support.HandshakeWebSocketService; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilterChain; import static org.springframework.cloud.gateway.config.HttpClientProperties.Pool.PoolType.DISABLED; import static org.springframework.cloud.gateway.config.HttpClientProperties.Pool.PoolType.FIXED; @@ -243,6 +247,19 @@ public class GatewayAutoConfiguration { } } + //TODO: remove when not needed anymore + // either https://jira.spring.io/browse/SPR-17291 or + // https://github.com/spring-projects/spring-boot/issues/14520 needs to be fixed + @Bean + public HiddenHttpMethodFilter hiddenHttpMethodFilter() { + return new HiddenHttpMethodFilter() { + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + return chain.filter(exchange); + } + }; + } + @Bean public RouteLocatorBuilder routeLocatorBuilder(ConfigurableApplicationContext context) { return new RouteLocatorBuilder(context);