feign icon indicating copy to clipboard operation
feign copied to clipboard

Some entities get encoded twice

Open AdrienNguyenWorldline opened this issue 2 years ago • 1 comments

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]>"

AdrienNguyenWorldline avatar Jan 11 '23 09:01 AdrienNguyenWorldline

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

AdrienNguyenWorldline avatar Jan 11 '23 16:01 AdrienNguyenWorldline