From 350c9cda6dcce96f76f41b91e64bf8884c6d440e Mon Sep 17 00:00:00 2001 From: jiangyuan Date: Thu, 6 Mar 2025 21:26:50 +0800 Subject: [PATCH 1/9] RetryGatewayFilterFactory RetryConfig support Jitter & Timeout Signed-off-by: jiangyuan --- .../factory/RetryGatewayFilterFactory.java | 76 ++++++++++++++++++- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java index d8046e27..ad6889e5 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java @@ -30,6 +30,7 @@ import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; import reactor.netty.Connection; import reactor.retry.Backoff; +import reactor.retry.Jitter; import reactor.retry.Repeat; import reactor.retry.RepeatContext; import reactor.retry.Retry; @@ -70,7 +71,7 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory shortcutFieldOrder() { return Arrays.asList("retries", "statuses", "methods", "backoff.firstBackoff", "backoff.maxBackoff", - "backoff.factor", "backoff.basedOnPreviousValue"); + "backoff.factor", "backoff.basedOnPreviousValue", "jitter.randomFactor", "timeout"); } @Override @@ -124,9 +125,16 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory exceptionRetry = null; if (!retryConfig.getExceptions().isEmpty()) { @@ -163,6 +171,14 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory= 0"); + } + } + + public Duration getTimeout() { + return timeout; + } + + public RetryConfig setTimeout(Duration timeout) { + this.timeout = timeout; + return this; + } + + public JitterConfig getJitter() { + return jitter; + } + + public RetryConfig setJitter(JitterConfig jitter) { + this.jitter = jitter; + return this; } public BackoffConfig getBackoff() { @@ -439,4 +487,28 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory= 0 && randomFactor <= 1, "random factor must be between 0 and 1 (default 0.5)"); + } + + public JitterConfig() { + } + + public JitterConfig(double randomFactor) { + this.randomFactor = randomFactor; + } + + public double getRandomFactor() { + return randomFactor; + } + + public void setRandomFactor(double randomFactor) { + this.randomFactor = randomFactor; + } + } + } From 9eb9165927d1d12af1a61b3d3675502448b68572 Mon Sep 17 00:00:00 2001 From: jiangyuan Date: Wed, 12 Mar 2025 15:44:15 +0800 Subject: [PATCH 2/9] add docs and unit-test Signed-off-by: jiangyuan --- .../retry-factory.adoc | 13 ++++++++++++- .../factory/RetryGatewayFilterFactory.java | 4 +++- ...yGatewayFilterFactoryIntegrationTests.java | 12 ++++++++++++ .../test/resources/application-retrytests.yml | 19 +++++++++++++++++++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway/gatewayfilter-factories/retry-factory.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway/gatewayfilter-factories/retry-factory.adoc index 62d58904..afc650f5 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway/gatewayfilter-factories/retry-factory.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway/gatewayfilter-factories/retry-factory.adoc @@ -12,6 +12,9 @@ The `Retry` `GatewayFilter` factory supports the following parameters: Retries are performed after a backoff interval of `firstBackoff * (factor ^ n)`, where `n` is the iteration. If `maxBackoff` is configured, the maximum backoff applied is limited to `maxBackoff`. If `basedOnPreviousValue` is true, the backoff is calculated by using `prevBackoff * factor`. +* `jitter`: The configured random jitter for the retries. +Generating a backoff between `[backoff - backoff*randomFactor, backoff + backoff*randomFactor]` +* `timeout`: The configured timeout for the retries. The following defaults are configured for `Retry` filter, if enabled: @@ -20,6 +23,8 @@ The following defaults are configured for `Retry` filter, if enabled: * `methods`: GET method * `exceptions`: `IOException` and `TimeoutException` * `backoff`: disabled +* `jitter`: disabled +* `timeout`: unlimited The following listing configures a Retry `GatewayFilter`: @@ -45,6 +50,9 @@ spring: maxBackoff: 50ms factor: 2 basedOnPreviousValue: false + jitter: + randomFactor: 0.5 + timeout: 100ms ---- NOTE: When using the retry filter with a `forward:` prefixed URL, the target endpoint should be written carefully so that, in case of an error, it does not do anything that could result in a response being sent to the client and committed. @@ -77,10 +85,13 @@ spring: maxBackoff: 50ms factor: 2 basedOnPreviousValue: false + jitter: + randomFactor: 0.5 + timeout: 100ms - id: retryshortcut_route uri: https://example.org filters: - - Retry=3,INTERNAL_SERVER_ERROR,GET,10ms,50ms,2,false + - Retry=3,INTERNAL_SERVER_ERROR,GET,10ms,50ms,2,false,0.5,100ms ---- diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java index ad6889e5..91a73ae0 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java @@ -492,7 +492,8 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory= 0 && randomFactor <= 1, "random factor must be between 0 and 1 (default 0.5)"); + Assert.isTrue(randomFactor >= 0 && randomFactor <= 1, + "random factor must be between 0 and 1 (default 0.5)"); } public JitterConfig() { @@ -509,6 +510,7 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory Date: Thu, 13 Mar 2025 12:31:46 +0800 Subject: [PATCH 3/9] add unit-test Signed-off-by: jiangyuan --- .../factory/RetryGatewayFilterFactory.java | 9 ++++++-- ...yGatewayFilterFactoryIntegrationTests.java | 21 +++++++++++++++++++ .../test/resources/application-retrytests.yml | 19 ----------------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java index 91a73ae0..1f42e412 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java @@ -173,11 +173,11 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory timeout + testClient.get() + .uri("/retry?key=retry-with-backoff-timeout&count=3") + .header(HttpHeaders.HOST, "www.retrywithbackofftimeout.org") + .exchange() + .expectStatus() + .isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + } + @Test public void retryFilterGetJavaDsl() { testClient.get() @@ -375,6 +386,16 @@ public class RetryGatewayFilterFactoryIntegrationTests extends BaseWebClientTest r -> r.host("**.retrywithbackoff.org").filters(f -> f.prefixPath("/httpbin").retry(config -> { config.setRetries(2).setBackoff(Duration.ofMillis(100), null, 2, true); })).uri(uri)) + .route("retry_with_backoff_jitter_timeout_test", + r -> r.host("**.retrywithbackoffjittertimeout.org").filters(f -> f.prefixPath("/httpbin").retry(config -> { + config.setRetries(3).setBackoff(Duration.ofMillis(50), Duration.ofMillis(100), 2, true) + .setJitter(0.1).setTimeout(Duration.ofMillis(1000)); + })).uri(uri)) + .route("retry_with_backoff_timeout_test", + r -> r.host("**.retrywithbackofftimeout.org").filters(f -> f.prefixPath("/httpbin").retry(config -> { + config.setRetries(3).setBackoff(Duration.ofMillis(100), null, 2, true) + .setTimeout(Duration.ofMillis(100)); + })).uri(uri)) .route("retry_with_loadbalancer", r -> r.host("**.retrywithloadbalancer.org") diff --git a/spring-cloud-gateway-server/src/test/resources/application-retrytests.yml b/spring-cloud-gateway-server/src/test/resources/application-retrytests.yml index c9f8cc8c..ea5e1be8 100644 --- a/spring-cloud-gateway-server/src/test/resources/application-retrytests.yml +++ b/spring-cloud-gateway-server/src/test/resources/application-retrytests.yml @@ -29,22 +29,3 @@ spring: - name: Retry args: methods: GET,POST - # ===================================== - - id: retry_with_backoff_jitter_timeout_test - uri: ${test.uri} - predicates: - - Host=**.retrywithbackoffjittertimeout.org - filters: - - name: Retry - args: - retries: 3 - statuses: INTERNAL_SERVER_ERROR - methods: GET - backoff: - firstBackoff: 10ms - maxBackoff: 50ms - factor: 2 - basedOnPreviousValue: false - jitter: - randomFactor: 0.5 - timeout: 200ms \ No newline at end of file From 5e9753efb6a5400ec69ffa85e899799331ef3a06 Mon Sep 17 00:00:00 2001 From: joecqupt Date: Sat, 15 Mar 2025 13:47:18 +0800 Subject: [PATCH 4/9] add toString info Signed-off-by: joecqupt --- .../factory/RetryGatewayFilterFactory.java | 20 ++++++++++- ...yGatewayFilterFactoryIntegrationTests.java | 35 +++++++++++-------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java index 1f42e412..e47307da 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java @@ -42,6 +42,7 @@ import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.support.HasRouteId; import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; import org.springframework.cloud.gateway.support.TimeoutException; +import org.springframework.core.style.ToStringCreator; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus.Series; @@ -135,7 +136,6 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory exceptionRetry = null; if (!retryConfig.getExceptions().isEmpty()) { Predicate> retryContextPredicate = context -> { @@ -196,6 +196,9 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory timeout testClient.get() - .uri("/retry?key=retry-with-backoff-timeout&count=3") - .header(HttpHeaders.HOST, "www.retrywithbackofftimeout.org") - .exchange() - .expectStatus() - .isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + .uri("/retry?key=retry-with-backoff-timeout&count=3") + .header(HttpHeaders.HOST, "www.retrywithbackofftimeout.org") + .exchange() + .expectStatus() + .isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); } @Test @@ -386,16 +386,21 @@ public class RetryGatewayFilterFactoryIntegrationTests extends BaseWebClientTest r -> r.host("**.retrywithbackoff.org").filters(f -> f.prefixPath("/httpbin").retry(config -> { config.setRetries(2).setBackoff(Duration.ofMillis(100), null, 2, true); })).uri(uri)) - .route("retry_with_backoff_jitter_timeout_test", - r -> r.host("**.retrywithbackoffjittertimeout.org").filters(f -> f.prefixPath("/httpbin").retry(config -> { - config.setRetries(3).setBackoff(Duration.ofMillis(50), Duration.ofMillis(100), 2, true) - .setJitter(0.1).setTimeout(Duration.ofMillis(1000)); - })).uri(uri)) - .route("retry_with_backoff_timeout_test", - r -> r.host("**.retrywithbackofftimeout.org").filters(f -> f.prefixPath("/httpbin").retry(config -> { - config.setRetries(3).setBackoff(Duration.ofMillis(100), null, 2, true) - .setTimeout(Duration.ofMillis(100)); - })).uri(uri)) + .route("retry_with_backoff_jitter_timeout_test", r -> r.host("**.retrywithbackoffjittertimeout.org") + .filters(f -> f.prefixPath("/httpbin").retry(config -> { + config.setRetries(3) + .setBackoff(Duration.ofMillis(50), Duration.ofMillis(100), 2, true) + .setJitter(0.1) + .setTimeout(Duration.ofMillis(1000)); + })) + .uri(uri)) + .route("retry_with_backoff_timeout_test", r -> r.host("**.retrywithbackofftimeout.org") + .filters(f -> f.prefixPath("/httpbin").retry(config -> { + config.setRetries(3) + .setBackoff(Duration.ofMillis(100), null, 2, true) + .setTimeout(Duration.ofMillis(100)); + })) + .uri(uri)) .route("retry_with_loadbalancer", r -> r.host("**.retrywithloadbalancer.org") From f6e68fac8ecf5ce645eafa108aaf0a0e2e9126fb Mon Sep 17 00:00:00 2001 From: Jonathon Henderson Date: Sat, 29 Jun 2024 18:32:26 +0100 Subject: [PATCH 5/9] Adds support for saving original request URL to request attributes This enables X-Forwarded-Prefix. Fixes gh-3354 Fixes gh-3443 --- .../gateway/server/mvc/common/MvcUtils.java | 15 +++++++++++++ .../mvc/filter/BeforeFilterFunctions.java | 3 +++ .../XForwardedRequestHeadersFilter.java | 10 ++++----- .../server/mvc/ServerMvcIntegrationTests.java | 20 ++++++++++++++++-- .../server/mvc/test/HttpbinUriResolver.java | 21 +++++++++++++++++++ 5 files changed, 61 insertions(+), 8 deletions(-) diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/MvcUtils.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/MvcUtils.java index 72f30289..5eec2dd6 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/MvcUtils.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/MvcUtils.java @@ -25,6 +25,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -72,6 +73,11 @@ public abstract class MvcUtils { */ public static final String GATEWAY_ATTRIBUTES_ATTR = qualify("gatewayAttributes"); + /** + * Gateway original request URL attribute name. + */ + public static final String GATEWAY_ORIGINAL_REQUEST_URL_ATTR = qualify("gatewayOriginalRequestUrl"); + /** * Gateway request URL attribute name. */ @@ -250,6 +256,15 @@ public abstract class MvcUtils { request.servletRequest().setAttribute(GATEWAY_REQUEST_URL_ATTR, url); } + public static void addOriginalRequestUrl(ServerRequest request, URI url) { + LinkedHashSet urls = getAttribute(request, GATEWAY_ORIGINAL_REQUEST_URL_ATTR); + if (urls == null) { + urls = new LinkedHashSet<>(); + } + urls.add(url); + putAttribute(request, GATEWAY_ORIGINAL_REQUEST_URL_ATTR, urls); + } + private record ByteArrayInputMessage(ServerRequest request, ByteArrayInputStream body) implements HttpInputMessage { @Override diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctions.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctions.java index 650f9ca6..352bacbb 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctions.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctions.java @@ -389,6 +389,7 @@ public abstract class BeforeFilterFunctions { public static Function stripPrefix(int parts) { return request -> { + MvcUtils.addOriginalRequestUrl(request, request.uri()); // TODO: gateway url attributes String path = request.uri().getRawPath(); // TODO: begin duplicate code from StripPrefixGatewayFilterFactory @@ -414,6 +415,8 @@ public abstract class BeforeFilterFunctions { .replacePath(newPath.toString()) .build(true) .toUri(); + MvcUtils.setRequestUrl(request, prefixedUri); + return ServerRequest.from(request).uri(prefixedUri).build(); }; } diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/XForwardedRequestHeadersFilter.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/XForwardedRequestHeadersFilter.java index db5e0539..9b8b9bcb 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/XForwardedRequestHeadersFilter.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/XForwardedRequestHeadersFilter.java @@ -25,6 +25,7 @@ import java.util.Map; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.DeprecatedConfigurationProperty; import org.springframework.boot.context.properties.PropertyMapper; +import org.springframework.cloud.gateway.server.mvc.common.MvcUtils; import org.springframework.core.Ordered; import org.springframework.http.HttpHeaders; import org.springframework.util.ObjectUtils; @@ -397,18 +398,15 @@ public class XForwardedRequestHeadersFilter implements HttpHeadersFilter.Request // - see XForwardedHeadersFilterTests, so first get uris, then extract paths // and remove one from another if it's the ending part. - LinkedHashSet originalUris = null; // TODO: - // exchange.getAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR); - URI requestUri = null; // TODO: - // exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); + LinkedHashSet originalUris = MvcUtils.getAttribute(request, + MvcUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR); + URI requestUri = MvcUtils.getAttribute(request, MvcUtils.GATEWAY_REQUEST_URL_ATTR); if (originalUris != null && requestUri != null) { originalUris.forEach(originalUri -> { if (originalUri != null && originalUri.getPath() != null) { - String prefix = originalUri.getPath(); - // strip trailing slashes before checking if request path is end // of original path String originalUriPath = stripTrailingSlash(originalUri); diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java index 9deff0ed..d5f46144 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java @@ -242,6 +242,14 @@ public class ServerMvcIntegrationTests { .consumeWith(res -> { Map map = res.getResponseBody(); Map headers = getMap(map, "headers"); + assertThat(headers).containsKeys( + XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, + XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER, + XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER, + XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER, + XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER); + assertThat(headers).containsEntry( + XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, "/long/path/to"); assertThat(headers).containsEntry("X-Test", "stripPrefix"); }); } @@ -260,6 +268,14 @@ public class ServerMvcIntegrationTests { Map map = res.getResponseBody(); assertThat(map).containsEntry("data", "hello"); Map headers = getMap(map, "headers"); + assertThat(headers).containsKeys( + XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, + XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER, + XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER, + XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER, + XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER); + assertThat(headers).containsEntry( + XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, "/long/path/to"); assertThat(headers).containsEntry("X-Test", "stripPrefixPost"); }); } @@ -1068,9 +1084,9 @@ public class ServerMvcIntegrationTests { public RouterFunction gatewayRouterFunctionsStripPrefix() { // @formatter:off return route(GET("/long/path/to/get"), http()) - .filter(new HttpbinUriResolver()) .filter(stripPrefix(3)) .filter(addRequestHeader("X-Test", "stripPrefix")) + .filter(new HttpbinUriResolver(true)) .withAttribute(MvcUtils.GATEWAY_ROUTE_ID_ATTR, "teststripprefix"); // @formatter:on } @@ -1080,9 +1096,9 @@ public class ServerMvcIntegrationTests { // @formatter:off return route("teststripprefixpost") .route(POST("/long/path/to/post").and(host("**.stripprefixpost.org")), http()) - .filter(new HttpbinUriResolver()) .filter(stripPrefix(3)) .filter(addRequestHeader("X-Test", "stripPrefixPost")) + .filter(new HttpbinUriResolver(true)) .build(); // @formatter:on } diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/test/HttpbinUriResolver.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/test/HttpbinUriResolver.java index c46e1f25..0d327185 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/test/HttpbinUriResolver.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/test/HttpbinUriResolver.java @@ -17,6 +17,7 @@ package org.springframework.cloud.gateway.server.mvc.test; import java.net.URI; +import java.net.URISyntaxException; import java.util.function.Function; import org.springframework.cloud.gateway.server.mvc.common.MvcUtils; @@ -30,12 +31,32 @@ import org.springframework.web.servlet.function.ServerResponse; public class HttpbinUriResolver implements Function, HandlerFilterFunction { + private final boolean preservePath; + + public HttpbinUriResolver(boolean preservePath) { + this.preservePath = preservePath; + } + + public HttpbinUriResolver() { + this(false); + } + protected URI uri(ServerRequest request) { ApplicationContext context = MvcUtils.getApplicationContext(request); Integer port = context.getEnvironment().getProperty("httpbin.port", Integer.class); String host = context.getEnvironment().getProperty("httpbin.host"); Assert.hasText(host, "httpbin.host is not set, did you initialize HttpbinTestcontainers?"); Assert.notNull(port, "httpbin.port is not set, did you initialize HttpbinTestcontainers?"); + if (preservePath) { + URI original = request.uri(); + try { + return new URI("http", original.getUserInfo(), host, port, original.getPath(), + original.getQuery(), original.getFragment()); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e.getMessage(), e); + } + } + return URI.create(String.format("http://%s:%d", host, port)); } From 9843fdf1478ca7c310d7ca5e8221336d3aecaacc Mon Sep 17 00:00:00 2001 From: spencergibb Date: Mon, 17 Mar 2025 15:44:11 -0400 Subject: [PATCH 6/9] Updates for forwarded prefix header. Adjusts all path related filters to add the original uri and set the request url See gh-3443 --- .../gateway/server/mvc/common/MvcUtils.java | 8 +-- .../mvc/filter/BeforeFilterFunctions.java | 16 ++--- .../filter/LoadBalancerFilterFunctions.java | 2 + .../server/mvc/ServerMvcIntegrationTests.java | 63 ++++++++++++++----- .../server/mvc/test/HttpbinUriResolver.java | 27 +++----- 5 files changed, 70 insertions(+), 46 deletions(-) diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/MvcUtils.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/MvcUtils.java index 5eec2dd6..05c073ed 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/MvcUtils.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/MvcUtils.java @@ -256,13 +256,11 @@ public abstract class MvcUtils { request.servletRequest().setAttribute(GATEWAY_REQUEST_URL_ATTR, url); } + @SuppressWarnings("unchecked") public static void addOriginalRequestUrl(ServerRequest request, URI url) { - LinkedHashSet urls = getAttribute(request, GATEWAY_ORIGINAL_REQUEST_URL_ATTR); - if (urls == null) { - urls = new LinkedHashSet<>(); - } + LinkedHashSet urls = (LinkedHashSet) request.attributes() + .computeIfAbsent(GATEWAY_ORIGINAL_REQUEST_URL_ATTR, s -> new LinkedHashSet<>()); urls.add(url); - putAttribute(request, GATEWAY_ORIGINAL_REQUEST_URL_ATTR, urls); } private record ByteArrayInputMessage(ServerRequest request, ByteArrayInputStream body) implements HttpInputMessage { diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctions.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctions.java index 352bacbb..96dac3d1 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctions.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctions.java @@ -189,12 +189,14 @@ public abstract class BeforeFilterFunctions { final UriTemplate uriTemplate = new UriTemplate(prefix); return request -> { + MvcUtils.addOriginalRequestUrl(request, request.uri()); Map uriVariables = MvcUtils.getUriTemplateVariables(request); URI uri = uriTemplate.expand(uriVariables); String newPath = uri.getRawPath() + request.uri().getRawPath(); URI prefixedUri = UriComponentsBuilder.fromUri(request.uri()).replacePath(newPath).build().toUri(); + MvcUtils.setRequestUrl(request, prefixedUri); return ServerRequest.from(request).uri(prefixedUri).build(); }; } @@ -326,7 +328,7 @@ public abstract class BeforeFilterFunctions { String normalizedReplacement = replacement.replace("$\\", "$"); Pattern pattern = Pattern.compile(regexp); return request -> { - // TODO: original request url + MvcUtils.addOriginalRequestUrl(request, request.uri()); String path = request.uri().getRawPath(); String newPath = pattern.matcher(path).replaceAll(normalizedReplacement); @@ -334,8 +336,7 @@ public abstract class BeforeFilterFunctions { ServerRequest modified = ServerRequest.from(request).uri(rewrittenUri).build(); - // TODO: can this be restored at some point? - // MvcUtils.setRequestUrl(modified, modified.uri()); + MvcUtils.setRequestUrl(request, rewrittenUri); return modified; }; } @@ -351,14 +352,13 @@ public abstract class BeforeFilterFunctions { UriTemplate uriTemplate = new UriTemplate(path); return request -> { + MvcUtils.addOriginalRequestUrl(request, request.uri()); Map uriVariables = MvcUtils.getUriTemplateVariables(request); URI uri = uriTemplate.expand(uriVariables); - URI prefixedUri = UriComponentsBuilder.fromUri(request.uri()) - .replacePath(uri.getRawPath()) - .build(true) - .toUri(); - return ServerRequest.from(request).uri(prefixedUri).build(); + URI newUri = UriComponentsBuilder.fromUri(request.uri()).replacePath(uri.getRawPath()).build(true).toUri(); + MvcUtils.setRequestUrl(request, newUri); + return ServerRequest.from(request).uri(newUri).build(); }; } diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/LoadBalancerFilterFunctions.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/LoadBalancerFilterFunctions.java index ab6d47dc..db7b1464 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/LoadBalancerFilterFunctions.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/LoadBalancerFilterFunctions.java @@ -63,6 +63,8 @@ public abstract class LoadBalancerFilterFunctions { public static HandlerFilterFunction lb(String serviceId, BiFunction reconstructUriFunction) { return (request, next) -> { + MvcUtils.addOriginalRequestUrl(request, request.uri()); + LoadBalancerClientFactory clientFactory = getApplicationContext(request) .getBean(LoadBalancerClientFactory.class); Set supportedLifecycleProcessors = LoadBalancerLifecycleValidator diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java index d5f46144..4188d73a 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcIntegrationTests.java @@ -235,6 +235,7 @@ public class ServerMvcIntegrationTests { public void stripPrefixWorks() { restClient.get() .uri("/long/path/to/get") + .header("Host", "www.stripprefix.org") .exchange() .expectStatus() .isOk() @@ -242,14 +243,13 @@ public class ServerMvcIntegrationTests { .consumeWith(res -> { Map map = res.getResponseBody(); Map headers = getMap(map, "headers"); - assertThat(headers).containsKeys( - XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, + assertThat(headers).containsKeys(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER, XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER, XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER, XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER); - assertThat(headers).containsEntry( - XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, "/long/path/to"); + assertThat(headers).containsEntry(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, + "/long/path/to"); assertThat(headers).containsEntry("X-Test", "stripPrefix"); }); } @@ -268,18 +268,40 @@ public class ServerMvcIntegrationTests { Map map = res.getResponseBody(); assertThat(map).containsEntry("data", "hello"); Map headers = getMap(map, "headers"); - assertThat(headers).containsKeys( - XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, + assertThat(headers).containsKeys(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER, XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER, XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER, XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER); - assertThat(headers).containsEntry( - XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, "/long/path/to"); + assertThat(headers).containsEntry(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, + "/long/path/to"); assertThat(headers).containsEntry("X-Test", "stripPrefixPost"); }); } + @Test + public void stripPrefixLbWorks() { + restClient.get() + .uri("/long/path/to/get") + .header("Host", "www.stripprefixlb.org") + .exchange() + .expectStatus() + .isOk() + .expectBody(Map.class) + .consumeWith(res -> { + Map map = res.getResponseBody(); + Map headers = getMap(map, "headers"); + assertThat(headers).containsKeys(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, + XForwardedRequestHeadersFilter.X_FORWARDED_HOST_HEADER, + XForwardedRequestHeadersFilter.X_FORWARDED_PORT_HEADER, + XForwardedRequestHeadersFilter.X_FORWARDED_PROTO_HEADER, + XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER); + assertThat(headers).containsEntry(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, + "/long/path/to"); + assertThat(headers).containsEntry("X-Test", "stripPrefix"); + }); + } + @Test public void setStatusGatewayRouterFunctionWorks() { restClient.get() @@ -1074,8 +1096,8 @@ public class ServerMvcIntegrationTests { // @formatter:off return route("testsetpath") .route(POST("/mycustompath{extra}").and(host("**.setpathpost.org")), http()) - .filter(new HttpbinUriResolver()) .filter(setPath("/{extra}")) + .filter(new HttpbinUriResolver()) .build(); // @formatter:on } @@ -1083,11 +1105,12 @@ public class ServerMvcIntegrationTests { @Bean public RouterFunction gatewayRouterFunctionsStripPrefix() { // @formatter:off - return route(GET("/long/path/to/get"), http()) + return route("teststripprefix") + .route(GET("/long/path/to/get").and(host("**.stripprefix.org")), http()) .filter(stripPrefix(3)) .filter(addRequestHeader("X-Test", "stripPrefix")) - .filter(new HttpbinUriResolver(true)) - .withAttribute(MvcUtils.GATEWAY_ROUTE_ID_ATTR, "teststripprefix"); + .filter(new HttpbinUriResolver()) + .build(); // @formatter:on } @@ -1098,7 +1121,19 @@ public class ServerMvcIntegrationTests { .route(POST("/long/path/to/post").and(host("**.stripprefixpost.org")), http()) .filter(stripPrefix(3)) .filter(addRequestHeader("X-Test", "stripPrefixPost")) - .filter(new HttpbinUriResolver(true)) + .filter(new HttpbinUriResolver()) + .build(); + // @formatter:on + } + + @Bean + public RouterFunction gatewayRouterFunctionsStripPrefixLb() { + // @formatter:off + return route("teststripprefix") + .route(GET("/long/path/to/get").and(host("**.stripprefixlb.org")), http()) + .filter(stripPrefix(3)) + .filter(addRequestHeader("X-Test", "stripPrefix")) + .filter(lb("httpbin")) .build(); // @formatter:on } @@ -1433,8 +1468,8 @@ public class ServerMvcIntegrationTests { return route("requestheadertorequesturi") .route(cloudFoundryRouteService().and(host("**.requestheadertorequesturi.org")), http()) //.before(new HttpbinUriResolver()) NO URI RESOLVER! - .before(requestHeaderToRequestUri("X-CF-Forwarded-Url")) .filter(setPath("/hello")) + .before(requestHeaderToRequestUri("X-CF-Forwarded-Url")) .build(); // @formatter:on } diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/test/HttpbinUriResolver.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/test/HttpbinUriResolver.java index 0d327185..93b6bb51 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/test/HttpbinUriResolver.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/test/HttpbinUriResolver.java @@ -16,6 +16,7 @@ package org.springframework.cloud.gateway.server.mvc.test; +import java.lang.reflect.UndeclaredThrowableException; import java.net.URI; import java.net.URISyntaxException; import java.util.function.Function; @@ -31,33 +32,21 @@ import org.springframework.web.servlet.function.ServerResponse; public class HttpbinUriResolver implements Function, HandlerFilterFunction { - private final boolean preservePath; - - public HttpbinUriResolver(boolean preservePath) { - this.preservePath = preservePath; - } - - public HttpbinUriResolver() { - this(false); - } - protected URI uri(ServerRequest request) { ApplicationContext context = MvcUtils.getApplicationContext(request); Integer port = context.getEnvironment().getProperty("httpbin.port", Integer.class); String host = context.getEnvironment().getProperty("httpbin.host"); Assert.hasText(host, "httpbin.host is not set, did you initialize HttpbinTestcontainers?"); Assert.notNull(port, "httpbin.port is not set, did you initialize HttpbinTestcontainers?"); - if (preservePath) { - URI original = request.uri(); - try { - return new URI("http", original.getUserInfo(), host, port, original.getPath(), - original.getQuery(), original.getFragment()); - } catch (URISyntaxException e) { - throw new IllegalArgumentException(e.getMessage(), e); - } + URI original = request.uri(); + try { + return new URI("http", original.getUserInfo(), host, port, original.getPath(), original.getQuery(), + original.getFragment()); + } + catch (URISyntaxException e) { + throw new UndeclaredThrowableException(e); } - return URI.create(String.format("http://%s:%d", host, port)); } @Override From c1c741d0227850ac8e2cfd0f0015db788e4ddf20 Mon Sep 17 00:00:00 2001 From: spencergibb Date: Mon, 17 Mar 2025 16:12:09 -0400 Subject: [PATCH 7/9] For lb: scheme, put associated filters last. This makes sure the lb filter runs after path modifying filters like stripPrefix. See gh-3443 --- .../filters/loadbalancer.adoc | 2 ++ .../filters/prefixpath.adoc | 3 ++- .../filters/rewritepath.adoc | 1 + .../spring-cloud-gateway-server-mvc/filters/setpath.adoc | 1 + .../filters/stripprefix.adoc | 1 + .../server/mvc/config/RouterFunctionHolderFactory.java | 9 ++++++++- ...GatewayMvcPropertiesBeanDefinitionRegistrarTests.java | 4 ++-- ...pplication-propertiesbeandefinitionregistrartests.yml | 5 ++++- 8 files changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/loadbalancer.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/loadbalancer.adoc index 58859f9c..2135aaf2 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/loadbalancer.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/loadbalancer.adoc @@ -43,6 +43,8 @@ spring: - Path=/api/** ---- +WARNING: If using the `lb()` filter, it needs to be after any filter that manipulates the path such as `setPath()` or `stripPrefix()`, otherwise the resulting url could be incorrect. The `lb:` scheme handler in configuration, automatically puts the filter in the highest precedence order. + NOTE: By default, when a service instance cannot be found by the `ReactorLoadBalancer`, a `503` is returned. // TODO: implement use404 // You can configure the gateway to return a `404` by setting `spring.cloud.gateway.loadbalancer.use404=true`. diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/prefixpath.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/prefixpath.adoc index 2313b6e5..5bc8b5e8 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/prefixpath.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/prefixpath.adoc @@ -33,7 +33,7 @@ class RouteConfiguration { public RouterFunction gatewayRouterFunctionsPrefixPath() { return route("prefixpath_route") .GET("/**", http("https://example.org")) - .before("/mypath") + .before(prefixPath("/mypath")) .build(); } } @@ -42,3 +42,4 @@ class RouteConfiguration { This prefixes `/mypath` to the path of all matching requests. So a request to `/hello` is sent to `/mypath/hello`. +WARNING: If using the `lb()` filter, it needs to be after the `prefixPath()` filter, otherwise the resulting url could be incorrect. The `lb:` scheme handler in configuration, automatically puts the filter in the highest precedence order. diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewritepath.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewritepath.adoc index 7247db46..3b50b615 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewritepath.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewritepath.adoc @@ -43,3 +43,4 @@ class RouteConfiguration { For a request path of `/red/blue`, this sets the path to `/blue` before making the downstream request. Note that in `application.yml` the `$` should be replaced with `$\` because of the YAML specification. +WARNING: If using the `lb()` filter, it needs to be after the `rewritePath()` filter, otherwise the resulting url could be incorrect. The `lb:` scheme handler in configuration, automatically puts the filter in the highest precedence order. \ No newline at end of file diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setpath.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setpath.adoc index 261345e6..9355a068 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setpath.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setpath.adoc @@ -45,3 +45,4 @@ class RouteConfiguration { For a request path of `/red/blue`, this sets the path to `/blue` before making the downstream request. +WARNING: If using the `lb()` filter, it needs to be after the `setPath()` filter, otherwise the resulting url could be incorrect. The `lb:` scheme handler in configuration, automatically puts the filter in the highest precedence order. \ No newline at end of file diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/stripprefix.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/stripprefix.adoc index ee2d2efa..dc2be798 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/stripprefix.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/stripprefix.adoc @@ -43,3 +43,4 @@ class RouteConfiguration { When a request is made through the gateway to `/name/blue/red`, the request made to `nameservice` looks like `https://nameservice/red`. +WARNING: If using the `lb()` filter, it needs to be after the `stripPrefix()` filter, otherwise the resulting url could be incorrect. The `lb:` scheme handler in configuration, automatically puts the filter in the highest precedence order. \ No newline at end of file diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/RouterFunctionHolderFactory.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/RouterFunctionHolderFactory.java index 687bdec8..dcebc7e7 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/RouterFunctionHolderFactory.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/RouterFunctionHolderFactory.java @@ -16,6 +16,7 @@ package org.springframework.cloud.gateway.server.mvc.config; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; @@ -178,12 +179,15 @@ public class RouterFunctionHolderFactory { NormalizedOperationMethod normalizedOpMethod = handlerOperationMethod.get(); Object response = invokeOperation(normalizedOpMethod, normalizedOpMethod.getNormalizedArgs()); HandlerFunction handlerFunction = null; + + // filters added by HandlerDiscoverer need to go last, so save them + List> handlerFilterFunctionFilters = new ArrayList<>(); if (response instanceof HandlerFunction) { handlerFunction = (HandlerFunction) response; } else if (response instanceof HandlerDiscoverer.Result result) { handlerFunction = result.getHandlerFunction(); - result.getFilters().forEach(builder::filter); + handlerFilterFunctionFilters.addAll(result.getFilters()); } if (handlerFunction == null) { throw new IllegalStateException( @@ -221,6 +225,9 @@ public class RouterFunctionHolderFactory { translate(filterOperations, filterProperties.getName(), args, HandlerFilterFunction.class, builder::filter); }); + // HandlerDiscoverer filters need higher priority, so put them last + handlerFilterFunctionFilters.forEach(builder::filter); + builder.withAttribute(MvcUtils.GATEWAY_ROUTE_ID_ATTR, routeId); return builder.build(); diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/config/GatewayMvcPropertiesBeanDefinitionRegistrarTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/config/GatewayMvcPropertiesBeanDefinitionRegistrarTests.java index f4f7675c..861b1694 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/config/GatewayMvcPropertiesBeanDefinitionRegistrarTests.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/config/GatewayMvcPropertiesBeanDefinitionRegistrarTests.java @@ -122,7 +122,7 @@ public class GatewayMvcPropertiesBeanDefinitionRegistrarTests { predicate.accept(new AbstractRequestPredicatesVisitor() { @Override public void path(String pattern) { - assertThat(pattern).isEqualTo("/anything/listRoute3"); + assertThat(pattern).isEqualTo("/extra/anything/listRoute3"); } @Override @@ -181,7 +181,7 @@ public class GatewayMvcPropertiesBeanDefinitionRegistrarTests { @SuppressWarnings("unchecked") public void lbRouteWorks() { restClient.get() - .uri("/anything/listRoute3") + .uri("/extra/anything/listRoute3") .header("MyHeaderName", "MyHeaderVal") .exchange() .expectStatus() diff --git a/spring-cloud-gateway-server-mvc/src/test/resources/application-propertiesbeandefinitionregistrartests.yml b/spring-cloud-gateway-server-mvc/src/test/resources/application-propertiesbeandefinitionregistrartests.yml index 5270b8cb..a91fe14d 100644 --- a/spring-cloud-gateway-server-mvc/src/test/resources/application-propertiesbeandefinitionregistrartests.yml +++ b/spring-cloud-gateway-server-mvc/src/test/resources/application-propertiesbeandefinitionregistrartests.yml @@ -38,9 +38,12 @@ spring.cloud.gateway.mvc: - id: listRoute3 uri: lb://httpbin predicates: - - Path=/anything/listRoute3 + - Path=/extra/anything/listRoute3 - Header=MyHeaderName,MyHeader.* filters: + - name: StripPrefix + args: + parts: 1 - name: AddRequestHeader args: name: X-Test From 0f3112077d0ac34eb594f735b42c1ca27a38bd30 Mon Sep 17 00:00:00 2001 From: jiangyuan Date: Tue, 18 Mar 2025 15:46:14 +0800 Subject: [PATCH 8/9] update unit test Signed-off-by: jiangyuan --- .../factory/RetryGatewayFilterFactoryIntegrationTests.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactoryIntegrationTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactoryIntegrationTests.java index bacd256c..fb1439d1 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactoryIntegrationTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactoryIntegrationTests.java @@ -130,6 +130,7 @@ public class RetryGatewayFilterFactoryIntegrationTests extends BaseWebClientTest .exchange() .expectStatus() .isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + assertThat(TestConfig.map.get("retry-with-backoff-timeout")).isNotNull().hasValue(2); } @Test @@ -398,7 +399,7 @@ public class RetryGatewayFilterFactoryIntegrationTests extends BaseWebClientTest .filters(f -> f.prefixPath("/httpbin").retry(config -> { config.setRetries(3) .setBackoff(Duration.ofMillis(100), null, 2, true) - .setTimeout(Duration.ofMillis(100)); + .setTimeout(Duration.ofMillis(200)); })) .uri(uri)) From 2175fc1c8e374e361ef801b95c2ee00a79c0cd4d Mon Sep 17 00:00:00 2001 From: spring-builds Date: Tue, 18 Mar 2025 13:26:51 +0000 Subject: [PATCH 9/9] Bumping versions --- .../SpringCloudCircuitBreakerFilterFactoryTests.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactoryTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactoryTests.java index 30eb9a8b..87c26cc6 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactoryTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactoryTests.java @@ -246,9 +246,15 @@ public abstract class SpringCloudCircuitBreakerFilterFactoryTests extends BaseWe @Test public void filterPostFallback() { - testClient.post().uri("/post").body(BodyInserters.fromValue("hello")) - .header("Host", "www.circuitbreakerfallbackpost.org").exchange().expectStatus() - .isOk().expectBody().json("{\"body\":\"hello\"}"); + testClient.post() + .uri("/post") + .body(BodyInserters.fromValue("hello")) + .header("Host", "www.circuitbreakerfallbackpost.org") + .exchange() + .expectStatus() + .isOk() + .expectBody() + .json("{\"body\":\"hello\"}"); } }