feign
feign copied to clipboard
Some entities get encoded twice
Using feign-core:11.8 and feign-httpclient:11.8 and feign-form-spring:3.5.0
I am passing a smtp message id to a GET query parameter
When the input value is [email protected]
I see that the url called contains messageIds=%3CHelloWorld%40mail.gmail.com%3E and the receiving webservice is getting the initial value
However when the value contains and "=" sign, for example [email protected] the request ends up containing messageIds=%253CHelloWorld%40mail.gmail.com%253E
the @ symbol still gets encoded only once as %40 But the < and > symbols are getting encoded twice, first to %3C and %3E the the "%" itself gets encoded again as %25 Which ends up %253C and %253E
The webservice consuming the value is then getting "%[email protected]%3E" instead of "[email protected]>"
I went into deep debug and I could not find a solution but I know what goes wrong
My query parameter (GET) contains both <,> and =
As defined in feign.template.UriUtils "=" is a reserved character so it does not get encoded if allowReserved is true and "<" and ">" are not, so they do get encoded
I end up out of feign with a request that looks like something?messageIds=%[email protected]%3E
What goes wrong next, is that I am using spring cloud loadbalancer to dispatch the request (micro service env with multiple pods)
see https://github.com/spring-cloud/spring-cloud-commons/blob/main/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerUriTools.java#L55
LoadBalancerUriTools does look at the URL query parameters to decide if they should be url encoded or not with the given rules :
if there is no % symbol at all, it's not encoded => url encode the parameter if there is a % symbol, and everything is valid => do not encode anything if there is a % symbol but something is not valid => encode everything including the < that was already encoded as %3C
Check is done using UriComponentsBuilder.fromUri(uri).build(true) here which says : Invalid character '=' for QUERY_PARAM in "%[email protected]%3E"
private static boolean containsEncodedParts(URI uri) {
boolean encoded = (uri.getRawQuery() != null && uri.getRawQuery().contains(PERCENTAGE_SIGN))
|| (uri.getRawPath() != null && uri.getRawPath().contains(PERCENTAGE_SIGN))
|| (uri.getRawFragment() != null && uri.getRawFragment().contains(PERCENTAGE_SIGN));
// Verify if it is really fully encoded. Treat partial encoded as unencoded.
if (encoded) {
try {
UriComponentsBuilder.fromUri(uri).build(true);
return true;
}
catch (IllegalArgumentException ignore) {
}
return false;
}
return false;
}
Which does as the comment says and end up rencoding the < and > a second time
from https://github.com/spring-cloud/spring-cloud-commons/blob/6ed50fcbc7ab9e41eac5eb4641cde952fa81823e/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerUriTools.java#L55