From 350c9cda6dcce96f76f41b91e64bf8884c6d440e Mon Sep 17 00:00:00 2001 From: jiangyuan Date: Thu, 6 Mar 2025 21:26:50 +0800 Subject: [PATCH 01/43] 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 02/43] 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 03/43] 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 9816b1adfecb30bf35205c6f2b11ec3c952c1b24 Mon Sep 17 00:00:00 2001 From: spring-builds Date: Thu, 13 Mar 2025 13:28:24 +0000 Subject: [PATCH 04/43] Bumping versions Signed-off-by: qnnn <65326092+qnnn@users.noreply.github.com> --- README.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.adoc b/README.adoc index 9d84e36e..d4195c40 100644 --- a/README.adoc +++ b/README.adoc @@ -224,7 +224,7 @@ Spring Cloud Build brings along the `basepom:duplicate-finder-maven-plugin`, th [[duplicate-finder-configuration]] === Duplicate Finder configuration -Duplicate finder is *enabled by default* and will run in the `verify` phase of your Maven build, but it will only take effect in your project if you add the `duplicate-finder-maven-plugin` to the `build` section of the projecst's `pom.xml`. +Duplicate finder is *enabled by default* and will run in the `verify` phase of your Maven build, but it will only take effect in your project if you add the `duplicate-finder-maven-plugin` to the `build` section of the project's `pom.xml`. .pom.xml [source,xml] From fcba3da5d3c4564c674e688fbc9e07ddb47b5682 Mon Sep 17 00:00:00 2001 From: qnnn <65326092+qnnn@users.noreply.github.com> Date: Fri, 14 Mar 2025 13:23:37 +0800 Subject: [PATCH 05/43] Enable body caching in CircuitBreakerFilterFactory. Signed-off-by: qnnn <65326092+qnnn@users.noreply.github.com> --- .../factory/AbstractGatewayFilterFactory.java | 8 ++++++++ .../filter/factory/RetryGatewayFilterFactory.java | 6 +----- .../SpringCloudCircuitBreakerFilterFactory.java | 1 + ...SpringCloudCircuitBreakerFilterFactoryTests.java | 8 ++++++++ .../SpringCloudCircuitBreakerTestConfig.java | 7 +++++++ .../src/test/resources/application.yml | 13 +++++++++++++ 6 files changed, 38 insertions(+), 5 deletions(-) diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractGatewayFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractGatewayFilterFactory.java index e94a2ede..2413fbda 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractGatewayFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/AbstractGatewayFilterFactory.java @@ -16,6 +16,7 @@ package org.springframework.cloud.gateway.filter.factory; +import org.springframework.cloud.gateway.event.EnableBodyCachingEvent; import org.springframework.cloud.gateway.support.AbstractConfigurable; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; @@ -43,6 +44,13 @@ public abstract class AbstractGatewayFilterFactory extends AbstractConfigurab return this.publisher; } + protected void enableBodyCaching(String routeId) { + if (routeId != null && getPublisher() != null) { + // send an event to enable caching + getPublisher().publishEvent(new EnableBodyCachingEvent(this, routeId)); + } + } + @Override public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { this.publisher = publisher; 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..814a6ecf 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 @@ -35,7 +35,6 @@ import reactor.retry.RepeatContext; import reactor.retry.Retry; import reactor.retry.RetryContext; -import org.springframework.cloud.gateway.event.EnableBodyCachingEvent; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.support.HasRouteId; @@ -229,10 +228,7 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory repeat, Retry retry) { - if (routeId != null && getPublisher() != null) { - // send an event to enable caching - getPublisher().publishEvent(new EnableBodyCachingEvent(this, routeId)); - } + enableBodyCaching(routeId); return (exchange, chain) -> { trace("Entering retry-filter"); diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java index 0cbc3eca..455da438 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java @@ -89,6 +89,7 @@ public abstract class SpringCloudCircuitBreakerFilterFactory @Override public GatewayFilter apply(Config config) { + enableBodyCaching(config.getRouteId()); ReactiveCircuitBreaker cb = reactiveCircuitBreakerFactory.create(config.getId()); Set statuses = config.getStatusCodes() .stream() 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 1966fa9e..30eb9a8b 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 @@ -21,6 +21,7 @@ import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable; import org.springframework.cloud.gateway.test.BaseWebClientTests; import org.springframework.http.HttpStatus; +import org.springframework.web.reactive.function.BodyInserters; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.http.MediaType.APPLICATION_JSON; @@ -243,4 +244,11 @@ public abstract class SpringCloudCircuitBreakerFilterFactoryTests extends BaseWe .valueEquals(ROUTE_ID_HEADER, "circuitbreaker_resume_without_error"); } + @Test + public void filterPostFallback() { + testClient.post().uri("/post").body(BodyInserters.fromValue("hello")) + .header("Host", "www.circuitbreakerfallbackpost.org").exchange().expectStatus() + .isOk().expectBody().json("{\"body\":\"hello\"}"); + } + } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerTestConfig.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerTestConfig.java index 1b481ee2..fb31edcc 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerTestConfig.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerTestConfig.java @@ -38,6 +38,8 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -68,6 +70,11 @@ public class SpringCloudCircuitBreakerTestConfig { return Collections.singletonMap("from", "circuitbreakerfallbackcontroller"); } + @PostMapping("/circuitbreakerPostFallbackController") + public Map postFallbackController(@RequestBody String body) { + return Collections.singletonMap("body", body); + } + @GetMapping("/circuitbreakerUriFallbackController/**") public Map uriFallbackcontroller(ServerWebExchange exchange, @RequestParam("a") String a) { return Collections.singletonMap("uri", exchange.getRequest().getURI().toString()); diff --git a/spring-cloud-gateway-server/src/test/resources/application.yml b/spring-cloud-gateway-server/src/test/resources/application.yml index 2b1fde13..a08f82da 100644 --- a/spring-cloud-gateway-server/src/test/resources/application.yml +++ b/spring-cloud-gateway-server/src/test/resources/application.yml @@ -104,6 +104,19 @@ spring: name: fallbackcmd fallbackUri: forward:/circuitbreakerFallbackController + # ===================================== + - id: circuitbreaker_fallback_test_post + uri: ${test.uri} + predicates: + - Host=**.circuitbreakerfallbackpost.org + filters: + - name: CircuitBreaker + args: + name: fallbackcmd + statusCodes: + - 200 + fallbackUri: forward:/circuitbreakerPostFallbackController + # ===================================== - id: circuitbreaker_fallback_test_variables uri: ${test.uri} From 5e9753efb6a5400ec69ffa85e899799331ef3a06 Mon Sep 17 00:00:00 2001 From: joecqupt Date: Sat, 15 Mar 2025 13:47:18 +0800 Subject: [PATCH 06/43] 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 611ae85b2d19a7153351f8d0ea2ade863601a3d2 Mon Sep 17 00:00:00 2001 From: qnnn <65326092+qnnn@users.noreply.github.com> Date: Sat, 15 Mar 2025 14:02:32 +0800 Subject: [PATCH 07/43] enable body caching if fallbackUri is configured Signed-off-by: qnnn <65326092+qnnn@users.noreply.github.com> --- .../factory/SpringCloudCircuitBreakerFilterFactory.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java index 455da438..823eac83 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SpringCloudCircuitBreakerFilterFactory.java @@ -89,7 +89,9 @@ public abstract class SpringCloudCircuitBreakerFilterFactory @Override public GatewayFilter apply(Config config) { - enableBodyCaching(config.getRouteId()); + if (config.getFallbackUri() != null) { + enableBodyCaching(config.getRouteId()); + } ReactiveCircuitBreaker cb = reactiveCircuitBreakerFactory.create(config.getId()); Set statuses = config.getStatusCodes() .stream() From 160c82057ee06c90d249bddf405a2ffd01d228d7 Mon Sep 17 00:00:00 2001 From: spring-builds Date: Sat, 15 Mar 2025 13:26:33 +0000 Subject: [PATCH 08/43] Bumping versions --- docs/modules/ROOT/partials/_configprops.adoc | 4 ++-- .../cloud/gateway/config/HttpClientProperties.java | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/modules/ROOT/partials/_configprops.adoc b/docs/modules/ROOT/partials/_configprops.adoc index 98a11c38..81866e90 100644 --- a/docs/modules/ROOT/partials/_configprops.adoc +++ b/docs/modules/ROOT/partials/_configprops.adoc @@ -89,12 +89,12 @@ |spring.cloud.gateway.httpclient.pool.max-life-time | | Duration after which the channel will be closed. If NULL, there is no max life time. |spring.cloud.gateway.httpclient.pool.metrics | `+++false+++` | Enables channel pools metrics to be collected and registered in Micrometer. Disabled by default. |spring.cloud.gateway.httpclient.pool.name | `+++proxy+++` | The channel pool map name, defaults to proxy. -|spring.cloud.gateway.httpclient.pool.type | | Type of pool for HttpClient to use, defaults to ELASTIC. +|spring.cloud.gateway.httpclient.pool.type | | Type of pool for HttpClient to use (elastic, fixed or disabled). |spring.cloud.gateway.httpclient.proxy.host | | Hostname for proxy configuration of Netty HttpClient. |spring.cloud.gateway.httpclient.proxy.non-proxy-hosts-pattern | | Regular expression (Java) for a configured list of hosts. that should be reached directly, bypassing the proxy |spring.cloud.gateway.httpclient.proxy.password | | Password for proxy configuration of Netty HttpClient. |spring.cloud.gateway.httpclient.proxy.port | | Port for proxy configuration of Netty HttpClient. -|spring.cloud.gateway.httpclient.proxy.type | | proxyType for proxy configuration of Netty HttpClient. +|spring.cloud.gateway.httpclient.proxy.type | | proxyType for proxy configuration of Netty HttpClient (http, socks4 or socks5). |spring.cloud.gateway.httpclient.proxy.username | | Username for proxy configuration of Netty HttpClient. |spring.cloud.gateway.httpclient.response-timeout | | The response timeout. |spring.cloud.gateway.httpclient.ssl.close-notify-flush-timeout | `+++3000ms+++` | SSL close_notify flush timeout. Default to 3000 ms. diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/HttpClientProperties.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/HttpClientProperties.java index 0f586c5a..8631cf2b 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/HttpClientProperties.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/HttpClientProperties.java @@ -303,8 +303,7 @@ public class HttpClientProperties { public static class Proxy { /** - * proxyType for proxy configuration of Netty HttpClient (http, socks4 or - * socks5). + * proxyType for proxy configuration of Netty HttpClient (http, socks4 or socks5). */ private ProxyProvider.Proxy type = ProxyProvider.Proxy.HTTP; From 5e4aff5786663529e71b0dffec59560b5f0a3eeb Mon Sep 17 00:00:00 2001 From: spring-builds Date: Sun, 16 Mar 2025 09:55:41 +0000 Subject: [PATCH 09/43] Bumping versions --- docs/modules/ROOT/partials/_configprops.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/modules/ROOT/partials/_configprops.adoc b/docs/modules/ROOT/partials/_configprops.adoc index f1e590f2..a8eed641 100644 --- a/docs/modules/ROOT/partials/_configprops.adoc +++ b/docs/modules/ROOT/partials/_configprops.adoc @@ -84,18 +84,18 @@ |spring.cloud.gateway.httpclient.max-initial-line-length | | The max initial line length. |spring.cloud.gateway.httpclient.pool.acquire-timeout | | Only for type FIXED, the maximum time in millis to wait for acquiring. |spring.cloud.gateway.httpclient.pool.eviction-interval | `+++0+++` | Perform regular eviction checks in the background at a specified interval. Disabled by default ({@link Duration#ZERO}) -|spring.cloud.gateway.httpclient.pool.leasing-strategy | `+++fifo+++` | Configures the leasing strategy for the pool, defaults to FIFO which is Netty's default. +|spring.cloud.gateway.httpclient.pool.leasing-strategy | `+++fifo+++` | Configures the leasing strategy for the pool (fifo or lifo), defaults to FIFO which is Netty's default. |spring.cloud.gateway.httpclient.pool.max-connections | | Only for type FIXED, the maximum number of connections before starting pending acquisition on existing ones. |spring.cloud.gateway.httpclient.pool.max-idle-time | | Time in millis after which the channel will be closed. If NULL, there is no max idle time. |spring.cloud.gateway.httpclient.pool.max-life-time | | Duration after which the channel will be closed. If NULL, there is no max life time. |spring.cloud.gateway.httpclient.pool.metrics | `+++false+++` | Enables channel pools metrics to be collected and registered in Micrometer. Disabled by default. |spring.cloud.gateway.httpclient.pool.name | `+++proxy+++` | The channel pool map name, defaults to proxy. -|spring.cloud.gateway.httpclient.pool.type | `+++elastic+++` | Type of pool for HttpClient to use, defaults to ELASTIC. +|spring.cloud.gateway.httpclient.pool.type | `+++elastic+++` | Type of pool for HttpClient to use (elastic, fixed or disabled). |spring.cloud.gateway.httpclient.proxy.host | | Hostname for proxy configuration of Netty HttpClient. |spring.cloud.gateway.httpclient.proxy.non-proxy-hosts-pattern | | Regular expression (Java) for a configured list of hosts. that should be reached directly, bypassing the proxy |spring.cloud.gateway.httpclient.proxy.password | | Password for proxy configuration of Netty HttpClient. |spring.cloud.gateway.httpclient.proxy.port | | Port for proxy configuration of Netty HttpClient. -|spring.cloud.gateway.httpclient.proxy.type | `+++http+++` | proxyType for proxy configuration of Netty HttpClient. +|spring.cloud.gateway.httpclient.proxy.type | `+++http+++` | proxyType for proxy configuration of Netty HttpClient (http, socks4 or socks5). |spring.cloud.gateway.httpclient.proxy.username | | Username for proxy configuration of Netty HttpClient. |spring.cloud.gateway.httpclient.response-timeout | | The response timeout. |spring.cloud.gateway.httpclient.ssl.close-notify-flush-timeout | `+++3000ms+++` | SSL close_notify flush timeout. Default to 3000 ms. From f6e68fac8ecf5ce645eafa108aaf0a0e2e9126fb Mon Sep 17 00:00:00 2001 From: Jonathon Henderson Date: Sat, 29 Jun 2024 18:32:26 +0100 Subject: [PATCH 10/43] 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 11/43] 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 12/43] 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 d6d8a28e7fcc12f38d2108d86bd5c3118bd5c719 Mon Sep 17 00:00:00 2001 From: Jonathon Henderson Date: Sat, 29 Jun 2024 18:32:26 +0100 Subject: [PATCH 13/43] 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 5cc63233c60d506f29d4ddb3a9f5cab50019cb4b Mon Sep 17 00:00:00 2001 From: spencergibb Date: Mon, 17 Mar 2025 15:44:11 -0400 Subject: [PATCH 14/43] 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 5cf94a498780c97f9abc315e1afbaf3d8109b4dd Mon Sep 17 00:00:00 2001 From: spencergibb Date: Mon, 17 Mar 2025 16:12:09 -0400 Subject: [PATCH 15/43] 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 b8200a39851fb546ac20d0f64373c639530676a6 Mon Sep 17 00:00:00 2001 From: Jonathon Henderson Date: Sat, 29 Jun 2024 18:32:26 +0100 Subject: [PATCH 16/43] 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 a6665d06..83904cb5 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 @@ -410,6 +410,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 @@ -435,6 +436,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 1e91f7b6..02a89446 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 @@ -246,6 +246,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"); }); } @@ -264,6 +272,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"); }); } @@ -1077,9 +1093,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 } @@ -1089,9 +1105,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 b82da55faef8d4135c94dea7653c0ffc8ca0ab89 Mon Sep 17 00:00:00 2001 From: spencergibb Date: Mon, 17 Mar 2025 15:44:11 -0400 Subject: [PATCH 17/43] 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 83904cb5..3d81e508 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; }; } @@ -372,14 +373,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 02a89446..38bfa681 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 @@ -239,6 +239,7 @@ public class ServerMvcIntegrationTests { public void stripPrefixWorks() { restClient.get() .uri("/long/path/to/get") + .header("Host", "www.stripprefix.org") .exchange() .expectStatus() .isOk() @@ -246,14 +247,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"); }); } @@ -272,18 +272,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() @@ -1083,8 +1105,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 } @@ -1092,11 +1114,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 } @@ -1107,7 +1130,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 } @@ -1442,8 +1477,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 f6f90df4042b7bb61d2181aa537d8e5e9a28d3a8 Mon Sep 17 00:00:00 2001 From: spencergibb Date: Mon, 17 Mar 2025 16:12:09 -0400 Subject: [PATCH 18/43] 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 + .../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-webmvc/filters/loadbalancer.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/filters/loadbalancer.adoc index 58859f9c..2135aaf2 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/filters/loadbalancer.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/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-webmvc/filters/prefixpath.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/filters/prefixpath.adoc index 2313b6e5..5bc8b5e8 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/filters/prefixpath.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/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-webmvc/filters/rewritepath.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/filters/rewritepath.adoc index 7247db46..3b50b615 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/filters/rewritepath.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/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-webmvc/filters/setpath.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/filters/setpath.adoc index 261345e6..9355a068 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/filters/setpath.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/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-webmvc/filters/stripprefix.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/filters/stripprefix.adoc index ee2d2efa..dc2be798 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/filters/stripprefix.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/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 c278a43108dc10c27e54c57ba1bb5266fd22fecc Mon Sep 17 00:00:00 2001 From: Tillmann Heigel Date: Thu, 30 Jun 2022 10:29:09 +0200 Subject: [PATCH 19/43] Adds Forwarded By Header Fixes gh-2658 --- .../headers/ForwardedHeadersFilter.java | 56 ++++++++++++++++++- .../headers/ForwardedHeadersFilterTests.java | 45 ++++++++++++++- .../headers/XForwardedHeadersFilterTests.java | 20 +++---- .../gateway/test/GatewayIntegrationTests.java | 11 ++-- 4 files changed, 112 insertions(+), 20 deletions(-) diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java index b95cb191..54d7c297 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java @@ -20,11 +20,17 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.URI; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.core.Ordered; import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -34,8 +40,21 @@ import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.server.ServerWebExchange; +/** + * @author Olga Maciaszek-Sharma + * @author Tillmann Heigel + */ +@ConfigurationProperties("spring.cloud.gateway.forwarded") public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { + @Value("${server.port}") + private int serverPort; + + private final Log logger = LogFactory.getLog(getClass()); + + /** If Forwarded: by header is enabled. */ + private boolean byEnabled = true; + /** * Forwarded header. */ @@ -120,8 +139,7 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { String forValue; if (remoteAddress.isUnresolved()) { forValue = remoteAddress.getHostName(); - } - else { + } else { InetAddress address = remoteAddress.getAddress(); forValue = remoteAddress.getAddress().getHostAddress(); if (address instanceof Inet6Address) { @@ -134,13 +152,37 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { } forwarded.put("for", forValue); } - // TODO: support by? + + if (byEnabled) { + addForwardedByHeader(forwarded); + } updated.add(FORWARDED_HEADER, forwarded.toHeaderValue()); return updated; } + private void addForwardedByHeader(Forwarded forwarded) { + try { + addForwardedBy(forwarded, InetAddress.getLocalHost()); + } catch (UnknownHostException e) { + this.logger.warn("Can not resolve host address, skipping Forwarded 'by' header", e); + } + } + + /* visible for testing */ void addForwardedBy(Forwarded forwarded, InetAddress localAddress) { + if (localAddress != null) { + String byValue = localAddress.getHostAddress(); + if (localAddress instanceof Inet6Address) { + byValue = "[" + byValue + "]"; + } + if (serverPort > 0) { + byValue = byValue + ":" + serverPort; + } + forwarded.put("by", byValue); + } + } + /* for testing */ static class Forwarded { private static final char EQUALS = '='; @@ -195,4 +237,12 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { } + public boolean isByEnabled() { + return byEnabled; + } + + public void setByEnabled(boolean byEnabled) { + this.byEnabled = byEnabled; + } + } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilterTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilterTests.java index 73cb0003..ce2d57ee 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilterTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilterTests.java @@ -16,6 +16,9 @@ package org.springframework.cloud.gateway.filter.headers; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.cloud.gateway.filter.headers.ForwardedHeadersFilter.FORWARDED_HEADER; + import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; @@ -25,6 +28,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.cloud.gateway.filter.headers.ForwardedHeadersFilter.Forwarded; @@ -33,9 +37,6 @@ import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.util.StringUtils; -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.cloud.gateway.filter.headers.ForwardedHeadersFilter.FORWARDED_HEADER; - /** * @author Spencer Gibb */ @@ -201,4 +202,42 @@ public class ForwardedHeadersFilterTests { } } + @Test + public void forwardedByForIpv4AddressIsAdded() throws UnknownHostException { + Forwarded forwarded = new Forwarded(); + InetAddress ipv4Address = InetAddress.getByName("216.103.69.111"); + ForwardedHeadersFilter forwardedHeadersFilter = new ForwardedHeadersFilter(); + forwardedHeadersFilter.setByEnabled(true); + + forwardedHeadersFilter.addForwardedBy(forwarded, ipv4Address); + + Assertions.assertThat(forwarded.getValues()).containsEntry("by", "216.103.69.111"); + + } + + @Test + public void forwardedByForIpv6AddressIsAdded() throws UnknownHostException { + Forwarded forwarded = new Forwarded(); + InetAddress ipv6Address = InetAddress.getByName("abc4:babf:955f:1724:11bc:0153:275c:d36e"); + ForwardedHeadersFilter forwardedHeadersFilter = new ForwardedHeadersFilter(); + forwardedHeadersFilter.setByEnabled(true); + + forwardedHeadersFilter.addForwardedBy(forwarded, ipv6Address); + + Assertions.assertThat(forwarded.getValues()).containsEntry("by", + "\"[abc4:babf:955f:1724:11bc:153:275c:d36e]\""); + + } + + @Test + public void forwardedByIsNotAddedIfFeatureIsDisabled() throws UnknownHostException { + Forwarded forwarded = new Forwarded(); + InetAddress ipv4Address = InetAddress.getByName("216.103.69.111"); + ForwardedHeadersFilter forwardedHeadersFilter = new ForwardedHeadersFilter(); + forwardedHeadersFilter.setByEnabled(false); + + forwardedHeadersFilter.addForwardedBy(forwarded, ipv4Address); + + Assertions.assertThat(forwarded.getValues()).containsEntry("by", "216.103.69.111"); + } } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/XForwardedHeadersFilterTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/XForwardedHeadersFilterTests.java index 5bfcd7e8..b899986c 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/XForwardedHeadersFilterTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/XForwardedHeadersFilterTests.java @@ -16,6 +16,15 @@ package org.springframework.cloud.gateway.filter.headers; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_FOR_HEADER; +import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_HOST_HEADER; +import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_PORT_HEADER; +import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_PREFIX_HEADER; +import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_PROTO_HEADER; +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; + import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.URI; @@ -29,22 +38,13 @@ import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.util.UriComponentsBuilder; -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_FOR_HEADER; -import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_HOST_HEADER; -import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_PORT_HEADER; -import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_PREFIX_HEADER; -import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_PROTO_HEADER; -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 */ public class XForwardedHeadersFilterTests { @Test - public void remoteAddressIsNull() throws Exception { + public void remoteAddressIsNull() { MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost:8080/get") .header(HttpHeaders.HOST, "myhost") .build(); diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java index f998d8e0..b91e9693 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java @@ -16,6 +16,10 @@ package org.springframework.cloud.gateway.test; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.cloud.gateway.test.TestUtils.getMap; + import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -58,9 +62,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ServerWebExchange; -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; -import static org.springframework.cloud.gateway.test.TestUtils.getMap; +import reactor.core.publisher.Mono; @SpringBootTest(webEnvironment = RANDOM_PORT) @DirtiesContext @@ -125,7 +127,8 @@ class GatewayIntegrationTests extends BaseWebClientTests { assertThat(headers.get(ForwardedHeadersFilter.FORWARDED_HEADER)).asString() .contains("proto=http") .contains("host=\"localhost:") - .contains("for=\"127.0.0.1:"); + .contains("for=\"127.0.0.1:") + .contains("by="); assertThat(headers.get(XForwardedHeadersFilter.X_FORWARDED_HOST_HEADER)).asString() .isEqualTo("localhost:" + this.port); assertThat(headers.get(XForwardedHeadersFilter.X_FORWARDED_PORT_HEADER)).asString() From 996ab88122c68f0724af5d145210876f315d8026 Mon Sep 17 00:00:00 2001 From: spencergibb Date: Mon, 17 Mar 2025 16:45:38 -0400 Subject: [PATCH 20/43] Formatting See gh-2658 --- .../headers/ForwardedHeadersFilter.java | 22 +-- ...CloudCircuitBreakerFilterFactoryTests.java | 12 +- .../headers/ForwardedHeadersFilterTests.java | 11 +- .../headers/XForwardedHeadersFilterTests.java | 18 +- .../gateway/test/GatewayIntegrationTests.java | 8 +- .../gateway/route/builder/RouteDslTests.kt | 154 +++++++++--------- 6 files changed, 116 insertions(+), 109 deletions(-) diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java index 54d7c297..7add0e43 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java @@ -103,6 +103,14 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { return result; } + public boolean isByEnabled() { + return byEnabled; + } + + public void setByEnabled(boolean byEnabled) { + this.byEnabled = byEnabled; + } + @Override public int getOrder() { return 0; @@ -139,7 +147,8 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { String forValue; if (remoteAddress.isUnresolved()) { forValue = remoteAddress.getHostName(); - } else { + } + else { InetAddress address = remoteAddress.getAddress(); forValue = remoteAddress.getAddress().getHostAddress(); if (address instanceof Inet6Address) { @@ -165,7 +174,8 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { private void addForwardedByHeader(Forwarded forwarded) { try { addForwardedBy(forwarded, InetAddress.getLocalHost()); - } catch (UnknownHostException e) { + } + catch (UnknownHostException e) { this.logger.warn("Can not resolve host address, skipping Forwarded 'by' header", e); } } @@ -237,12 +247,4 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { } - public boolean isByEnabled() { - return byEnabled; - } - - public void setByEnabled(boolean byEnabled) { - this.byEnabled = byEnabled; - } - } 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\"}"); } } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilterTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilterTests.java index ce2d57ee..b9069bf5 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilterTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilterTests.java @@ -16,9 +16,6 @@ package org.springframework.cloud.gateway.filter.headers; -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.cloud.gateway.filter.headers.ForwardedHeadersFilter.FORWARDED_HEADER; - import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; @@ -37,6 +34,9 @@ import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.util.StringUtils; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.cloud.gateway.filter.headers.ForwardedHeadersFilter.FORWARDED_HEADER; + /** * @author Spencer Gibb */ @@ -224,8 +224,8 @@ public class ForwardedHeadersFilterTests { forwardedHeadersFilter.addForwardedBy(forwarded, ipv6Address); - Assertions.assertThat(forwarded.getValues()).containsEntry("by", - "\"[abc4:babf:955f:1724:11bc:153:275c:d36e]\""); + Assertions.assertThat(forwarded.getValues()) + .containsEntry("by", "\"[abc4:babf:955f:1724:11bc:153:275c:d36e]\""); } @@ -240,4 +240,5 @@ public class ForwardedHeadersFilterTests { Assertions.assertThat(forwarded.getValues()).containsEntry("by", "216.103.69.111"); } + } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/XForwardedHeadersFilterTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/XForwardedHeadersFilterTests.java index b899986c..44698524 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/XForwardedHeadersFilterTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/XForwardedHeadersFilterTests.java @@ -16,15 +16,6 @@ package org.springframework.cloud.gateway.filter.headers; -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_FOR_HEADER; -import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_HOST_HEADER; -import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_PORT_HEADER; -import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_PREFIX_HEADER; -import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_PROTO_HEADER; -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; - import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.URI; @@ -38,6 +29,15 @@ import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.util.UriComponentsBuilder; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_FOR_HEADER; +import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_HOST_HEADER; +import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_PORT_HEADER; +import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_PREFIX_HEADER; +import static org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter.X_FORWARDED_PROTO_HEADER; +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 */ diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java index b91e9693..4b276168 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java @@ -16,10 +16,6 @@ package org.springframework.cloud.gateway.test; -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; -import static org.springframework.cloud.gateway.test.TestUtils.getMap; - import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -62,7 +58,9 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.cloud.gateway.test.TestUtils.getMap; @SpringBootTest(webEnvironment = RANDOM_PORT) @DirtiesContext diff --git a/spring-cloud-gateway-server/src/test/kotlin/org/springframework/cloud/gateway/route/builder/RouteDslTests.kt b/spring-cloud-gateway-server/src/test/kotlin/org/springframework/cloud/gateway/route/builder/RouteDslTests.kt index ce14c50a..03cbacfb 100644 --- a/spring-cloud-gateway-server/src/test/kotlin/org/springframework/cloud/gateway/route/builder/RouteDslTests.kt +++ b/spring-cloud-gateway-server/src/test/kotlin/org/springframework/cloud/gateway/route/builder/RouteDslTests.kt @@ -32,91 +32,91 @@ import java.net.URI @SpringBootTest(classes = [Config::class]) class RouteDslTests { - @Autowired - lateinit var builder: RouteLocatorBuilder + @Autowired + lateinit var builder: RouteLocatorBuilder - @Test - fun sampleRouteDsl() { - val routeLocator = builder.routes { - route(id = "test") { - host("**.abc.org") and path("/image/png") - filters { - addResponseHeader("X-TestHeader", "foobar") - } - uri("http://httpbin.org:80") - } + @Test + fun sampleRouteDsl() { + val routeLocator = builder.routes { + route(id = "test") { + host("**.abc.org") and path("/image/png") + filters { + addResponseHeader("X-TestHeader", "foobar") + } + uri("http://httpbin.org:80") + } - route(id = "test2") { - path("/image/webp") or path("/image/anotherone") - filters { - addResponseHeader("X-AnotherHeader", "baz") - addResponseHeader("X-AnotherHeader-2", "baz-2") - } - uri("https://httpbin.org:443") - } - } + route(id = "test2") { + path("/image/webp") or path("/image/anotherone") + filters { + addResponseHeader("X-AnotherHeader", "baz") + addResponseHeader("X-AnotherHeader-2", "baz-2") + } + uri("https://httpbin.org:443") + } + } - StepVerifier - .create(routeLocator.routes) - .expectNextMatches({ - it.id == "test" && it.filters.size == 1 && it.uri == URI.create("http://httpbin.org:80") - }) - .expectNextMatches({ - it.id == "test2" && it.filters.size == 2 && it.uri == URI.create("https://httpbin.org:443") - }) - .expectComplete() - .verify() + StepVerifier + .create(routeLocator.routes) + .expectNextMatches({ + it.id == "test" && it.filters.size == 1 && it.uri == URI.create("http://httpbin.org:80") + }) + .expectNextMatches({ + it.id == "test2" && it.filters.size == 2 && it.uri == URI.create("https://httpbin.org:443") + }) + .expectComplete() + .verify() - val sampleExchange: ServerWebExchange = MockServerWebExchange.from(MockServerHttpRequest.get("/image/webp") - .header("Host", "test.abc.org").build()) + val sampleExchange: ServerWebExchange = MockServerWebExchange.from(MockServerHttpRequest.get("/image/webp") + .header("Host", "test.abc.org").build()) - val filteredRoutes = routeLocator.routes.filter({ - sampleExchange.attributes.put(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR, it.id) - it.predicate.apply(sampleExchange).toMono().block() - }) + val filteredRoutes = routeLocator.routes.filter({ + sampleExchange.attributes.put(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR, it.id) + it.predicate.apply(sampleExchange).toMono().block() + }) - StepVerifier.create(filteredRoutes) - .expectNextMatches({ - it.id == "test2" && it.filters.size == 2 && it.uri == URI.create("https://httpbin.org:443") - }) - .expectComplete() - .verify() - } + StepVerifier.create(filteredRoutes) + .expectNextMatches({ + it.id == "test2" && it.filters.size == 2 && it.uri == URI.create("https://httpbin.org:443") + }) + .expectComplete() + .verify() + } - @Test - fun dslWithFunctionParameters() { - val routerLocator = builder.routes { - route(id = "test1", order = 10, uri = "http://httpbin.org") { - host("**.abc.org") - } - route(id = "test2", order = 10, uri = "http://someurl") { - host("**.abc.org") - uri("http://override-url") - } - } + @Test + fun dslWithFunctionParameters() { + val routerLocator = builder.routes { + route(id = "test1", order = 10, uri = "http://httpbin.org") { + host("**.abc.org") + } + route(id = "test2", order = 10, uri = "http://someurl") { + host("**.abc.org") + uri("http://override-url") + } + } - StepVerifier.create(routerLocator.routes) - .expectNextMatches({ - it.id == "test1" && - it.uri == URI.create("http://httpbin.org:80") && - it.order == 10 && - it.predicate.apply(MockServerWebExchange - .from(MockServerHttpRequest - .get("/someuri").header("Host", "test.abc.org"))) - .toMono().block() - }) - .expectNextMatches({ - it.id == "test2" && - it.uri == URI.create("http://override-url:80") && - it.order == 10 && - it.predicate.apply(MockServerWebExchange - .from(MockServerHttpRequest - .get("/someuri").header("Host", "test.abc.org"))) - .toMono().block() - }) - .expectComplete() - .verify() - } + StepVerifier.create(routerLocator.routes) + .expectNextMatches({ + it.id == "test1" && + it.uri == URI.create("http://httpbin.org:80") && + it.order == 10 && + it.predicate.apply(MockServerWebExchange + .from(MockServerHttpRequest + .get("/someuri").header("Host", "test.abc.org"))) + .toMono().block() + }) + .expectNextMatches({ + it.id == "test2" && + it.uri == URI.create("http://override-url:80") && + it.order == 10 && + it.predicate.apply(MockServerWebExchange + .from(MockServerHttpRequest + .get("/someuri").header("Host", "test.abc.org"))) + .toMono().block() + }) + .expectComplete() + .verify() + } } @Configuration(proxyBeanMethods = false) From 6ce73c6ec72af38f2de880e64bbc40cb17096402 Mon Sep 17 00:00:00 2001 From: spencergibb Date: Mon, 17 Mar 2025 16:59:19 -0400 Subject: [PATCH 21/43] Changes property to spring.cloud.gateway.forwarded.by.enabled Defaults to false. See gh-2658 --- .../httpheadersfilters.adoc | 4 ++++ .../gateway/config/GatewayAutoConfiguration.java | 7 +++++-- .../filter/headers/ForwardedHeadersFilter.java | 15 ++++----------- .../additional-spring-configuration-metadata.json | 6 ++++++ .../headers/ForwardedHeadersFilterTests.java | 6 +++--- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-webflux/httpheadersfilters.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-webflux/httpheadersfilters.adoc index e2a6ac82..49a227e1 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-webflux/httpheadersfilters.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-webflux/httpheadersfilters.adoc @@ -7,6 +7,10 @@ == Forwarded Headers Filter The `Forwarded` Headers Filter creates a `Forwarded` header to send to the downstream service. It adds the `Host` header, scheme and port of the current request to any existing `Forwarded` header. +The `Forwarded by` header part can be enabled by setting the following property to true (defaults to false): + +- `spring.cloud.gateway.forwarded.by.enabled=true` + [[removehopbyhop-headers-filter]] == RemoveHopByHop Headers Filter The `RemoveHopByHop` Headers Filter removes headers from forwarded requests. The default list of headers that is removed comes from the https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-14#section-7.1.3[IETF]. 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 f95112cf..622af6c7 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 @@ -317,8 +317,11 @@ public class GatewayAutoConfiguration { @Bean @ConditionalOnProperty(name = "spring.cloud.gateway.forwarded.enabled", matchIfMissing = true) - public ForwardedHeadersFilter forwardedHeadersFilter() { - return new ForwardedHeadersFilter(); + public ForwardedHeadersFilter forwardedHeadersFilter(Environment env) { + boolean forwardedByEnabled = env.getProperty("spring.cloud.gateway.forwarded.by.enabled", Boolean.class, false); + ForwardedHeadersFilter forwardedHeadersFilter = new ForwardedHeadersFilter(); + forwardedHeadersFilter.setForwardedByEnabled(forwardedByEnabled); + return forwardedHeadersFilter; } // HttpHeaderFilter beans diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java index 7add0e43..39336326 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java @@ -30,7 +30,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.core.Ordered; import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -44,7 +43,6 @@ import org.springframework.web.server.ServerWebExchange; * @author Olga Maciaszek-Sharma * @author Tillmann Heigel */ -@ConfigurationProperties("spring.cloud.gateway.forwarded") public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { @Value("${server.port}") @@ -52,8 +50,7 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { private final Log logger = LogFactory.getLog(getClass()); - /** If Forwarded: by header is enabled. */ - private boolean byEnabled = true; + private boolean forwardedByEnabled = false; /** * Forwarded header. @@ -103,12 +100,8 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { return result; } - public boolean isByEnabled() { - return byEnabled; - } - - public void setByEnabled(boolean byEnabled) { - this.byEnabled = byEnabled; + public void setForwardedByEnabled(boolean forwardedByEnabled) { + this.forwardedByEnabled = forwardedByEnabled; } @Override @@ -162,7 +155,7 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { forwarded.put("for", forValue); } - if (byEnabled) { + if (forwardedByEnabled) { addForwardedByHeader(forwarded); } diff --git a/spring-cloud-gateway-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-gateway-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 4fb4e6b8..1aea9f85 100644 --- a/spring-cloud-gateway-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-gateway-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -389,6 +389,12 @@ "description": "Enables the ForwardedHeadersFilter.", "defaultValue": "true" }, + { + "name": "spring.cloud.gateway.forwarded.by.enabled", + "type": "java.lang.Boolean", + "description": "Enables the Forwarded: by header part.", + "defaultValue": "false" + }, { "name": "spring.cloud.gateway.httpserver.wiretap", "type": "java.lang.Boolean", diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilterTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilterTests.java index b9069bf5..0bc91049 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilterTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilterTests.java @@ -207,7 +207,7 @@ public class ForwardedHeadersFilterTests { Forwarded forwarded = new Forwarded(); InetAddress ipv4Address = InetAddress.getByName("216.103.69.111"); ForwardedHeadersFilter forwardedHeadersFilter = new ForwardedHeadersFilter(); - forwardedHeadersFilter.setByEnabled(true); + forwardedHeadersFilter.setForwardedByEnabled(true); forwardedHeadersFilter.addForwardedBy(forwarded, ipv4Address); @@ -220,7 +220,7 @@ public class ForwardedHeadersFilterTests { Forwarded forwarded = new Forwarded(); InetAddress ipv6Address = InetAddress.getByName("abc4:babf:955f:1724:11bc:0153:275c:d36e"); ForwardedHeadersFilter forwardedHeadersFilter = new ForwardedHeadersFilter(); - forwardedHeadersFilter.setByEnabled(true); + forwardedHeadersFilter.setForwardedByEnabled(true); forwardedHeadersFilter.addForwardedBy(forwarded, ipv6Address); @@ -234,7 +234,7 @@ public class ForwardedHeadersFilterTests { Forwarded forwarded = new Forwarded(); InetAddress ipv4Address = InetAddress.getByName("216.103.69.111"); ForwardedHeadersFilter forwardedHeadersFilter = new ForwardedHeadersFilter(); - forwardedHeadersFilter.setByEnabled(false); + forwardedHeadersFilter.setForwardedByEnabled(false); forwardedHeadersFilter.addForwardedBy(forwarded, ipv4Address); From 8d79a175cbead011e84c2ab9bc33b7725ffa6114 Mon Sep 17 00:00:00 2001 From: spencergibb Date: Mon, 17 Mar 2025 19:10:19 -0400 Subject: [PATCH 22/43] Make ForwardedHeadersFilter.serverPort nullable See gh-2658 --- .../cloud/gateway/config/GatewayAutoConfiguration.java | 3 ++- .../gateway/filter/headers/ForwardedHeadersFilter.java | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) 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 622af6c7..5d399d95 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 @@ -317,10 +317,11 @@ public class GatewayAutoConfiguration { @Bean @ConditionalOnProperty(name = "spring.cloud.gateway.forwarded.enabled", matchIfMissing = true) - public ForwardedHeadersFilter forwardedHeadersFilter(Environment env) { + public ForwardedHeadersFilter forwardedHeadersFilter(Environment env, ServerProperties serverProperties) { boolean forwardedByEnabled = env.getProperty("spring.cloud.gateway.forwarded.by.enabled", Boolean.class, false); ForwardedHeadersFilter forwardedHeadersFilter = new ForwardedHeadersFilter(); forwardedHeadersFilter.setForwardedByEnabled(forwardedByEnabled); + forwardedHeadersFilter.setServerPort(serverProperties.getPort()); return forwardedHeadersFilter; } diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java index 39336326..e55871f3 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/headers/ForwardedHeadersFilter.java @@ -29,7 +29,6 @@ import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.annotation.Value; import org.springframework.core.Ordered; import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -45,8 +44,7 @@ import org.springframework.web.server.ServerWebExchange; */ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { - @Value("${server.port}") - private int serverPort; + private Integer serverPort; private final Log logger = LogFactory.getLog(getClass()); @@ -104,6 +102,10 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { this.forwardedByEnabled = forwardedByEnabled; } + public void setServerPort(Integer serverPort) { + this.serverPort = serverPort; + } + @Override public int getOrder() { return 0; @@ -179,7 +181,7 @@ public class ForwardedHeadersFilter implements HttpHeadersFilter, Ordered { if (localAddress instanceof Inet6Address) { byValue = "[" + byValue + "]"; } - if (serverPort > 0) { + if (serverPort != null && serverPort > 0) { byValue = byValue + ":" + serverPort; } forwarded.put("by", byValue); From 8ab0279d0f1051305b6e8d5cf417b1340de3a685 Mon Sep 17 00:00:00 2001 From: spencergibb Date: Mon, 17 Mar 2025 22:39:37 -0400 Subject: [PATCH 23/43] Set spring.cloud.gateway.forwarded.by.enabled=true See gh-2658 --- .../cloud/gateway/test/GatewayIntegrationTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java index 4b276168..2ca53a27 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/GatewayIntegrationTests.java @@ -62,7 +62,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; import static org.springframework.cloud.gateway.test.TestUtils.getMap; -@SpringBootTest(webEnvironment = RANDOM_PORT) +@SpringBootTest(webEnvironment = RANDOM_PORT, properties = "spring.cloud.gateway.forwarded.by.enabled=true") @DirtiesContext @SuppressWarnings("unchecked") @ExtendWith(OutputCaptureExtension.class) From 0f3112077d0ac34eb594f735b42c1ca27a38bd30 Mon Sep 17 00:00:00 2001 From: jiangyuan Date: Tue, 18 Mar 2025 15:46:14 +0800 Subject: [PATCH 24/43] 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 e27e3200233eb2ae5862bdbde8068f5dcac991d9 Mon Sep 17 00:00:00 2001 From: Jens Mallien Date: Tue, 30 Jul 2024 13:52:17 +0200 Subject: [PATCH 25/43] Use getPath instead of getRawPath to prevent doulbe encoding of the URI Signed-off-by: Jens Mallien <108389225+jensmatw@users.noreply.github.com> --- .../cloud/gateway/server/mvc/filter/BeforeFilterFunctions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 93a3eb7c..343bfe8e 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 @@ -324,7 +324,7 @@ public abstract class BeforeFilterFunctions { Pattern pattern = Pattern.compile(regexp); return request -> { // TODO: original request url - String path = request.uri().getRawPath(); + String path = request.uri().getPath(); String newPath = pattern.matcher(path).replaceAll(normalizedReplacement); URI rewrittenUri = UriComponentsBuilder.fromUri(request.uri()).replacePath(newPath).build().toUri(); From ea0c5ef3d2dc96131a7bc63b8af13c93806f5441 Mon Sep 17 00:00:00 2001 From: Jens Mallien Date: Fri, 27 Sep 2024 18:13:14 +0200 Subject: [PATCH 26/43] Adds a test Signed-off-by: Jens Mallien <108389225+jensmatw@users.noreply.github.com> --- .../filter/BeforeFilterFunctionsTests.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java new file mode 100644 index 00000000..f933ef78 --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java @@ -0,0 +1,58 @@ +/* + * Copyright 2013-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.gateway.server.mvc.filter; + +import java.util.Collections; + +import org.junit.jupiter.api.Test; + +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.web.servlet.function.ServerRequest; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Jens Mallien + */ +public class BeforeFilterFunctionsTests { + + @Test + public void rewritePath() { + MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get") + .buildRequest(null); + + ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList()); + + ServerRequest modified = BeforeFilterFunctions.rewritePath("get", "modified").apply(request); + + assertThat(modified.uri().getRawPath()).isEqualTo("/modified"); + } + + @Test + public void rewritePathWithSpace() { + MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get/path/with spaces") + .buildRequest(null); + + ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList()); + + ServerRequest modified = BeforeFilterFunctions.rewritePath("get", "modified").apply(request); + + assertThat(modified.uri().getRawPath()).isEqualTo("/modified/path/with%20spaces"); + } + +} From 367cb80db94df5f23e95e22e13987437588d804b Mon Sep 17 00:00:00 2001 From: Jens Mallien Date: Mon, 30 Sep 2024 15:24:26 +0200 Subject: [PATCH 27/43] Fixes unicode url-rewrite Signed-off-by: Jens Mallien <108389225+jensmatw@users.noreply.github.com> --- .../mvc/filter/BeforeFilterFunctions.java | 2 +- .../filter/BeforeFilterFunctionsTests.java | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) 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 343bfe8e..c8ac1a42 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 @@ -327,7 +327,7 @@ public abstract class BeforeFilterFunctions { String path = request.uri().getPath(); String newPath = pattern.matcher(path).replaceAll(normalizedReplacement); - URI rewrittenUri = UriComponentsBuilder.fromUri(request.uri()).replacePath(newPath).build().toUri(); + URI rewrittenUri = UriComponentsBuilder.fromUri(request.uri()).replacePath(newPath).encode().build().toUri(); ServerRequest modified = ServerRequest.from(request).uri(rewrittenUri).build(); diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java index f933ef78..889ceeab 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java @@ -55,4 +55,28 @@ public class BeforeFilterFunctionsTests { assertThat(modified.uri().getRawPath()).isEqualTo("/modified/path/with%20spaces"); } + @Test + public void rewritePathWithEnDash() { + MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get/path/with–en–dashes") + .buildRequest(null); + + ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList()); + + ServerRequest modified = BeforeFilterFunctions.rewritePath("get", "modified").apply(request); + + assertThat(modified.uri().getRawPath()).isEqualTo("/modified/path/with%E2%80%93en%E2%80%93dashes"); + } + + @Test + public void rewritePathWithEnDashAndSpace() { + MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get/path/with–en–dashes and spaces") + .buildRequest(null); + + ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList()); + + ServerRequest modified = BeforeFilterFunctions.rewritePath("get", "modified").apply(request); + + assertThat(modified.uri().getRawPath()).isEqualTo("/modified/path/with%E2%80%93en%E2%80%93dashes%20and%20spaces"); + } + } From 2175fc1c8e374e361ef801b95c2ee00a79c0cd4d Mon Sep 17 00:00:00 2001 From: spring-builds Date: Tue, 18 Mar 2025 13:26:51 +0000 Subject: [PATCH 28/43] 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\"}"); } } From 014a8511e4f257a41f1e30508557de352fb8744a Mon Sep 17 00:00:00 2001 From: spring-builds Date: Tue, 18 Mar 2025 18:52:12 +0000 Subject: [PATCH 29/43] Bumping versions --- docs/modules/ROOT/partials/_configprops.adoc | 43 ++++++++++---------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/docs/modules/ROOT/partials/_configprops.adoc b/docs/modules/ROOT/partials/_configprops.adoc index 7a042b63..ae7609d3 100644 --- a/docs/modules/ROOT/partials/_configprops.adoc +++ b/docs/modules/ROOT/partials/_configprops.adoc @@ -3,10 +3,10 @@ |spring.cloud.gateway.default-filters | | List of filter definitions that are applied to every route. |spring.cloud.gateway.discovery.locator.enabled | `+++false+++` | Flag that enables DiscoveryClient gateway integration. -|spring.cloud.gateway.discovery.locator.filters | | +|spring.cloud.gateway.discovery.locator.filters | | |spring.cloud.gateway.discovery.locator.include-expression | `+++true+++` | SpEL expression that will evaluate whether to include a service in gateway integration or not, defaults to: true. |spring.cloud.gateway.discovery.locator.lower-case-service-id | `+++false+++` | Option to lower case serviceId in predicates and filters, defaults to false. Useful with eureka when it automatically uppercases serviceId. so MYSERIVCE, would match /myservice/** -|spring.cloud.gateway.discovery.locator.predicates | | +|spring.cloud.gateway.discovery.locator.predicates | | |spring.cloud.gateway.discovery.locator.route-id-prefix | | The prefix for the routeId, defaults to discoveryClient.getClass().getSimpleName() + "_". Service Id will be appended to create the routeId. |spring.cloud.gateway.discovery.locator.url-expression | `+++'lb://'+serviceId+++` | SpEL expression that create the uri for each route, defaults to: 'lb://'+serviceId. |spring.cloud.gateway.enabled | `+++true+++` | Enables gateway functionality. @@ -20,7 +20,7 @@ |spring.cloud.gateway.filter.hystrix.enabled | `+++true+++` | Enables the hystrix filter. |spring.cloud.gateway.filter.json-to-grpc.enabled | `+++true+++` | Enables the JSON to gRPC filter. |spring.cloud.gateway.filter.local-response-cache.enabled | `+++false+++` | Enables the local-response-cache filter. -|spring.cloud.gateway.filter.local-response-cache.request.no-cache-strategy | `+++skip-update-cache-entry+++` | +|spring.cloud.gateway.filter.local-response-cache.request.no-cache-strategy | `+++skip-update-cache-entry+++` | |spring.cloud.gateway.filter.local-response-cache.size | | Maximum size of the cache to evict entries for this route (in KB, MB and GB). |spring.cloud.gateway.filter.local-response-cache.time-to-live | `+++5m+++` | Time to expire a cache entry (expressed in s for seconds, m for minutes, and h for hours). |spring.cloud.gateway.filter.map-request-header.enabled | `+++true+++` | Enables the map-request-header filter. @@ -29,15 +29,15 @@ |spring.cloud.gateway.filter.prefix-path.enabled | `+++true+++` | Enables the prefix-path filter. |spring.cloud.gateway.filter.preserve-host-header.enabled | `+++true+++` | Enables the preserve-host-header filter. |spring.cloud.gateway.filter.redirect-to.enabled | `+++true+++` | Enables the redirect-to filter. -|spring.cloud.gateway.filter.remove-hop-by-hop.headers | | -|spring.cloud.gateway.filter.remove-hop-by-hop.order | `+++0+++` | +|spring.cloud.gateway.filter.remove-hop-by-hop.headers | | +|spring.cloud.gateway.filter.remove-hop-by-hop.order | `+++0+++` | |spring.cloud.gateway.filter.remove-request-header.enabled | `+++true+++` | Enables the remove-request-header filter. |spring.cloud.gateway.filter.remove-request-parameter.enabled | `+++true+++` | Enables the remove-request-parameter filter. |spring.cloud.gateway.filter.remove-response-header.enabled | `+++true+++` | Enables the remove-response-header filter. |spring.cloud.gateway.filter.request-header-size.enabled | `+++true+++` | Enables the request-header-size filter. |spring.cloud.gateway.filter.request-header-to-request-uri.enabled | `+++true+++` | Enables the request-header-to-request-uri filter. -|spring.cloud.gateway.filter.request-rate-limiter.default-key-resolver | | -|spring.cloud.gateway.filter.request-rate-limiter.default-rate-limiter | | +|spring.cloud.gateway.filter.request-rate-limiter.default-key-resolver | | +|spring.cloud.gateway.filter.request-rate-limiter.default-rate-limiter | | |spring.cloud.gateway.filter.request-rate-limiter.enabled | `+++true+++` | Enables the request-rate-limiter filter. |spring.cloud.gateway.filter.request-size.enabled | `+++true+++` | Enables the request-size filter. |spring.cloud.gateway.filter.retry.enabled | `+++true+++` | Enables the retry filter. @@ -47,22 +47,23 @@ |spring.cloud.gateway.filter.rewrite-request-parameter.enabled | `+++true+++` | Enables the rewrite-request-parameter filter. |spring.cloud.gateway.filter.rewrite-response-header.enabled | `+++true+++` | Enables the rewrite-response-header filter. |spring.cloud.gateway.filter.save-session.enabled | `+++true+++` | Enables the save-session filter. -|spring.cloud.gateway.filter.secure-headers.content-security-policy | `+++default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'+++` | -|spring.cloud.gateway.filter.secure-headers.content-type-options | `+++nosniff+++` | -|spring.cloud.gateway.filter.secure-headers.disable | | -|spring.cloud.gateway.filter.secure-headers.download-options | `+++noopen+++` | +|spring.cloud.gateway.filter.secure-headers.content-security-policy | `+++default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'+++` | +|spring.cloud.gateway.filter.secure-headers.content-type-options | `+++nosniff+++` | +|spring.cloud.gateway.filter.secure-headers.disable | | +|spring.cloud.gateway.filter.secure-headers.download-options | `+++noopen+++` | |spring.cloud.gateway.filter.secure-headers.enabled | `+++true+++` | Enables the secure-headers filter. -|spring.cloud.gateway.filter.secure-headers.frame-options | `+++DENY+++` | -|spring.cloud.gateway.filter.secure-headers.permitted-cross-domain-policies | `+++none+++` | -|spring.cloud.gateway.filter.secure-headers.referrer-policy | `+++no-referrer+++` | -|spring.cloud.gateway.filter.secure-headers.strict-transport-security | `+++max-age=631138519+++` | -|spring.cloud.gateway.filter.secure-headers.xss-protection-header | `+++1 ; mode=block+++` | +|spring.cloud.gateway.filter.secure-headers.frame-options | `+++DENY+++` | +|spring.cloud.gateway.filter.secure-headers.permitted-cross-domain-policies | `+++none+++` | +|spring.cloud.gateway.filter.secure-headers.referrer-policy | `+++no-referrer+++` | +|spring.cloud.gateway.filter.secure-headers.strict-transport-security | `+++max-age=631138519+++` | +|spring.cloud.gateway.filter.secure-headers.xss-protection-header | `+++1 ; mode=block+++` | |spring.cloud.gateway.filter.set-path.enabled | `+++true+++` | Enables the set-path filter. |spring.cloud.gateway.filter.set-request-header.enabled | `+++true+++` | Enables the set-request-header filter. |spring.cloud.gateway.filter.set-request-host-header.enabled | `+++true+++` | Enables the set-request-host-header filter. |spring.cloud.gateway.filter.set-response-header.enabled | `+++true+++` | Enables the set-response-header filter. |spring.cloud.gateway.filter.set-status.enabled | `+++true+++` | Enables the set-status filter. |spring.cloud.gateway.filter.strip-prefix.enabled | `+++true+++` | Enables the strip-prefix filter. +|spring.cloud.gateway.forwarded.by.enabled | `+++false+++` | Enables the Forwarded: by header part. |spring.cloud.gateway.forwarded.enabled | `+++true+++` | Enables the ForwardedHeadersFilter. |spring.cloud.gateway.global-filter.adapt-cached-body.enabled | `+++true+++` | Enables the adapt-cached-body global filter. |spring.cloud.gateway.global-filter.forward-path.enabled | `+++true+++` | Enables the forward-path global filter. @@ -76,7 +77,7 @@ |spring.cloud.gateway.global-filter.route-to-request-url.enabled | `+++true+++` | Enables the route-to-request-url global filter. |spring.cloud.gateway.global-filter.websocket-routing.enabled | `+++true+++` | Enables the websocket-routing global filter. |spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping | `+++false+++` | If global CORS config should be added to the URL handler. -|spring.cloud.gateway.globalcors.cors-configurations | | +|spring.cloud.gateway.globalcors.cors-configurations | | |spring.cloud.gateway.handler-mapping.order | `+++1+++` | The order of RoutePredicateHandlerMapping. |spring.cloud.gateway.httpclient.compression | `+++false+++` | Enables compression for Netty HttpClient. |spring.cloud.gateway.httpclient.connect-timeout | | The connect timeout in millis, the default is 30s. @@ -113,7 +114,7 @@ |spring.cloud.gateway.httpclient.websocket.proxy-ping | `+++true+++` | Proxy ping frames to downstream services, defaults to true. |spring.cloud.gateway.httpclient.wiretap | `+++false+++` | Enables wiretap debugging for Netty HttpClient. |spring.cloud.gateway.httpserver.wiretap | `+++false+++` | Enables wiretap debugging for Netty HttpServer. -|spring.cloud.gateway.loadbalancer.use404 | `+++false+++` | +|spring.cloud.gateway.loadbalancer.use404 | `+++false+++` | |spring.cloud.gateway.metrics.enabled | `+++false+++` | Enables the collection of metrics data. |spring.cloud.gateway.metrics.prefix | `+++spring.cloud.gateway+++` | The prefix of all metrics emitted by gateway. |spring.cloud.gateway.metrics.tags | | Tags map that added to metrics. @@ -161,7 +162,7 @@ |spring.cloud.gateway.predicate.weight.enabled | `+++true+++` | Enables the weight predicate. |spring.cloud.gateway.predicate.xforwarded-remote-addr.enabled | `+++true+++` | Enables the xforwarded-remote-addr predicate. |spring.cloud.gateway.redis-rate-limiter.burst-capacity-header | `+++X-RateLimit-Burst-Capacity+++` | The name of the header that returns the burst capacity configuration. -|spring.cloud.gateway.redis-rate-limiter.config | | +|spring.cloud.gateway.redis-rate-limiter.config | | |spring.cloud.gateway.redis-rate-limiter.include-headers | `+++true+++` | Whether or not to include headers containing rate limiter information, defaults to true. |spring.cloud.gateway.redis-rate-limiter.remaining-header | `+++X-RateLimit-Remaining+++` | The name of the header that returns number of remaining requests during the current second. |spring.cloud.gateway.redis-rate-limiter.replenish-rate-header | `+++X-RateLimit-Replenish-Rate+++` | The name of the header that returns the replenish rate configuration. @@ -172,7 +173,7 @@ |spring.cloud.gateway.route-refresh-listener.enabled | `+++true+++` | If RouteRefreshListener should be turned on. |spring.cloud.gateway.routes | | List of Routes. |spring.cloud.gateway.set-status.original-status-header-name | | The name of the header which contains http code of the proxied request. -|spring.cloud.gateway.streaming-media-types | | +|spring.cloud.gateway.streaming-media-types | | |spring.cloud.gateway.x-forwarded.enabled | `+++true+++` | If the XForwardedHeadersFilter is enabled. |spring.cloud.gateway.x-forwarded.for-append | `+++true+++` | If appending X-Forwarded-For as a list is enabled. |spring.cloud.gateway.x-forwarded.for-enabled | `+++true+++` | If X-Forwarded-For is enabled. @@ -186,4 +187,4 @@ |spring.cloud.gateway.x-forwarded.proto-append | `+++true+++` | If appending X-Forwarded-Proto as a list is enabled. |spring.cloud.gateway.x-forwarded.proto-enabled | `+++true+++` | If X-Forwarded-Proto is enabled. -|=== +|=== \ No newline at end of file From 263e8135582238256e2c416f627c0f3fb197ef51 Mon Sep 17 00:00:00 2001 From: spring-builds Date: Tue, 18 Mar 2025 23:46:46 +0000 Subject: [PATCH 30/43] Update SNAPSHOT to 4.2.1 --- docs/modules/ROOT/partials/_configprops.adoc | 42 +++++++++---------- docs/pom.xml | 2 +- pom.xml | 8 ++-- spring-cloud-gateway-dependencies/pom.xml | 4 +- .../grpc/pom.xml | 2 +- .../http2/pom.xml | 2 +- .../mvc-failure-analyzer/pom.xml | 2 +- .../pom.xml | 2 +- spring-cloud-gateway-mvc/pom.xml | 2 +- spring-cloud-gateway-sample/pom.xml | 2 +- spring-cloud-gateway-server-mvc/pom.xml | 2 +- spring-cloud-gateway-server/pom.xml | 2 +- ...CloudCircuitBreakerFilterFactoryTests.java | 12 ++++-- spring-cloud-gateway-webflux/pom.xml | 2 +- spring-cloud-starter-gateway-mvc/pom.xml | 2 +- spring-cloud-starter-gateway/pom.xml | 2 +- 16 files changed, 48 insertions(+), 42 deletions(-) diff --git a/docs/modules/ROOT/partials/_configprops.adoc b/docs/modules/ROOT/partials/_configprops.adoc index 7a042b63..a8eed641 100644 --- a/docs/modules/ROOT/partials/_configprops.adoc +++ b/docs/modules/ROOT/partials/_configprops.adoc @@ -3,10 +3,10 @@ |spring.cloud.gateway.default-filters | | List of filter definitions that are applied to every route. |spring.cloud.gateway.discovery.locator.enabled | `+++false+++` | Flag that enables DiscoveryClient gateway integration. -|spring.cloud.gateway.discovery.locator.filters | | +|spring.cloud.gateway.discovery.locator.filters | | |spring.cloud.gateway.discovery.locator.include-expression | `+++true+++` | SpEL expression that will evaluate whether to include a service in gateway integration or not, defaults to: true. |spring.cloud.gateway.discovery.locator.lower-case-service-id | `+++false+++` | Option to lower case serviceId in predicates and filters, defaults to false. Useful with eureka when it automatically uppercases serviceId. so MYSERIVCE, would match /myservice/** -|spring.cloud.gateway.discovery.locator.predicates | | +|spring.cloud.gateway.discovery.locator.predicates | | |spring.cloud.gateway.discovery.locator.route-id-prefix | | The prefix for the routeId, defaults to discoveryClient.getClass().getSimpleName() + "_". Service Id will be appended to create the routeId. |spring.cloud.gateway.discovery.locator.url-expression | `+++'lb://'+serviceId+++` | SpEL expression that create the uri for each route, defaults to: 'lb://'+serviceId. |spring.cloud.gateway.enabled | `+++true+++` | Enables gateway functionality. @@ -20,7 +20,7 @@ |spring.cloud.gateway.filter.hystrix.enabled | `+++true+++` | Enables the hystrix filter. |spring.cloud.gateway.filter.json-to-grpc.enabled | `+++true+++` | Enables the JSON to gRPC filter. |spring.cloud.gateway.filter.local-response-cache.enabled | `+++false+++` | Enables the local-response-cache filter. -|spring.cloud.gateway.filter.local-response-cache.request.no-cache-strategy | `+++skip-update-cache-entry+++` | +|spring.cloud.gateway.filter.local-response-cache.request.no-cache-strategy | `+++skip-update-cache-entry+++` | |spring.cloud.gateway.filter.local-response-cache.size | | Maximum size of the cache to evict entries for this route (in KB, MB and GB). |spring.cloud.gateway.filter.local-response-cache.time-to-live | `+++5m+++` | Time to expire a cache entry (expressed in s for seconds, m for minutes, and h for hours). |spring.cloud.gateway.filter.map-request-header.enabled | `+++true+++` | Enables the map-request-header filter. @@ -29,15 +29,15 @@ |spring.cloud.gateway.filter.prefix-path.enabled | `+++true+++` | Enables the prefix-path filter. |spring.cloud.gateway.filter.preserve-host-header.enabled | `+++true+++` | Enables the preserve-host-header filter. |spring.cloud.gateway.filter.redirect-to.enabled | `+++true+++` | Enables the redirect-to filter. -|spring.cloud.gateway.filter.remove-hop-by-hop.headers | | -|spring.cloud.gateway.filter.remove-hop-by-hop.order | `+++0+++` | +|spring.cloud.gateway.filter.remove-hop-by-hop.headers | | +|spring.cloud.gateway.filter.remove-hop-by-hop.order | `+++0+++` | |spring.cloud.gateway.filter.remove-request-header.enabled | `+++true+++` | Enables the remove-request-header filter. |spring.cloud.gateway.filter.remove-request-parameter.enabled | `+++true+++` | Enables the remove-request-parameter filter. |spring.cloud.gateway.filter.remove-response-header.enabled | `+++true+++` | Enables the remove-response-header filter. |spring.cloud.gateway.filter.request-header-size.enabled | `+++true+++` | Enables the request-header-size filter. |spring.cloud.gateway.filter.request-header-to-request-uri.enabled | `+++true+++` | Enables the request-header-to-request-uri filter. -|spring.cloud.gateway.filter.request-rate-limiter.default-key-resolver | | -|spring.cloud.gateway.filter.request-rate-limiter.default-rate-limiter | | +|spring.cloud.gateway.filter.request-rate-limiter.default-key-resolver | | +|spring.cloud.gateway.filter.request-rate-limiter.default-rate-limiter | | |spring.cloud.gateway.filter.request-rate-limiter.enabled | `+++true+++` | Enables the request-rate-limiter filter. |spring.cloud.gateway.filter.request-size.enabled | `+++true+++` | Enables the request-size filter. |spring.cloud.gateway.filter.retry.enabled | `+++true+++` | Enables the retry filter. @@ -47,16 +47,16 @@ |spring.cloud.gateway.filter.rewrite-request-parameter.enabled | `+++true+++` | Enables the rewrite-request-parameter filter. |spring.cloud.gateway.filter.rewrite-response-header.enabled | `+++true+++` | Enables the rewrite-response-header filter. |spring.cloud.gateway.filter.save-session.enabled | `+++true+++` | Enables the save-session filter. -|spring.cloud.gateway.filter.secure-headers.content-security-policy | `+++default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'+++` | -|spring.cloud.gateway.filter.secure-headers.content-type-options | `+++nosniff+++` | -|spring.cloud.gateway.filter.secure-headers.disable | | -|spring.cloud.gateway.filter.secure-headers.download-options | `+++noopen+++` | +|spring.cloud.gateway.filter.secure-headers.content-security-policy | `+++default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'+++` | +|spring.cloud.gateway.filter.secure-headers.content-type-options | `+++nosniff+++` | +|spring.cloud.gateway.filter.secure-headers.disable | | +|spring.cloud.gateway.filter.secure-headers.download-options | `+++noopen+++` | |spring.cloud.gateway.filter.secure-headers.enabled | `+++true+++` | Enables the secure-headers filter. -|spring.cloud.gateway.filter.secure-headers.frame-options | `+++DENY+++` | -|spring.cloud.gateway.filter.secure-headers.permitted-cross-domain-policies | `+++none+++` | -|spring.cloud.gateway.filter.secure-headers.referrer-policy | `+++no-referrer+++` | -|spring.cloud.gateway.filter.secure-headers.strict-transport-security | `+++max-age=631138519+++` | -|spring.cloud.gateway.filter.secure-headers.xss-protection-header | `+++1 ; mode=block+++` | +|spring.cloud.gateway.filter.secure-headers.frame-options | `+++DENY+++` | +|spring.cloud.gateway.filter.secure-headers.permitted-cross-domain-policies | `+++none+++` | +|spring.cloud.gateway.filter.secure-headers.referrer-policy | `+++no-referrer+++` | +|spring.cloud.gateway.filter.secure-headers.strict-transport-security | `+++max-age=631138519+++` | +|spring.cloud.gateway.filter.secure-headers.xss-protection-header | `+++1 ; mode=block+++` | |spring.cloud.gateway.filter.set-path.enabled | `+++true+++` | Enables the set-path filter. |spring.cloud.gateway.filter.set-request-header.enabled | `+++true+++` | Enables the set-request-header filter. |spring.cloud.gateway.filter.set-request-host-header.enabled | `+++true+++` | Enables the set-request-host-header filter. @@ -76,7 +76,7 @@ |spring.cloud.gateway.global-filter.route-to-request-url.enabled | `+++true+++` | Enables the route-to-request-url global filter. |spring.cloud.gateway.global-filter.websocket-routing.enabled | `+++true+++` | Enables the websocket-routing global filter. |spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping | `+++false+++` | If global CORS config should be added to the URL handler. -|spring.cloud.gateway.globalcors.cors-configurations | | +|spring.cloud.gateway.globalcors.cors-configurations | | |spring.cloud.gateway.handler-mapping.order | `+++1+++` | The order of RoutePredicateHandlerMapping. |spring.cloud.gateway.httpclient.compression | `+++false+++` | Enables compression for Netty HttpClient. |spring.cloud.gateway.httpclient.connect-timeout | | The connect timeout in millis, the default is 30s. @@ -113,7 +113,7 @@ |spring.cloud.gateway.httpclient.websocket.proxy-ping | `+++true+++` | Proxy ping frames to downstream services, defaults to true. |spring.cloud.gateway.httpclient.wiretap | `+++false+++` | Enables wiretap debugging for Netty HttpClient. |spring.cloud.gateway.httpserver.wiretap | `+++false+++` | Enables wiretap debugging for Netty HttpServer. -|spring.cloud.gateway.loadbalancer.use404 | `+++false+++` | +|spring.cloud.gateway.loadbalancer.use404 | `+++false+++` | |spring.cloud.gateway.metrics.enabled | `+++false+++` | Enables the collection of metrics data. |spring.cloud.gateway.metrics.prefix | `+++spring.cloud.gateway+++` | The prefix of all metrics emitted by gateway. |spring.cloud.gateway.metrics.tags | | Tags map that added to metrics. @@ -161,7 +161,7 @@ |spring.cloud.gateway.predicate.weight.enabled | `+++true+++` | Enables the weight predicate. |spring.cloud.gateway.predicate.xforwarded-remote-addr.enabled | `+++true+++` | Enables the xforwarded-remote-addr predicate. |spring.cloud.gateway.redis-rate-limiter.burst-capacity-header | `+++X-RateLimit-Burst-Capacity+++` | The name of the header that returns the burst capacity configuration. -|spring.cloud.gateway.redis-rate-limiter.config | | +|spring.cloud.gateway.redis-rate-limiter.config | | |spring.cloud.gateway.redis-rate-limiter.include-headers | `+++true+++` | Whether or not to include headers containing rate limiter information, defaults to true. |spring.cloud.gateway.redis-rate-limiter.remaining-header | `+++X-RateLimit-Remaining+++` | The name of the header that returns number of remaining requests during the current second. |spring.cloud.gateway.redis-rate-limiter.replenish-rate-header | `+++X-RateLimit-Replenish-Rate+++` | The name of the header that returns the replenish rate configuration. @@ -172,7 +172,7 @@ |spring.cloud.gateway.route-refresh-listener.enabled | `+++true+++` | If RouteRefreshListener should be turned on. |spring.cloud.gateway.routes | | List of Routes. |spring.cloud.gateway.set-status.original-status-header-name | | The name of the header which contains http code of the proxied request. -|spring.cloud.gateway.streaming-media-types | | +|spring.cloud.gateway.streaming-media-types | | |spring.cloud.gateway.x-forwarded.enabled | `+++true+++` | If the XForwardedHeadersFilter is enabled. |spring.cloud.gateway.x-forwarded.for-append | `+++true+++` | If appending X-Forwarded-For as a list is enabled. |spring.cloud.gateway.x-forwarded.for-enabled | `+++true+++` | If X-Forwarded-For is enabled. @@ -186,4 +186,4 @@ |spring.cloud.gateway.x-forwarded.proto-append | `+++true+++` | If appending X-Forwarded-Proto as a list is enabled. |spring.cloud.gateway.x-forwarded.proto-enabled | `+++true+++` | If X-Forwarded-Proto is enabled. -|=== +|=== \ No newline at end of file diff --git a/docs/pom.xml b/docs/pom.xml index df489971..b5f9c482 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.1 spring-cloud-gateway-docs jar diff --git a/pom.xml b/pom.xml index 87438173..9506a997 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.1 pom Spring Cloud Gateway @@ -15,7 +15,7 @@ org.springframework.cloud spring-cloud-build - 4.2.1-SNAPSHOT + 4.2.1 @@ -55,8 +55,8 @@ 1.0.8.RELEASE 17 2.3.0 - 3.2.1-SNAPSHOT - 4.2.1-SNAPSHOT + 3.2.1 + 4.2.1 diff --git a/spring-cloud-gateway-dependencies/pom.xml b/spring-cloud-gateway-dependencies/pom.xml index 6d255037..12006ace 100644 --- a/spring-cloud-gateway-dependencies/pom.xml +++ b/spring-cloud-gateway-dependencies/pom.xml @@ -6,12 +6,12 @@ spring-cloud-dependencies-parent org.springframework.cloud - 4.2.1-SNAPSHOT + 4.2.1 spring-cloud-gateway-dependencies - 4.2.1-SNAPSHOT + 4.2.1 pom spring-cloud-gateway-dependencies diff --git a/spring-cloud-gateway-integration-tests/grpc/pom.xml b/spring-cloud-gateway-integration-tests/grpc/pom.xml index 606d6cf6..0c8a671c 100644 --- a/spring-cloud-gateway-integration-tests/grpc/pom.xml +++ b/spring-cloud-gateway-integration-tests/grpc/pom.xml @@ -18,7 +18,7 @@ org.springframework.cloud spring-cloud-gateway-integration-tests - 4.2.1-SNAPSHOT + 4.2.1 .. diff --git a/spring-cloud-gateway-integration-tests/http2/pom.xml b/spring-cloud-gateway-integration-tests/http2/pom.xml index f833c541..d62efe0d 100644 --- a/spring-cloud-gateway-integration-tests/http2/pom.xml +++ b/spring-cloud-gateway-integration-tests/http2/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway-integration-tests - 4.2.1-SNAPSHOT + 4.2.1 .. diff --git a/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml b/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml index c79551c2..d8ccedaf 100644 --- a/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml +++ b/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway-integration-tests - 4.2.1-SNAPSHOT + 4.2.1 .. diff --git a/spring-cloud-gateway-integration-tests/pom.xml b/spring-cloud-gateway-integration-tests/pom.xml index e9bf2a3c..69787cdc 100644 --- a/spring-cloud-gateway-integration-tests/pom.xml +++ b/spring-cloud-gateway-integration-tests/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.1 .. diff --git a/spring-cloud-gateway-mvc/pom.xml b/spring-cloud-gateway-mvc/pom.xml index ed68626d..603d949a 100644 --- a/spring-cloud-gateway-mvc/pom.xml +++ b/spring-cloud-gateway-mvc/pom.xml @@ -11,7 +11,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.1 .. diff --git a/spring-cloud-gateway-sample/pom.xml b/spring-cloud-gateway-sample/pom.xml index 41884109..9ee987ab 100644 --- a/spring-cloud-gateway-sample/pom.xml +++ b/spring-cloud-gateway-sample/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.1 .. diff --git a/spring-cloud-gateway-server-mvc/pom.xml b/spring-cloud-gateway-server-mvc/pom.xml index ba17bdf7..d2db5be5 100644 --- a/spring-cloud-gateway-server-mvc/pom.xml +++ b/spring-cloud-gateway-server-mvc/pom.xml @@ -22,7 +22,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.1 .. spring-cloud-gateway-server-mvc diff --git a/spring-cloud-gateway-server/pom.xml b/spring-cloud-gateway-server/pom.xml index c365ed4b..12d05627 100644 --- a/spring-cloud-gateway-server/pom.xml +++ b/spring-cloud-gateway-server/pom.xml @@ -7,7 +7,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.1 .. spring-cloud-gateway-server 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\"}"); } } diff --git a/spring-cloud-gateway-webflux/pom.xml b/spring-cloud-gateway-webflux/pom.xml index fe8b8ece..31371a1c 100644 --- a/spring-cloud-gateway-webflux/pom.xml +++ b/spring-cloud-gateway-webflux/pom.xml @@ -11,7 +11,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.1 .. diff --git a/spring-cloud-starter-gateway-mvc/pom.xml b/spring-cloud-starter-gateway-mvc/pom.xml index a2c8eba8..66d548ad 100644 --- a/spring-cloud-starter-gateway-mvc/pom.xml +++ b/spring-cloud-starter-gateway-mvc/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.1 .. spring-cloud-starter-gateway-mvc diff --git a/spring-cloud-starter-gateway/pom.xml b/spring-cloud-starter-gateway/pom.xml index f32a7980..5399cc25 100644 --- a/spring-cloud-starter-gateway/pom.xml +++ b/spring-cloud-starter-gateway/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.1 .. spring-cloud-starter-gateway From 0ee6a5d6da432c7d7378329d8281c160e32e1bea Mon Sep 17 00:00:00 2001 From: spring-builds Date: Tue, 18 Mar 2025 23:49:59 +0000 Subject: [PATCH 31/43] Going back to snapshots --- docs/modules/ROOT/partials/_configprops.adoc | 42 +++++++++---------- docs/pom.xml | 2 +- pom.xml | 8 ++-- spring-cloud-gateway-dependencies/pom.xml | 4 +- .../grpc/pom.xml | 2 +- .../http2/pom.xml | 2 +- .../mvc-failure-analyzer/pom.xml | 2 +- .../pom.xml | 2 +- spring-cloud-gateway-mvc/pom.xml | 2 +- spring-cloud-gateway-sample/pom.xml | 2 +- spring-cloud-gateway-server-mvc/pom.xml | 2 +- spring-cloud-gateway-server/pom.xml | 2 +- ...CloudCircuitBreakerFilterFactoryTests.java | 12 ++---- spring-cloud-gateway-webflux/pom.xml | 2 +- spring-cloud-starter-gateway-mvc/pom.xml | 2 +- spring-cloud-starter-gateway/pom.xml | 2 +- 16 files changed, 42 insertions(+), 48 deletions(-) diff --git a/docs/modules/ROOT/partials/_configprops.adoc b/docs/modules/ROOT/partials/_configprops.adoc index a8eed641..7a042b63 100644 --- a/docs/modules/ROOT/partials/_configprops.adoc +++ b/docs/modules/ROOT/partials/_configprops.adoc @@ -3,10 +3,10 @@ |spring.cloud.gateway.default-filters | | List of filter definitions that are applied to every route. |spring.cloud.gateway.discovery.locator.enabled | `+++false+++` | Flag that enables DiscoveryClient gateway integration. -|spring.cloud.gateway.discovery.locator.filters | | +|spring.cloud.gateway.discovery.locator.filters | | |spring.cloud.gateway.discovery.locator.include-expression | `+++true+++` | SpEL expression that will evaluate whether to include a service in gateway integration or not, defaults to: true. |spring.cloud.gateway.discovery.locator.lower-case-service-id | `+++false+++` | Option to lower case serviceId in predicates and filters, defaults to false. Useful with eureka when it automatically uppercases serviceId. so MYSERIVCE, would match /myservice/** -|spring.cloud.gateway.discovery.locator.predicates | | +|spring.cloud.gateway.discovery.locator.predicates | | |spring.cloud.gateway.discovery.locator.route-id-prefix | | The prefix for the routeId, defaults to discoveryClient.getClass().getSimpleName() + "_". Service Id will be appended to create the routeId. |spring.cloud.gateway.discovery.locator.url-expression | `+++'lb://'+serviceId+++` | SpEL expression that create the uri for each route, defaults to: 'lb://'+serviceId. |spring.cloud.gateway.enabled | `+++true+++` | Enables gateway functionality. @@ -20,7 +20,7 @@ |spring.cloud.gateway.filter.hystrix.enabled | `+++true+++` | Enables the hystrix filter. |spring.cloud.gateway.filter.json-to-grpc.enabled | `+++true+++` | Enables the JSON to gRPC filter. |spring.cloud.gateway.filter.local-response-cache.enabled | `+++false+++` | Enables the local-response-cache filter. -|spring.cloud.gateway.filter.local-response-cache.request.no-cache-strategy | `+++skip-update-cache-entry+++` | +|spring.cloud.gateway.filter.local-response-cache.request.no-cache-strategy | `+++skip-update-cache-entry+++` | |spring.cloud.gateway.filter.local-response-cache.size | | Maximum size of the cache to evict entries for this route (in KB, MB and GB). |spring.cloud.gateway.filter.local-response-cache.time-to-live | `+++5m+++` | Time to expire a cache entry (expressed in s for seconds, m for minutes, and h for hours). |spring.cloud.gateway.filter.map-request-header.enabled | `+++true+++` | Enables the map-request-header filter. @@ -29,15 +29,15 @@ |spring.cloud.gateway.filter.prefix-path.enabled | `+++true+++` | Enables the prefix-path filter. |spring.cloud.gateway.filter.preserve-host-header.enabled | `+++true+++` | Enables the preserve-host-header filter. |spring.cloud.gateway.filter.redirect-to.enabled | `+++true+++` | Enables the redirect-to filter. -|spring.cloud.gateway.filter.remove-hop-by-hop.headers | | -|spring.cloud.gateway.filter.remove-hop-by-hop.order | `+++0+++` | +|spring.cloud.gateway.filter.remove-hop-by-hop.headers | | +|spring.cloud.gateway.filter.remove-hop-by-hop.order | `+++0+++` | |spring.cloud.gateway.filter.remove-request-header.enabled | `+++true+++` | Enables the remove-request-header filter. |spring.cloud.gateway.filter.remove-request-parameter.enabled | `+++true+++` | Enables the remove-request-parameter filter. |spring.cloud.gateway.filter.remove-response-header.enabled | `+++true+++` | Enables the remove-response-header filter. |spring.cloud.gateway.filter.request-header-size.enabled | `+++true+++` | Enables the request-header-size filter. |spring.cloud.gateway.filter.request-header-to-request-uri.enabled | `+++true+++` | Enables the request-header-to-request-uri filter. -|spring.cloud.gateway.filter.request-rate-limiter.default-key-resolver | | -|spring.cloud.gateway.filter.request-rate-limiter.default-rate-limiter | | +|spring.cloud.gateway.filter.request-rate-limiter.default-key-resolver | | +|spring.cloud.gateway.filter.request-rate-limiter.default-rate-limiter | | |spring.cloud.gateway.filter.request-rate-limiter.enabled | `+++true+++` | Enables the request-rate-limiter filter. |spring.cloud.gateway.filter.request-size.enabled | `+++true+++` | Enables the request-size filter. |spring.cloud.gateway.filter.retry.enabled | `+++true+++` | Enables the retry filter. @@ -47,16 +47,16 @@ |spring.cloud.gateway.filter.rewrite-request-parameter.enabled | `+++true+++` | Enables the rewrite-request-parameter filter. |spring.cloud.gateway.filter.rewrite-response-header.enabled | `+++true+++` | Enables the rewrite-response-header filter. |spring.cloud.gateway.filter.save-session.enabled | `+++true+++` | Enables the save-session filter. -|spring.cloud.gateway.filter.secure-headers.content-security-policy | `+++default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'+++` | -|spring.cloud.gateway.filter.secure-headers.content-type-options | `+++nosniff+++` | -|spring.cloud.gateway.filter.secure-headers.disable | | -|spring.cloud.gateway.filter.secure-headers.download-options | `+++noopen+++` | +|spring.cloud.gateway.filter.secure-headers.content-security-policy | `+++default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'+++` | +|spring.cloud.gateway.filter.secure-headers.content-type-options | `+++nosniff+++` | +|spring.cloud.gateway.filter.secure-headers.disable | | +|spring.cloud.gateway.filter.secure-headers.download-options | `+++noopen+++` | |spring.cloud.gateway.filter.secure-headers.enabled | `+++true+++` | Enables the secure-headers filter. -|spring.cloud.gateway.filter.secure-headers.frame-options | `+++DENY+++` | -|spring.cloud.gateway.filter.secure-headers.permitted-cross-domain-policies | `+++none+++` | -|spring.cloud.gateway.filter.secure-headers.referrer-policy | `+++no-referrer+++` | -|spring.cloud.gateway.filter.secure-headers.strict-transport-security | `+++max-age=631138519+++` | -|spring.cloud.gateway.filter.secure-headers.xss-protection-header | `+++1 ; mode=block+++` | +|spring.cloud.gateway.filter.secure-headers.frame-options | `+++DENY+++` | +|spring.cloud.gateway.filter.secure-headers.permitted-cross-domain-policies | `+++none+++` | +|spring.cloud.gateway.filter.secure-headers.referrer-policy | `+++no-referrer+++` | +|spring.cloud.gateway.filter.secure-headers.strict-transport-security | `+++max-age=631138519+++` | +|spring.cloud.gateway.filter.secure-headers.xss-protection-header | `+++1 ; mode=block+++` | |spring.cloud.gateway.filter.set-path.enabled | `+++true+++` | Enables the set-path filter. |spring.cloud.gateway.filter.set-request-header.enabled | `+++true+++` | Enables the set-request-header filter. |spring.cloud.gateway.filter.set-request-host-header.enabled | `+++true+++` | Enables the set-request-host-header filter. @@ -76,7 +76,7 @@ |spring.cloud.gateway.global-filter.route-to-request-url.enabled | `+++true+++` | Enables the route-to-request-url global filter. |spring.cloud.gateway.global-filter.websocket-routing.enabled | `+++true+++` | Enables the websocket-routing global filter. |spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping | `+++false+++` | If global CORS config should be added to the URL handler. -|spring.cloud.gateway.globalcors.cors-configurations | | +|spring.cloud.gateway.globalcors.cors-configurations | | |spring.cloud.gateway.handler-mapping.order | `+++1+++` | The order of RoutePredicateHandlerMapping. |spring.cloud.gateway.httpclient.compression | `+++false+++` | Enables compression for Netty HttpClient. |spring.cloud.gateway.httpclient.connect-timeout | | The connect timeout in millis, the default is 30s. @@ -113,7 +113,7 @@ |spring.cloud.gateway.httpclient.websocket.proxy-ping | `+++true+++` | Proxy ping frames to downstream services, defaults to true. |spring.cloud.gateway.httpclient.wiretap | `+++false+++` | Enables wiretap debugging for Netty HttpClient. |spring.cloud.gateway.httpserver.wiretap | `+++false+++` | Enables wiretap debugging for Netty HttpServer. -|spring.cloud.gateway.loadbalancer.use404 | `+++false+++` | +|spring.cloud.gateway.loadbalancer.use404 | `+++false+++` | |spring.cloud.gateway.metrics.enabled | `+++false+++` | Enables the collection of metrics data. |spring.cloud.gateway.metrics.prefix | `+++spring.cloud.gateway+++` | The prefix of all metrics emitted by gateway. |spring.cloud.gateway.metrics.tags | | Tags map that added to metrics. @@ -161,7 +161,7 @@ |spring.cloud.gateway.predicate.weight.enabled | `+++true+++` | Enables the weight predicate. |spring.cloud.gateway.predicate.xforwarded-remote-addr.enabled | `+++true+++` | Enables the xforwarded-remote-addr predicate. |spring.cloud.gateway.redis-rate-limiter.burst-capacity-header | `+++X-RateLimit-Burst-Capacity+++` | The name of the header that returns the burst capacity configuration. -|spring.cloud.gateway.redis-rate-limiter.config | | +|spring.cloud.gateway.redis-rate-limiter.config | | |spring.cloud.gateway.redis-rate-limiter.include-headers | `+++true+++` | Whether or not to include headers containing rate limiter information, defaults to true. |spring.cloud.gateway.redis-rate-limiter.remaining-header | `+++X-RateLimit-Remaining+++` | The name of the header that returns number of remaining requests during the current second. |spring.cloud.gateway.redis-rate-limiter.replenish-rate-header | `+++X-RateLimit-Replenish-Rate+++` | The name of the header that returns the replenish rate configuration. @@ -172,7 +172,7 @@ |spring.cloud.gateway.route-refresh-listener.enabled | `+++true+++` | If RouteRefreshListener should be turned on. |spring.cloud.gateway.routes | | List of Routes. |spring.cloud.gateway.set-status.original-status-header-name | | The name of the header which contains http code of the proxied request. -|spring.cloud.gateway.streaming-media-types | | +|spring.cloud.gateway.streaming-media-types | | |spring.cloud.gateway.x-forwarded.enabled | `+++true+++` | If the XForwardedHeadersFilter is enabled. |spring.cloud.gateway.x-forwarded.for-append | `+++true+++` | If appending X-Forwarded-For as a list is enabled. |spring.cloud.gateway.x-forwarded.for-enabled | `+++true+++` | If X-Forwarded-For is enabled. @@ -186,4 +186,4 @@ |spring.cloud.gateway.x-forwarded.proto-append | `+++true+++` | If appending X-Forwarded-Proto as a list is enabled. |spring.cloud.gateway.x-forwarded.proto-enabled | `+++true+++` | If X-Forwarded-Proto is enabled. -|=== \ No newline at end of file +|=== diff --git a/docs/pom.xml b/docs/pom.xml index b5f9c482..df489971 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1 + 4.2.1-SNAPSHOT spring-cloud-gateway-docs jar diff --git a/pom.xml b/pom.xml index 9506a997..87438173 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1 + 4.2.1-SNAPSHOT pom Spring Cloud Gateway @@ -15,7 +15,7 @@ org.springframework.cloud spring-cloud-build - 4.2.1 + 4.2.1-SNAPSHOT @@ -55,8 +55,8 @@ 1.0.8.RELEASE 17 2.3.0 - 3.2.1 - 4.2.1 + 3.2.1-SNAPSHOT + 4.2.1-SNAPSHOT diff --git a/spring-cloud-gateway-dependencies/pom.xml b/spring-cloud-gateway-dependencies/pom.xml index 12006ace..6d255037 100644 --- a/spring-cloud-gateway-dependencies/pom.xml +++ b/spring-cloud-gateway-dependencies/pom.xml @@ -6,12 +6,12 @@ spring-cloud-dependencies-parent org.springframework.cloud - 4.2.1 + 4.2.1-SNAPSHOT spring-cloud-gateway-dependencies - 4.2.1 + 4.2.1-SNAPSHOT pom spring-cloud-gateway-dependencies diff --git a/spring-cloud-gateway-integration-tests/grpc/pom.xml b/spring-cloud-gateway-integration-tests/grpc/pom.xml index 0c8a671c..606d6cf6 100644 --- a/spring-cloud-gateway-integration-tests/grpc/pom.xml +++ b/spring-cloud-gateway-integration-tests/grpc/pom.xml @@ -18,7 +18,7 @@ org.springframework.cloud spring-cloud-gateway-integration-tests - 4.2.1 + 4.2.1-SNAPSHOT .. diff --git a/spring-cloud-gateway-integration-tests/http2/pom.xml b/spring-cloud-gateway-integration-tests/http2/pom.xml index d62efe0d..f833c541 100644 --- a/spring-cloud-gateway-integration-tests/http2/pom.xml +++ b/spring-cloud-gateway-integration-tests/http2/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway-integration-tests - 4.2.1 + 4.2.1-SNAPSHOT .. diff --git a/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml b/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml index d8ccedaf..c79551c2 100644 --- a/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml +++ b/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway-integration-tests - 4.2.1 + 4.2.1-SNAPSHOT .. diff --git a/spring-cloud-gateway-integration-tests/pom.xml b/spring-cloud-gateway-integration-tests/pom.xml index 69787cdc..e9bf2a3c 100644 --- a/spring-cloud-gateway-integration-tests/pom.xml +++ b/spring-cloud-gateway-integration-tests/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1 + 4.2.1-SNAPSHOT .. diff --git a/spring-cloud-gateway-mvc/pom.xml b/spring-cloud-gateway-mvc/pom.xml index 603d949a..ed68626d 100644 --- a/spring-cloud-gateway-mvc/pom.xml +++ b/spring-cloud-gateway-mvc/pom.xml @@ -11,7 +11,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1 + 4.2.1-SNAPSHOT .. diff --git a/spring-cloud-gateway-sample/pom.xml b/spring-cloud-gateway-sample/pom.xml index 9ee987ab..41884109 100644 --- a/spring-cloud-gateway-sample/pom.xml +++ b/spring-cloud-gateway-sample/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1 + 4.2.1-SNAPSHOT .. diff --git a/spring-cloud-gateway-server-mvc/pom.xml b/spring-cloud-gateway-server-mvc/pom.xml index d2db5be5..ba17bdf7 100644 --- a/spring-cloud-gateway-server-mvc/pom.xml +++ b/spring-cloud-gateway-server-mvc/pom.xml @@ -22,7 +22,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1 + 4.2.1-SNAPSHOT .. spring-cloud-gateway-server-mvc diff --git a/spring-cloud-gateway-server/pom.xml b/spring-cloud-gateway-server/pom.xml index 12d05627..c365ed4b 100644 --- a/spring-cloud-gateway-server/pom.xml +++ b/spring-cloud-gateway-server/pom.xml @@ -7,7 +7,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1 + 4.2.1-SNAPSHOT .. spring-cloud-gateway-server 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 87c26cc6..30eb9a8b 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,15 +246,9 @@ 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\"}"); } } diff --git a/spring-cloud-gateway-webflux/pom.xml b/spring-cloud-gateway-webflux/pom.xml index 31371a1c..fe8b8ece 100644 --- a/spring-cloud-gateway-webflux/pom.xml +++ b/spring-cloud-gateway-webflux/pom.xml @@ -11,7 +11,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1 + 4.2.1-SNAPSHOT .. diff --git a/spring-cloud-starter-gateway-mvc/pom.xml b/spring-cloud-starter-gateway-mvc/pom.xml index 66d548ad..a2c8eba8 100644 --- a/spring-cloud-starter-gateway-mvc/pom.xml +++ b/spring-cloud-starter-gateway-mvc/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1 + 4.2.1-SNAPSHOT .. spring-cloud-starter-gateway-mvc diff --git a/spring-cloud-starter-gateway/pom.xml b/spring-cloud-starter-gateway/pom.xml index 5399cc25..f32a7980 100644 --- a/spring-cloud-starter-gateway/pom.xml +++ b/spring-cloud-starter-gateway/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1 + 4.2.1-SNAPSHOT .. spring-cloud-starter-gateway From 50bf1d72a0b87ab232749a13e3f5d7a51b68caee Mon Sep 17 00:00:00 2001 From: spring-builds Date: Tue, 18 Mar 2025 23:50:00 +0000 Subject: [PATCH 32/43] Bumping versions to 4.2.2-SNAPSHOT after release --- docs/pom.xml | 2 +- pom.xml | 2 +- spring-cloud-gateway-dependencies/pom.xml | 2 +- spring-cloud-gateway-integration-tests/grpc/pom.xml | 2 +- spring-cloud-gateway-integration-tests/http2/pom.xml | 2 +- .../mvc-failure-analyzer/pom.xml | 2 +- spring-cloud-gateway-integration-tests/pom.xml | 2 +- spring-cloud-gateway-mvc/pom.xml | 2 +- spring-cloud-gateway-sample/pom.xml | 2 +- spring-cloud-gateway-server-mvc/pom.xml | 2 +- spring-cloud-gateway-server/pom.xml | 2 +- spring-cloud-gateway-webflux/pom.xml | 2 +- spring-cloud-starter-gateway-mvc/pom.xml | 2 +- spring-cloud-starter-gateway/pom.xml | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index df489971..6819860b 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.2-SNAPSHOT spring-cloud-gateway-docs jar diff --git a/pom.xml b/pom.xml index 87438173..c25bbd63 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.2-SNAPSHOT pom Spring Cloud Gateway diff --git a/spring-cloud-gateway-dependencies/pom.xml b/spring-cloud-gateway-dependencies/pom.xml index 6d255037..6064d8b6 100644 --- a/spring-cloud-gateway-dependencies/pom.xml +++ b/spring-cloud-gateway-dependencies/pom.xml @@ -11,7 +11,7 @@ spring-cloud-gateway-dependencies - 4.2.1-SNAPSHOT + 4.2.2-SNAPSHOT pom spring-cloud-gateway-dependencies diff --git a/spring-cloud-gateway-integration-tests/grpc/pom.xml b/spring-cloud-gateway-integration-tests/grpc/pom.xml index 606d6cf6..7cad9cdc 100644 --- a/spring-cloud-gateway-integration-tests/grpc/pom.xml +++ b/spring-cloud-gateway-integration-tests/grpc/pom.xml @@ -18,7 +18,7 @@ org.springframework.cloud spring-cloud-gateway-integration-tests - 4.2.1-SNAPSHOT + 4.2.2-SNAPSHOT .. diff --git a/spring-cloud-gateway-integration-tests/http2/pom.xml b/spring-cloud-gateway-integration-tests/http2/pom.xml index f833c541..6e53c34a 100644 --- a/spring-cloud-gateway-integration-tests/http2/pom.xml +++ b/spring-cloud-gateway-integration-tests/http2/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway-integration-tests - 4.2.1-SNAPSHOT + 4.2.2-SNAPSHOT .. diff --git a/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml b/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml index c79551c2..05b46aa5 100644 --- a/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml +++ b/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway-integration-tests - 4.2.1-SNAPSHOT + 4.2.2-SNAPSHOT .. diff --git a/spring-cloud-gateway-integration-tests/pom.xml b/spring-cloud-gateway-integration-tests/pom.xml index e9bf2a3c..d8b10218 100644 --- a/spring-cloud-gateway-integration-tests/pom.xml +++ b/spring-cloud-gateway-integration-tests/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.2-SNAPSHOT .. diff --git a/spring-cloud-gateway-mvc/pom.xml b/spring-cloud-gateway-mvc/pom.xml index ed68626d..7a38b262 100644 --- a/spring-cloud-gateway-mvc/pom.xml +++ b/spring-cloud-gateway-mvc/pom.xml @@ -11,7 +11,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.2-SNAPSHOT .. diff --git a/spring-cloud-gateway-sample/pom.xml b/spring-cloud-gateway-sample/pom.xml index 41884109..135316d2 100644 --- a/spring-cloud-gateway-sample/pom.xml +++ b/spring-cloud-gateway-sample/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.2-SNAPSHOT .. diff --git a/spring-cloud-gateway-server-mvc/pom.xml b/spring-cloud-gateway-server-mvc/pom.xml index ba17bdf7..2c725c88 100644 --- a/spring-cloud-gateway-server-mvc/pom.xml +++ b/spring-cloud-gateway-server-mvc/pom.xml @@ -22,7 +22,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.2-SNAPSHOT .. spring-cloud-gateway-server-mvc diff --git a/spring-cloud-gateway-server/pom.xml b/spring-cloud-gateway-server/pom.xml index c365ed4b..8abe6b8e 100644 --- a/spring-cloud-gateway-server/pom.xml +++ b/spring-cloud-gateway-server/pom.xml @@ -7,7 +7,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.2-SNAPSHOT .. spring-cloud-gateway-server diff --git a/spring-cloud-gateway-webflux/pom.xml b/spring-cloud-gateway-webflux/pom.xml index fe8b8ece..901ed835 100644 --- a/spring-cloud-gateway-webflux/pom.xml +++ b/spring-cloud-gateway-webflux/pom.xml @@ -11,7 +11,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.2-SNAPSHOT .. diff --git a/spring-cloud-starter-gateway-mvc/pom.xml b/spring-cloud-starter-gateway-mvc/pom.xml index a2c8eba8..d5e924ae 100644 --- a/spring-cloud-starter-gateway-mvc/pom.xml +++ b/spring-cloud-starter-gateway-mvc/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.2-SNAPSHOT .. spring-cloud-starter-gateway-mvc diff --git a/spring-cloud-starter-gateway/pom.xml b/spring-cloud-starter-gateway/pom.xml index f32a7980..572ce693 100644 --- a/spring-cloud-starter-gateway/pom.xml +++ b/spring-cloud-starter-gateway/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.2.1-SNAPSHOT + 4.2.2-SNAPSHOT .. spring-cloud-starter-gateway From a5bd46a91dc798673c3a59fc9a7a53d6b3996d92 Mon Sep 17 00:00:00 2001 From: spring-builds Date: Tue, 18 Mar 2025 23:51:12 +0000 Subject: [PATCH 33/43] Bumping dependency versions after release --- pom.xml | 6 +++--- spring-cloud-gateway-dependencies/pom.xml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index c25bbd63..b493fc0f 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ org.springframework.cloud spring-cloud-build - 4.2.1-SNAPSHOT + 4.2.2-SNAPSHOT @@ -55,8 +55,8 @@ 1.0.8.RELEASE 17 2.3.0 - 3.2.1-SNAPSHOT - 4.2.1-SNAPSHOT + 3.2.2-SNAPSHOT + 4.2.2-SNAPSHOT diff --git a/spring-cloud-gateway-dependencies/pom.xml b/spring-cloud-gateway-dependencies/pom.xml index 6064d8b6..d1da19ac 100644 --- a/spring-cloud-gateway-dependencies/pom.xml +++ b/spring-cloud-gateway-dependencies/pom.xml @@ -6,7 +6,7 @@ spring-cloud-dependencies-parent org.springframework.cloud - 4.2.1-SNAPSHOT + 4.2.2-SNAPSHOT From 0c1b54926d41a0929a9a3ee877849d08ce3cccd9 Mon Sep 17 00:00:00 2001 From: spring-builds Date: Thu, 20 Mar 2025 09:57:45 +0000 Subject: [PATCH 34/43] Bumping versions --- docs/modules/ROOT/partials/_configprops.adoc | 42 +++++++++---------- ...CloudCircuitBreakerFilterFactoryTests.java | 12 ++++-- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/docs/modules/ROOT/partials/_configprops.adoc b/docs/modules/ROOT/partials/_configprops.adoc index 7a042b63..a8eed641 100644 --- a/docs/modules/ROOT/partials/_configprops.adoc +++ b/docs/modules/ROOT/partials/_configprops.adoc @@ -3,10 +3,10 @@ |spring.cloud.gateway.default-filters | | List of filter definitions that are applied to every route. |spring.cloud.gateway.discovery.locator.enabled | `+++false+++` | Flag that enables DiscoveryClient gateway integration. -|spring.cloud.gateway.discovery.locator.filters | | +|spring.cloud.gateway.discovery.locator.filters | | |spring.cloud.gateway.discovery.locator.include-expression | `+++true+++` | SpEL expression that will evaluate whether to include a service in gateway integration or not, defaults to: true. |spring.cloud.gateway.discovery.locator.lower-case-service-id | `+++false+++` | Option to lower case serviceId in predicates and filters, defaults to false. Useful with eureka when it automatically uppercases serviceId. so MYSERIVCE, would match /myservice/** -|spring.cloud.gateway.discovery.locator.predicates | | +|spring.cloud.gateway.discovery.locator.predicates | | |spring.cloud.gateway.discovery.locator.route-id-prefix | | The prefix for the routeId, defaults to discoveryClient.getClass().getSimpleName() + "_". Service Id will be appended to create the routeId. |spring.cloud.gateway.discovery.locator.url-expression | `+++'lb://'+serviceId+++` | SpEL expression that create the uri for each route, defaults to: 'lb://'+serviceId. |spring.cloud.gateway.enabled | `+++true+++` | Enables gateway functionality. @@ -20,7 +20,7 @@ |spring.cloud.gateway.filter.hystrix.enabled | `+++true+++` | Enables the hystrix filter. |spring.cloud.gateway.filter.json-to-grpc.enabled | `+++true+++` | Enables the JSON to gRPC filter. |spring.cloud.gateway.filter.local-response-cache.enabled | `+++false+++` | Enables the local-response-cache filter. -|spring.cloud.gateway.filter.local-response-cache.request.no-cache-strategy | `+++skip-update-cache-entry+++` | +|spring.cloud.gateway.filter.local-response-cache.request.no-cache-strategy | `+++skip-update-cache-entry+++` | |spring.cloud.gateway.filter.local-response-cache.size | | Maximum size of the cache to evict entries for this route (in KB, MB and GB). |spring.cloud.gateway.filter.local-response-cache.time-to-live | `+++5m+++` | Time to expire a cache entry (expressed in s for seconds, m for minutes, and h for hours). |spring.cloud.gateway.filter.map-request-header.enabled | `+++true+++` | Enables the map-request-header filter. @@ -29,15 +29,15 @@ |spring.cloud.gateway.filter.prefix-path.enabled | `+++true+++` | Enables the prefix-path filter. |spring.cloud.gateway.filter.preserve-host-header.enabled | `+++true+++` | Enables the preserve-host-header filter. |spring.cloud.gateway.filter.redirect-to.enabled | `+++true+++` | Enables the redirect-to filter. -|spring.cloud.gateway.filter.remove-hop-by-hop.headers | | -|spring.cloud.gateway.filter.remove-hop-by-hop.order | `+++0+++` | +|spring.cloud.gateway.filter.remove-hop-by-hop.headers | | +|spring.cloud.gateway.filter.remove-hop-by-hop.order | `+++0+++` | |spring.cloud.gateway.filter.remove-request-header.enabled | `+++true+++` | Enables the remove-request-header filter. |spring.cloud.gateway.filter.remove-request-parameter.enabled | `+++true+++` | Enables the remove-request-parameter filter. |spring.cloud.gateway.filter.remove-response-header.enabled | `+++true+++` | Enables the remove-response-header filter. |spring.cloud.gateway.filter.request-header-size.enabled | `+++true+++` | Enables the request-header-size filter. |spring.cloud.gateway.filter.request-header-to-request-uri.enabled | `+++true+++` | Enables the request-header-to-request-uri filter. -|spring.cloud.gateway.filter.request-rate-limiter.default-key-resolver | | -|spring.cloud.gateway.filter.request-rate-limiter.default-rate-limiter | | +|spring.cloud.gateway.filter.request-rate-limiter.default-key-resolver | | +|spring.cloud.gateway.filter.request-rate-limiter.default-rate-limiter | | |spring.cloud.gateway.filter.request-rate-limiter.enabled | `+++true+++` | Enables the request-rate-limiter filter. |spring.cloud.gateway.filter.request-size.enabled | `+++true+++` | Enables the request-size filter. |spring.cloud.gateway.filter.retry.enabled | `+++true+++` | Enables the retry filter. @@ -47,16 +47,16 @@ |spring.cloud.gateway.filter.rewrite-request-parameter.enabled | `+++true+++` | Enables the rewrite-request-parameter filter. |spring.cloud.gateway.filter.rewrite-response-header.enabled | `+++true+++` | Enables the rewrite-response-header filter. |spring.cloud.gateway.filter.save-session.enabled | `+++true+++` | Enables the save-session filter. -|spring.cloud.gateway.filter.secure-headers.content-security-policy | `+++default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'+++` | -|spring.cloud.gateway.filter.secure-headers.content-type-options | `+++nosniff+++` | -|spring.cloud.gateway.filter.secure-headers.disable | | -|spring.cloud.gateway.filter.secure-headers.download-options | `+++noopen+++` | +|spring.cloud.gateway.filter.secure-headers.content-security-policy | `+++default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'+++` | +|spring.cloud.gateway.filter.secure-headers.content-type-options | `+++nosniff+++` | +|spring.cloud.gateway.filter.secure-headers.disable | | +|spring.cloud.gateway.filter.secure-headers.download-options | `+++noopen+++` | |spring.cloud.gateway.filter.secure-headers.enabled | `+++true+++` | Enables the secure-headers filter. -|spring.cloud.gateway.filter.secure-headers.frame-options | `+++DENY+++` | -|spring.cloud.gateway.filter.secure-headers.permitted-cross-domain-policies | `+++none+++` | -|spring.cloud.gateway.filter.secure-headers.referrer-policy | `+++no-referrer+++` | -|spring.cloud.gateway.filter.secure-headers.strict-transport-security | `+++max-age=631138519+++` | -|spring.cloud.gateway.filter.secure-headers.xss-protection-header | `+++1 ; mode=block+++` | +|spring.cloud.gateway.filter.secure-headers.frame-options | `+++DENY+++` | +|spring.cloud.gateway.filter.secure-headers.permitted-cross-domain-policies | `+++none+++` | +|spring.cloud.gateway.filter.secure-headers.referrer-policy | `+++no-referrer+++` | +|spring.cloud.gateway.filter.secure-headers.strict-transport-security | `+++max-age=631138519+++` | +|spring.cloud.gateway.filter.secure-headers.xss-protection-header | `+++1 ; mode=block+++` | |spring.cloud.gateway.filter.set-path.enabled | `+++true+++` | Enables the set-path filter. |spring.cloud.gateway.filter.set-request-header.enabled | `+++true+++` | Enables the set-request-header filter. |spring.cloud.gateway.filter.set-request-host-header.enabled | `+++true+++` | Enables the set-request-host-header filter. @@ -76,7 +76,7 @@ |spring.cloud.gateway.global-filter.route-to-request-url.enabled | `+++true+++` | Enables the route-to-request-url global filter. |spring.cloud.gateway.global-filter.websocket-routing.enabled | `+++true+++` | Enables the websocket-routing global filter. |spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping | `+++false+++` | If global CORS config should be added to the URL handler. -|spring.cloud.gateway.globalcors.cors-configurations | | +|spring.cloud.gateway.globalcors.cors-configurations | | |spring.cloud.gateway.handler-mapping.order | `+++1+++` | The order of RoutePredicateHandlerMapping. |spring.cloud.gateway.httpclient.compression | `+++false+++` | Enables compression for Netty HttpClient. |spring.cloud.gateway.httpclient.connect-timeout | | The connect timeout in millis, the default is 30s. @@ -113,7 +113,7 @@ |spring.cloud.gateway.httpclient.websocket.proxy-ping | `+++true+++` | Proxy ping frames to downstream services, defaults to true. |spring.cloud.gateway.httpclient.wiretap | `+++false+++` | Enables wiretap debugging for Netty HttpClient. |spring.cloud.gateway.httpserver.wiretap | `+++false+++` | Enables wiretap debugging for Netty HttpServer. -|spring.cloud.gateway.loadbalancer.use404 | `+++false+++` | +|spring.cloud.gateway.loadbalancer.use404 | `+++false+++` | |spring.cloud.gateway.metrics.enabled | `+++false+++` | Enables the collection of metrics data. |spring.cloud.gateway.metrics.prefix | `+++spring.cloud.gateway+++` | The prefix of all metrics emitted by gateway. |spring.cloud.gateway.metrics.tags | | Tags map that added to metrics. @@ -161,7 +161,7 @@ |spring.cloud.gateway.predicate.weight.enabled | `+++true+++` | Enables the weight predicate. |spring.cloud.gateway.predicate.xforwarded-remote-addr.enabled | `+++true+++` | Enables the xforwarded-remote-addr predicate. |spring.cloud.gateway.redis-rate-limiter.burst-capacity-header | `+++X-RateLimit-Burst-Capacity+++` | The name of the header that returns the burst capacity configuration. -|spring.cloud.gateway.redis-rate-limiter.config | | +|spring.cloud.gateway.redis-rate-limiter.config | | |spring.cloud.gateway.redis-rate-limiter.include-headers | `+++true+++` | Whether or not to include headers containing rate limiter information, defaults to true. |spring.cloud.gateway.redis-rate-limiter.remaining-header | `+++X-RateLimit-Remaining+++` | The name of the header that returns number of remaining requests during the current second. |spring.cloud.gateway.redis-rate-limiter.replenish-rate-header | `+++X-RateLimit-Replenish-Rate+++` | The name of the header that returns the replenish rate configuration. @@ -172,7 +172,7 @@ |spring.cloud.gateway.route-refresh-listener.enabled | `+++true+++` | If RouteRefreshListener should be turned on. |spring.cloud.gateway.routes | | List of Routes. |spring.cloud.gateway.set-status.original-status-header-name | | The name of the header which contains http code of the proxied request. -|spring.cloud.gateway.streaming-media-types | | +|spring.cloud.gateway.streaming-media-types | | |spring.cloud.gateway.x-forwarded.enabled | `+++true+++` | If the XForwardedHeadersFilter is enabled. |spring.cloud.gateway.x-forwarded.for-append | `+++true+++` | If appending X-Forwarded-For as a list is enabled. |spring.cloud.gateway.x-forwarded.for-enabled | `+++true+++` | If X-Forwarded-For is enabled. @@ -186,4 +186,4 @@ |spring.cloud.gateway.x-forwarded.proto-append | `+++true+++` | If appending X-Forwarded-Proto as a list is enabled. |spring.cloud.gateway.x-forwarded.proto-enabled | `+++true+++` | If X-Forwarded-Proto is enabled. -|=== +|=== \ No newline at end of file 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\"}"); } } From 8553c04a10b683e3971f63f36368d08fe8a2d825 Mon Sep 17 00:00:00 2001 From: spring-builds Date: Thu, 20 Mar 2025 18:53:32 +0000 Subject: [PATCH 35/43] Bumping versions --- .../server/mvc/filter/BeforeFilterFunctions.java | 6 +++++- .../mvc/filter/BeforeFilterFunctionsTests.java | 15 ++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) 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 2941213c..eb860a30 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 @@ -332,7 +332,11 @@ public abstract class BeforeFilterFunctions { String path = request.uri().getPath(); String newPath = pattern.matcher(path).replaceAll(normalizedReplacement); - URI rewrittenUri = UriComponentsBuilder.fromUri(request.uri()).replacePath(newPath).encode().build().toUri(); + URI rewrittenUri = UriComponentsBuilder.fromUri(request.uri()) + .replacePath(newPath) + .encode() + .build() + .toUri(); ServerRequest modified = ServerRequest.from(request).uri(rewrittenUri).build(); diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java index 6079199e..f6ba296e 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java @@ -186,8 +186,7 @@ class BeforeFilterFunctionsTests { @Test void rewritePath() { - MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get") - .buildRequest(null); + MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get").buildRequest(null); ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList()); @@ -199,7 +198,7 @@ class BeforeFilterFunctionsTests { @Test void rewritePathWithSpace() { MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get/path/with spaces") - .buildRequest(null); + .buildRequest(null); ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList()); @@ -211,7 +210,7 @@ class BeforeFilterFunctionsTests { @Test void rewritePathWithEnDash() { MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get/path/with–en–dashes") - .buildRequest(null); + .buildRequest(null); ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList()); @@ -222,14 +221,16 @@ class BeforeFilterFunctionsTests { @Test void rewritePathWithEnDashAndSpace() { - MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get/path/with–en–dashes and spaces") - .buildRequest(null); + MockHttpServletRequest servletRequest = MockMvcRequestBuilders + .get("http://localhost/get/path/with–en–dashes and spaces") + .buildRequest(null); ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList()); ServerRequest modified = BeforeFilterFunctions.rewritePath("get", "modified").apply(request); - assertThat(modified.uri().getRawPath()).isEqualTo("/modified/path/with%E2%80%93en%E2%80%93dashes%20and%20spaces"); + assertThat(modified.uri().getRawPath()) + .isEqualTo("/modified/path/with%E2%80%93en%E2%80%93dashes%20and%20spaces"); } } From 33c99530bf609a12d080d053c68fff3d58b4bea0 Mon Sep 17 00:00:00 2001 From: spring-builds Date: Fri, 21 Mar 2025 09:55:27 +0000 Subject: [PATCH 36/43] Bumping versions --- .../server/mvc/filter/BeforeFilterFunctions.java | 6 +++++- .../mvc/filter/BeforeFilterFunctionsTests.java | 15 ++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) 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 c96fffdd..dba4e260 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 @@ -332,7 +332,11 @@ public abstract class BeforeFilterFunctions { String path = request.uri().getPath(); String newPath = pattern.matcher(path).replaceAll(normalizedReplacement); - URI rewrittenUri = UriComponentsBuilder.fromUri(request.uri()).replacePath(newPath).encode().build().toUri(); + URI rewrittenUri = UriComponentsBuilder.fromUri(request.uri()) + .replacePath(newPath) + .encode() + .build() + .toUri(); ServerRequest modified = ServerRequest.from(request).uri(rewrittenUri).build(); diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java index 309b81bf..ae6a7ee2 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java @@ -184,8 +184,7 @@ class BeforeFilterFunctionsTests { @Test void rewritePath() { - MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get") - .buildRequest(null); + MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get").buildRequest(null); ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList()); @@ -197,7 +196,7 @@ class BeforeFilterFunctionsTests { @Test void rewritePathWithSpace() { MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get/path/with spaces") - .buildRequest(null); + .buildRequest(null); ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList()); @@ -209,7 +208,7 @@ class BeforeFilterFunctionsTests { @Test void rewritePathWithEnDash() { MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get/path/with–en–dashes") - .buildRequest(null); + .buildRequest(null); ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList()); @@ -220,14 +219,16 @@ class BeforeFilterFunctionsTests { @Test void rewritePathWithEnDashAndSpace() { - MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get/path/with–en–dashes and spaces") - .buildRequest(null); + MockHttpServletRequest servletRequest = MockMvcRequestBuilders + .get("http://localhost/get/path/with–en–dashes and spaces") + .buildRequest(null); ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList()); ServerRequest modified = BeforeFilterFunctions.rewritePath("get", "modified").apply(request); - assertThat(modified.uri().getRawPath()).isEqualTo("/modified/path/with%E2%80%93en%E2%80%93dashes%20and%20spaces"); + assertThat(modified.uri().getRawPath()) + .isEqualTo("/modified/path/with%E2%80%93en%E2%80%93dashes%20and%20spaces"); } } From 51fb9aa36c9e58eb589154b1fb9f5af6ed50cfa2 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Fri, 21 Mar 2025 17:17:15 +0100 Subject: [PATCH 37/43] Add support for default routing functionality to functions in server webmvc (#3716) * Initial functionality for default function routing Signed-off-by: Oleg Zhurakousky * Add ability to default to s-c-function's RoutingFunction if target function can't be found Signed-off-by: Oleg Zhurakousky * Fix tests Added to teh tests that do not rely on spring-cloud-function Signed-off-by: Oleg Zhurakousky --------- Signed-off-by: Oleg Zhurakousky --- .../config/DefaultFunctionConfiguration.java | 46 +++++++ ...unctionHandlerRequestProcessingHelper.java | 38 +++--- .../server/mvc/handler/HandlerFunctions.java | 50 ++++++- ...ot.autoconfigure.AutoConfiguration.imports | 3 +- .../server/mvc/ServerMvcIntegrationTests.java | 3 +- ...ropertiesBeanDefinitionRegistrarTests.java | 3 +- .../mvc/filter/RetryFilterFunctionTests.java | 4 +- .../DefaultRouteFunctionHandlerTests.java | 124 ++++++++++++++++++ .../ratelimit/RedisRateLimiterTests.java | 2 +- 9 files changed, 240 insertions(+), 33 deletions(-) create mode 100644 spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/DefaultFunctionConfiguration.java create mode 100644 spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/handler/DefaultRouteFunctionHandlerTests.java diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/DefaultFunctionConfiguration.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/DefaultFunctionConfiguration.java new file mode 100644 index 00000000..c90e3e58 --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/DefaultFunctionConfiguration.java @@ -0,0 +1,46 @@ +/* + * Copyright 2013-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.gateway.server.mvc.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.function.RouterFunction; +import org.springframework.web.servlet.function.ServerResponse; + +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.fn; + +@Configuration +@ConditionalOnClass(name = "org.springframework.cloud.function.context.FunctionCatalog") +@ConditionalOnProperty(name = "spring.cloud.gateway.function.enabled", havingValue = "true", matchIfMissing = true) +public class DefaultFunctionConfiguration { + + @Bean + RouterFunction gatewayToFunctionRouter() { + // @formatter:off + return route("functionroute") + .POST("/{path}/{name}", fn("{path}/{name}")) + .POST("/{path}", fn("{path}")) + .GET("/{path}/{name}", fn("{path}/{name}")) + .GET("/{path}", fn("{path}")) + .build(); + // @formatter:on + } + +} diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/FunctionHandlerRequestProcessingHelper.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/FunctionHandlerRequestProcessingHelper.java index 1990991b..fd4f158a 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/FunctionHandlerRequestProcessingHelper.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/FunctionHandlerRequestProcessingHelper.java @@ -17,6 +17,7 @@ package org.springframework.cloud.gateway.server.mvc.handler; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import java.util.stream.StreamSupport; @@ -50,9 +51,16 @@ final class FunctionHandlerRequestProcessingHelper { } - @SuppressWarnings({ "rawtypes", "unchecked" }) static ServerResponse processRequest(ServerRequest request, FunctionInvocationWrapper function, Object argument, boolean eventStream, List ignoredHeaders, List requestOnlyHeaders) { + return processRequest(request, function, argument, eventStream, ignoredHeaders, requestOnlyHeaders, null); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + static ServerResponse processRequest(ServerRequest request, FunctionInvocationWrapper function, Object argument, + boolean eventStream, List ignoredHeaders, List requestOnlyHeaders, + Map additionalHeaders) { + if (argument == null) { argument = ""; } @@ -70,42 +78,29 @@ final class FunctionHandlerRequestProcessingHelper { builder = builder.setHeader(FunctionHandlerHeaderUtils.HTTP_REQUEST_PARAM, request.params().toSingleValueMap()); } + + if (!CollectionUtils.isEmpty(additionalHeaders)) { + builder.copyHeaders(additionalHeaders); + } inputMessage = builder.copyHeaders(headers.toSingleValueMap()).build(); if (function.isRoutingFunction()) { function.setSkipOutputConversion(true); } + if (logger.isDebugEnabled()) { + logger.debug("Sending request to " + function + " with argument: " + inputMessage); + } Object result = function.apply(inputMessage); if (function.isConsumer()) { - /* - * if (result instanceof Publisher) { Mono.from((Publisher) - * result).subscribe(); } - */ return HttpMethod.DELETE.equals(request.method()) ? ServerResponse.ok().build() : ServerResponse.accepted() .headers(h -> h.addAll(sanitize(headers, ignoredHeaders, requestOnlyHeaders))) .build(); - // Mono.empty() : - // Mono.just(ResponseEntity.accepted().headers(FunctionHandlerHeaderUtils.sanitize(headers, - // ignoredHeaders, requestOnlyHeaders)).build()); } BodyBuilder responseOkBuilder = ServerResponse.ok() .headers(h -> h.addAll(sanitize(headers, ignoredHeaders, requestOnlyHeaders))); - - // FIXME: Mono/Flux - /* - * Publisher pResult; if (result instanceof Publisher) { pResult = (Publisher) - * result; if (eventStream) { return Flux.from(pResult); } - * - * if (pResult instanceof Flux) { pResult = ((Flux) pResult).onErrorContinue((e, - * v) -> { logger.error("Failed to process value: " + v, (Throwable) e); - * }).collectList(); } pResult = Mono.from(pResult); } else { pResult = - * Mono.just(result); } - */ - - // return Mono.from(pResult).map(v -> { if (result instanceof Iterable i) { List aggregatedResult = (List) StreamSupport.stream(i.spliterator(), false).map(m -> { return m instanceof Message ? processMessage(responseOkBuilder, (Message) m, ignoredHeaders) : m; @@ -118,7 +113,6 @@ final class FunctionHandlerRequestProcessingHelper { else { return responseOkBuilder.body(result); } - // }); } private static Object processMessage(BodyBuilder responseOkBuilder, Message message, diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerFunctions.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerFunctions.java index 04cfc1a0..640ef7f4 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerFunctions.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerFunctions.java @@ -22,12 +22,19 @@ import java.net.URI; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import jakarta.servlet.ServletException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.cloud.function.context.FunctionCatalog; +import org.springframework.cloud.function.context.FunctionProperties; import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; +import org.springframework.cloud.function.context.config.RoutingFunction; +import org.springframework.cloud.gateway.server.mvc.GatewayMvcClassPathWarningAutoConfiguration; import org.springframework.cloud.gateway.server.mvc.common.MvcUtils; import org.springframework.cloud.gateway.server.mvc.config.RouteProperties; import org.springframework.cloud.stream.function.StreamOperations; @@ -43,6 +50,8 @@ import static org.springframework.cloud.gateway.server.mvc.handler.FunctionHandl public abstract class HandlerFunctions { + private static final Log log = LogFactory.getLog(GatewayMvcClassPathWarningAutoConfiguration.class); + private HandlerFunctions() { } @@ -56,13 +65,44 @@ public abstract class HandlerFunctions { public static HandlerFunction fn(String functionName) { Assert.hasText(functionName, "'functionName' must not be empty"); return request -> { - String expandedFunctionName = MvcUtils.expand(request, functionName); FunctionCatalog functionCatalog = MvcUtils.getApplicationContext(request).getBean(FunctionCatalog.class); - FunctionInvocationWrapper function = functionCatalog.lookup(expandedFunctionName, - request.headers().accept().stream().map(MimeType::toString).toArray(String[]::new)); + String expandedFunctionName = MvcUtils.expand(request, functionName); + FunctionInvocationWrapper function; + Object body = null; + if (expandedFunctionName.contains("/")) { + String[] functionBodySplit = expandedFunctionName.split("/"); + function = functionCatalog.lookup(functionBodySplit[0], + request.headers().accept().stream().map(MimeType::toString).toArray(String[]::new)); + if (function != null && function.isSupplier()) { + log.warn("Supplier must not have any arguments. Supplier: '" + function.getFunctionDefinition() + + "' has '" + functionBodySplit[1] + "' as an argument which is ignored."); + } + body = functionBodySplit[1]; + } + else { + function = functionCatalog.lookup(expandedFunctionName, + request.headers().accept().stream().map(MimeType::toString).toArray(String[]::new)); + } + + /* + * If function can not be found in the current runtime, we will default to + * RoutingFunction which has additional logic to determine the function to + * invoke. + */ + Map additionalRequestHeaders = new HashMap<>(); + if (function == null) { + additionalRequestHeaders.put(FunctionProperties.FUNCTION_DEFINITION, expandedFunctionName); + + function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME, + request.headers().accept().stream().map(MimeType::toString).toArray(String[]::new)); + } + if (function != null) { - Object body = function.isSupplier() ? null : request.body(function.getRawInputType()); - return processRequest(request, function, body, false, Collections.emptyList(), Collections.emptyList()); + if (body == null) { + body = function.isSupplier() ? null : request.body(function.getRawInputType()); + } + return processRequest(request, function, body, false, Collections.emptyList(), Collections.emptyList(), + additionalRequestHeaders); } return ServerResponse.notFound().build(); }; diff --git a/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 0b209bc3..94282943 100644 --- a/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,4 +1,5 @@ org.springframework.cloud.gateway.server.mvc.GatewayServerMvcAutoConfiguration org.springframework.cloud.gateway.server.mvc.GatewayMvcClassPathWarningAutoConfiguration org.springframework.cloud.gateway.server.mvc.handler.GatewayMultipartAutoConfiguration -org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration \ No newline at end of file +org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration +org.springframework.cloud.gateway.server.mvc.config.DefaultFunctionConfiguration \ No newline at end of file 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 38bfa681..6d2a51a9 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 @@ -140,7 +140,8 @@ import static org.springframework.web.servlet.function.RequestPredicates.POST; import static org.springframework.web.servlet.function.RequestPredicates.path; @SuppressWarnings("unchecked") -@SpringBootTest(properties = { "spring.cloud.gateway.mvc.http-client.type=jdk" }, +@SpringBootTest( + properties = { "spring.cloud.gateway.mvc.http-client.type=jdk", "spring.cloud.gateway.function.enabled=false" }, webEnvironment = WebEnvironment.RANDOM_PORT) @ContextConfiguration(initializers = HttpbinTestcontainers.class) @ExtendWith(OutputCaptureExtension.class) 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 861b1694..41f6bff8 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 @@ -53,7 +53,8 @@ import org.springframework.web.servlet.function.ServerRequest; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.cloud.gateway.server.mvc.test.TestUtils.getMap; -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, + properties = { "spring.cloud.gateway.function.enabled=false" }) @ActiveProfiles("propertiesbeandefinitionregistrartests") @ContextConfiguration(initializers = HttpbinTestcontainers.class) public class GatewayMvcPropertiesBeanDefinitionRegistrarTests { diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/RetryFilterFunctionTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/RetryFilterFunctionTests.java index edae11e4..d93dfc57 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/RetryFilterFunctionTests.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/RetryFilterFunctionTests.java @@ -56,8 +56,8 @@ import static org.springframework.cloud.gateway.server.mvc.filter.RetryFilterFun import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; -@SuppressWarnings("unchecked") -@SpringBootTest(properties = {}, webEnvironment = WebEnvironment.RANDOM_PORT) +@SpringBootTest(properties = { "spring.cloud.gateway.function.enabled=false" }, + webEnvironment = WebEnvironment.RANDOM_PORT) @ContextConfiguration(initializers = HttpbinTestcontainers.class) public class RetryFilterFunctionTests { diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/handler/DefaultRouteFunctionHandlerTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/handler/DefaultRouteFunctionHandlerTests.java new file mode 100644 index 00000000..14f7c956 --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/handler/DefaultRouteFunctionHandlerTests.java @@ -0,0 +1,124 @@ +/* + * Copyright 2013-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.gateway.server.mvc.handler; + +import java.util.Locale; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.server.mvc.test.client.TestRestClient; +import org.springframework.context.annotation.Bean; +import org.springframework.http.MediaType; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(properties = {}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class DefaultRouteFunctionHandlerTests { + + @Autowired + private TestRestClient restClient; + + @Test + public void testSupplierWorks() { + restClient.get() + .uri("/hello") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo("hello"); + } + + @Test + public void testFunctionWorksGET() { + restClient.get() + .uri("/upper/bob") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo("BOB"); + } + + @Test + public void testFunctionWorksPOST() { + restClient.post() + .uri("/upper") + .accept(MediaType.APPLICATION_JSON) + .bodyValue("bob") + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo("BOB"); + } + + @Test + public void testConsumerWorksGET() { + restClient.get().uri("/consume/hello").accept(MediaType.TEXT_PLAIN).exchange().expectStatus().isAccepted(); + assertThat(TestConfiguration.consumerInvoked).isTrue(); + } + + @Test + public void testConsumerWorksPOST() { + restClient.post() + .uri("/consume") + .accept(MediaType.APPLICATION_JSON) + .bodyValue("hello") + .exchange() + .expectStatus() + .isAccepted(); + assertThat(TestConfiguration.consumerInvoked).isTrue(); + } + + @SpringBootConfiguration + @EnableAutoConfiguration + protected static class TestConfiguration { + + static boolean consumerInvoked; + + @Bean + Function upper() { + return s -> s.toUpperCase(Locale.ROOT); + } + + @Bean + Consumer consume() { + return s -> { + consumerInvoked = false; + assertThat(s).isEqualTo("hello"); + consumerInvoked = true; + }; + } + + @Bean + Supplier hello() { + return () -> "hello"; + } + + } + +} diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterTests.java index 9181ce03..2be6617a 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/RedisRateLimiterTests.java @@ -54,7 +54,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen * @author Denis Cutic * @author Andrey Muchnik */ -@SpringBootTest(webEnvironment = RANDOM_PORT) +@SpringBootTest(webEnvironment = RANDOM_PORT, properties = { "spring.cloud.gateway.function.enabled=false" }) @DirtiesContext @Testcontainers @Tag("DockerRequired") From b382bb6cf09a4e9617d93dbfc50b5366c8b3869f Mon Sep 17 00:00:00 2001 From: spring-builds Date: Sat, 22 Mar 2025 13:26:55 +0000 Subject: [PATCH 38/43] Bumping versions --- .../server/mvc/filter/BeforeFilterFunctions.java | 6 +++++- .../mvc/filter/BeforeFilterFunctionsTests.java | 15 ++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) 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 c96fffdd..dba4e260 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 @@ -332,7 +332,11 @@ public abstract class BeforeFilterFunctions { String path = request.uri().getPath(); String newPath = pattern.matcher(path).replaceAll(normalizedReplacement); - URI rewrittenUri = UriComponentsBuilder.fromUri(request.uri()).replacePath(newPath).encode().build().toUri(); + URI rewrittenUri = UriComponentsBuilder.fromUri(request.uri()) + .replacePath(newPath) + .encode() + .build() + .toUri(); ServerRequest modified = ServerRequest.from(request).uri(rewrittenUri).build(); diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java index 309b81bf..ae6a7ee2 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java @@ -184,8 +184,7 @@ class BeforeFilterFunctionsTests { @Test void rewritePath() { - MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get") - .buildRequest(null); + MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get").buildRequest(null); ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList()); @@ -197,7 +196,7 @@ class BeforeFilterFunctionsTests { @Test void rewritePathWithSpace() { MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get/path/with spaces") - .buildRequest(null); + .buildRequest(null); ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList()); @@ -209,7 +208,7 @@ class BeforeFilterFunctionsTests { @Test void rewritePathWithEnDash() { MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get/path/with–en–dashes") - .buildRequest(null); + .buildRequest(null); ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList()); @@ -220,14 +219,16 @@ class BeforeFilterFunctionsTests { @Test void rewritePathWithEnDashAndSpace() { - MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/get/path/with–en–dashes and spaces") - .buildRequest(null); + MockHttpServletRequest servletRequest = MockMvcRequestBuilders + .get("http://localhost/get/path/with–en–dashes and spaces") + .buildRequest(null); ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList()); ServerRequest modified = BeforeFilterFunctions.rewritePath("get", "modified").apply(request); - assertThat(modified.uri().getRawPath()).isEqualTo("/modified/path/with%E2%80%93en%E2%80%93dashes%20and%20spaces"); + assertThat(modified.uri().getRawPath()) + .isEqualTo("/modified/path/with%E2%80%93en%E2%80%93dashes%20and%20spaces"); } } From 55b3cd0c51b907430f830c1b7a1ed2b1a827d4a6 Mon Sep 17 00:00:00 2001 From: Spencer Gibb Date: Fri, 28 Mar 2025 14:59:19 -0400 Subject: [PATCH 39/43] Fixes routing issue with path filters and static uri (#3747) * Fixes routing issue with path filters and static uri The problem arises from when MvcUtils.setRequestUrl is called depending on if the route was created via config, using `http(uri)` or having it set in a before filter. To fix this, the uri is now always set in a before filter and the `http(uri)` methods are deprecated in favor of a BeforeFilterFunctions.uri() method. A new DefaultHandlerSupplier was created to use an updated HandlerDiscoverer.Result that has fields for lowerPrecedenceFilters and higherPrecedenceFilters as the `http()` family of Handler methods need associated filters. This results in a more consistent handling of the request url attribute. Existing methods and constructors are deprecated. Fixes gh-3736 --- .../filters/addrequestheader.adoc | 20 ++- .../addrequestheadersifnotpresent.adoc | 28 +++- .../filters/addrequestparameter.adoc | 26 ++-- .../filters/addresponseheader.adoc | 20 ++- .../filters/circuitbreaker-filter.adoc | 32 ++-- .../filters/deduperesponseheader.adoc | 7 +- .../filters/fallback-headers.adoc | 8 +- .../filters/maprequestheader.adoc | 8 +- .../filters/modifyrequestbody.adoc | 14 +- .../filters/modifyresponsebody.adoc | 25 +--- .../filters/prefixpath.adoc | 12 +- .../filters/preservehostheader.adoc | 12 +- .../filters/ratelimiter.adoc | 14 +- .../filters/redirectto.adoc | 12 +- .../filters/removerequestheader.adoc | 14 +- .../filters/removerequestparameter.adoc | 16 +- .../filters/removeresponseheader.adoc | 10 +- .../filters/requestheadersize.adoc | 12 +- .../filters/requestsize.adoc | 10 +- .../filters/retry.adoc | 23 +-- .../rewritelocationresponseheader.adoc | 13 +- .../filters/rewritepath.adoc | 10 +- .../filters/rewriterequestparameter.adoc | 12 +- .../filters/rewriteresponseheader.adoc | 12 +- .../filters/setpath.adoc | 10 +- .../filters/setrequestheader.adoc | 24 +-- .../filters/setrequesthostheader.adoc | 10 +- .../filters/setresponseheader.adoc | 20 ++- .../filters/setstatus.adoc | 13 +- .../filters/stripprefix.adoc | 16 +- .../filters/tokenrelay.adoc | 12 +- .../gateway-request-predicates.adoc | 121 ++++++++------- .../java-routes-api.adoc | 14 +- ...writing-custom-predicates-and-filters.adoc | 35 +++-- .../config/RouterFunctionHolderFactory.java | 21 +-- .../mvc/filter/BeforeFilterFunctions.java | 15 +- .../server/mvc/filter/FilterFunctions.java | 9 ++ .../filter/LoadBalancerHandlerSupplier.java | 2 +- .../XForwardedRequestHeadersFilter.java | 2 +- .../mvc/handler/DefaultHandlerSupplier.java | 92 ++++++++++++ .../server/mvc/handler/HandlerDiscoverer.java | 25 +++- .../server/mvc/handler/HandlerFunctions.java | 23 ++- .../main/resources/META-INF/spring.factories | 2 +- .../server/mvc/ServerMvcIntegrationTests.java | 30 +++- .../filter/BeforeFilterFunctionsTests.java | 12 ++ .../filter/StripPrefixStaticPortTests.java | 141 ++++++++++++++++++ .../mvc/test/HttpbinTestcontainers.java | 5 - .../application-stripprefixstaticport.yml | 12 ++ 48 files changed, 754 insertions(+), 282 deletions(-) create mode 100644 spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/DefaultHandlerSupplier.java create mode 100644 spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/StripPrefixStaticPortTests.java create mode 100644 spring-cloud-gateway-server-mvc/src/test/resources/application-stripprefixstaticport.yml diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestheader.adoc index d189f531..0b0921a1 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestheader.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestheader.adoc @@ -14,6 +14,8 @@ spring: routes: - id: add_request_header_route uri: https://example.org + predicates: + - Path=/red filters: - AddRequestHeader=X-Request-red, blue ---- @@ -21,6 +23,7 @@ spring: .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestHeader; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -30,8 +33,10 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsAddReqHeader() { - return route(GET("/red"), http("https://example.org")) - .before(addRequestHeader("X-Request-red", "blue")); + return route("add_request_header_route") + .GET("/red", http()) + .before(uri("https://example.org")) + .before(addRequestHeader("X-Request-red", "blue")); } } ---- @@ -45,13 +50,20 @@ The following example configures an `AddRequestHeader` filter that uses a variab .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestHeader; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + @Configuration class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsAddReqHeader() { - return route(GET("/red/{segment}"), http("https://example.org")) - .before(addRequestHeader("X-Request-red", "blue-{segment}")); + return route("add_request_header_route") + .GET("/red/{segment}", http()) + .before(uri("https://example.org")) + .before(addRequestHeader("X-Request-red", "blue-{segment}")); } } ---- diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestheadersifnotpresent.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestheadersifnotpresent.adoc index b4455a6b..fda40278 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestheadersifnotpresent.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestheadersifnotpresent.adoc @@ -12,8 +12,10 @@ spring: gateway: mvc: routes: - - id: add_request_headers_route + - id: add_request_headers_route_inp uri: https://example.org + predicates: + - Path=/red filters: - AddRequestHeadersIfNotPresent=X-Request-Color-1:blue,X-Request-Color-2:green ---- @@ -21,6 +23,7 @@ spring: .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestHeadersIfNotPresent; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -29,9 +32,11 @@ import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFuncti class RouteConfiguration { @Bean - public RouterFunction gatewayRouterFunctionsAddReqHeader() { - return route(GET("/red"), http("https://example.org")) - .before(addRequestHeadersIfNotPresent("X-Request-Color-1:blue","X-Request-Color-2:green")); + public RouterFunction gatewayRouterFunctionsAddReqHeaderInp() { + return route("add_request_headers_route_inp") + .GET("/red", http()) + .before(uri("https://example.org")) + .before(addRequestHeadersIfNotPresent("X-Request-Color-1:blue","X-Request-Color-2:green")); } } ---- @@ -49,13 +54,20 @@ The following example configures an `AddRequestHeadersIfNotPresent` filter that .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestHeadersIfNotPresent; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + @Configuration class RouteConfiguration { @Bean - public RouterFunction gatewayRouterFunctionsAddReqHeader() { - return route(GET("/red/{segment}"), http("https://example.org")) - .before(addRequestHeadersIfNotPresent("X-Request-red", "blue-{segment}")); + public RouterFunction gatewayRouterFunctionsAddReqHeaderInp() { + return route("add_request_header_route_inp") + .GET("/red/{segment}", http()) + .before(uri("https://example.org")) + .before(addRequestHeadersIfNotPresent("X-Request-red", "blue-{segment}")); } } ---- @@ -66,7 +78,7 @@ spring: cloud: gateway: routes: - - id: add_request_header_route + - id: add_request_header_route_inp uri: https://example.org predicates: - Path=/red/{segment} diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestparameter.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestparameter.adoc index 6ce332b4..53d306ad 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestparameter.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addrequestparameter.adoc @@ -14,6 +14,8 @@ spring: routes: - id: add_request_parameter_route uri: https://example.org + predicates: + - Path=/anything/addrequestparam filters: - AddRequestParameter=red, blue ---- @@ -21,6 +23,7 @@ spring: .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestParameter; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -29,11 +32,12 @@ import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFuncti class RouteConfiguration { @Bean - public RouterFunction gatewayRouterFunctionsAddReqHeader() { - return route("add_request_parameter_route") - .GET("/anything/addrequestparam", http("https://example.org")) - .before(addRequestParameter("red", "blue")) - .build(); + public RouterFunction gatewayRouterFunctionsAddReqParameter() { + return route("add_request_parameter_route") + .GET("/anything/addrequestparam", http()) + .before(uri("https://example.org")) + .before(addRequestParameter("red", "blue")) + .build(); } } ---- @@ -47,6 +51,7 @@ The following example configures an `AddRequestParameter` filter that uses a var .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestParameter; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -56,11 +61,12 @@ import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequ class RouteConfiguration { @Bean - public RouterFunction gatewayRouterFunctionsAddReqHeader() { - return route("add_request_parameter_route") - .route(host("{segment}.myhost.org"), http("https://example.org")) - .before(addRequestParameter("foo", "bar-{segment}")) - .build(); + public RouterFunction gatewayRouterFunctionsAddReqParameter() { + return route("add_request_parameter_route") + .route(host("{segment}.myhost.org"), http()) + .before(uri("https://example.org")) + .before(addRequestParameter("foo", "bar-{segment}")) + .build(); } } ---- \ No newline at end of file diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addresponseheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addresponseheader.adoc index e04ed408..0c678abd 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addresponseheader.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/addresponseheader.adoc @@ -14,6 +14,8 @@ spring: routes: - id: add_response_header_route uri: https://example.org + predicates: + - Path=/anything/addresheader filters: - AddResponseHeader=X-Response-Red, Blue ---- @@ -21,6 +23,7 @@ spring: .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.addResponseHeader; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -30,9 +33,10 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsAddRespHeader() { - return route("addresponseheader") - .GET("/anything/addresheader", http("https://example.org")) - .after(addResponseHeader("X-Response-Red", "Blue")) + return route("add_response_header_route") + .GET("/anything/addresheader", http()) + .before(uri("https://example.org")) + .after(addResponseHeader("X-Response-Red", "Blue")) .build(); } } @@ -47,6 +51,7 @@ The following example configures an `AddResponseHeader` filter that uses a varia .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.addResponseHeader; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -57,10 +62,11 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsAddRespHeader() { - return route("add_response_header_route") - .route(host("{segment}.myhost.org"), http("https://example.org")) - .after(addResponseHeader("foo", "bar-{segment}")) - .build(); + return route("add_response_header_route") + .route(host("{segment}.myhost.org"), http()) + .before(uri("https://example.org")) + .after(addResponseHeader("foo", "bar-{segment}")) + .build(); } } ---- diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/circuitbreaker-filter.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/circuitbreaker-filter.adoc index 8ec00ffd..8a1a50c8 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/circuitbreaker-filter.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/circuitbreaker-filter.adoc @@ -15,14 +15,17 @@ spring: gateway: mvc: routes: - - id: circuitbreaker_route + - id: circuitbreakernofallback uri: https://example.org + predicates: + - Path=/anything/circuitbreakernofallback filters: - CircuitBreaker=myCircuitBreaker ---- .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.CircuitBreakerFilterFunctions.circuitBreaker; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -33,9 +36,10 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsCircuitBreakerNoFallback() { return route("circuitbreakernofallback") - .route(path("/anything/circuitbreakernofallback"), http("https://example.org")) - .filter(circuitBreaker("mycb3")) - .build(); + .route(path("/anything/circuitbreakernofallback"), http()) + .before(uri("https://example.org")) + .filter(circuitBreaker("myCircuitBreaker")) + .build(); } } ---- @@ -74,6 +78,7 @@ The following listing does the same thing in Java: [source,java] ---- import java.net.URI; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.CircuitBreakerFilterFunctions.circuitBreaker; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -84,9 +89,10 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsCircuitBreakerFallback() { return route("circuitbreaker_route") - .route(path("/consumingServiceEndpoint"), http("https://example.org")) - .filter(circuitBreaker("myCircuitBreaker", URI.create("forward:/inCaseOfFailureUseThis"))) - .build(); + .route(path("/consumingServiceEndpoint"), http()) + .before(uri("https://example.org")) + .filter(circuitBreaker("myCircuitBreaker", URI.create("forward:/inCaseOfFailureUseThis"))) + .build(); } } ---- @@ -102,6 +108,7 @@ In the example below the call `consumingServiceEndpoint/users/1` will be redirec [source,java] ---- import java.net.URI; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.CircuitBreakerFilterFunctions.circuitBreaker; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -112,9 +119,10 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsCircuitBreakerFallback() { return route("circuitbreaker_route") - .route(path("/consumingServiceEndpoint/{*segments}"), http("https://example.org")) - .filter(circuitBreaker("myCircuitBreaker", URI.create("forward:/inCaseOfFailureUseThis/{segments}"))) - .build(); + .route(path("/consumingServiceEndpoint/{*segments}"), http()) + .before(uri("https://example.org")) + .filter(circuitBreaker("myCircuitBreaker", URI.create("forward:/inCaseOfFailureUseThis/{segments}"))) + .build(); } } ---- @@ -126,6 +134,7 @@ However, you can also reroute the request to a controller or handler in an exter .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.CircuitBreakerFilterFunctions.circuitBreaker; import static org.springframework.cloud.gateway.server.mvc.filter.LoadBalancerFilterFunctions.lb; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; @@ -142,7 +151,8 @@ class RouteConfiguration { .filter(circuitBreaker("fetchIngredients", URI.create("forward:/fallback"))) .build() .and(route("ingredients-fallback") - .route(path("/fallback"), http("http://localhost:9994")) + .route(path("/fallback"), http()) + .before(uri("https://localhost:9994")) .build()); } } diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/deduperesponseheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/deduperesponseheader.adoc index 9659f2cf..5f8a7a23 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/deduperesponseheader.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/deduperesponseheader.adoc @@ -14,6 +14,8 @@ spring: routes: - id: dedupe_response_header_route uri: https://example.org + predicates: + - Path=/hello filters: - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin ---- @@ -21,18 +23,19 @@ spring: [source,java] ---- import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.dedupeResponseHeader; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; import static org.springframework.web.servlet.function.RequestPredicates.path; - @Configuration class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsDedupeResponseHeader() { return route("dedupe_response_header_route") - .route(path("/hello"), http("https://example.org")) + .route(path("/hello"), http()) + .before(uri("https://example.org")) .after(dedupeResponseHeader("Access-Control-Allow-Credentials Access-Control-Allow-Origin")) .build(); } diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/fallback-headers.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/fallback-headers.adoc index 22f4114b..300276ac 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/fallback-headers.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/fallback-headers.adoc @@ -14,7 +14,7 @@ spring: - id: ingredients uri: lb://ingredients predicates: - - Path=//ingredients/** + - Path=/ingredients/** filters: - name: CircuitBreaker args: @@ -33,8 +33,9 @@ spring: .GatewaySampleApplication.java [source,java] ---- -import static org.springframework.cloud.gateway.server.mvc.filter.CircuitBreakerFilterFunctions.circuitBreaker; import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.fallbackHeaders; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; +import static org.springframework.cloud.gateway.server.mvc.filter.CircuitBreakerFilterFunctions.circuitBreaker; import static org.springframework.cloud.gateway.server.mvc.filter.LoadBalancerFilterFunctions.lb; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -50,7 +51,8 @@ class RouteConfiguration { .filter(circuitBreaker("fetchIngredients", URI.create("forward:/fallback"))) .build() .and(route("ingredients-fallback") - .route(path("/fallback"), http("http://localhost:9994")) + .route(path("/fallback"), http()) + .before(uri("http://localhost:9994")) .before(fallbackHeaders()) .build()); } diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/maprequestheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/maprequestheader.adoc index 2f8f30b6..e85d08b9 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/maprequestheader.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/maprequestheader.adoc @@ -17,6 +17,8 @@ spring: routes: - id: map_request_header_route uri: https://example.org + predicates: + - Path=/mypath filters: - MapRequestHeader=Blue, X-Request-Red ---- @@ -24,7 +26,8 @@ spring: .GatewaySampleApplication.java [source,java] ---- -import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestParameter; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.mapRequestHeader; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -34,7 +37,8 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsMapRequestHeader() { return route("map_request_header_route") - .GET("/mypath", http("https://example.org")) + .GET("/mypath", http()) + .before(uri("https://example.org")) .before(mapRequestHeader("Blue", "X-Request-Red")) .build(); } diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/modifyrequestbody.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/modifyrequestbody.adoc index 963b26f0..a18dbff1 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/modifyrequestbody.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/modifyrequestbody.adoc @@ -11,6 +11,7 @@ The following listing shows how to modify a request body filter: [source,java] ---- import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.modifyRequestBody; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.host; @@ -20,12 +21,13 @@ import org.springframework.http.MediaType; class RouteConfiguration { @Bean - public RouterFunction gatewayRouterFunctionsAddReqHeader() { - return route("rewrite_request_obj") - .route(host("*.rewriterequestobj.org"), http("https://example.org")) - .before(modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE, - (request, s) -> new Hello(s.toUpperCase()))) - .build(); + public RouterFunction gatewayRouterFunctionsModifyRequestBody() { + return route("modify_request_body") + .route(host("*.modifyrequestbody.org"), http()) + .before(uri("https://example.org")) + .before(modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE, + (request, s) -> new Hello(s.toUpperCase()))) + .build(); } record Hello(String message) { } diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/modifyresponsebody.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/modifyresponsebody.adoc index f8211ed0..c8a8d9d7 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/modifyresponsebody.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/modifyresponsebody.adoc @@ -7,22 +7,11 @@ NOTE: This filter can be configured only by using the Java DSL. The following listing shows how to modify a response body filter: -[source,java] ----- -@Bean -public RouteLocator routes(RouteLocatorBuilder builder) { - return builder.routes() - .route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org") - .filters(f -> f.prefixPath("/httpbin") - .modifyResponseBody(String.class, String.class, - (exchange, s) -> Mono.just(s.toUpperCase()))).uri(uri)) - .build(); -} ----- .GatewaySampleApplication.java [source,java] ---- import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.modifyResponseBody; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.host; @@ -32,11 +21,13 @@ import org.springframework.http.MediaType; class RouteConfiguration { @Bean - public RouterFunction gatewayRouterFunctionsAddReqHeader() { - return route("rewrite_request_obj") - .route(host("*.rewriterequestobj.org"), http("https://example.org")) - .before(modifyResponseBody(String.class, String.class, MediaType.APPLICATION_JSON_VALUE, (request, s) -> s.toUpperCase())) - .build(); + public RouterFunction gatewayRouterFunctionsModifyResponseBody() { + return route("modify_response_bodu") + .route(host("*.modifyresponsebodu.org"), http()) + .before(uri("https://example.org")) + .after(modifyResponseBody(String.class, String.class, MediaType.APPLICATION_JSON_VALUE, + (request, s) -> s.toUpperCase())) + .build(); } } 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 5bc8b5e8..5a4d1343 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 @@ -15,6 +15,8 @@ spring: routes: - id: prefixpath_route uri: https://example.org + predicates: + - Path=/** filters: - PrefixPath=/mypath ---- @@ -22,6 +24,7 @@ spring: .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.prefixPath; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -31,10 +34,11 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsPrefixPath() { - return route("prefixpath_route") - .GET("/**", http("https://example.org")) - .before(prefixPath("/mypath")) - .build(); + return route("prefixpath_route") + .GET("/**", http()) + .before(uri("https://example.org")) + .before(prefixPath("/mypath")) + .build(); } } ---- diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/preservehostheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/preservehostheader.adoc index 518c0ac3..462cf01f 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/preservehostheader.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/preservehostheader.adoc @@ -16,6 +16,8 @@ spring: routes: - id: preserve_host_route uri: https://example.org + predicates: + - Path=/** filters: - PreserveHostHeader ---- @@ -23,6 +25,7 @@ spring: .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.preserveHostHeader; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -32,10 +35,11 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsPreserveHostHeader() { - return route("preserve_host_route") - .GET("/**", http("https://example.org")) - .before(preserveHostHeader()) - .build(); + return route("preserve_host_route") + .GET("/**", http()) + .before(uri("https://example.org")) + .before(preserveHostHeader()) + .build(); } } ---- diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/ratelimiter.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/ratelimiter.adoc index cfde996c..7fa31982 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/ratelimiter.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/ratelimiter.adoc @@ -47,6 +47,7 @@ The following is an example of configuring a route with rate limiting: .RouteConfiguration.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.Bucket4jFilterFunctions.rateLimit; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -56,12 +57,13 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsRateLimited() { - return route("rate_limited_route") - .GET("/api/**", http("https://example.org")) - .filter(rateLimit(c -> c.setCapacity(100) - .setPeriod(Duration.ofMinutes(1)) - .setKeyResolver(request -> request.servletRequest().getUserPrincipal().getName()))) - .build(); + return route("rate_limited_route") + .GET("/api/**", http()) + .before(uri("https://example.org")) + .filter(rateLimit(c -> c.setCapacity(100) + .setPeriod(Duration.ofMinutes(1)) + .setKeyResolver(request -> request.servletRequest().getUserPrincipal().getName()))) + .build(); } } ---- diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/redirectto.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/redirectto.adoc index f1816d36..69f2d538 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/redirectto.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/redirectto.adoc @@ -18,6 +18,8 @@ spring: routes: - id: redirectto_route uri: https://example.org + predicates: + - Path=/** filters: - RedirectTo=302, https://acme.org ---- @@ -25,6 +27,7 @@ spring: .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions.redirectTo; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -34,10 +37,11 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsRedirectTo() { - return route("redirectto_route") - .GET("/**", http("https://example.org")) - .filter(redirectTo(302, URI.create("acme.org"))) - .build(); + return route("redirectto_route") + .GET("/**", http()) + .before(uri("https://example.org")) + .filter(redirectTo(302, URI.create("acme.org"))) + .build(); } } ---- diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removerequestheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removerequestheader.adoc index c85e8d9a..03150906 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removerequestheader.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removerequestheader.adoc @@ -16,6 +16,8 @@ spring: routes: - id: removerequestheader_route uri: https://example.org + predicates: + - Path=/** filters: - RemoveRequestHeader=X-Request-Foo ---- @@ -23,6 +25,7 @@ spring: .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.removeRequestHeader; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -31,11 +34,12 @@ import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFuncti class RouteConfiguration { @Bean - public RouterFunction gatewayRouterFunctionsremoveRequestHeader() { - return route("removerequestheader_route") - .GET("/**", http("https://example.org")) - .before(removeRequestHeader("X-Request-Foo")) - .build(); + public RouterFunction gatewayRouterFunctionsRemoveRequestHeader() { + return route("removerequestheader_route") + .GET("/**", http()) + .before(uri("https://example.org")) + .before(removeRequestHeader("X-Request-Foo")) + .build(); } } ---- diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removerequestparameter.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removerequestparameter.adoc index 5ff79940..e60eaa9d 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removerequestparameter.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removerequestparameter.adoc @@ -16,6 +16,8 @@ spring: routes: - id: removerequestparameter_route uri: https://example.org + predicates: + - Path=/** filters: - RemoveRequestParameter=red ---- @@ -23,7 +25,8 @@ spring: .GatewaySampleApplication.java [source,java] ---- -import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestParameter; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.removeRequestParameter; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -31,11 +34,12 @@ import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFuncti class RouteConfiguration { @Bean - public RouterFunction gatewayRouterFunctionsAddReqHeader() { - return route("removerequestparameter_route") - .GET("/**", http("https://example.org")) - .before(removeRequestParameter("red")) - .build(); + public RouterFunction gatewayRouterFunctionsRemoveRequestParameter() { + return route("removerequestparameter_route") + .GET("/**", http()) + .before(uri("https://example.org")) + .before(removeRequestParameter("red")) + .build(); } } ---- diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removeresponseheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removeresponseheader.adoc index fda397b1..d767aba0 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removeresponseheader.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/removeresponseheader.adoc @@ -15,6 +15,8 @@ spring: routes: - id: removeresponseheader_route uri: https://example.org + predicates: + - Path=/anything/removeresponseheader filters: - RemoveResponseHeader=X-Response-Foo ---- @@ -22,6 +24,7 @@ spring: .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.removeResponseHeader; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -31,9 +34,10 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsRemoveResponseHeader() { - return route("addresponseheader") - .GET("/anything/addresheader", http("https://example.org")) - .after(removeResponseHeader("X-Response-Foo")) + return route("removeresponseheader_route") + .GET("/anything/removeresponseheader", http()) + .before(uri("https://example.org")) + .after(removeResponseHeader("X-Response-Foo")) .build(); } } diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/requestheadersize.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/requestheadersize.adoc index 5f8f4066..e94b2ff0 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/requestheadersize.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/requestheadersize.adoc @@ -16,6 +16,8 @@ spring: routes: - id: requestheadersize_route uri: https://example.org + predicates: + - Path=/** filters: - RequestHeaderSize=1000B ---- @@ -23,6 +25,7 @@ spring: .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.requestHeaderSize; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -32,10 +35,11 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsRequestHeaderSize() { - return route("requestheadersize_route") - .GET("/**", http("https://example.org")) - .before(requestHeaderSize("1000B")) - .build(); + return route("requestheadersize_route") + .GET("/**", http()) + .before(uri("https://example.org")) + .before(requestHeaderSize("1000B")) + .build(); } } ---- diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/requestsize.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/requestsize.adoc index 29135ea8..7eb84314 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/requestsize.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/requestsize.adoc @@ -28,6 +28,7 @@ spring: .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.requestSize; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -37,10 +38,11 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsRequestSize() { - return route("request_size_route") - .GET("/upload", http("http://localhost:8080")) - .before(requestSize("5000000")) - .build(); + return route("request_size_route") + .GET("/upload", http()) + .before(uri("http://localhost:8080")) + .before(requestSize("5000000")) + .build(); } } ---- diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/retry.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/retry.adoc index 96165f0d..be99e402 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/retry.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/retry.adoc @@ -36,7 +36,7 @@ spring: gateway: mvc: routes: - - id: retry_test + - id: retry_route uri: http://localhost:8080/flakey predicates: - Host=*.retry.com @@ -53,6 +53,7 @@ spring: .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions.adaptCachedBody; import static org.springframework.cloud.gateway.server.mvc.filter.RetryFilterFunctions.retry; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; @@ -63,12 +64,16 @@ import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequ class RouteConfiguration { @Bean - public RouterFunction gatewayRouterFunctionsAddReqHeader() { - return route("add_request_parameter_route") - .route(host("*.retry.com"), http("https://example.org")) - .filter(retry(config -> config.setRetries(3).setSeries(Set.of(HttpStatus.Series.SERVER_ERROR)).setMethods(Set.of(HttpMethod.GET, HttpMethod.POST)).setCacheBody(true))) - .filter(adaptCachedBody()) - .build(); + public RouterFunction gatewayRouterFunctionsRetry() { + return route("retry_route") + .route(host("*.retry.com"), http()) + .before(uri("http://localhost:8080/flakey")) + .filter(retry(config -> config.setRetries(3) + .setSeries(Set.of(HttpStatus.Series.SERVER_ERROR)) + .setMethods(Set.of(HttpMethod.GET, HttpMethod.POST)) + .setCacheBody(true))) + .filter(adaptCachedBody()) + .build(); } } ---- @@ -77,11 +82,11 @@ NOTE: When using the retry filter with a `forward:` prefixed URL, the target end For example, if the target endpoint is an annotated controller, the target controller method should not return `ResponseEntity` with an error status code. Instead, it should throw an `Exception` or signal an error (for example, through a `Mono.error(ex)` return value), which the retry filter can be configured to handle by retrying. -// WARNING: When using the retry filter with any HTTP method with a body, the body will be cached and the gateway will become memory constrained. The body is cached in a request attribute defined by `ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR`. The type of the object is `org.springframework.core.io.buffer.DataBuffer`. +WARNING: When using the retry filter with any HTTP method with a body and `cacheBody=true`, the body will be cached and the gateway will become memory constrained. The body is cached in a request attribute defined by `MvcUtils.CACHED_REQUEST_BODY_ATT`. The type of the object is `ByteArrayInputStream`. A simplified "shortcut" notation can be added with a single `status` and `method`. -The following two examples are equivalent: +The following two example routes are equivalent: .application.yml [source,yaml] diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewritelocationresponseheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewritelocationresponseheader.adoc index 28918320..6af573a1 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewritelocationresponseheader.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewritelocationresponseheader.adoc @@ -15,6 +15,8 @@ spring: routes: - id: rewritelocationresponseheader_route uri: http://example.org + predicates: + - Path=/** filters: - RewriteLocationResponseHeader=AS_IN_REQUEST, Location, , ---- @@ -22,7 +24,8 @@ spring: .GatewaySampleApplication.java [source,java] ---- -import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.addResponseHeader; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; +import static org.springframework.cloud.gateway.server.mvc.filter.RewriteLocationResponseHeaderFilterFunctions.rewriteLocationResponseHeader; import static org.springframework.cloud.gateway.server.mvc.filter.RewriteLocationResponseHeaderFilterFunctions.StripVersion; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -33,9 +36,11 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsRewriteLocationResponseHeader() { return route("rewritelocationresponseheader_route") - .GET("/**", http("https://example.org")) - .after(rewriteLocationResponseHeader(config -> config.setLocationHeaderName("Location").setStripVersion(StripVersion.AS_IN_REQUEST))) - .build(); + .GET("/**", http()) + .before(uri("https://example.org")) + .after(rewriteLocationResponseHeader(config -> config.setLocationHeaderName("Location") + .setStripVersion(StripVersion.AS_IN_REQUEST))) + .build(); } } ---- 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 3b50b615..d423af39 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 @@ -25,6 +25,7 @@ spring: [source,java] ---- import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.rewritePath; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -33,10 +34,11 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsRewritePath() { - return route("rewritepath_route") - .GET("/red/**", http("https://example.org")) - .before(rewritePath("/red/(?.*)", "/${segment}")) - .build(); + return route("rewritepath_route") + .GET("/red/**", http()) + .before(uri("https://example.org")) + .before(rewritePath("/red/(?.*)", "/${segment}")) + .build(); } } ---- diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewriterequestparameter.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewriterequestparameter.adoc index ba7c38c5..7ad61594 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewriterequestparameter.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewriterequestparameter.adoc @@ -26,6 +26,7 @@ spring: [source,java] ---- import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.addRequestParameter; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -33,11 +34,12 @@ import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFuncti class RouteConfiguration { @Bean - public RouterFunction gatewayRouterFunctionsAddReqHeader() { - return route("add_request_parameter_route") - .GET("/anything/addrequestparam", http("https://example.org")) - .before(addRequestParameter("red", "blue")) - .build(); + public RouterFunction gatewayRouterFunctionsRewriteRequestParameter() { + return route("rewriterequestparameter_route") + .GET("products", http()) + .before(uri("https://example.org")) + .before(addRequestParameter("red", "blue")) + .build(); } } ---- diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewriteresponseheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewriteresponseheader.adoc index 7ba0f9b0..7e0cb2c9 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewriteresponseheader.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/rewriteresponseheader.adoc @@ -16,14 +16,17 @@ spring: routes: - id: rewriteresponseheader_route uri: https://example.org + predicates: + - Path=/** filters: - - RewriteResponseHeader=X-Response-Red, , password=[^&]+, password=*** + - RewriteResponseHeader=X-Response-Red, password=[^&]+, password=*** ---- .GatewaySampleApplication.java [source,java] ---- -import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.addResponseHeader; +import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.rewriteResponseHeader; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -33,8 +36,9 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsRewriteResponseHeader() { return route("rewriteresponseheader_route") - .GET("/**", http("https://example.org")) - .after(rewriteResponseHeader("X-Request-Red", "password=[^&]+", "password=***")) + .GET("/**", http()) + .before(uri("https://example.org")) + .after(rewriteResponseHeader("X-Request-Red", "password=[^&]+", "password=***")) .build(); } } 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 9355a068..73624a70 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 @@ -27,6 +27,7 @@ spring: [source,java] ---- import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.setPath; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -35,10 +36,11 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsSetPath() { - return route("add_request_parameter_route") - .GET("/red/{segment}", http("https://example.org")) - .before(setPath("/{segment")) - .build(); + return route("setpath_route") + .GET("/red/{segment}", http()) + .before(uri("https://example.org")) + .before(setPath("/{segment")) + .build(); } } ---- diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setrequestheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setrequestheader.adoc index f131486a..a2c7676b 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setrequestheader.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setrequestheader.adoc @@ -14,6 +14,8 @@ spring: routes: - id: setrequestheader_route uri: https://example.org + predicates: + - Path=/** filters: - SetRequestHeader=X-Request-Red, Blue ---- @@ -22,6 +24,7 @@ spring: [source,java] ---- import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.setRequestHostHeader; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -30,10 +33,11 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsSetRequestHeader() { - return route("add_request_parameter_route") - .GET("/**", http("https://example.org")) - .before(setRequestHostHeader("X-Request-Red", "Blue")) - .build(); + return route("setrequestheader_route") + .GET("/**", http()) + .before(uri("https://example.org")) + .before(setRequestHostHeader("X-Request-Red", "Blue")) + .build(); } } ---- @@ -64,19 +68,21 @@ spring: [source,java] ---- import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.setRequestHostHeader; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; -import static import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.host; +import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.host; @Configuration class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsSetRequestHeader() { - return route("add_request_parameter_route") - .route(host("{segment}.myhost.org"), http("https://example.org")) - .before(setRequestHostHeader("X-Request-Red", "Blue-{segment}")) - .build(); + return route("setrequestheader_route") + .route(host("{segment}.myhost.org"), http()) + .before(uri("https://example.org")) + .before(setRequestHostHeader("X-Request-Red", "Blue-{segment}")) + .build(); } } ---- \ No newline at end of file diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setrequesthostheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setrequesthostheader.adoc index 3f8faad9..d12b23c6 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setrequesthostheader.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setrequesthostheader.adoc @@ -26,6 +26,7 @@ spring: .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.setRequestHostHeader; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -35,10 +36,11 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsSetRequestHostHeader() { - return route("set_request_host_header_route") - .GET("/headers", http("http://localhost:8080")) - .before(setRequestHostHeader("example.org")) - .build(); + return route("set_request_host_header_route") + .GET("/headers", http()) + .before(uri("http://localhost:8080")) + .before(setRequestHostHeader("example.org")) + .build(); } } ---- diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setresponseheader.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setresponseheader.adoc index ee7698e9..55375546 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setresponseheader.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setresponseheader.adoc @@ -14,6 +14,8 @@ spring: routes: - id: setresponseheader_route uri: https://example.org + predicates: + - Path=/anything/setresponseheader filters: - SetResponseHeader=X-Response-Red, Blue ---- @@ -22,6 +24,7 @@ spring: [source,java] ---- import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.setResponseHeader; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -30,9 +33,10 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsSetResponseHeader() { - return route("addresponseheader") - .GET("/anything/addresheader", http("https://example.org")) - .after(setResponseHeader("X-Response-Red", "Blue")) + return route("setresponseheader_route") + .GET("/anything/setresponseheader", http()) + .before(uri("https://example.org")) + .after(setResponseHeader("X-Response-Red", "Blue")) .build(); } } @@ -64,6 +68,7 @@ spring: [source,java] ---- import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.setResponseHeader; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.host; @@ -73,10 +78,11 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsSetResponseHeader() { - return route("add_response_header_route") - .route(host("{segment}.myhost.org"), http("https://example.org")) - .after(setResponseHeader("foo", "bar-{segment}")) - .build(); + return route("setresponseheader_route") + .route(host("{segment}.myhost.org"), http()) + .before(uri("https://example.org")) + .after(setResponseHeader("foo", "bar-{segment}")) + .build(); } } ---- diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setstatus.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setstatus.adoc index c3022b52..bd9bbe25 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setstatus.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/setstatus.adoc @@ -16,10 +16,14 @@ spring: routes: - id: setstatusstring_route uri: https://example.org + predicates: + - Path=/path1 filters: - SetStatus=UNAUTHORIZED - id: setstatusint_route uri: https://example.org + predicates: + - Path=/path2 filters: - SetStatus=401 ---- @@ -28,6 +32,7 @@ spring: [source,java] ---- import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.setStatus; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -37,13 +42,15 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsSetStatus() { return route("setstatus_route") - .GET("/path1", http("https://example.org")) + .GET("/path1", http()) + .before(uri("https://example.org")) // setStatus("UNAUTHORIZED") works as well .after(setStatus(HttpStatus.UNAUTHORIZED)) .build().and(route("setstatusint_route") - .GET("/path2", http("https://example.org")) + .before(uri("https://example.org")) + .GET("/path2", http()) .after(setStatus("401")) - .build()); + .build()); } } ---- 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 dc2be798..902974ce 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 @@ -13,8 +13,8 @@ spring: gateway: mvc: routes: - - id: nameRoot - uri: https://nameservice + - id: strip_prefix_route + uri: https://example.org predicates: - Path=/name/** filters: @@ -25,6 +25,7 @@ spring: [source,java] ---- import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.stripPrefix; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -33,14 +34,15 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsStripPrefix() { - return route("nameRoot") - .GET("/name/**", http("https://example.org")) - .before(stripPrefix(2)) - .build(); + return route("strip_prefix_route") + .GET("/name/**", http()) + .before(uri("https://example.org")) + .before(stripPrefix(2)) + .build(); } } ---- -When a request is made through the gateway to `/name/blue/red`, the request made to `nameservice` looks like `https://nameservice/red`. +When a request is made through the gateway to `/name/blue/red`, the full request url looks like `https://example.org/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/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/tokenrelay.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/tokenrelay.adoc index 2bf618aa..086bd3eb 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/tokenrelay.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/tokenrelay.adoc @@ -54,6 +54,7 @@ Spring Cloud Gateway Server MVC can forward the OAuth2 access token of the curre .RouteConfiguration.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.TokenRelayFilterFunctions.tokenRelay; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -62,11 +63,12 @@ import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFuncti class RouteConfiguration { @Bean - public RouterFunction gatewayRouterFunctionsAddReqHeader() { - return route("resource") - .GET("/resource", http("http://localhost:9000")) - .filter(tokenRelay()) - .build(); + public RouterFunction gatewayRouterFunctionsTokenRelay() { + return route("resource") + .GET("/resource", http()) + .before(uri("https://localhost:9000")) + .filter(tokenRelay()) + .build(); } } ---- diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/gateway-request-predicates.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/gateway-request-predicates.adoc index 1bca61e6..5631cbf8 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/gateway-request-predicates.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/gateway-request-predicates.adoc @@ -31,6 +31,7 @@ spring: [source,java] ---- import java.time.ZonedDateTime; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.after; @@ -40,9 +41,10 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsAfter() { - return route("after_route") - .route(after(ZonedDateTime.parse("2017-01-20T17:42:47.789-07:00[America/Denver]")), http("https://example.org")) - .build(); + return route("after_route") + .route(after(ZonedDateTime.parse("2017-01-20T17:42:47.789-07:00[America/Denver]")), http()) + .before(uri("https://example.org")) + .build(); } } ---- @@ -74,6 +76,7 @@ spring: [source,java] ---- import java.time.ZonedDateTime; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.before; @@ -83,9 +86,10 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsBefore() { - return route("before_route") - .route(before(ZonedDateTime.parse("2017-01-20T17:42:47.789-07:00[America/Denver]")), http("https://example.org")) - .build(); + return route("before_route") + .route(before(ZonedDateTime.parse("2017-01-20T17:42:47.789-07:00[America/Denver]")), http() + .before(uri("https://example.org")) + .build(); } } ---- @@ -119,6 +123,7 @@ spring: [source,java] ---- import java.time.ZonedDateTime; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.between; @@ -128,9 +133,11 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsBetween() { - return route("between_route") - .route(between(ZonedDateTime.parse("2017-01-20T17:42:47.789-07:00[America/Denver]"), ZonedDateTime.parse("2017-01-21T17:42:47.789-07:00[America/Denver]")), http("https://example.org")) - .build(); + return route("between_route") + .route(between(ZonedDateTime.parse("2017-01-20T17:42:47.789-07:00[America/Denver]"), + ZonedDateTime.parse("2017-01-21T17:42:47.789-07:00[America/Denver]")), http()) + .before(uri("https://example.org")) + .build(); } } ---- @@ -162,18 +169,20 @@ spring: .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; -import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.between; +import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.cookie; @Configuration class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsCookie() { - return route("cookie_route") - .route(cookie("chocolate", "ch.p"), http("https://example.org")) - .build(); + return route("cookie_route") + .route(cookie("chocolate", "ch.p"), http()) + .before(uri("https://example.org")) + .build(); } } ---- @@ -204,6 +213,7 @@ spring: .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.header; @@ -212,10 +222,11 @@ import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequ class RouteConfiguration { @Bean - public RouterFunction gatewayRouterFunctionsCookie() { - return route("cookie_route") - .route(header("X-Request-Id", "\\d+"), http("https://example.org")) - .build(); + public RouterFunction gatewayRouterFunctionsHeader() { + return route("header_route") + .route(header("X-Request-Id", "\\d+"), http()) + .before(uri("https://example.org")) + .build(); } } ---- @@ -247,6 +258,7 @@ spring: .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.host; @@ -256,9 +268,10 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsHost() { - return route("host_route") - .route(host("**.somehost.org", "**.anotherhost.org"), http("https://example.org")) - .build(); + return route("host_route") + .route(host("**.somehost.org", "**.anotherhost.org"), http()) + .before(uri("https://example.org")) + .build(); } } ---- @@ -296,6 +309,7 @@ spring: [source,java] ---- import org.springframework.http.HttpMethod; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.method; @@ -305,9 +319,10 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsMethod() { - return route("method_route") - .route(method(HttpMethod.GET, HttpMethod.POST), http("https://example.org")) - .build(); + return route("method_route") + .route(method(HttpMethod.GET, HttpMethod.POST), http()) + .before(uri("https://example.org")) + .build(); } } ---- @@ -319,19 +334,19 @@ This route matches if the request method was a `GET` or a `POST`. .GatewaySampleApplication.java [source,java] ---- -import org.springframework.http.HttpMethod; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; -import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.methods; @Configuration class RouteConfiguration { @Bean - public RouterFunction gatewayRouterFunctionsMethod() { - return route("method_route") - .GET("/mypath", http("https://example.org")) - .build(); + public RouterFunction gatewayRouterFunctionsMethodAndPath() { + return route("method_and_path_route") + .GET("/mypath", http()) + .before(uri("https://example.org")) + .build(); } } ---- @@ -363,19 +378,20 @@ spring: .GatewaySampleApplication.java [source,java] ---- -import org.springframework.http.HttpMethod; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; -import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.method; +import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.path; @Configuration class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsPath() { - return route("path_route") - .route(path("/red/{segment}", "/blue/{segment}"), http("https://example.org")) - .build(); + return route("path_route") + .route(path("/red/{segment}", "/blue/{segment}"), http()) + .before(uri("https://example.org")) + .build(); } } ---- @@ -421,7 +437,7 @@ spring: .GatewaySampleApplication.java [source,java] ---- -import org.springframework.http.HttpMethod; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.query; @@ -431,9 +447,10 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsQuery() { - return route("query_route") - .route(query("green"), http("https://example.org")) - .build(); + return route("query_route") + .route(query("green"), http()) + .before(uri("https://example.org")) + .build(); } } ---- @@ -457,7 +474,7 @@ spring: .GatewaySampleApplication.java [source,java] ---- -import org.springframework.http.HttpMethod; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.query; @@ -467,9 +484,10 @@ class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsQuery() { - return route("query_route") - .route(query("red", "gree."), http("https://example.org")) - .build(); + return route("query_route") + .route(query("red", "gree."), http()) + .before(uri("https://example.org")) + .build(); } } ---- @@ -585,22 +603,25 @@ spring: .GatewaySampleApplication.java [source,java] ---- -import org.springframework.http.HttpMethod; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; -import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.method; +import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.path; +import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.weight; @Configuration class RouteConfiguration { @Bean public RouterFunction gatewayRouterFunctionsWeights() { - return route("weight_high") - .route(weight("group1", 8).and(path("/**")), http("https://weighthigh.org")) - .build().and( - route("weight_low") - .route(weight("group1", 2).and(path("/**")), http("https://weightlow.org")) - .build()); + return route("weight_high") + .route(weight("group1", 8).and(path("/**")), http()) + .before(uri("https://weighthigh.org")) + .build().and( + route("weight_low") + .route(weight("group1", 2).and(path("/**")), http()) + .before(uri("https://weightlow.org")) + .build()); } } ---- diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/java-routes-api.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/java-routes-api.adoc index d15b79e0..f653d36e 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/java-routes-api.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/java-routes-api.adoc @@ -9,12 +9,15 @@ A https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springfra [source,java] ---- import static org.springframework.web.servlet.function.RouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; class SimpleGateway { @Bean public RouterFunction getRoute() { - return route().GET("/get", http("https://httpbin.org")).build(); + return route().GET("/get", http()) + .before(uri("https://example.org")) + .build(); } } ---- @@ -29,13 +32,16 @@ Some advanced filters require some metadata to be added to request attributes. T .GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; class SimpleGateway { @Bean public RouterFunction getRoute() { - return route("simple_route").GET("/get", http("https://httpbin.org")).build(); + return route("simple_route").GET("/get", http()) + .before(uri("https://example.org")) + .build(); } } ---- @@ -43,4 +49,6 @@ class SimpleGateway { [[gateway-handlerfunctions]] == Gateway MVC Handler Functions -Various `RouterFunctions.Builder` methods require a `HandlerFunction`. To create a route that is proxied by the MVC Gateway, `HandlerFunction` implementations are supplied in `org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions`. The most basic is the `http()` `HandlerFunction`. If a `URI` is supplied as a parameter, that is the `URI` used as the downstream target for sending the HTTP requests (as seen in the example above). If no parameter is passed, the function looks for a `URI` in the `org.springframework.cloud.gateway.server.mvc.common.MvcUtils.GATEWAY_REQUEST_URL_ATTR` request attribute. This allows for dynamic targets such as load balancing to set the `URI`. +Various `RouterFunctions.Builder` methods require a `HandlerFunction`. To create a route that is proxied by the MVC Gateway, `HandlerFunction` implementations are supplied in `org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions`. The most basic is the `http()` `HandlerFunction`. The function looks for a `URI` in the `org.springframework.cloud.gateway.server.mvc.common.MvcUtils.GATEWAY_REQUEST_URL_ATTR` request attribute. This allows for dynamic targets such as load balancing to set the `URI`. + +WARNING: As of version 4.1.7, `HandlerFunctions.http(String)` and `HandlerFunctions.http(URI)` are now deprecated. Please use `HandlerFunctions.http()` in combination with the `BeforeFilterFunctions.uri()` filter instead. This fixes inconsistencies in dealing with the route url request attribute. \ No newline at end of file diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/writing-custom-predicates-and-filters.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/writing-custom-predicates-and-filters.adoc index fdabf35d..30aef31e 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/writing-custom-predicates-and-filters.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/writing-custom-predicates-and-filters.adoc @@ -48,6 +48,7 @@ To use our new `headerExists` `RequestPredicate`, we need to plug it in to an ap [source,java] ---- import static SampleRequestPredicates.headerExists; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -56,9 +57,10 @@ class RouteConfiguration { @Bean public RouterFunction headerExistsRoute() { - return route("header_exists_route") - .route(headerExists("X-Green"), http("https://example.org")) - .build(); + return route("header_exists_route") + .route(headerExists("X-Green"), http()) + .before(uri("https://example.org")) + .build(); } } ---- @@ -111,6 +113,7 @@ First, a new `ServerRequest` is created from the existing request. This allows u [source,java] ---- import static SampleHandlerFilterFunctions.instrument; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -120,9 +123,10 @@ class RouteConfiguration { @Bean public RouterFunction instrumentRoute() { return route("instrument_route") - .GET("/**", http("https://example.org")) - .filter(instrument("X-Request-Id", "X-Response-Id")) - .build(); + .GET("/**", http()) + .filter(instrument("X-Request-Id", "X-Response-Id")) + .before(uri("https://example.org")) + .build(); } } ---- @@ -160,6 +164,7 @@ A new `ServerRequest` is created from the existing request. This allows us to ad [source,java] ---- import static SampleBeforeFilterFunctions.instrument; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -168,10 +173,10 @@ class RouteConfiguration { @Bean public RouterFunction instrumentRoute() { - return route("instrument_route") - .GET("/**", http("https://example.org")) - .before(instrument("X-Request-Id")) - .build(); + return route("instrument_route").GET("/**", http()) + .before(uri("https://example.org")) + .before(instrument("X-Request-Id")) + .build(); } } ---- @@ -213,6 +218,7 @@ In this case we simply add the header to the response and return it. [source,java] ---- import static SampleAfterFilterFunctions.instrument; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; @@ -221,10 +227,11 @@ class RouteConfiguration { @Bean public RouterFunction instrumentRoute() { - return route("instrument_route") - .GET("/**", http("https://example.org")) - .after(instrument("X-Response-Id")) - .build(); + return route("instrument_route") + .GET("/**", http()) + .before(uri("https://example.org")) + .after(instrument("X-Response-Id")) + .build(); } } ---- 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 dcebc7e7..6bc52a52 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 @@ -39,7 +39,6 @@ import org.springframework.boot.context.properties.source.ConfigurationPropertyS import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; import org.springframework.cloud.gateway.server.mvc.common.Configurable; import org.springframework.cloud.gateway.server.mvc.common.MvcUtils; -import org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions; import org.springframework.cloud.gateway.server.mvc.filter.FilterDiscoverer; import org.springframework.cloud.gateway.server.mvc.handler.HandlerDiscoverer; import org.springframework.cloud.gateway.server.mvc.invoke.InvocationContext; @@ -150,15 +149,6 @@ public class RouterFunctionHolderFactory { RouterFunctions.Builder builder = route(routeId); - // MVC.fn users won't need this anonymous filter as url will be set directly. - // Put this function first, so if a filter from a handler changes the url - // it is after this one. - builder.filter((request, next) -> { - MvcUtils.setRequestUrl(request, routeProperties.getUri()); - return next.handle(request); - }); - builder.before(BeforeFilterFunctions.routeId(routeId)); - MultiValueMap handlerOperations = handlerDiscoverer.getOperations(); // TODO: cache? // translate handlerFunction @@ -181,13 +171,15 @@ public class RouterFunctionHolderFactory { HandlerFunction handlerFunction = null; // filters added by HandlerDiscoverer need to go last, so save them - List> handlerFilterFunctionFilters = new ArrayList<>(); + List> lowerPrecedenceFilters = new ArrayList<>(); + List> higherPrecedenceFilters = new ArrayList<>(); if (response instanceof HandlerFunction) { handlerFunction = (HandlerFunction) response; } else if (response instanceof HandlerDiscoverer.Result result) { handlerFunction = result.getHandlerFunction(); - handlerFilterFunctionFilters.addAll(result.getFilters()); + lowerPrecedenceFilters.addAll(result.getLowerPrecedenceFilters()); + higherPrecedenceFilters.addAll(result.getHigherPrecedenceFilters()); } if (handlerFunction == null) { throw new IllegalStateException( @@ -218,6 +210,9 @@ public class RouterFunctionHolderFactory { builder.route(predicate.get(), handlerFunction); predicate.set(null); + // HandlerDiscoverer filters needing lower priority, so put them first + lowerPrecedenceFilters.forEach(builder::filter); + // translate filters MultiValueMap filterOperations = filterDiscoverer.getOperations(); routeProperties.getFilters().forEach(filterProperties -> { @@ -226,7 +221,7 @@ public class RouterFunctionHolderFactory { }); // HandlerDiscoverer filters need higher priority, so put them last - handlerFilterFunctionFilters.forEach(builder::filter); + higherPrecedenceFilters.forEach(builder::filter); builder.withAttribute(MvcUtils.GATEWAY_ROUTE_ID_ATTR, routeId); 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 dba4e260..0e95bf82 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 @@ -196,7 +196,6 @@ public abstract class BeforeFilterFunctions { 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(); }; } @@ -340,7 +339,6 @@ public abstract class BeforeFilterFunctions { ServerRequest modified = ServerRequest.from(request).uri(rewrittenUri).build(); - MvcUtils.setRequestUrl(request, rewrittenUri); return modified; }; } @@ -361,7 +359,6 @@ public abstract class BeforeFilterFunctions { URI uri = uriTemplate.expand(uriVariables); URI newUri = UriComponentsBuilder.fromUri(request.uri()).replacePath(uri.getRawPath()).build(true).toUri(); - MvcUtils.setRequestUrl(request, newUri); return ServerRequest.from(request).uri(newUri).build(); }; } @@ -419,12 +416,22 @@ public abstract class BeforeFilterFunctions { .replacePath(newPath.toString()) .build(true) .toUri(); - MvcUtils.setRequestUrl(request, prefixedUri); return ServerRequest.from(request).uri(prefixedUri).build(); }; } + public static Function uri(String uri) { + return uri(URI.create(uri)); + } + + public static Function uri(URI uri) { + return request -> { + MvcUtils.setRequestUrl(request, uri); + return request; + }; + } + public static class FallbackHeadersConfig { private String executionExceptionTypeHeaderName = CB_EXECUTION_EXCEPTION_TYPE; diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/FilterFunctions.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/FilterFunctions.java index c7cb910f..55b6602c 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/FilterFunctions.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/FilterFunctions.java @@ -228,6 +228,15 @@ public interface FilterFunctions { return ofResponseProcessor(AfterFilterFunctions.setStatus(statusCode)); } + static HandlerFilterFunction uri(String uri) { + return ofRequestProcessor(BeforeFilterFunctions.uri(uri)); + } + + @Shortcut + static HandlerFilterFunction uri(URI uri) { + return ofRequestProcessor(BeforeFilterFunctions.uri(uri)); + } + class FilterSupplier extends SimpleFilterSupplier { public FilterSupplier() { diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/LoadBalancerHandlerSupplier.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/LoadBalancerHandlerSupplier.java index 3cedfa35..c2a0ad58 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/LoadBalancerHandlerSupplier.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/LoadBalancerHandlerSupplier.java @@ -40,7 +40,7 @@ public class LoadBalancerHandlerSupplier implements HandlerSupplier { public static HandlerDiscoverer.Result lb(URI uri) { // TODO: how to do something other than http - return new HandlerDiscoverer.Result(HandlerFunctions.http(), + return new HandlerDiscoverer.Result(HandlerFunctions.http(), Collections.emptyList(), Collections.singletonList(LoadBalancerFilterFunctions.lb(uri.getHost()))); } 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 9b8b9bcb..bee93756 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 @@ -400,7 +400,7 @@ public class XForwardedRequestHeadersFilter implements HttpHeadersFilter.Request LinkedHashSet originalUris = MvcUtils.getAttribute(request, MvcUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR); - URI requestUri = MvcUtils.getAttribute(request, MvcUtils.GATEWAY_REQUEST_URL_ATTR); + URI requestUri = request.uri(); if (originalUris != null && requestUri != null) { diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/DefaultHandlerSupplier.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/DefaultHandlerSupplier.java new file mode 100644 index 00000000..91304f9f --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/DefaultHandlerSupplier.java @@ -0,0 +1,92 @@ +/* + * Copyright 2013-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.gateway.server.mvc.handler; + +import java.lang.reflect.Method; +import java.net.URI; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import org.springframework.cloud.gateway.server.mvc.common.MvcUtils; +import org.springframework.cloud.gateway.server.mvc.config.RouteProperties; +import org.springframework.web.servlet.function.HandlerFilterFunction; +import org.springframework.web.servlet.function.HandlerFunction; +import org.springframework.web.servlet.function.ServerResponse; + +class DefaultHandlerSupplier implements HandlerSupplier { + + @Override + public Collection get() { + return Arrays.asList(getClass().getMethods()); + } + + public static HandlerDiscoverer.Result forward(RouteProperties routeProperties) { + return forward(routeProperties.getId(), routeProperties.getUri()); + } + + public static HandlerDiscoverer.Result forward(String id, URI uri) { + return new HandlerDiscoverer.Result(HandlerFunctions.forward(uri.getPath()), Collections.emptyList()); + } + + public static HandlerDiscoverer.Result http(RouteProperties routeProperties) { + return http(routeProperties.getId(), routeProperties.getUri()); + } + + public static HandlerDiscoverer.Result http(String id, URI uri) { + HandlerFunction http = HandlerFunctions.http(); + return getResult(id, uri, http); + } + + public static HandlerDiscoverer.Result https(RouteProperties routeProperties) { + return https(routeProperties.getId(), routeProperties.getUri()); + } + + public static HandlerDiscoverer.Result https(String id, URI uri) { + return getResult(id, uri, HandlerFunctions.https()); + } + + public static HandlerDiscoverer.Result no(RouteProperties routeProperties) { + return no(routeProperties.getId(), routeProperties.getUri()); + } + + public static HandlerDiscoverer.Result no(String id, URI uri) { + return getResult(id, uri, HandlerFunctions.no()); + } + + private static HandlerDiscoverer.Result getResult(String id, URI uri, + HandlerFunction handlerFunction) { + HandlerFilterFunction setId = setIdFilter(id); + HandlerFilterFunction setRequest = setRequestUrlFilter(uri); + return new HandlerDiscoverer.Result(handlerFunction, Arrays.asList(setId, setRequest), Collections.emptyList()); + } + + private static HandlerFilterFunction setIdFilter(String id) { + return (request, next) -> { + MvcUtils.setRouteId(request, id); + return next.handle(request); + }; + } + + private static HandlerFilterFunction setRequestUrlFilter(URI uri) { + return (request, next) -> { + MvcUtils.setRequestUrl(request, uri); + return next.handle(request); + }; + } + +} diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerDiscoverer.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerDiscoverer.java index be0dd7cf..7569842d 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerDiscoverer.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerDiscoverer.java @@ -37,20 +37,39 @@ public class HandlerDiscoverer extends AbstractGatewayDiscoverer { private final HandlerFunction handlerFunction; - private final List> filters; + private final List> lowerPrecedenceFilters; + private final List> higherPrecedenceFilters; + + @Deprecated public Result(HandlerFunction handlerFunction, List> filters) { + this(handlerFunction, Collections.emptyList(), filters); + } + + public Result(HandlerFunction handlerFunction, + List> lowerPrecedenceFilters, + List> higherPrecedenceFilters) { this.handlerFunction = handlerFunction; - this.filters = Objects.requireNonNullElse(filters, Collections.emptyList()); + this.lowerPrecedenceFilters = Objects.requireNonNullElse(lowerPrecedenceFilters, Collections.emptyList()); + this.higherPrecedenceFilters = Objects.requireNonNullElse(higherPrecedenceFilters, Collections.emptyList()); } public HandlerFunction getHandlerFunction() { return handlerFunction; } + @Deprecated public List> getFilters() { - return filters; + return getHigherPrecedenceFilters(); + } + + public List> getLowerPrecedenceFilters() { + return lowerPrecedenceFilters; + } + + public List> getHigherPrecedenceFilters() { + return higherPrecedenceFilters; } } diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerFunctions.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerFunctions.java index 9b3d118b..0b9f99ac 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerFunctions.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerFunctions.java @@ -19,8 +19,8 @@ package org.springframework.cloud.gateway.server.mvc.handler; import java.io.IOException; import java.lang.reflect.Method; import java.net.URI; -import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.concurrent.atomic.AtomicReference; import jakarta.servlet.ServletException; @@ -60,14 +60,17 @@ public abstract class HandlerFunctions { // TODO: current discovery only goes by method name // so last one wins, so put parameterless last + @Deprecated public static HandlerFunction http(String uri) { return http(URI.create(uri)); } + @Deprecated public static HandlerFunction http(URI uri) { return new LookupProxyExchangeHandlerFunction(uri); } + @Deprecated public static HandlerFunction https(URI uri) { return new LookupProxyExchangeHandlerFunction(uri); } @@ -86,6 +89,7 @@ public abstract class HandlerFunctions { static class LookupProxyExchangeHandlerFunction implements HandlerFunction { + @Deprecated private final URI uri; private AtomicReference proxyExchangeHandlerFunction = new AtomicReference<>(); @@ -94,6 +98,7 @@ public abstract class HandlerFunctions { this.uri = null; } + @Deprecated LookupProxyExchangeHandlerFunction(URI uri) { this.uri = uri; } @@ -101,12 +106,15 @@ public abstract class HandlerFunctions { @Override public ServerResponse handle(ServerRequest serverRequest) { if (uri != null) { - // TODO: in 2 places now, here and - // GatewayMvcPropertiesBeanDefinitionRegistrar - MvcUtils.putAttribute(serverRequest, MvcUtils.GATEWAY_REQUEST_URL_ATTR, uri); + // TODO: log warning of deprecated usage + MvcUtils.setRequestUrl(serverRequest, uri); } - this.proxyExchangeHandlerFunction.compareAndSet(null, lookup(serverRequest)); - return proxyExchangeHandlerFunction.get().handle(serverRequest); + return proxyExchangeHandlerFunction.updateAndGet(function -> { + if (function == null) { + return lookup(serverRequest); + } + return function; + }).handle(serverRequest); } private static ProxyExchangeHandlerFunction lookup(ServerRequest request) { @@ -124,12 +132,13 @@ public abstract class HandlerFunctions { } + @Deprecated public static class HandlerSupplier implements org.springframework.cloud.gateway.server.mvc.handler.HandlerSupplier { @Override public Collection get() { - return Arrays.asList(HandlerFunctions.class.getMethods()); + return Collections.emptyList(); } } diff --git a/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring.factories b/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring.factories index 4b1b5077..b2be9e16 100644 --- a/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring.factories @@ -23,7 +23,7 @@ org.springframework.cloud.gateway.server.mvc.filter.FilterSupplier=\ org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions.FilterSupplier org.springframework.cloud.gateway.server.mvc.handler.HandlerSupplier=\ - org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.HandlerSupplier,\ + org.springframework.cloud.gateway.server.mvc.handler.DefaultHandlerSupplier,\ org.springframework.cloud.gateway.server.mvc.filter.LoadBalancerHandlerSupplier org.springframework.cloud.gateway.server.mvc.predicate.PredicateSupplier=\ 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 4188d73a..aaf409af 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 @@ -76,6 +76,7 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StreamUtils; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -107,6 +108,7 @@ import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFu import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.requestHeaderToRequestUri; import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.requestSize; import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.routeId; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; import static org.springframework.cloud.gateway.server.mvc.filter.Bucket4jFilterFunctions.rateLimit; import static org.springframework.cloud.gateway.server.mvc.filter.CircuitBreakerFilterFunctions.circuitBreaker; import static org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions.addRequestHeader; @@ -298,7 +300,7 @@ public class ServerMvcIntegrationTests { XForwardedRequestHeadersFilter.X_FORWARDED_FOR_HEADER); assertThat(headers).containsEntry(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, "/long/path/to"); - assertThat(headers).containsEntry("X-Test", "stripPrefix"); + assertThat(headers).containsEntry("X-Test", "stripPrefixLb"); }); } @@ -418,7 +420,8 @@ public class ServerMvcIntegrationTests { public void circuitBreakerInvalidFallbackThrowsException() { // @formatter:off Assertions.assertThatThrownBy(() -> route("testcircuitbreakergatewayfallback") - .route(path("/anything/circuitbreakergatewayfallback"), http(URI.create("https://nonexistantdomain.com1234"))) + .route(path("/anything/circuitbreakergatewayfallback"), http()) + .before(uri("https://nonexistantdomain.com1234")) .filter(circuitBreaker("mycb2", URI.create("http://example.com"))) .build()).isInstanceOf(IllegalArgumentException.class); // @formatter:on @@ -1016,6 +1019,11 @@ public class ServerMvcIntegrationTests { @LoadBalancerClient(name = "httpbin", configuration = TestLoadBalancerConfig.Httpbin.class) protected static class TestConfiguration { + @Bean + StaticPortController staticPortController() { + return new StaticPortController(); + } + @Bean TestHandler testHandler() { return new TestHandler(); @@ -1132,7 +1140,7 @@ public class ServerMvcIntegrationTests { return route("teststripprefix") .route(GET("/long/path/to/get").and(host("**.stripprefixlb.org")), http()) .filter(stripPrefix(3)) - .filter(addRequestHeader("X-Test", "stripPrefix")) + .filter(addRequestHeader("X-Test", "stripPrefixLb")) .filter(lb("httpbin")) .build(); // @formatter:on @@ -1185,7 +1193,8 @@ public class ServerMvcIntegrationTests { public RouterFunction gatewayRouterFunctionsCircuitBreakerFallback() { // @formatter:off return route("testcircuitbreakerfallback") - .route(path("/anything/circuitbreakerfallback"), http(URI.create("https://nonexistantdomain.com1234"))) + .route(path("/anything/circuitbreakerfallback"), http()) + .before(uri("https://nonexistantdomain.com1234")) .filter(circuitBreaker("mycb1", "/hello")) .build(); // @formatter:on @@ -1195,7 +1204,8 @@ public class ServerMvcIntegrationTests { public RouterFunction gatewayRouterFunctionsCircuitBreakerFallbackToGatewayRoute() { // @formatter:off return route("testcircuitbreakergatewayfallback") - .route(path("/anything/circuitbreakergatewayfallback"), http(URI.create("https://nonexistantdomain.com1234"))) + .route(path("/anything/circuitbreakergatewayfallback"), http()) + .before(uri("https://nonexistantdomain.com1234")) .filter(circuitBreaker("mycb2", URI.create("forward:/anything/gatewayfallback"))) .build() .and(route("testgatewayfallback") @@ -1686,6 +1696,16 @@ public class ServerMvcIntegrationTests { } + @RestController + protected static class StaticPortController { + + @GetMapping(path = "/anything/staticport", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity messageEvents() { + return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).build(); + } + + } + @RestController protected static class EventController { diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java index ae6a7ee2..8c8a12ee 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java @@ -156,6 +156,18 @@ class BeforeFilterFunctionsTests { assertThat(result.uri().toString()).hasToString("http://localhost/depth3"); } + @Test + void stripPrefixWithPort() { + MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost:77/depth1/depth2/depth3") + .buildRequest(null); + + ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList()); + + ServerRequest result = BeforeFilterFunctions.stripPrefix(2).apply(request); + + assertThat(result.uri().toString()).hasToString("http://localhost:77/depth3"); + } + @Test void stripPrefixWithEncodedPath() { MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/depth1/depth2/depth3/é") diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/StripPrefixStaticPortTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/StripPrefixStaticPortTests.java new file mode 100644 index 00000000..4052a3f6 --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/StripPrefixStaticPortTests.java @@ -0,0 +1,141 @@ +/* + * Copyright 2013-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.gateway.server.mvc.filter; + +import java.util.Map; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.cloud.gateway.server.mvc.test.HttpbinTestcontainers; +import org.springframework.cloud.gateway.server.mvc.test.TestLoadBalancerConfig; +import org.springframework.cloud.gateway.server.mvc.test.client.TestRestClient; +import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; +import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.function.RouterFunction; +import org.springframework.web.servlet.function.ServerResponse; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions.stripPrefix; +import static org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions.uri; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; +import static org.springframework.cloud.gateway.server.mvc.test.TestUtils.getMap; + +@SuppressWarnings("unchecked") +@SpringBootTest(properties = {}, webEnvironment = WebEnvironment.RANDOM_PORT) +@ActiveProfiles("stripprefixstaticport") +@ContextConfiguration(initializers = HttpbinTestcontainers.class) +public class StripPrefixStaticPortTests { + + @Autowired + TestRestClient restClient; + + @BeforeAll + static void beforeAll() { + HttpbinTestcontainers.initializeSystemProperties(); + } + + @Test + public void stripPrefixStaticPort() { + restClient.get() + .uri("/long/path/to/anything/staticport") + .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) + .containsEntry(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, "/long/path/to"); + }); + } + + @Test + public void stripPrefixStaticPortDsl() { + restClient.get() + .uri("/long/path/to/anything/staticportdsl") + .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) + .containsEntry(XForwardedRequestHeadersFilter.X_FORWARDED_PREFIX_HEADER, "/long/path/to"); + }); + } + + @SpringBootConfiguration + @EnableAutoConfiguration + @LoadBalancerClient(name = "httpbin", configuration = TestLoadBalancerConfig.Httpbin.class) + protected static class TestConfiguration { + + @Bean + StaticPortController staticPortController() { + return new StaticPortController(); + } + + @Bean + public RouterFunction gatewayRouterFunctionsStripPrefixStaticPortDsl(Environment env) { + // @formatter:off + return route("teststripprefixstaticportdsl") + .GET("/long/path/to/anything/staticportdsl", http()) + .filter(uri(env.getProperty("strip.prefix.static.uri"))) + .filter(stripPrefix(3)) + .build(); + // @formatter:on + } + + } + + @RestController + protected static class StaticPortController { + + @GetMapping(path = "/anything/staticport", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity messageEvents() { + return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).build(); + } + + } + +} diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/test/HttpbinTestcontainers.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/test/HttpbinTestcontainers.java index e1dc98a4..95345fce 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/test/HttpbinTestcontainers.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/test/HttpbinTestcontainers.java @@ -18,8 +18,6 @@ package org.springframework.cloud.gateway.server.mvc.test; import java.util.HashMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; import org.testcontainers.utility.DockerImageName; @@ -35,8 +33,6 @@ public class HttpbinTestcontainers implements ApplicationContextInitializer map = new HashMap<>(); map.put("httpbin.port", String.valueOf(mappedPort)); diff --git a/spring-cloud-gateway-server-mvc/src/test/resources/application-stripprefixstaticport.yml b/spring-cloud-gateway-server-mvc/src/test/resources/application-stripprefixstaticport.yml new file mode 100644 index 00000000..c27795c3 --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/test/resources/application-stripprefixstaticport.yml @@ -0,0 +1,12 @@ +strip.prefix.static.uri: http://${httpbin.host}:${httpbin.port} +spring.cloud.gateway.mvc: + routes: + - id: strip_prefix_static_port_config + uri: ${strip.prefix.static.uri} + predicates: + - Path=/long/path/to/anything/staticport + filters: + - StripPrefix=3 +logging: + level: + org.springframework.cloud.gateway.server.mvc: TRACE From 8f534ebfa767cee814b8bec8e4626aed6c99b7ee Mon Sep 17 00:00:00 2001 From: spencergibb Date: Fri, 28 Mar 2025 15:13:58 -0400 Subject: [PATCH 40/43] polishing modifyresponsebody.adoc --- .../filters/modifyresponsebody.adoc | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/modifyresponsebody.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/modifyresponsebody.adoc index b7bd7163..da97b6e6 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/modifyresponsebody.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-mvc/filters/modifyresponsebody.adoc @@ -5,20 +5,29 @@ You can use the `ModifyResponseBody` filter to modify the response body before i NOTE: This filter can be configured only by using the Java DSL. -The following listing shows how to modify a response body filter: +The following listing shows how to modify a response body filter: +.GatewaySampleApplication.java [source,java] ---- +import static org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.modifyResponseBody; +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri; +import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; + @Bean public RouterFunction gatewayRouterFunctionsModifyResponseBodySimple() { return route("modify_response_body") .GET("/anything/modifyresponsebody", http()) - .before(new HttpbinUriResolver()) + .before(uri("https://example.org")) .after(modifyResponseBody(String.class, String.class, null, (request, response, s) -> s.replace("fooval", "FOOVAL"))) .build(); } ---- + +The sample above does not change the content type or do anything dynamic. Below, the sample changes the content type and dynamically modifies the content. + .GatewaySampleApplication.java [source,java] ---- From 03dcf23e55214fce07d951abcfc33a999e0cfc34 Mon Sep 17 00:00:00 2001 From: spencergibb Date: Fri, 28 Mar 2025 17:12:18 -0400 Subject: [PATCH 41/43] Adds fn and stream to DefaultHandlerSupplier --- .../mvc/handler/DefaultHandlerSupplier.java | 21 +++++++++++++++++++ .../server/mvc/handler/HandlerFunctions.java | 12 ----------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/DefaultHandlerSupplier.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/DefaultHandlerSupplier.java index 91304f9f..0adc142e 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/DefaultHandlerSupplier.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/DefaultHandlerSupplier.java @@ -35,6 +35,16 @@ class DefaultHandlerSupplier implements HandlerSupplier { return Arrays.asList(getClass().getMethods()); } + public static HandlerDiscoverer.Result fn(RouteProperties routeProperties) { + // fn:fnName + return fn(routeProperties.getUri().getSchemeSpecificPart()); + } + + public static HandlerDiscoverer.Result fn(String functionName) { + return new HandlerDiscoverer.Result(HandlerFunctions.fn(functionName), Collections.emptyList(), + Collections.emptyList()); + } + public static HandlerDiscoverer.Result forward(RouteProperties routeProperties) { return forward(routeProperties.getId(), routeProperties.getUri()); } @@ -68,6 +78,17 @@ class DefaultHandlerSupplier implements HandlerSupplier { return getResult(id, uri, HandlerFunctions.no()); } + // for properties + public static HandlerDiscoverer.Result stream(RouteProperties routeProperties) { + // stream:bindingName + return stream(routeProperties.getUri().getSchemeSpecificPart()); + } + + public static HandlerDiscoverer.Result stream(String bindingName) { + return new HandlerDiscoverer.Result(HandlerFunctions.stream(bindingName), Collections.emptyList(), + Collections.emptyList()); + } + private static HandlerDiscoverer.Result getResult(String id, URI uri, HandlerFunction handlerFunction) { HandlerFilterFunction setId = setIdFilter(id); diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerFunctions.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerFunctions.java index 5963e0f4..14ea609c 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerFunctions.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerFunctions.java @@ -55,12 +55,6 @@ public abstract class HandlerFunctions { } - // for properties - public static HandlerFunction fn(RouteProperties routeProperties) { - // fn:fnName - return fn(routeProperties.getUri().getSchemeSpecificPart()); - } - public static HandlerFunction fn(String functionName) { Assert.hasText(functionName, "'functionName' must not be empty"); return request -> { @@ -107,12 +101,6 @@ public abstract class HandlerFunctions { }; } - // for properties - public static HandlerFunction stream(RouteProperties routeProperties) { - // stream:bindingName - return stream(routeProperties.getUri().getSchemeSpecificPart()); - } - public static HandlerFunction stream(String bindingName) { Assert.hasText(bindingName, "'bindingName' must not be empty"); // TODO: validate bindingName From 7951cef77f4c9f366d6944b7fab5220c19f177f9 Mon Sep 17 00:00:00 2001 From: spring-builds Date: Mon, 7 Apr 2025 11:52:16 +0000 Subject: [PATCH 42/43] Update SNAPSHOT to 4.3.0-M3 --- docs/pom.xml | 2 +- pom.xml | 12 ++++++------ spring-cloud-gateway-dependencies/pom.xml | 4 ++-- spring-cloud-gateway-integration-tests/grpc/pom.xml | 2 +- spring-cloud-gateway-integration-tests/http2/pom.xml | 2 +- .../mvc-failure-analyzer/pom.xml | 2 +- spring-cloud-gateway-integration-tests/pom.xml | 2 +- spring-cloud-gateway-mvc/pom.xml | 2 +- spring-cloud-gateway-proxyexchange-webflux/pom.xml | 2 +- spring-cloud-gateway-proxyexchange-webmvc/pom.xml | 2 +- spring-cloud-gateway-sample/pom.xml | 2 +- spring-cloud-gateway-server-mvc/pom.xml | 2 +- spring-cloud-gateway-server-webflux/pom.xml | 2 +- spring-cloud-gateway-server-webmvc/pom.xml | 2 +- spring-cloud-gateway-server/pom.xml | 2 +- spring-cloud-gateway-webflux/pom.xml | 2 +- spring-cloud-starter-gateway-mvc/pom.xml | 2 +- spring-cloud-starter-gateway-server-webflux/pom.xml | 2 +- spring-cloud-starter-gateway-server-webmvc/pom.xml | 2 +- spring-cloud-starter-gateway/pom.xml | 2 +- 20 files changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 709dc57e..5ec4b05e 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-SNAPSHOT + 4.3.0-M3 spring-cloud-gateway-docs jar diff --git a/pom.xml b/pom.xml index d1c8473c..bff4661e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-SNAPSHOT + 4.3.0-M3 pom Spring Cloud Gateway @@ -15,7 +15,7 @@ org.springframework.cloud spring-cloud-build - 4.3.0-SNAPSHOT + 4.3.0-M3 @@ -55,10 +55,10 @@ 1.0.8.RELEASE 17 2.3.0 - 3.3.0-SNAPSHOT - 4.3.0-SNAPSHOT - 4.3.0-SNAPSHOT - 4.3.0-SNAPSHOT + 3.3.0-M3 + 4.3.0-M3 + 4.3.0-M3 + 4.3.0-M3 diff --git a/spring-cloud-gateway-dependencies/pom.xml b/spring-cloud-gateway-dependencies/pom.xml index 66103cf7..cbede087 100644 --- a/spring-cloud-gateway-dependencies/pom.xml +++ b/spring-cloud-gateway-dependencies/pom.xml @@ -6,12 +6,12 @@ spring-cloud-dependencies-parent org.springframework.cloud - 4.3.0-SNAPSHOT + 4.3.0-M3 spring-cloud-gateway-dependencies - 4.3.0-SNAPSHOT + 4.3.0-M3 pom spring-cloud-gateway-dependencies diff --git a/spring-cloud-gateway-integration-tests/grpc/pom.xml b/spring-cloud-gateway-integration-tests/grpc/pom.xml index 6842696a..e7bec499 100644 --- a/spring-cloud-gateway-integration-tests/grpc/pom.xml +++ b/spring-cloud-gateway-integration-tests/grpc/pom.xml @@ -18,7 +18,7 @@ org.springframework.cloud spring-cloud-gateway-integration-tests - 4.3.0-SNAPSHOT + 4.3.0-M3 .. diff --git a/spring-cloud-gateway-integration-tests/http2/pom.xml b/spring-cloud-gateway-integration-tests/http2/pom.xml index 49e2de46..cf8a3b0b 100644 --- a/spring-cloud-gateway-integration-tests/http2/pom.xml +++ b/spring-cloud-gateway-integration-tests/http2/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway-integration-tests - 4.3.0-SNAPSHOT + 4.3.0-M3 .. diff --git a/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml b/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml index 810172ab..ba1ecc38 100644 --- a/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml +++ b/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway-integration-tests - 4.3.0-SNAPSHOT + 4.3.0-M3 .. diff --git a/spring-cloud-gateway-integration-tests/pom.xml b/spring-cloud-gateway-integration-tests/pom.xml index 85b3c76d..a40c03cc 100644 --- a/spring-cloud-gateway-integration-tests/pom.xml +++ b/spring-cloud-gateway-integration-tests/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-SNAPSHOT + 4.3.0-M3 .. diff --git a/spring-cloud-gateway-mvc/pom.xml b/spring-cloud-gateway-mvc/pom.xml index 14d0d2aa..728fdf3e 100644 --- a/spring-cloud-gateway-mvc/pom.xml +++ b/spring-cloud-gateway-mvc/pom.xml @@ -11,7 +11,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-SNAPSHOT + 4.3.0-M3 .. diff --git a/spring-cloud-gateway-proxyexchange-webflux/pom.xml b/spring-cloud-gateway-proxyexchange-webflux/pom.xml index 3243a645..a4724c81 100644 --- a/spring-cloud-gateway-proxyexchange-webflux/pom.xml +++ b/spring-cloud-gateway-proxyexchange-webflux/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-SNAPSHOT + 4.3.0-M3 .. spring-cloud-gateway-proxyexchange-webflux diff --git a/spring-cloud-gateway-proxyexchange-webmvc/pom.xml b/spring-cloud-gateway-proxyexchange-webmvc/pom.xml index 8d04d069..9ae6db77 100644 --- a/spring-cloud-gateway-proxyexchange-webmvc/pom.xml +++ b/spring-cloud-gateway-proxyexchange-webmvc/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-SNAPSHOT + 4.3.0-M3 .. spring-cloud-gateway-proxyexchange-webmvc diff --git a/spring-cloud-gateway-sample/pom.xml b/spring-cloud-gateway-sample/pom.xml index 20671002..de90648b 100644 --- a/spring-cloud-gateway-sample/pom.xml +++ b/spring-cloud-gateway-sample/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-SNAPSHOT + 4.3.0-M3 .. diff --git a/spring-cloud-gateway-server-mvc/pom.xml b/spring-cloud-gateway-server-mvc/pom.xml index e9f6b80c..8687154a 100644 --- a/spring-cloud-gateway-server-mvc/pom.xml +++ b/spring-cloud-gateway-server-mvc/pom.xml @@ -22,7 +22,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-SNAPSHOT + 4.3.0-M3 .. spring-cloud-gateway-server-mvc diff --git a/spring-cloud-gateway-server-webflux/pom.xml b/spring-cloud-gateway-server-webflux/pom.xml index 4f9f40e0..f8d13a02 100644 --- a/spring-cloud-gateway-server-webflux/pom.xml +++ b/spring-cloud-gateway-server-webflux/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-SNAPSHOT + 4.3.0-M3 .. spring-cloud-gateway-server-webflux diff --git a/spring-cloud-gateway-server-webmvc/pom.xml b/spring-cloud-gateway-server-webmvc/pom.xml index 9792dfe9..c0c2e088 100644 --- a/spring-cloud-gateway-server-webmvc/pom.xml +++ b/spring-cloud-gateway-server-webmvc/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-SNAPSHOT + 4.3.0-M3 .. spring-cloud-gateway-server-webmvc diff --git a/spring-cloud-gateway-server/pom.xml b/spring-cloud-gateway-server/pom.xml index 2a61283f..c4e7174a 100644 --- a/spring-cloud-gateway-server/pom.xml +++ b/spring-cloud-gateway-server/pom.xml @@ -7,7 +7,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-SNAPSHOT + 4.3.0-M3 .. spring-cloud-gateway-server diff --git a/spring-cloud-gateway-webflux/pom.xml b/spring-cloud-gateway-webflux/pom.xml index 5859d52d..6a991f44 100644 --- a/spring-cloud-gateway-webflux/pom.xml +++ b/spring-cloud-gateway-webflux/pom.xml @@ -11,7 +11,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-SNAPSHOT + 4.3.0-M3 .. diff --git a/spring-cloud-starter-gateway-mvc/pom.xml b/spring-cloud-starter-gateway-mvc/pom.xml index e86d2d1f..43c74463 100644 --- a/spring-cloud-starter-gateway-mvc/pom.xml +++ b/spring-cloud-starter-gateway-mvc/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-SNAPSHOT + 4.3.0-M3 .. spring-cloud-starter-gateway-mvc diff --git a/spring-cloud-starter-gateway-server-webflux/pom.xml b/spring-cloud-starter-gateway-server-webflux/pom.xml index ab94fcc5..9830daf7 100644 --- a/spring-cloud-starter-gateway-server-webflux/pom.xml +++ b/spring-cloud-starter-gateway-server-webflux/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-SNAPSHOT + 4.3.0-M3 .. spring-cloud-starter-gateway-server-webflux diff --git a/spring-cloud-starter-gateway-server-webmvc/pom.xml b/spring-cloud-starter-gateway-server-webmvc/pom.xml index 00420392..6a65f53b 100644 --- a/spring-cloud-starter-gateway-server-webmvc/pom.xml +++ b/spring-cloud-starter-gateway-server-webmvc/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-SNAPSHOT + 4.3.0-M3 .. spring-cloud-starter-gateway-server-webmvc diff --git a/spring-cloud-starter-gateway/pom.xml b/spring-cloud-starter-gateway/pom.xml index 8de3f70e..6693e7d2 100644 --- a/spring-cloud-starter-gateway/pom.xml +++ b/spring-cloud-starter-gateway/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-SNAPSHOT + 4.3.0-M3 .. spring-cloud-starter-gateway From e116c072872d3a3151a08411b8715ffe2d052641 Mon Sep 17 00:00:00 2001 From: spring-builds Date: Mon, 7 Apr 2025 11:54:43 +0000 Subject: [PATCH 43/43] Going back to snapshots --- docs/pom.xml | 2 +- pom.xml | 12 ++++++------ spring-cloud-gateway-dependencies/pom.xml | 4 ++-- spring-cloud-gateway-integration-tests/grpc/pom.xml | 2 +- spring-cloud-gateway-integration-tests/http2/pom.xml | 2 +- .../mvc-failure-analyzer/pom.xml | 2 +- spring-cloud-gateway-integration-tests/pom.xml | 2 +- spring-cloud-gateway-mvc/pom.xml | 2 +- spring-cloud-gateway-proxyexchange-webflux/pom.xml | 2 +- spring-cloud-gateway-proxyexchange-webmvc/pom.xml | 2 +- spring-cloud-gateway-sample/pom.xml | 2 +- spring-cloud-gateway-server-mvc/pom.xml | 2 +- spring-cloud-gateway-server-webflux/pom.xml | 2 +- spring-cloud-gateway-server-webmvc/pom.xml | 2 +- spring-cloud-gateway-server/pom.xml | 2 +- spring-cloud-gateway-webflux/pom.xml | 2 +- spring-cloud-starter-gateway-mvc/pom.xml | 2 +- spring-cloud-starter-gateway-server-webflux/pom.xml | 2 +- spring-cloud-starter-gateway-server-webmvc/pom.xml | 2 +- spring-cloud-starter-gateway/pom.xml | 2 +- 20 files changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 5ec4b05e..709dc57e 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-M3 + 4.3.0-SNAPSHOT spring-cloud-gateway-docs jar diff --git a/pom.xml b/pom.xml index bff4661e..d1c8473c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-M3 + 4.3.0-SNAPSHOT pom Spring Cloud Gateway @@ -15,7 +15,7 @@ org.springframework.cloud spring-cloud-build - 4.3.0-M3 + 4.3.0-SNAPSHOT @@ -55,10 +55,10 @@ 1.0.8.RELEASE 17 2.3.0 - 3.3.0-M3 - 4.3.0-M3 - 4.3.0-M3 - 4.3.0-M3 + 3.3.0-SNAPSHOT + 4.3.0-SNAPSHOT + 4.3.0-SNAPSHOT + 4.3.0-SNAPSHOT diff --git a/spring-cloud-gateway-dependencies/pom.xml b/spring-cloud-gateway-dependencies/pom.xml index cbede087..66103cf7 100644 --- a/spring-cloud-gateway-dependencies/pom.xml +++ b/spring-cloud-gateway-dependencies/pom.xml @@ -6,12 +6,12 @@ spring-cloud-dependencies-parent org.springframework.cloud - 4.3.0-M3 + 4.3.0-SNAPSHOT spring-cloud-gateway-dependencies - 4.3.0-M3 + 4.3.0-SNAPSHOT pom spring-cloud-gateway-dependencies diff --git a/spring-cloud-gateway-integration-tests/grpc/pom.xml b/spring-cloud-gateway-integration-tests/grpc/pom.xml index e7bec499..6842696a 100644 --- a/spring-cloud-gateway-integration-tests/grpc/pom.xml +++ b/spring-cloud-gateway-integration-tests/grpc/pom.xml @@ -18,7 +18,7 @@ org.springframework.cloud spring-cloud-gateway-integration-tests - 4.3.0-M3 + 4.3.0-SNAPSHOT .. diff --git a/spring-cloud-gateway-integration-tests/http2/pom.xml b/spring-cloud-gateway-integration-tests/http2/pom.xml index cf8a3b0b..49e2de46 100644 --- a/spring-cloud-gateway-integration-tests/http2/pom.xml +++ b/spring-cloud-gateway-integration-tests/http2/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway-integration-tests - 4.3.0-M3 + 4.3.0-SNAPSHOT .. diff --git a/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml b/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml index ba1ecc38..810172ab 100644 --- a/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml +++ b/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway-integration-tests - 4.3.0-M3 + 4.3.0-SNAPSHOT .. diff --git a/spring-cloud-gateway-integration-tests/pom.xml b/spring-cloud-gateway-integration-tests/pom.xml index a40c03cc..85b3c76d 100644 --- a/spring-cloud-gateway-integration-tests/pom.xml +++ b/spring-cloud-gateway-integration-tests/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-M3 + 4.3.0-SNAPSHOT .. diff --git a/spring-cloud-gateway-mvc/pom.xml b/spring-cloud-gateway-mvc/pom.xml index 728fdf3e..14d0d2aa 100644 --- a/spring-cloud-gateway-mvc/pom.xml +++ b/spring-cloud-gateway-mvc/pom.xml @@ -11,7 +11,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-M3 + 4.3.0-SNAPSHOT .. diff --git a/spring-cloud-gateway-proxyexchange-webflux/pom.xml b/spring-cloud-gateway-proxyexchange-webflux/pom.xml index a4724c81..3243a645 100644 --- a/spring-cloud-gateway-proxyexchange-webflux/pom.xml +++ b/spring-cloud-gateway-proxyexchange-webflux/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-M3 + 4.3.0-SNAPSHOT .. spring-cloud-gateway-proxyexchange-webflux diff --git a/spring-cloud-gateway-proxyexchange-webmvc/pom.xml b/spring-cloud-gateway-proxyexchange-webmvc/pom.xml index 9ae6db77..8d04d069 100644 --- a/spring-cloud-gateway-proxyexchange-webmvc/pom.xml +++ b/spring-cloud-gateway-proxyexchange-webmvc/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-M3 + 4.3.0-SNAPSHOT .. spring-cloud-gateway-proxyexchange-webmvc diff --git a/spring-cloud-gateway-sample/pom.xml b/spring-cloud-gateway-sample/pom.xml index de90648b..20671002 100644 --- a/spring-cloud-gateway-sample/pom.xml +++ b/spring-cloud-gateway-sample/pom.xml @@ -16,7 +16,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-M3 + 4.3.0-SNAPSHOT .. diff --git a/spring-cloud-gateway-server-mvc/pom.xml b/spring-cloud-gateway-server-mvc/pom.xml index 8687154a..e9f6b80c 100644 --- a/spring-cloud-gateway-server-mvc/pom.xml +++ b/spring-cloud-gateway-server-mvc/pom.xml @@ -22,7 +22,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-M3 + 4.3.0-SNAPSHOT .. spring-cloud-gateway-server-mvc diff --git a/spring-cloud-gateway-server-webflux/pom.xml b/spring-cloud-gateway-server-webflux/pom.xml index f8d13a02..4f9f40e0 100644 --- a/spring-cloud-gateway-server-webflux/pom.xml +++ b/spring-cloud-gateway-server-webflux/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-M3 + 4.3.0-SNAPSHOT .. spring-cloud-gateway-server-webflux diff --git a/spring-cloud-gateway-server-webmvc/pom.xml b/spring-cloud-gateway-server-webmvc/pom.xml index c0c2e088..9792dfe9 100644 --- a/spring-cloud-gateway-server-webmvc/pom.xml +++ b/spring-cloud-gateway-server-webmvc/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-M3 + 4.3.0-SNAPSHOT .. spring-cloud-gateway-server-webmvc diff --git a/spring-cloud-gateway-server/pom.xml b/spring-cloud-gateway-server/pom.xml index c4e7174a..2a61283f 100644 --- a/spring-cloud-gateway-server/pom.xml +++ b/spring-cloud-gateway-server/pom.xml @@ -7,7 +7,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-M3 + 4.3.0-SNAPSHOT .. spring-cloud-gateway-server diff --git a/spring-cloud-gateway-webflux/pom.xml b/spring-cloud-gateway-webflux/pom.xml index 6a991f44..5859d52d 100644 --- a/spring-cloud-gateway-webflux/pom.xml +++ b/spring-cloud-gateway-webflux/pom.xml @@ -11,7 +11,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-M3 + 4.3.0-SNAPSHOT .. diff --git a/spring-cloud-starter-gateway-mvc/pom.xml b/spring-cloud-starter-gateway-mvc/pom.xml index 43c74463..e86d2d1f 100644 --- a/spring-cloud-starter-gateway-mvc/pom.xml +++ b/spring-cloud-starter-gateway-mvc/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-M3 + 4.3.0-SNAPSHOT .. spring-cloud-starter-gateway-mvc diff --git a/spring-cloud-starter-gateway-server-webflux/pom.xml b/spring-cloud-starter-gateway-server-webflux/pom.xml index 9830daf7..ab94fcc5 100644 --- a/spring-cloud-starter-gateway-server-webflux/pom.xml +++ b/spring-cloud-starter-gateway-server-webflux/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-M3 + 4.3.0-SNAPSHOT .. spring-cloud-starter-gateway-server-webflux diff --git a/spring-cloud-starter-gateway-server-webmvc/pom.xml b/spring-cloud-starter-gateway-server-webmvc/pom.xml index 6a65f53b..00420392 100644 --- a/spring-cloud-starter-gateway-server-webmvc/pom.xml +++ b/spring-cloud-starter-gateway-server-webmvc/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-M3 + 4.3.0-SNAPSHOT .. spring-cloud-starter-gateway-server-webmvc diff --git a/spring-cloud-starter-gateway/pom.xml b/spring-cloud-starter-gateway/pom.xml index 6693e7d2..8de3f70e 100644 --- a/spring-cloud-starter-gateway/pom.xml +++ b/spring-cloud-starter-gateway/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-gateway - 4.3.0-M3 + 4.3.0-SNAPSHOT .. spring-cloud-starter-gateway