Skip to content

Commit 107e8d3

Browse files
committed
Fix incorrect handling of '+' character in query parameter encoding
Signed-off-by: raccoonback <kosb15@naver.com>
1 parent 9217b37 commit 107e8d3

File tree

3 files changed

+21
-6
lines changed

3 files changed

+21
-6
lines changed

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2013-2023 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.
@@ -21,6 +21,7 @@
2121
import java.io.InputStream;
2222
import java.io.UncheckedIOException;
2323
import java.net.URI;
24+
import java.nio.charset.StandardCharsets;
2425
import java.util.Arrays;
2526
import java.util.Collection;
2627
import java.util.Collections;
@@ -40,11 +41,14 @@
4041
import org.springframework.http.converter.HttpMessageConverter;
4142
import org.springframework.util.Assert;
4243
import org.springframework.util.CollectionUtils;
44+
import org.springframework.util.LinkedMultiValueMap;
45+
import org.springframework.util.MultiValueMap;
4346
import org.springframework.util.StreamUtils;
4447
import org.springframework.web.context.WebApplicationContext;
4548
import org.springframework.web.servlet.function.ServerRequest;
4649
import org.springframework.web.servlet.support.RequestContextUtils;
4750
import org.springframework.web.util.UriComponentsBuilder;
51+
import org.springframework.web.util.UriUtils;
4852

4953
import static org.springframework.web.servlet.function.RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
5054

@@ -263,6 +267,17 @@ public static void addOriginalRequestUrl(ServerRequest request, URI url) {
263267
urls.add(url);
264268
}
265269

270+
public static MultiValueMap<String, String> encodeQueryParams(MultiValueMap<String, String> params) {
271+
MultiValueMap<String, String> encodedQueryParams = new LinkedMultiValueMap<>(params.size());
272+
for (Map.Entry<String, List<String>> entry : params.entrySet()) {
273+
for (String value : entry.getValue()) {
274+
encodedQueryParams.add(UriUtils.encode(entry.getKey(), StandardCharsets.UTF_8),
275+
UriUtils.encode(value, StandardCharsets.UTF_8));
276+
}
277+
}
278+
return CollectionUtils.unmodifiableMultiValueMap(encodedQueryParams);
279+
}
280+
266281
private record ByteArrayInputMessage(ServerRequest request, ByteArrayInputStream body) implements HttpInputMessage {
267282

268283
@Override

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import org.springframework.web.servlet.function.ServerRequest;
3737
import org.springframework.web.servlet.function.ServerResponse;
3838
import org.springframework.web.util.UriComponentsBuilder;
39-
import org.springframework.web.util.UriUtils;
4039

4140
/**
4241
* @author raccoonback
@@ -139,7 +138,7 @@ private static MultiValueMap<String, String> ensureEncodedQueryParameters(Server
139138
boolean encoded = containsEncodedQuery(serverRequest.uri(), serverRequest.params());
140139
MultiValueMap<String, String> params = serverRequest.params();
141140
if (!encoded) {
142-
params = UriUtils.encodeQueryParams(serverRequest.params());
141+
params = MvcUtils.encodeQueryParams(serverRequest.params());
143142
}
144143
return params;
145144
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public Stream<ResponseHttpHeadersFilter> orderedStream() {
103103
function.onApplicationEvent(null);
104104

105105
MockHttpServletRequest servletRequest = MockMvcRequestBuilders
106-
.get("http://localhost/é?foo=value1 value2&bar=value3=")
106+
.get("http://localhost/é?foo=value1 value2&bar=value3=&qux=value4+")
107107
.buildRequest(null);
108108
servletRequest.setAttribute(MvcUtils.GATEWAY_REQUEST_URL_ATTR, URI.create("http://localhost:8080"));
109109
ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList());
@@ -112,10 +112,11 @@ public Stream<ResponseHttpHeadersFilter> orderedStream() {
112112

113113
URI uri = proxyExchange.getRequest().getUri();
114114

115-
assertThat(uri).hasToString("http://localhost:8080/%C3%A9?foo=value1%20value2&bar=value3%3D")
115+
assertThat(uri).hasToString("http://localhost:8080/%C3%A9?foo=value1%20value2&bar=value3%3D&qux=value4%2B")
116116
.hasPath("/é")
117117
.hasParameter("foo", "value1 value2")
118-
.hasParameter("bar", "value3=");
118+
.hasParameter("bar", "value3=")
119+
.hasParameter("qux", "value4+");
119120
}
120121

121122
private class TestProxyExchange extends AbstractProxyExchange {

0 commit comments

Comments
 (0)