diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index be248adf..801097a0 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -19,7 +19,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v4
with:
- distribution: 'temurin'
+ distribution: 'liberica'
java-version: '17'
cache: 'maven'
- name: Build with Maven
diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-webflux/gatewayfilter-factories/seturi-factory.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-webflux/gatewayfilter-factories/seturi-factory.adoc
new file mode 100644
index 00000000..2251d13b
--- /dev/null
+++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-webflux/gatewayfilter-factories/seturi-factory.adoc
@@ -0,0 +1,26 @@
+[[seturi-gatewayfilter-factory]]
+= `SetRequestUri` `GatewayFilter` Factory
+
+The `SetRequestUri` `GatewayFilter` factory takes a `uri` parameter.
+It offers a simple way to manipulate the request uri by allowing templated segments of the path.
+This uses the URI templates from Spring Framework.
+Multiple matching segments are allowed.
+The following listing configures a `SetRequestUri` `GatewayFilter`:
+
+.application.yml
+[source,yaml]
+----
+spring:
+ cloud:
+ gateway:
+ routes:
+ - id: seturi_route
+ uri: no://op
+ predicates:
+ - Path=/{appId}/**
+ filters:
+ - SetRequestUri=http://{appId}.example.com
+----
+
+For a request path of `/red-application/blue`, this sets the uri to `http://red-application.example.com` before making the downstream request and the final url, including path is going to be `http://red-application.example.com/red-application/blue`
+
diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/starter.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/starter.adoc
index 6d3b7bd8..d8f759ce 100644
--- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/starter.adoc
+++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/starter.adoc
@@ -5,7 +5,7 @@
To include Spring Cloud Gateway Server Web MVC in your project, use the starter with a group ID of `org.springframework.cloud` and an artifact ID of `spring-cloud-starter-gateway-server-webmvc`.
See the https://projects.spring.io/spring-cloud/[Spring Cloud Project page] for details on setting up your build system with the current Spring Cloud Release Train.
-If you include the starter, but you do not want the gateway to be enabled, set `spring.cloud.gateway.mvc.enabled=false`.
+If you include the starter, but you do not want the gateway to be enabled, set `spring.cloud.gateway.server.webmvc.enabled=false`.
IMPORTANT: Spring Cloud Gateway Server MVC is built on https://spring.io/projects/spring-boot#learn[Spring Boot] and https://docs.spring.io/spring-framework/reference/web/webmvc-functional.html[Spring WebMvc.fn].
As a consequence, many of the asynchronous or reactive libraries may not apply when you use Spring Cloud Gateway Server MVC.
diff --git a/spring-cloud-gateway-server-mvc/pom.xml b/spring-cloud-gateway-server-mvc/pom.xml
index ad20bf03..01c47d60 100644
--- a/spring-cloud-gateway-server-mvc/pom.xml
+++ b/spring-cloud-gateway-server-mvc/pom.xml
@@ -146,4 +146,30 @@
test
+
+
+
+ github_actions
+
+
+
+ env.GITHUB_ACTIONS
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ host
+
+
+
+
+
+
+
diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/GatewayMvcClassPathWarningAutoConfiguration.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/GatewayMvcClassPathWarningAutoConfiguration.java
index 4fef7bc7..21173cf9 100644
--- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/GatewayMvcClassPathWarningAutoConfiguration.java
+++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/GatewayMvcClassPathWarningAutoConfiguration.java
@@ -22,11 +22,12 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.gateway.server.mvc.config.GatewayMvcProperties;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(GatewayServerMvcAutoConfiguration.class)
-@ConditionalOnProperty(name = "spring.cloud.gateway.mvc.enabled", matchIfMissing = true)
+@ConditionalOnProperty(name = GatewayMvcProperties.PREFIX + ".enabled", matchIfMissing = true)
public class GatewayMvcClassPathWarningAutoConfiguration {
private static final Log log = LogFactory.getLog(GatewayMvcClassPathWarningAutoConfiguration.class);
diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfiguration.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfiguration.java
index 37cd5245..2aecabe1 100644
--- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfiguration.java
+++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfiguration.java
@@ -78,7 +78,7 @@ import org.springframework.web.client.RestClient;
@AutoConfiguration(after = { HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class,
RestClientAutoConfiguration.class, FilterAutoConfiguration.class, HandlerFunctionAutoConfiguration.class,
PredicateAutoConfiguration.class })
-@ConditionalOnProperty(name = "spring.cloud.gateway.mvc.enabled", matchIfMissing = true)
+@ConditionalOnProperty(name = GatewayMvcProperties.PREFIX + ".enabled", matchIfMissing = true)
@Import(GatewayMvcPropertiesBeanDefinitionRegistrar.class)
@ImportRuntimeHints(GatewayMvcAotRuntimeHintsRegistrar.class)
public class GatewayServerMvcAutoConfiguration {
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 80e859ac..ce2b2404 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
@@ -45,7 +45,6 @@ import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.UriTemplate;
-import org.springframework.web.util.UriUtils;
import static org.springframework.cloud.gateway.server.mvc.common.MvcUtils.CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR;
import static org.springframework.util.CollectionUtils.unmodifiableMultiValueMap;
@@ -216,7 +215,7 @@ public abstract class BeforeFilterFunctions {
MultiValueMap queryParams = new LinkedMultiValueMap<>(request.params());
queryParams.remove(name);
- MultiValueMap encodedQueryParams = UriUtils.encodeQueryParams(queryParams);
+ MultiValueMap encodedQueryParams = MvcUtils.encodeQueryParams(queryParams);
// remove from uri
URI newUri = UriComponentsBuilder.fromUri(request.uri())
diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfigurationTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfigurationTests.java
index 0eb0ff71..01f8f9f4 100644
--- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfigurationTests.java
+++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfigurationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013-2024 the original author or authors.
+ * 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.
@@ -34,6 +34,7 @@ import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
import org.springframework.boot.http.client.SimpleClientHttpRequestFactoryBuilder;
+import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.gateway.server.mvc.filter.FilterAutoConfiguration;
import org.springframework.cloud.gateway.server.mvc.filter.FormFilter;
@@ -47,6 +48,7 @@ import org.springframework.cloud.gateway.server.mvc.filter.WeightCalculatorFilte
import org.springframework.cloud.gateway.server.mvc.filter.XForwardedRequestHeadersFilter;
import org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctionAutoConfiguration;
import org.springframework.cloud.gateway.server.mvc.predicate.PredicateAutoConfiguration;
+import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
@@ -204,6 +206,27 @@ public class GatewayServerMvcAutoConfigurationTests {
assertThat(builder).isInstanceOf(SimpleClientHttpRequestFactoryBuilder.class);
}
+ @Test
+ void loadBalancerFunctionHandlerAdded() {
+ new ApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(FilterAutoConfiguration.class, PredicateAutoConfiguration.class,
+ HandlerFunctionAutoConfiguration.class, GatewayServerMvcAutoConfiguration.class,
+ HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class,
+ RestClientAutoConfiguration.class))
+ .run(context -> assertThat(context).hasBean("lbHandlerFunctionDefinition"));
+ }
+
+ @Test
+ void loadBalancerFunctionHandlerNotAddedWhenNoLoadBalancerClientOnClasspath() {
+ new ApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(FilterAutoConfiguration.class, PredicateAutoConfiguration.class,
+ HandlerFunctionAutoConfiguration.class, GatewayServerMvcAutoConfiguration.class,
+ HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class,
+ RestClientAutoConfiguration.class))
+ .withClassLoader(new FilteredClassLoader(LoadBalancerClient.class))
+ .run(context -> assertThat(context).doesNotHaveBean("lbHandlerFunctionDefinition"));
+ }
+
@SpringBootConfiguration
@EnableAutoConfiguration
static class TestConfig {
diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcLoadBalancerIntegrationTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcLoadBalancerIntegrationTests.java
new file mode 100644
index 00000000..d2e5889b
--- /dev/null
+++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/ServerMvcLoadBalancerIntegrationTests.java
@@ -0,0 +1,62 @@
+/*
+ * 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;
+
+import org.junit.jupiter.api.Test;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.cloud.gateway.server.mvc.filter.FilterAutoConfiguration;
+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.test.context.ActiveProfiles;
+import org.springframework.test.context.ContextConfiguration;
+
+/**
+ * Integration tests for {@link FilterAutoConfiguration.LoadBalancerHandlerConfiguration}.
+ *
+ * @author Olga Maciaszek-Sharma
+ *
+ */
+@SpringBootTest(classes = { ServerMvcLoadBalancerIntegrationTests.Config.class, FilterAutoConfiguration.class },
+ webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@ContextConfiguration(initializers = HttpbinTestcontainers.class)
+@ActiveProfiles("lb")
+public class ServerMvcLoadBalancerIntegrationTests {
+
+ @LocalServerPort
+ int port;
+
+ @Autowired
+ TestRestClient testRestClient;
+
+ @Test
+ void shouldUseLbHandlerFunctionDefinitionToResolveHost() {
+ testRestClient.get().uri("http://localhost:" + port + "/test").exchange().expectStatus().isOk();
+ }
+
+ @SpringBootApplication
+ @LoadBalancerClient(name = "httpbin", configuration = TestLoadBalancerConfig.Httpbin.class)
+ static class Config {
+
+ }
+
+}
diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/VanillaRouterFunctionTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/VanillaRouterFunctionTests.java
index fd96c938..81d1128c 100644
--- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/VanillaRouterFunctionTests.java
+++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/VanillaRouterFunctionTests.java
@@ -43,7 +43,7 @@ import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFuncti
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.host;
@SuppressWarnings("unchecked")
-@SpringBootTest(properties = { "spring.cloud.gateway.mvc.http-client.type=jdk" },
+@SpringBootTest(properties = { "spring.http.client.factory=jdk" },
webEnvironment = WebEnvironment.RANDOM_PORT)
@ContextConfiguration(initializers = HttpbinTestcontainers.class)
public class VanillaRouterFunctionTests {
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 1ef0b2c7..90dd5e19 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
@@ -119,6 +119,7 @@ class BeforeFilterFunctionsTests {
MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/path")
.param("foo[]", "bar")
.param("baz", "qux")
+ .param("quux", "corge+")
.buildRequest(null);
ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList());
@@ -126,7 +127,9 @@ class BeforeFilterFunctionsTests {
ServerRequest result = BeforeFilterFunctions.rewriteRequestParameter("foo[]", "replacement[]").apply(request);
assertThat(result.param("foo[]")).isPresent().hasValue("replacement[]");
- assertThat(result.uri().toString()).hasToString("http://localhost/path?baz=qux&foo%5B%5D=replacement%5B%5D");
+ assertThat(result.param("quux")).isPresent().hasValue("corge+");
+ assertThat(result.uri().toString())
+ .hasToString("http://localhost/path?quux=corge%2B&baz=qux&foo%5B%5D=replacement%5B%5D");
}
@Test
diff --git a/spring-cloud-gateway-server-mvc/src/test/resources/application-functionhandlerconfigtests.yml b/spring-cloud-gateway-server-mvc/src/test/resources/application-functionhandlerconfigtests.yml
index c7430ca4..847e7057 100644
--- a/spring-cloud-gateway-server-mvc/src/test/resources/application-functionhandlerconfigtests.yml
+++ b/spring-cloud-gateway-server-mvc/src/test/resources/application-functionhandlerconfigtests.yml
@@ -1,4 +1,4 @@
-spring.cloud.gateway.mvc:
+spring.cloud.gateway.server.webmvc:
routesMap:
testsimplefunction:
uri: fn:upper
diff --git a/spring-cloud-gateway-server-mvc/src/test/resources/application-lb.yml b/spring-cloud-gateway-server-mvc/src/test/resources/application-lb.yml
new file mode 100644
index 00000000..9d0437b3
--- /dev/null
+++ b/spring-cloud-gateway-server-mvc/src/test/resources/application-lb.yml
@@ -0,0 +1,11 @@
+spring:
+ cloud:
+ gateway:
+ mvc:
+ routes:
+ - id: test
+ uri: lb://httpbin
+ predicates:
+ - Path=/test/**
+ filters:
+ - StripPrefix=1
\ No newline at end of file
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 a91fe14d..cd55ac63 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
@@ -1,4 +1,4 @@
-spring.cloud.gateway.mvc:
+spring.cloud.gateway.server.webmvc:
routesMap:
route1:
uri: https://example1.com
diff --git a/spring-cloud-gateway-server-mvc/src/test/resources/application-streamhandlerconfigtests.yml b/spring-cloud-gateway-server-mvc/src/test/resources/application-streamhandlerconfigtests.yml
index 3e97fc80..00cfc6b9 100644
--- a/spring-cloud-gateway-server-mvc/src/test/resources/application-streamhandlerconfigtests.yml
+++ b/spring-cloud-gateway-server-mvc/src/test/resources/application-streamhandlerconfigtests.yml
@@ -1,4 +1,4 @@
-spring.cloud.gateway.mvc:
+spring.cloud.gateway.server.webmvc:
routesMap:
testsimplestream:
uri: stream:hello-out-0
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
index c27795c3..184cfee5 100644
--- a/spring-cloud-gateway-server-mvc/src/test/resources/application-stripprefixstaticport.yml
+++ b/spring-cloud-gateway-server-mvc/src/test/resources/application-stripprefixstaticport.yml
@@ -1,5 +1,5 @@
strip.prefix.static.uri: http://${httpbin.host}:${httpbin.port}
-spring.cloud.gateway.mvc:
+spring.cloud.gateway.server.webmvc:
routes:
- id: strip_prefix_static_port_config
uri: ${strip.prefix.static.uri}
diff --git a/spring-cloud-gateway-server-mvc/src/test/resources/application-weightrequestpredicateintegrationtests.yml b/spring-cloud-gateway-server-mvc/src/test/resources/application-weightrequestpredicateintegrationtests.yml
index 69d927b8..f4dc97ad 100644
--- a/spring-cloud-gateway-server-mvc/src/test/resources/application-weightrequestpredicateintegrationtests.yml
+++ b/spring-cloud-gateway-server-mvc/src/test/resources/application-weightrequestpredicateintegrationtests.yml
@@ -1,4 +1,4 @@
-spring.cloud.gateway.mvc:
+spring.cloud.gateway.server.webmvc:
routes:
- id: weight_high_test
uri: https://examplel1.com
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 3acaa689..b5022c46 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
@@ -111,6 +111,7 @@ import org.springframework.cloud.gateway.filter.factory.SecureHeadersProperties;
import org.springframework.cloud.gateway.filter.factory.SetPathGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.SetRequestHeaderGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.SetRequestHostHeaderGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.SetRequestUriGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.SetResponseHeaderGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.SetStatusGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.StripPrefixGatewayFilterFactory;
@@ -722,6 +723,12 @@ public class GatewayAutoConfiguration {
return new RequestHeaderToRequestUriGatewayFilterFactory();
}
+ @Bean
+ @ConditionalOnEnabledFilter
+ public SetRequestUriGatewayFilterFactory setRequestUriGatewayFilterFactory() {
+ return new SetRequestUriGatewayFilterFactory();
+ }
+
@Bean
@ConditionalOnEnabledFilter
public RequestSizeGatewayFilterFactory requestSizeGatewayFilterFactory() {
diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestParameterGatewayFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestParameterGatewayFilterFactory.java
index 9d17f6ef..b8469337 100644
--- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestParameterGatewayFilterFactory.java
+++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestParameterGatewayFilterFactory.java
@@ -24,12 +24,12 @@ import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
-import org.springframework.web.util.UriUtils;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
import static org.springframework.util.CollectionUtils.unmodifiableMultiValueMap;
@@ -59,7 +59,8 @@ public class RemoveRequestParameterGatewayFilterFactory
queryParams.remove(config.getName());
try {
- MultiValueMap encodedQueryParams = UriUtils.encodeQueryParams(queryParams);
+ MultiValueMap encodedQueryParams = ServerWebExchangeUtils
+ .encodeQueryParams(queryParams);
URI newUri = UriComponentsBuilder.fromUri(request.getURI())
.replaceQueryParams(unmodifiableMultiValueMap(encodedQueryParams))
.build(true)
diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RewriteRequestParameterGatewayFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RewriteRequestParameterGatewayFilterFactory.java
index 02bccdaf..18f5e588 100644
--- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RewriteRequestParameterGatewayFilterFactory.java
+++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RewriteRequestParameterGatewayFilterFactory.java
@@ -24,13 +24,13 @@ import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
-import org.springframework.web.util.UriUtils;
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
import static org.springframework.util.CollectionUtils.unmodifiableMultiValueMap;
@@ -71,7 +71,8 @@ public class RewriteRequestParameterGatewayFilterFactory
}
try {
- MultiValueMap encodedQueryParams = UriUtils.encodeQueryParams(queryParams);
+ MultiValueMap encodedQueryParams = ServerWebExchangeUtils
+ .encodeQueryParams(queryParams);
URI uri = uriComponentsBuilder.replaceQueryParams(unmodifiableMultiValueMap(encodedQueryParams))
.build(true)
.toUri();
diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SetRequestUriGatewayFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SetRequestUriGatewayFilterFactory.java
new file mode 100644
index 00000000..1fbdfaed
--- /dev/null
+++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/SetRequestUriGatewayFilterFactory.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2013-2020 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.filter.factory;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.cloud.gateway.filter.GatewayFilter;
+import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.util.UriComponentsBuilder;
+
+import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
+import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.getUriTemplateVariables;
+
+/**
+ * This filter changes the request uri.
+ *
+ * @author Stepan Mikhailiuk
+ */
+public class SetRequestUriGatewayFilterFactory
+ extends AbstractChangeRequestUriGatewayFilterFactory {
+
+ private static final Logger log = LoggerFactory.getLogger(SetRequestUriGatewayFilterFactory.class);
+
+ public SetRequestUriGatewayFilterFactory() {
+ super(Config.class);
+ }
+
+ @Override
+ public List shortcutFieldOrder() {
+ return Arrays.asList(NAME_KEY);
+ }
+
+ @Override
+ public GatewayFilter apply(Config config) {
+ // AbstractChangeRequestUriGatewayFilterFactory.apply() returns
+ // OrderedGatewayFilter
+ OrderedGatewayFilter gatewayFilter = (OrderedGatewayFilter) super.apply(config);
+ return new OrderedGatewayFilter(gatewayFilter, gatewayFilter.getOrder()) {
+ @Override
+ public String toString() {
+ return filterToStringCreator(SetRequestUriGatewayFilterFactory.this)
+ .append("template", config.getTemplate())
+ .toString();
+ }
+ };
+ }
+
+ String getUri(ServerWebExchange exchange, Config config) {
+ String template = config.getTemplate();
+
+ if (template.indexOf('{') == -1) {
+ return template;
+ }
+
+ Map variables = getUriTemplateVariables(exchange);
+ return UriComponentsBuilder.fromUriString(template).build().expand(variables).toUriString();
+ }
+
+ @Override
+ protected Optional determineRequestUri(ServerWebExchange exchange, Config config) {
+ try {
+ String url = getUri(exchange, config);
+ URI uri = URI.create(url);
+ if (!uri.isAbsolute()) {
+ log.info("Request url is invalid: url={}, error=URI is not absolute", url);
+ return Optional.ofNullable(null);
+ }
+ return Optional.of(uri);
+ }
+ catch (IllegalArgumentException e) {
+ log.info("Request url is invalid : url={}, error={}", config.getTemplate(), e.getMessage());
+ return Optional.ofNullable(null);
+ }
+ }
+
+ public static class Config {
+
+ private String template;
+
+ public String getTemplate() {
+ return template;
+ }
+
+ public void setTemplate(String template) {
+ this.template = template;
+ }
+
+ }
+
+}
diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpec.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpec.java
index 47723b74..8b87e45a 100644
--- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpec.java
+++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/route/builder/GatewayFilterSpec.java
@@ -58,6 +58,7 @@ import org.springframework.cloud.gateway.filter.factory.RemoveRequestParameterGa
import org.springframework.cloud.gateway.filter.factory.RemoveResponseHeaderGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RequestHeaderSizeGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RequestHeaderToRequestUriGatewayFilterFactory;
+import org.springframework.cloud.gateway.filter.factory.SetRequestUriGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RequestSizeGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RetryGatewayFilterFactory;
@@ -869,6 +870,16 @@ public class GatewayFilterSpec extends UriSpec {
return filter(getBean(RequestHeaderToRequestUriGatewayFilterFactory.class).apply(c -> c.setName(headerName)));
}
+ /**
+ * A filter which changes the URI the request will be routed to by the Gateway by
+ * pulling it from a header on the request.
+ * @param uri the URI
+ * @return a {@link GatewayFilterSpec} that can be used to apply additional filters
+ */
+ public GatewayFilterSpec setRequestUri(String uri) {
+ return filter(getBean(SetRequestUriGatewayFilterFactory.class).apply(c -> c.setTemplate(uri)));
+ }
+
/**
* A filter which change the URI the request will be routed to by the Gateway.
* @param determineRequestUri a {@link Function} which takes a
diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/ServerWebExchangeUtils.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/ServerWebExchangeUtils.java
index 7585a182..fd185504 100644
--- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/ServerWebExchangeUtils.java
+++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/ServerWebExchangeUtils.java
@@ -17,9 +17,11 @@
package org.springframework.cloud.gateway.support;
import java.net.URI;
+import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -48,9 +50,13 @@ import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.Assert;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.DispatcherHandler;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
+import org.springframework.web.util.UriUtils;
/**
* @author Spencer Gibb
@@ -260,6 +266,17 @@ public final class ServerWebExchangeUtils {
return encoded;
}
+ public static MultiValueMap encodeQueryParams(MultiValueMap params) {
+ MultiValueMap encodedQueryParams = new LinkedMultiValueMap<>(params.size());
+ for (Map.Entry> entry : params.entrySet()) {
+ for (String value : entry.getValue()) {
+ encodedQueryParams.add(UriUtils.encode(entry.getKey(), StandardCharsets.UTF_8),
+ UriUtils.encode(value, StandardCharsets.UTF_8));
+ }
+ }
+ return CollectionUtils.unmodifiableMultiValueMap(encodedQueryParams);
+ }
+
public static HttpStatus parse(String statusString) {
HttpStatus httpStatus;
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 1a646d71..13464a13 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
@@ -1966,16 +1966,6 @@
"since": "4.3.0"
}
},
- {
- "name": "spring.cloud.gateway.mvc.routes",
- "type": "java.util.List",
- "description": "List of Routes.",
- "deprecated": true,
- "deprecation": {
- "replacement": "spring.cloud.gateway.server.webflux.mvc.routes",
- "since": "4.3.0"
- }
- },
{
"name": "spring.cloud.gateway.observability.enabled",
"type": "java.lang.Boolean",
diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/DisableBuiltInFiltersTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/DisableBuiltInFiltersTests.java
index b581dfa0..66eacd76 100644
--- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/DisableBuiltInFiltersTests.java
+++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/conditional/DisableBuiltInFiltersTests.java
@@ -100,6 +100,7 @@ public class DisableBuiltInFiltersTests {
"spring.cloud.gateway.server.webflux.filter.secure-headers.enabled=false",
"spring.cloud.gateway.server.webflux.filter.set-request-header.enabled=false",
"spring.cloud.gateway.server.webflux.filter.set-request-host-header.enabled=false",
+ "spring.cloud.gateway.server.webflux.filter.set-request-uri.enabled=false",
"spring.cloud.gateway.server.webflux.filter.set-response-header.enabled=false",
"spring.cloud.gateway.server.webflux.filter.rewrite-response-header.enabled=false",
"spring.cloud.gateway.server.webflux.filter.rewrite-location-response-header.enabled=false",
diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestParameterGatewayFilterFactoryTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestParameterGatewayFilterFactoryTests.java
index a96aef38..2eb96bb6 100644
--- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestParameterGatewayFilterFactoryTests.java
+++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestParameterGatewayFilterFactoryTests.java
@@ -16,6 +16,8 @@
package org.springframework.cloud.gateway.filter.factory;
+import java.net.URI;
+
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
@@ -24,6 +26,7 @@ import reactor.core.publisher.Mono;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory.NameConfig;
+import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
@@ -123,6 +126,23 @@ class RemoveRequestParameterGatewayFilterFactoryTests {
assertThat(actualRequest.getQueryParams()).containsEntry("ccc", singletonList(",xyz"));
}
+ @Test
+ void removeRequestParameterFilterShouldHandleRemainingPlusSignParams() {
+ MockServerHttpRequest request = MockServerHttpRequest
+ .method(HttpMethod.GET, URI.create("http://localhost?foo=bar&aaa=%2Bxyz"))
+ .build();
+ exchange = MockServerWebExchange.from(request);
+ NameConfig config = new NameConfig();
+ config.setName("foo");
+ GatewayFilter filter = new RemoveRequestParameterGatewayFilterFactory().apply(config);
+
+ filter.filter(exchange, filterChain);
+
+ ServerHttpRequest actualRequest = captor.getValue().getRequest();
+ assertThat(actualRequest.getQueryParams()).doesNotContainKey("foo");
+ assertThat(actualRequest.getQueryParams()).containsEntry("aaa", singletonList("+xyz"));
+ }
+
@Test
void removeRequestParameterFilterShouldHandleEncodedParameterName() {
MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost")
diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RewriteRequestParameterGatewayFilterFactoryTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RewriteRequestParameterGatewayFilterFactoryTests.java
index f7803861..24aa900b 100644
--- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RewriteRequestParameterGatewayFilterFactoryTests.java
+++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RewriteRequestParameterGatewayFilterFactoryTests.java
@@ -88,6 +88,12 @@ class RewriteRequestParameterGatewayFilterFactoryTests {
Map.of("campaign[]", List.of("blue"), "color", List.of("white")));
}
+ @Test
+ void rewriteRequestParameterFilterWithPlusSign() {
+ testRewriteRequestParameterFilter("color", "white+", "campaign=blue%2B&color=green",
+ Map.of("campaign", List.of("blue+"), "color", List.of("white+")));
+ }
+
private void testRewriteRequestParameterFilter(String name, String replacement, String query,
Map> expectedQueryParams) {
GatewayFilter filter = new RewriteRequestParameterGatewayFilterFactory()
diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SetRequestUriGatewayFilterFactoryIntegrationTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SetRequestUriGatewayFilterFactoryIntegrationTests.java
new file mode 100644
index 00000000..b52a81af
--- /dev/null
+++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SetRequestUriGatewayFilterFactoryIntegrationTests.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2013-2020 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.filter.factory;
+
+import org.junit.jupiter.api.Test;
+
+import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.cloud.gateway.route.RouteLocator;
+import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
+import org.springframework.cloud.gateway.test.BaseWebClientTests;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.annotation.DirtiesContext;
+
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+
+/**
+ * @author Stepan Mikhailiuk
+ */
+@SpringBootTest(webEnvironment = RANDOM_PORT)
+@DirtiesContext
+public class SetRequestUriGatewayFilterFactoryIntegrationTests extends BaseWebClientTests {
+
+ @LocalServerPort
+ int port;
+
+ @Test
+ public void setUriWorkWithProperties() {
+ testClient.get().uri("/").header("Host", "testservice.setrequesturi.org").exchange().expectStatus().isOk();
+
+ testClient.get()
+ .uri("/service/testservice")
+ .header("Host", "setrequesturi.org")
+ .exchange()
+ .expectStatus()
+ .isOk();
+ }
+
+ @EnableAutoConfiguration
+ @SpringBootConfiguration
+ @Import(DefaultTestConfig.class)
+ public static class TestConfig {
+
+ @Bean
+ public RouteLocator routeLocator(RouteLocatorBuilder builder) {
+ return builder.routes()
+ .route("map_subdomain_to_service_name",
+ r -> r.host("{serviceName}.setrequesturi.org")
+ .filters(f -> f.prefixPath("/httpbin").setRequestUri("lb://{serviceName}"))
+ .uri("no://op"))
+ .route("map_path_to_service_name",
+ r -> r.host("setrequesturi.org")
+ .and()
+ .path("/service/{serviceName}")
+ .filters(f -> f.rewritePath("/.*", "/").setRequestUri("lb://{serviceName}"))
+ .uri("no://op"))
+ .build();
+ }
+
+ }
+
+}
diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SetRequestUriGatewayFilterFactoryTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SetRequestUriGatewayFilterFactoryTests.java
new file mode 100644
index 00000000..2366b0c8
--- /dev/null
+++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/SetRequestUriGatewayFilterFactoryTests.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2013-2020 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.filter.factory;
+
+import java.net.URI;
+
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import reactor.core.publisher.Mono;
+
+import org.springframework.cloud.gateway.filter.GatewayFilter;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
+import org.springframework.mock.web.server.MockServerWebExchange;
+import org.springframework.web.server.ServerWebExchange;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
+
+/**
+ * @author Stepan Mikhailiuk
+ */
+public class SetRequestUriGatewayFilterFactoryTests {
+
+ @Test
+ public void filterChangeRequestUri() {
+ SetRequestUriGatewayFilterFactory factory = new SetRequestUriGatewayFilterFactory();
+ GatewayFilter filter = factory.apply(c -> c.setTemplate("https://example.com"));
+ MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost").build();
+ ServerWebExchange exchange = MockServerWebExchange.from(request);
+ exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, URI.create("http://localhost"));
+ GatewayFilterChain filterChain = mock(GatewayFilterChain.class);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ServerWebExchange.class);
+ when(filterChain.filter(captor.capture())).thenReturn(Mono.empty());
+ filter.filter(exchange, filterChain);
+ ServerWebExchange webExchange = captor.getValue();
+ URI uri = (URI) webExchange.getAttributes().get(GATEWAY_REQUEST_URL_ATTR);
+ assertThat(uri).isNotNull();
+ assertThat(uri.toString()).isEqualTo("https://example.com");
+ }
+
+ @Test
+ public void filterDoesNotChangeRequestUriIfUriIsInvalid() throws Exception {
+ SetRequestUriGatewayFilterFactory factory = new SetRequestUriGatewayFilterFactory();
+ GatewayFilter filter = factory.apply(c -> c.setTemplate("invalid_uri"));
+ MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost").build();
+ ServerWebExchange exchange = MockServerWebExchange.from(request);
+ exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, URI.create("http://localhost"));
+ GatewayFilterChain filterChain = mock(GatewayFilterChain.class);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ServerWebExchange.class);
+ when(filterChain.filter(captor.capture())).thenReturn(Mono.empty());
+ filter.filter(exchange, filterChain);
+ ServerWebExchange webExchange = captor.getValue();
+ URI uri = (URI) webExchange.getAttributes().get(GATEWAY_REQUEST_URL_ATTR);
+ assertThat(uri).isNotNull();
+ assertThat(uri.toURL().toString()).isEqualTo("http://localhost");
+ }
+
+ @Test
+ public void toStringFormat() {
+ SetRequestUriGatewayFilterFactory.Config config = new SetRequestUriGatewayFilterFactory.Config();
+ config.setTemplate("http://localhost:8080");
+ GatewayFilter filter = new SetRequestUriGatewayFilterFactory().apply(config);
+ assertThat(filter.toString()).contains("http://localhost:8080");
+ }
+
+}
diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java
index 38041174..bba39646 100644
--- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java
+++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/AdhocTestSuite.java
@@ -66,6 +66,8 @@ import static org.junit.Assume.assumeThat;
RewriteLocationResponseHeaderGatewayFilterFactoryTests.class,
org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactoryTests.class,
org.springframework.cloud.gateway.filter.factory.RequestHeaderToRequestUriGatewayFilterFactoryIntegrationTests.class,
+ org.springframework.cloud.gateway.filter.factory.SetRequestUriGatewayFilterFactoryTests.class,
+ org.springframework.cloud.gateway.filter.factory.SetRequestUriGatewayFilterFactoryIntegrationTests.class,
org.springframework.cloud.gateway.filter.factory.RemoveResponseHeaderGatewayFilterFactoryTests.class,
org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactoryTests.class,
org.springframework.cloud.gateway.filter.factory.StripPrefixGatewayFilterFactoryIntegrationTests.class,
diff --git a/spring-cloud-starter-gateway-mvc/pom.xml b/spring-cloud-starter-gateway-mvc/pom.xml
index e86d2d1f..8e66ee4e 100644
--- a/spring-cloud-starter-gateway-mvc/pom.xml
+++ b/spring-cloud-starter-gateway-mvc/pom.xml
@@ -34,5 +34,10 @@
org.springframework.boot
spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-properties-migrator
+
diff --git a/spring-cloud-starter-gateway-server-webflux/pom.xml b/spring-cloud-starter-gateway-server-webflux/pom.xml
index ab94fcc5..ed3b79c4 100644
--- a/spring-cloud-starter-gateway-server-webflux/pom.xml
+++ b/spring-cloud-starter-gateway-server-webflux/pom.xml
@@ -33,5 +33,10 @@
org.springframework.boot
spring-boot-starter-webflux
+
+
+ org.springframework.boot
+ spring-boot-properties-migrator
+
diff --git a/spring-cloud-starter-gateway-server-webmvc/pom.xml b/spring-cloud-starter-gateway-server-webmvc/pom.xml
index 00420392..14b339e1 100644
--- a/spring-cloud-starter-gateway-server-webmvc/pom.xml
+++ b/spring-cloud-starter-gateway-server-webmvc/pom.xml
@@ -33,5 +33,10 @@
org.springframework.boot
spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-properties-migrator
+
diff --git a/spring-cloud-starter-gateway/pom.xml b/spring-cloud-starter-gateway/pom.xml
index 8de3f70e..9705acd7 100644
--- a/spring-cloud-starter-gateway/pom.xml
+++ b/spring-cloud-starter-gateway/pom.xml
@@ -34,5 +34,10 @@
org.springframework.boot
spring-boot-starter-webflux
+
+
+ org.springframework.boot
+ spring-boot-properties-migrator
+