Skip to content

Commit 0bb0d26

Browse files
committed
Merge branch 'main' into pr/3798
2 parents a840fca + 2dfc3f3 commit 0bb0d26

File tree

34 files changed

+529
-28
lines changed

34 files changed

+529
-28
lines changed

.github/workflows/maven.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
- name: Set up JDK
2020
uses: actions/setup-java@v4
2121
with:
22-
distribution: 'temurin'
22+
distribution: 'liberica'
2323
java-version: '17'
2424
cache: 'maven'
2525
- name: Build with Maven
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[[seturi-gatewayfilter-factory]]
2+
= `SetRequestUri` `GatewayFilter` Factory
3+
4+
The `SetRequestUri` `GatewayFilter` factory takes a `uri` parameter.
5+
It offers a simple way to manipulate the request uri by allowing templated segments of the path.
6+
This uses the URI templates from Spring Framework.
7+
Multiple matching segments are allowed.
8+
The following listing configures a `SetRequestUri` `GatewayFilter`:
9+
10+
.application.yml
11+
[source,yaml]
12+
----
13+
spring:
14+
cloud:
15+
gateway:
16+
routes:
17+
- id: seturi_route
18+
uri: no://op
19+
predicates:
20+
- Path=/{appId}/**
21+
filters:
22+
- SetRequestUri=http://{appId}.example.com
23+
----
24+
25+
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`
26+

docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/starter.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
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`.
66
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.
77

8-
If you include the starter, but you do not want the gateway to be enabled, set `spring.cloud.gateway.mvc.enabled=false`.
8+
If you include the starter, but you do not want the gateway to be enabled, set `spring.cloud.gateway.server.webmvc.enabled=false`.
99

1010
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].
1111
As a consequence, many of the asynchronous or reactive libraries may not apply when you use Spring Cloud Gateway Server MVC.

spring-cloud-gateway-server-mvc/pom.xml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,4 +146,30 @@
146146
<scope>test</scope>
147147
</dependency>
148148
</dependencies>
149+
150+
<profiles>
151+
<profile>
152+
<id>github_actions</id>
153+
<!-- TODO: github actions fails with restricted header host -->
154+
<activation>
155+
<property>
156+
<name>env.GITHUB_ACTIONS</name>
157+
<value>true</value>
158+
</property>
159+
</activation>
160+
<build>
161+
<plugins>
162+
<plugin>
163+
<groupId>org.apache.maven.plugins</groupId>
164+
<artifactId>maven-surefire-plugin</artifactId>
165+
<configuration>
166+
<systemPropertyVariables>
167+
<jdk.httpclient.allowRestrictedHeaders>host</jdk.httpclient.allowRestrictedHeaders>
168+
</systemPropertyVariables>
169+
</configuration>
170+
</plugin>
171+
</plugins>
172+
</build>
173+
</profile>
174+
</profiles>
149175
</project>

spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/GatewayMvcClassPathWarningAutoConfiguration.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@
2222
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
2323
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
2424
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
25+
import org.springframework.cloud.gateway.server.mvc.config.GatewayMvcProperties;
2526
import org.springframework.context.annotation.Configuration;
2627

2728
@Configuration(proxyBeanMethods = false)
2829
@AutoConfigureBefore(GatewayServerMvcAutoConfiguration.class)
29-
@ConditionalOnProperty(name = "spring.cloud.gateway.mvc.enabled", matchIfMissing = true)
30+
@ConditionalOnProperty(name = GatewayMvcProperties.PREFIX + ".enabled", matchIfMissing = true)
3031
public class GatewayMvcClassPathWarningAutoConfiguration {
3132

3233
private static final Log log = LogFactory.getLog(GatewayMvcClassPathWarningAutoConfiguration.class);

spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
@AutoConfiguration(after = { HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class,
7979
RestClientAutoConfiguration.class, FilterAutoConfiguration.class, HandlerFunctionAutoConfiguration.class,
8080
PredicateAutoConfiguration.class })
81-
@ConditionalOnProperty(name = "spring.cloud.gateway.mvc.enabled", matchIfMissing = true)
81+
@ConditionalOnProperty(name = GatewayMvcProperties.PREFIX + ".enabled", matchIfMissing = true)
8282
@Import(GatewayMvcPropertiesBeanDefinitionRegistrar.class)
8383
@ImportRuntimeHints(GatewayMvcAotRuntimeHintsRegistrar.class)
8484
public class GatewayServerMvcAutoConfiguration {

spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctions.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
import org.springframework.web.servlet.function.ServerRequest;
4646
import org.springframework.web.util.UriComponentsBuilder;
4747
import org.springframework.web.util.UriTemplate;
48-
import org.springframework.web.util.UriUtils;
4948

5049
import static org.springframework.cloud.gateway.server.mvc.common.MvcUtils.CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR;
5150
import static org.springframework.util.CollectionUtils.unmodifiableMultiValueMap;
@@ -216,7 +215,7 @@ public static Function<ServerRequest, ServerRequest> removeRequestParameter(Stri
216215
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>(request.params());
217216
queryParams.remove(name);
218217

219-
MultiValueMap<String, String> encodedQueryParams = UriUtils.encodeQueryParams(queryParams);
218+
MultiValueMap<String, String> encodedQueryParams = MvcUtils.encodeQueryParams(queryParams);
220219

221220
// remove from uri
222221
URI newUri = UriComponentsBuilder.fromUri(request.uri())

spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfigurationTests.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2013-2024 the original author or authors.
2+
* Copyright 2013-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@
3434
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
3535
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
3636
import org.springframework.boot.http.client.SimpleClientHttpRequestFactoryBuilder;
37+
import org.springframework.boot.test.context.FilteredClassLoader;
3738
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
3839
import org.springframework.cloud.gateway.server.mvc.filter.FilterAutoConfiguration;
3940
import org.springframework.cloud.gateway.server.mvc.filter.FormFilter;
@@ -47,6 +48,7 @@
4748
import org.springframework.cloud.gateway.server.mvc.filter.XForwardedRequestHeadersFilter;
4849
import org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctionAutoConfiguration;
4950
import org.springframework.cloud.gateway.server.mvc.predicate.PredicateAutoConfiguration;
51+
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
5052
import org.springframework.context.ConfigurableApplicationContext;
5153

5254
import static org.assertj.core.api.Assertions.assertThat;
@@ -204,6 +206,27 @@ void settingHttpClientFactoryWorks() {
204206
assertThat(builder).isInstanceOf(SimpleClientHttpRequestFactoryBuilder.class);
205207
}
206208

209+
@Test
210+
void loadBalancerFunctionHandlerAdded() {
211+
new ApplicationContextRunner()
212+
.withConfiguration(AutoConfigurations.of(FilterAutoConfiguration.class, PredicateAutoConfiguration.class,
213+
HandlerFunctionAutoConfiguration.class, GatewayServerMvcAutoConfiguration.class,
214+
HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class,
215+
RestClientAutoConfiguration.class))
216+
.run(context -> assertThat(context).hasBean("lbHandlerFunctionDefinition"));
217+
}
218+
219+
@Test
220+
void loadBalancerFunctionHandlerNotAddedWhenNoLoadBalancerClientOnClasspath() {
221+
new ApplicationContextRunner()
222+
.withConfiguration(AutoConfigurations.of(FilterAutoConfiguration.class, PredicateAutoConfiguration.class,
223+
HandlerFunctionAutoConfiguration.class, GatewayServerMvcAutoConfiguration.class,
224+
HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class,
225+
RestClientAutoConfiguration.class))
226+
.withClassLoader(new FilteredClassLoader(LoadBalancerClient.class))
227+
.run(context -> assertThat(context).doesNotHaveBean("lbHandlerFunctionDefinition"));
228+
}
229+
207230
@SpringBootConfiguration
208231
@EnableAutoConfiguration
209232
static class TestConfig {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2013-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.gateway.server.mvc;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.beans.factory.annotation.Autowired;
22+
import org.springframework.boot.autoconfigure.SpringBootApplication;
23+
import org.springframework.boot.test.context.SpringBootTest;
24+
import org.springframework.boot.test.web.server.LocalServerPort;
25+
import org.springframework.cloud.gateway.server.mvc.filter.FilterAutoConfiguration;
26+
import org.springframework.cloud.gateway.server.mvc.test.HttpbinTestcontainers;
27+
import org.springframework.cloud.gateway.server.mvc.test.TestLoadBalancerConfig;
28+
import org.springframework.cloud.gateway.server.mvc.test.client.TestRestClient;
29+
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
30+
import org.springframework.test.context.ActiveProfiles;
31+
import org.springframework.test.context.ContextConfiguration;
32+
33+
/**
34+
* Integration tests for {@link FilterAutoConfiguration.LoadBalancerHandlerConfiguration}.
35+
*
36+
* @author Olga Maciaszek-Sharma
37+
*
38+
*/
39+
@SpringBootTest(classes = { ServerMvcLoadBalancerIntegrationTests.Config.class, FilterAutoConfiguration.class },
40+
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
41+
@ContextConfiguration(initializers = HttpbinTestcontainers.class)
42+
@ActiveProfiles("lb")
43+
public class ServerMvcLoadBalancerIntegrationTests {
44+
45+
@LocalServerPort
46+
int port;
47+
48+
@Autowired
49+
TestRestClient testRestClient;
50+
51+
@Test
52+
void shouldUseLbHandlerFunctionDefinitionToResolveHost() {
53+
testRestClient.get().uri("http://localhost:" + port + "/test").exchange().expectStatus().isOk();
54+
}
55+
56+
@SpringBootApplication
57+
@LoadBalancerClient(name = "httpbin", configuration = TestLoadBalancerConfig.Httpbin.class)
58+
static class Config {
59+
60+
}
61+
62+
}

spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/VanillaRouterFunctionTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.host;
4444

4545
@SuppressWarnings("unchecked")
46-
@SpringBootTest(properties = { "spring.cloud.gateway.mvc.http-client.type=jdk" },
46+
@SpringBootTest(properties = { "spring.http.client.factory=jdk" },
4747
webEnvironment = WebEnvironment.RANDOM_PORT)
4848
@ContextConfiguration(initializers = HttpbinTestcontainers.class)
4949
public class VanillaRouterFunctionTests {

spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/BeforeFilterFunctionsTests.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,17 @@ void rewriteEncodedRequestParameter() {
119119
MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/path")
120120
.param("foo[]", "bar")
121121
.param("baz", "qux")
122+
.param("quux", "corge+")
122123
.buildRequest(null);
123124

124125
ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList());
125126

126127
ServerRequest result = BeforeFilterFunctions.rewriteRequestParameter("foo[]", "replacement[]").apply(request);
127128

128129
assertThat(result.param("foo[]")).isPresent().hasValue("replacement[]");
129-
assertThat(result.uri().toString()).hasToString("http://localhost/path?baz=qux&foo%5B%5D=replacement%5B%5D");
130+
assertThat(result.param("quux")).isPresent().hasValue("corge+");
131+
assertThat(result.uri().toString())
132+
.hasToString("http://localhost/path?quux=corge%2B&baz=qux&foo%5B%5D=replacement%5B%5D");
130133
}
131134

132135
@Test

spring-cloud-gateway-server-mvc/src/test/resources/application-functionhandlerconfigtests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
spring.cloud.gateway.mvc:
1+
spring.cloud.gateway.server.webmvc:
22
routesMap:
33
testsimplefunction:
44
uri: fn:upper
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
spring:
2+
cloud:
3+
gateway:
4+
mvc:
5+
routes:
6+
- id: test
7+
uri: lb://httpbin
8+
predicates:
9+
- Path=/test/**
10+
filters:
11+
- StripPrefix=1

spring-cloud-gateway-server-mvc/src/test/resources/application-propertiesbeandefinitionregistrartests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
spring.cloud.gateway.mvc:
1+
spring.cloud.gateway.server.webmvc:
22
routesMap:
33
route1:
44
uri: https://example1.com

spring-cloud-gateway-server-mvc/src/test/resources/application-streamhandlerconfigtests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
spring.cloud.gateway.mvc:
1+
spring.cloud.gateway.server.webmvc:
22
routesMap:
33
testsimplestream:
44
uri: stream:hello-out-0

spring-cloud-gateway-server-mvc/src/test/resources/application-stripprefixstaticport.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
strip.prefix.static.uri: http://${httpbin.host}:${httpbin.port}
2-
spring.cloud.gateway.mvc:
2+
spring.cloud.gateway.server.webmvc:
33
routes:
44
- id: strip_prefix_static_port_config
55
uri: ${strip.prefix.static.uri}

spring-cloud-gateway-server-mvc/src/test/resources/application-weightrequestpredicateintegrationtests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
spring.cloud.gateway.mvc:
1+
spring.cloud.gateway.server.webmvc:
22
routes:
33
- id: weight_high_test
44
uri: https://examplel1.com

spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
import org.springframework.cloud.gateway.filter.factory.SetPathGatewayFilterFactory;
112112
import org.springframework.cloud.gateway.filter.factory.SetRequestHeaderGatewayFilterFactory;
113113
import org.springframework.cloud.gateway.filter.factory.SetRequestHostHeaderGatewayFilterFactory;
114+
import org.springframework.cloud.gateway.filter.factory.SetRequestUriGatewayFilterFactory;
114115
import org.springframework.cloud.gateway.filter.factory.SetResponseHeaderGatewayFilterFactory;
115116
import org.springframework.cloud.gateway.filter.factory.SetStatusGatewayFilterFactory;
116117
import org.springframework.cloud.gateway.filter.factory.StripPrefixGatewayFilterFactory;
@@ -722,6 +723,12 @@ public RequestHeaderToRequestUriGatewayFilterFactory requestHeaderToRequestUriGa
722723
return new RequestHeaderToRequestUriGatewayFilterFactory();
723724
}
724725

726+
@Bean
727+
@ConditionalOnEnabledFilter
728+
public SetRequestUriGatewayFilterFactory setRequestUriGatewayFilterFactory() {
729+
return new SetRequestUriGatewayFilterFactory();
730+
}
731+
725732
@Bean
726733
@ConditionalOnEnabledFilter
727734
public RequestSizeGatewayFilterFactory requestSizeGatewayFilterFactory() {

spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestParameterGatewayFilterFactory.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@
2424

2525
import org.springframework.cloud.gateway.filter.GatewayFilter;
2626
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
27+
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
2728
import org.springframework.http.server.reactive.ServerHttpRequest;
2829
import org.springframework.util.LinkedMultiValueMap;
2930
import org.springframework.util.MultiValueMap;
3031
import org.springframework.web.server.ServerWebExchange;
3132
import org.springframework.web.util.UriComponentsBuilder;
32-
import org.springframework.web.util.UriUtils;
3333

3434
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
3535
import static org.springframework.util.CollectionUtils.unmodifiableMultiValueMap;
@@ -59,7 +59,8 @@ public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
5959
queryParams.remove(config.getName());
6060

6161
try {
62-
MultiValueMap<String, String> encodedQueryParams = UriUtils.encodeQueryParams(queryParams);
62+
MultiValueMap<String, String> encodedQueryParams = ServerWebExchangeUtils
63+
.encodeQueryParams(queryParams);
6364
URI newUri = UriComponentsBuilder.fromUri(request.getURI())
6465
.replaceQueryParams(unmodifiableMultiValueMap(encodedQueryParams))
6566
.build(true)

spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/RewriteRequestParameterGatewayFilterFactory.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@
2424

2525
import org.springframework.cloud.gateway.filter.GatewayFilter;
2626
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
27+
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
2728
import org.springframework.http.server.reactive.ServerHttpRequest;
2829
import org.springframework.util.Assert;
2930
import org.springframework.util.LinkedMultiValueMap;
3031
import org.springframework.util.MultiValueMap;
3132
import org.springframework.web.server.ServerWebExchange;
3233
import org.springframework.web.util.UriComponentsBuilder;
33-
import org.springframework.web.util.UriUtils;
3434

3535
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
3636
import static org.springframework.util.CollectionUtils.unmodifiableMultiValueMap;
@@ -71,7 +71,8 @@ public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
7171
}
7272

7373
try {
74-
MultiValueMap<String, String> encodedQueryParams = UriUtils.encodeQueryParams(queryParams);
74+
MultiValueMap<String, String> encodedQueryParams = ServerWebExchangeUtils
75+
.encodeQueryParams(queryParams);
7576
URI uri = uriComponentsBuilder.replaceQueryParams(unmodifiableMultiValueMap(encodedQueryParams))
7677
.build(true)
7778
.toUri();

0 commit comments

Comments
 (0)