diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java index ca2b7a1495..2ee392ba4c 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java @@ -27,6 +27,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties; import reactor.core.publisher.Flux; import reactor.netty.http.client.HttpClient; import reactor.netty.http.client.WebsocketClientSpec; @@ -452,8 +453,8 @@ public MethodRoutePredicateFactory methodRoutePredicateFactory() { @Bean @ConditionalOnEnabledPredicate - public PathRoutePredicateFactory pathRoutePredicateFactory() { - return new PathRoutePredicateFactory(); + public PathRoutePredicateFactory pathRoutePredicateFactory(WebFluxProperties webFluxProperties) { + return new PathRoutePredicateFactory(webFluxProperties); } @Bean diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactory.java index f4a1d7c739..a4facb854a 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactory.java @@ -24,8 +24,10 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties; import org.springframework.core.style.ToStringCreator; import org.springframework.http.server.PathContainer; +import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.util.pattern.PathPattern; @@ -42,6 +44,7 @@ /** * @author Spencer Gibb * @author Dhawal Kapil + * @author Guo FuYiNan */ public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory { @@ -51,8 +54,11 @@ public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory apply(Config config) { synchronized (this.pathPatternParser) { pathPatternParser.setMatchOptionalTrailingSeparator(config.isMatchTrailingSlash()); config.getPatterns().forEach(pattern -> { - PathPattern pathPattern = this.pathPatternParser.parse(pattern); + String basePath = webFluxProperties.getBasePath(); + if (StringUtils.hasText(basePath)) { + if (pattern.length() > 1 && !pattern.startsWith("/")) { + basePath += ("/"); + } + } + PathPattern pathPattern = this.pathPatternParser.parse(basePath + pattern); pathPatterns.add(pathPattern); }); } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/GatewayPredicateVisitorTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/GatewayPredicateVisitorTests.java index 8785657cac..f4bffbcf5f 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/GatewayPredicateVisitorTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/GatewayPredicateVisitorTests.java @@ -16,25 +16,41 @@ package org.springframework.cloud.gateway.handler.predicate; +import java.net.URI; import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; import java.util.function.Predicate; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties; +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.factory.StripPrefixGatewayFilterFactory; import org.springframework.cloud.gateway.handler.AsyncPredicate; import org.springframework.cloud.gateway.route.Route; +import org.springframework.mock.http.server.reactive.MockServerHttpRequest; +import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR; +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR; /** * @author Spencer Gibb + * @author Guo FuYiNan */ public class GatewayPredicateVisitorTests { @Test public void asyncPredicateVisitVisitsEachNode() { - PathRoutePredicateFactory pathRoutePredicateFactory = new PathRoutePredicateFactory(); + PathRoutePredicateFactory pathRoutePredicateFactory = new PathRoutePredicateFactory(new WebFluxProperties()); HostRoutePredicateFactory hostRoutePredicateFactory = new HostRoutePredicateFactory(); ReadBodyRoutePredicateFactory readBodyRoutePredicateFactory1 = new ReadBodyRoutePredicateFactory(); ReadBodyRoutePredicateFactory readBodyRoutePredicateFactory2 = new ReadBodyRoutePredicateFactory(); @@ -55,7 +71,7 @@ public void asyncPredicateVisitVisitsEachNode() { @Test public void predicateVisitVisitsEachNode() { - PathRoutePredicateFactory pathRoutePredicateFactory = new PathRoutePredicateFactory(); + PathRoutePredicateFactory pathRoutePredicateFactory = new PathRoutePredicateFactory(new WebFluxProperties()); HostRoutePredicateFactory hostRoutePredicateFactory = new HostRoutePredicateFactory(); Predicate predicate = pathRoutePredicateFactory.apply(pathRoutePredicateFactory.newConfig()) .and(hostRoutePredicateFactory.apply(hostRoutePredicateFactory.newConfig())); @@ -68,4 +84,57 @@ public void predicateVisitVisitsEachNode() { HostRoutePredicateFactory.Config.class); } + @Test + public void pathRoutePredicateVisitWithSetWebfluxBasePath() { + WebFluxProperties webFluxProperties = new WebFluxProperties(); + webFluxProperties.setBasePath("/gw/api/v1"); + + PathRoutePredicateFactory pathRoutePredicateFactory = new PathRoutePredicateFactory(webFluxProperties); + PathRoutePredicateFactory.Config config = new PathRoutePredicateFactory.Config() + .setPatterns(List.of("/temp/**")) + .setMatchTrailingSlash(true); + + Predicate predicate = pathRoutePredicateFactory.apply(config); + + ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("http://127.0.0.1:8080/gw/api/v1/temp/test").build()); + + assertThat(predicate.test(exchange)).isEqualTo(true); + } + + @Test + public void pathRoutePredicateVisitWithSetWebfluxBasePathStripPrefix() { + WebFluxProperties webFluxProperties = new WebFluxProperties(); + webFluxProperties.setBasePath("/gw/api/v1"); + + PathRoutePredicateFactory pathRoutePredicateFactory = new PathRoutePredicateFactory(webFluxProperties); + PathRoutePredicateFactory.Config config = new PathRoutePredicateFactory.Config() + .setPatterns(List.of("/temp/**")) + .setMatchTrailingSlash(true); + + Predicate predicate = pathRoutePredicateFactory.apply(config); + + ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("http://127.0.0.1:8080/gw/api/v1/temp/test").build()); + + assertThat(predicate.test(exchange)).isEqualTo(true); + + // webflux base path strips prefix is 3 + GatewayFilter filter = new StripPrefixGatewayFilterFactory().apply(c -> c.setParts(3)); + + GatewayFilterChain filterChain = mock(GatewayFilterChain.class); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ServerWebExchange.class); + when(filterChain.filter(captor.capture())).thenReturn(Mono.empty()); + + filter.filter(exchange, filterChain); + + ServerWebExchange webExchange = captor.getValue(); + + assertThat(webExchange.getRequest().getURI()).hasPath("/temp/test"); + + URI requestUrl = webExchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); + assertThat(requestUrl).hasScheme("http").hasHost("127.0.0.1").hasPort(8080).hasPath("/temp/test"); + + LinkedHashSet uris = webExchange.getRequiredAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR); + assertThat(uris).contains(exchange.getRequest().getURI()); + } } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactoryTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactoryTests.java index e2b4ddca1c..af5155a9d9 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactoryTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicateFactoryTests.java @@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping; import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory.Config; @@ -105,14 +106,14 @@ public void matchOptionalTrailingSeparatorCopiedToMatchTrailingSlash() { @Test public void toStringFormat() { Config config = new Config().setPatterns(Arrays.asList("patternA", "patternB")).setMatchTrailingSlash(false); - Predicate predicate = new PathRoutePredicateFactory().apply(config); + Predicate predicate = new PathRoutePredicateFactory(new WebFluxProperties()).apply(config); assertThat(predicate.toString()).contains("patternA").contains("patternB").contains("false"); } @Test public void toStringFormatMatchTrailingSlashTrue() { Config config = new Config().setPatterns(Arrays.asList("patternA", "patternB")).setMatchTrailingSlash(true); - Predicate predicate = new PathRoutePredicateFactory().apply(config); + Predicate predicate = new PathRoutePredicateFactory(new WebFluxProperties()).apply(config); assertThat(predicate.toString()).contains("patternA").contains("patternB").contains("true"); } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicatePathContainerAttrBenchMarkTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicatePathContainerAttrBenchMarkTests.java index 92032d0eaf..6a2b079013 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicatePathContainerAttrBenchMarkTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/handler/predicate/PathRoutePredicatePathContainerAttrBenchMarkTests.java @@ -31,6 +31,7 @@ import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.annotations.Warmup; +import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.web.server.ServerWebExchange; @@ -52,7 +53,7 @@ public class PathRoutePredicatePathContainerAttrBenchMarkTests { for (int i = 0; i < ROUTES_NUM; i++) { PathRoutePredicateFactory.Config config = new PathRoutePredicateFactory.Config() .setPatterns(Collections.singletonList(PATH_PATTERN_PREFIX + i)).setMatchTrailingSlash(true); - Predicate predicate = new PathRoutePredicateFactory().apply(config); + Predicate predicate = new PathRoutePredicateFactory(new WebFluxProperties()).apply(config); predicates.add(predicate); } } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/support/tagsprovider/GatewayPathTagsProviderTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/support/tagsprovider/GatewayPathTagsProviderTests.java index 0b8f550d71..276e14ea85 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/support/tagsprovider/GatewayPathTagsProviderTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/support/tagsprovider/GatewayPathTagsProviderTests.java @@ -22,6 +22,7 @@ import io.micrometer.core.instrument.Tags; import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties; import org.springframework.cloud.gateway.handler.predicate.HostRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.MethodRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory; @@ -53,7 +54,7 @@ void addPathToRoutes() { PathRoutePredicateFactory.Config pathConfig = new PathRoutePredicateFactory.Config().setPatterns(pathList); HostRoutePredicateFactory.Config hostConfig = new HostRoutePredicateFactory.Config() .setPatterns(Collections.singletonList("**.myhost.com")); - Route route = Route.async().id("git").uri(ROUTE_URI).predicate(new PathRoutePredicateFactory().apply(pathConfig) + Route route = Route.async().id("git").uri(ROUTE_URI).predicate(new PathRoutePredicateFactory(new WebFluxProperties()).apply(pathConfig) .and(new HostRoutePredicateFactory().apply(hostConfig))).build(); ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(ROUTE_URI).build()); @@ -74,8 +75,8 @@ void addsMultiplePathToRoutes() { PathRoutePredicateFactory.Config pathConfig = new PathRoutePredicateFactory.Config().setPatterns(pathList); PathRoutePredicateFactory.Config pathConfig2 = new PathRoutePredicateFactory.Config().setPatterns(pathList2); - Route route = Route.async().id("git").uri(ROUTE_URI).predicate(new PathRoutePredicateFactory().apply(pathConfig) - .or(new PathRoutePredicateFactory().apply(pathConfig2))).build(); + Route route = Route.async().id("git").uri(ROUTE_URI).predicate(new PathRoutePredicateFactory(new WebFluxProperties()).apply(pathConfig) + .or(new PathRoutePredicateFactory(new WebFluxProperties()).apply(pathConfig2))).build(); ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get(ROUTE_URI).build()); exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, route);