If I send a request with
Cookie: Nonce=Tl=Q/0AUSOx[n)2z4(t]20FZv#?[Ge%H
Spring will fail with
java.lang.IllegalArgumentException: Invalid encoded sequence "%H"
at org.springframework.util.StringUtils.uriDecode(StringUtils.java:780)
at org.springframework.web.util.UriUtils.decode(UriUtils.java:372)
at org.springframework.web.util.UrlPathHelper.decodeInternal(UrlPathHelper.java:522)
at org.springframework.web.util.UrlPathHelper.decodeRequestString(UrlPathHelper.java:513)
at org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver.resolveName(ServletCookieValueMethodArgumentResolver.java:66)
at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:108)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:878)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:792)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:652)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
Affects: 5.2.14
Comment From: jakub-bochenski
SO: https://stackoverflow.com/q/67725851/1237617
How can I disable this?
Comment From: rstoyanchev
Thanks for getting in touch, but it feels like this is a question that would be better suited to Stack Overflow. As mentioned in the guidelines for contributing, we prefer to use the issue tracker only for bugs and enhancements.
Comment From: jakub-bochenski
This is a bug, Spring should not throw IllegalArgumentException on valid input
I want to consume cookies with %
character and it's not possible with Spring now
Comment From: jakub-bochenski
I see this breaking change was introduced last year: https://github.com/spring-projects/spring-framework/issues/11945
So how about a request for enahancement: add a parameter to @Cookie annotation to support previous behaviour
Comment From: rstoyanchev
That issue is about 11 years old and the behavior has been in place for as long. Trying to find an RFC to justify the behavior of decoding the value, I see this in https://datatracker.ietf.org/doc/html/rfc6265#section-5.4:
Despite its name, the cookie-string is actually a sequence of octets, not a sequence of characters. To convert the cookie-string (or components thereof) into a sequence of characters (e.g., for presentation to the user), the user agent might wish to try using the UTF-8 character encoding [RFC3629] to decode the octet sequence. This decoding might fail, however, because not every sequence of octets is valid UTF-8.
So the behavior is seems OK, and it's been in use for so long, but you can also bypass @CookieValue
and have ServletRequest injected instead to access the cooking value directly from it.
Comment From: jakub-bochenski
I fail to see the relevance of decoding octet sequences into UTF-8 character string for URL decoding the value later.
That issue is about 11 years old and the behavior has been in place for as long.
I'm sorry for taking the comment dates at face value :(
spring-projects-issues commented on Jun 20, 2010
Comment From: rstoyanchev
You're right. Not the same as percent-encoded octets. At the moment I'm not seeing justification for the current behavior but it has been in place for very long, we could only correct it in a major or minor version. Also, that long ago, it's possible that there were other factors, like cookie values actually being encoded, as in that report which requested it.
In the mean time, if this causes an issue, the best I can think of is either access the cookie value directly from the request, or create your own annotation and register a resolver for it.
Comment From: jakub-bochenski
I think one easy improvement would be updating the Javadoc for @Cookie annotation, since now it makes no mention of the URL decoding. It might be documented somewhere in Spring Reference docs but obviously Javadoc is more visible
In the mean time, if this causes an issue, the best I can think of is either access the cookie value directly from the request, or create your own annotation and register a resolver for it.
Wouldn't I be able to override the default handler for the @Cookie annotation? I think this wold be the best for now. (I don't like using ServletRequest object, I might as well go back to using servlets then :))
I think a parameter to toggle the decoding would also be useful and you could set the default value to match the existing behaviour
Comment From: rstoyanchev
I've updated the Javadoc for @CookieValue
and realized you can also declare the argument to be javax.servlet.http.Cookie
as an alternative to injecting the request.
For argument resolvers it's easy to add custom ones, but replacing the built-in ones is not straight forward.
For the annotation attribute, if the behavior isn't backed by the spec and if there isn't a reason to URL decode the value, then it doesn't make sense to have it, only to then have it removed in 6.0.
Comment From: rstoyanchev
Given the long standing behavior, I would go with a plan to make this configurable, through a property on ServletCookieValueMethodArgumentResolver
. Along with that, we'll probably need an extendArgumentResolvers(List<HandlerMethodArgumentResolver>)
method on WebMvcConfigurer
in order to allow setting a property on a built-in resolver.
Comment From: henrywangsk
Hi @rstoyanchev, good job and noticed the ServletCookieValueMethodArgumentResolver has been implemented. How can it be configured (so we can set the urlDecode property to false)? Thanks