Skip to content

Commit 6fc7ee7

Browse files
authored
Merge pull request #3799 from raccoonback/encode-plus-character-query-parameter
Fix '+' character in query parameters is not encoded when remove query parameter
2 parents c09f383 + 330a43d commit 6fc7ee7

File tree

7 files changed

+53
-7
lines changed

7 files changed

+53
-7
lines changed

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/filter/BeforeFilterFunctionsTests.java

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

124125
ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList());
@@ -127,7 +128,8 @@ void removeRequestParameterWithEncodedRemainParameters() {
127128

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

133135
@Test

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();

spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/ServerWebExchangeUtils.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
package org.springframework.cloud.gateway.support;
1818

1919
import java.net.URI;
20+
import java.nio.charset.StandardCharsets;
2021
import java.util.Collections;
2122
import java.util.HashMap;
2223
import java.util.LinkedHashSet;
24+
import java.util.List;
2325
import java.util.Locale;
2426
import java.util.Map;
2527
import java.util.Set;
@@ -48,9 +50,13 @@
4850
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
4951
import org.springframework.http.server.reactive.ServerHttpResponse;
5052
import org.springframework.util.Assert;
53+
import org.springframework.util.CollectionUtils;
54+
import org.springframework.util.LinkedMultiValueMap;
55+
import org.springframework.util.MultiValueMap;
5156
import org.springframework.web.reactive.DispatcherHandler;
5257
import org.springframework.web.server.ServerWebExchange;
5358
import org.springframework.web.util.UriComponentsBuilder;
59+
import org.springframework.web.util.UriUtils;
5460

5561
/**
5662
* @author Spencer Gibb
@@ -260,6 +266,17 @@ public static boolean containsEncodedParts(URI uri) {
260266
return encoded;
261267
}
262268

269+
public static MultiValueMap<String, String> encodeQueryParams(MultiValueMap<String, String> params) {
270+
MultiValueMap<String, String> encodedQueryParams = new LinkedMultiValueMap<>(params.size());
271+
for (Map.Entry<String, List<String>> entry : params.entrySet()) {
272+
for (String value : entry.getValue()) {
273+
encodedQueryParams.add(UriUtils.encode(entry.getKey(), StandardCharsets.UTF_8),
274+
UriUtils.encode(value, StandardCharsets.UTF_8));
275+
}
276+
}
277+
return CollectionUtils.unmodifiableMultiValueMap(encodedQueryParams);
278+
}
279+
263280
public static HttpStatus parse(String statusString) {
264281
HttpStatus httpStatus;
265282

spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RemoveRequestParameterGatewayFilterFactoryTests.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.cloud.gateway.filter.factory;
1818

19+
import java.net.URI;
20+
1921
import org.junit.jupiter.api.BeforeEach;
2022
import org.junit.jupiter.api.Test;
2123
import org.mockito.ArgumentCaptor;
@@ -24,6 +26,7 @@
2426
import org.springframework.cloud.gateway.filter.GatewayFilter;
2527
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
2628
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory.NameConfig;
29+
import org.springframework.http.HttpMethod;
2730
import org.springframework.http.server.reactive.ServerHttpRequest;
2831
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
2932
import org.springframework.mock.web.server.MockServerWebExchange;
@@ -123,6 +126,23 @@ void removeRequestParameterFilterShouldHandleRemainingParamsWhichRequiringEncodi
123126
assertThat(actualRequest.getQueryParams()).containsEntry("ccc", singletonList(",xyz"));
124127
}
125128

129+
@Test
130+
void removeRequestParameterFilterShouldHandleRemainingPlusSignParams() {
131+
MockServerHttpRequest request = MockServerHttpRequest
132+
.method(HttpMethod.GET, URI.create("http://localhost?foo=bar&aaa=%2Bxyz"))
133+
.build();
134+
exchange = MockServerWebExchange.from(request);
135+
NameConfig config = new NameConfig();
136+
config.setName("foo");
137+
GatewayFilter filter = new RemoveRequestParameterGatewayFilterFactory().apply(config);
138+
139+
filter.filter(exchange, filterChain);
140+
141+
ServerHttpRequest actualRequest = captor.getValue().getRequest();
142+
assertThat(actualRequest.getQueryParams()).doesNotContainKey("foo");
143+
assertThat(actualRequest.getQueryParams()).containsEntry("aaa", singletonList("+xyz"));
144+
}
145+
126146
@Test
127147
void removeRequestParameterFilterShouldHandleEncodedParameterName() {
128148
MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost")

spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/factory/RewriteRequestParameterGatewayFilterFactoryTests.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ void rewriteRequestParameterFilterKeepsOtherParamsEncoded() {
8888
Map.of("campaign[]", List.of("blue"), "color", List.of("white")));
8989
}
9090

91+
@Test
92+
void rewriteRequestParameterFilterWithPlusSign() {
93+
testRewriteRequestParameterFilter("color", "white+", "campaign=blue%2B&color=green",
94+
Map.of("campaign", List.of("blue+"), "color", List.of("white+")));
95+
}
96+
9197
private void testRewriteRequestParameterFilter(String name, String replacement, String query,
9298
Map<String, List<String>> expectedQueryParams) {
9399
GatewayFilter filter = new RewriteRequestParameterGatewayFilterFactory()

0 commit comments

Comments
 (0)