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 47cbdf65..e33285fe 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 io.netty.channel.ChannelOption; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import reactor.netty.http.client.HttpClient; import reactor.netty.resources.ConnectionProvider; import reactor.netty.tcp.ProxyProvider; @@ -121,11 +122,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; @@ -241,6 +245,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 disabledHiddenHttpMethodFilter() { + 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); @@ -575,7 +592,7 @@ public class GatewayAutoConfiguration { public RequestHeaderToRequestUriGatewayFilterFactory requestHeaderToRequestUriGatewayFilterFactory() { return new RequestHeaderToRequestUriGatewayFilterFactory(); } - + @Bean public RequestSizeGatewayFilterFactory requestSizeGatewayFilterFactory() { return new RequestSizeGatewayFilterFactory(); 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 dfcb0e3c..3b6caecd 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}