-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Preserve original encoding of query parameters in ProxyExchangeHandlerFunction
#3789
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Preserve original encoding of query parameters in ProxyExchangeHandlerFunction
#3789
Conversation
5c8addf
to
e040e28
Compare
Is this only present in the main branch? |
@ryanjbaxter |
e040e28
to
ed5c74b
Compare
Signed-off-by: raccoonback <[email protected]>
… query parameters during proxy request ref. spring-cloud#3759 Signed-off-by: raccoonback <[email protected]>
ed5c74b
to
f8882d2
Compare
Signed-off-by: raccoonback <[email protected]>
f8882d2
to
9217b37
Compare
Related #3795 |
I'm new to spring-cloud-gateway contribution workflow, does it should be merge to the |
boolean encoded = containsEncodedQuery(serverRequest.uri(), serverRequest.params()); | ||
MultiValueMap<String, String> params = serverRequest.params(); | ||
if (!encoded) { | ||
params = UriUtils.encodeQueryParams(serverRequest.params()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since serverRequest.params()
can be modified by user-defined filters that precede this step, we should always encode the parameters to ensure correctness.
Additionally, note that the plus symbol (+
) is not encoded by UriUtils.encodeQueryParams
. To handle this case properly, we should use UriUtils.encode
instead, which will correctly encode all special characters, including the plus symbol, you could see more detail in #3795.
private static MultiValueMap<String, String> encodeQueryParams(MultiValueMap<String, String> params) {
Charset charset = StandardCharsets.UTF_8;
MultiValueMap<String, String> result = new LinkedMultiValueMap<>(params.size());
for (Map.Entry<String, List<String>> entry : params.entrySet()) {
for (String value : entry.getValue()) {
result.add(UriUtils.encode(entry.getKey(), charset), UriUtils.encode(value, charset));
}
}
return result;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Shawyeok
Good point. I'll check it.
assertThat(uri).hasToString("http://localhost:8080/%C3%A9?foo=value1%20value2&bar=value3%3D") | ||
.hasPath("/é") | ||
.hasParameter("foo", "value1 value2") | ||
.hasParameter("bar", "value3="); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also add a case for encode plus symbol (%2B).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Shawyeok
I have updated it.
As far as I know, the changes are applied to 4.1.x, 4.2.x, and main. |
Signed-off-by: raccoonback <[email protected]>
@Shawyeok we merge into the lowest maintained branch and then merge the change forward to the other branches |
@ryanjbaxter Thanks for letting me know! That's interesting! Does this approach help to reduce the cost of maintaining multiple branches? Is there any documentation or notes where I can learn more about this? |
@@ -84,14 +87,14 @@ private void init() { | |||
@Override | |||
public ServerResponse handle(ServerRequest serverRequest) { | |||
URI uri = uriResolver.apply(serverRequest); | |||
boolean encoded = containsEncodedQuery(serverRequest.uri(), serverRequest.params()); | |||
MultiValueMap<String, String> params = ensureEncodedQueryParameters(serverRequest); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
User can modify the query parameters in preceding filters, for example, add additional parameters to the request, these parameters (serverRequest.params()
) should be encoded regardless of whether the original request url contains %
or not.
MultiValueMap<String, String> params = ensureEncodedQueryParameters(serverRequest); | |
MultiValueMap<String, String> params = MvcUtils.encodeQueryParams(serverRequest.params()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First of all, If query parameters were added in a preceding filter, I believe that filter should be responsible for ensuring those parameters are properly encoded.
serverRequest.params()
relies on HttpServletRequest.getParameterMap()
, and from what I can tell, most major servlet containers (Tomcat, Jetty, Undertow) return already-decoded values.
So as you pointed out, it's likely safe to assume that serverRequest.params()
provides decoded query parameters.
(That said, if we can guarantee that serverRequest.params()
always returns decoded values, then it's fine to encode them again here. But if that guarantee isn't solid, we risk double-encoding parameters that were already percent-encoded by earlier filters.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we can guarantee that serverRequest.params() always returns decoded values, then it's fine to encode them again here.
IMO, the only reliable way to handle URI encoding is to treat the values returned by serverRequest.params()
as raw (i.e. decoded values). We're unable to determine whether a string is URI encoded or not, for example: Is %25
a encoded string? We cannot tell, cause user might intend to send the literal string %25
rather than the percent symbol. Therefore, we should consistently treat all parameter values as decoded and handle encoding at this stage.
It does help. There's no documentation on it as we are a small team. |
… parameters from serverRequest Signed-off-by: raccoonback <[email protected]>
Description
This PR improves
ProxyExchangeHandlerFunction
to ensure that the original encoding of query parameters is preserved when proxying requests.The return value of
ServerRequest.params()
is already decoded, so reconstructing the query using it results in the loss of the original encoding information.To preserve the original encoded state, this PR removes the unnecessary query parameter replacement performed in
ProxyExchangeHandlerFunction.handle()
.For example.
Related issue