Skip to content

Commit

Permalink
Fix Bug : Spring Cloud Gateway Setting Spring.webflux.base-path All P…
Browse files Browse the repository at this point in the history
…ath Predicates Return 404 Results

Changing the order of imports

Revert code style
  • Loading branch information
Guofuyinan committed Jun 21, 2023
1 parent 9073d3e commit c967c2c
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -42,6 +44,7 @@
/**
* @author Spencer Gibb
* @author Dhawal Kapil
* @author Guo FuYiNan
*/
public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory<PathRoutePredicateFactory.Config> {

Expand All @@ -51,8 +54,11 @@ public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory<Pat

private PathPatternParser pathPatternParser = new PathPatternParser();

public PathRoutePredicateFactory() {
private final WebFluxProperties webFluxProperties;

public PathRoutePredicateFactory(WebFluxProperties webFluxProperties) {
super(Config.class);
this.webFluxProperties = webFluxProperties;
}

private static void traceMatch(String prefix, Object desired, Object actual, boolean match) {
Expand Down Expand Up @@ -83,7 +89,13 @@ public Predicate<ServerWebExchange> 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);
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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<ServerWebExchange> predicate = pathRoutePredicateFactory.apply(pathRoutePredicateFactory.newConfig())
.and(hostRoutePredicateFactory.apply(hostRoutePredicateFactory.newConfig()));
Expand All @@ -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<ServerWebExchange> 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<ServerWebExchange> 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<ServerWebExchange> 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<URI> uris = webExchange.getRequiredAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
assertThat(uris).contains(exchange.getRequest().getURI());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<ServerWebExchange> predicate = new PathRoutePredicateFactory().apply(config);
Predicate<ServerWebExchange> predicate = new PathRoutePredicateFactory(new WebFluxProperties()).apply(config);
assertThat(predicate.toString()).contains("patternA").contains("patternB").contains("true");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<ServerWebExchange> predicate = new PathRoutePredicateFactory().apply(config);
Predicate<ServerWebExchange> predicate = new PathRoutePredicateFactory(new WebFluxProperties()).apply(config);
predicates.add(predicate);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
Expand All @@ -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);
Expand Down

0 comments on commit c967c2c

Please sign in to comment.