Spring Module and Version
Spring Web – v7.0.1
Issue Description
When using the RedirectTrailingSlashHandler variant of the UrlHandlerFilter, the path portion of the request URI is stripped of it's trailing slash, and the result is used as the new Location header. The location becomes a relative URL to the original request.
E.g. for a request to https://www.example.com/foo/bar/////:
- the path of the request URI will be /foo/bar/////
- the Location header will be set to /foo/bar
- the client will redirect to https://www.example.com/foo/bar
However for the same request with query parameters and/or a fragment, such as https://www.example.com/foo/bar/////?key=value#fragment:
- the path of the request URI will still be /foo/bar/////
- the Location header will again be set to /foo/bar
- the client will redirect to https://www.example.com/foo/bar and the query parameters and fragment will become lost
Expected Behaviour
The redirect should remove the trailing slashes from the path only, and should not remove the query parameters and fragment from the original request, as they are important parts of the original request.
E.g. for https://www.example.com/foo/bar/////?key=value#fragment, the Location header should be either of the following:
/foo/bar?key=value#fragment(relative)https://www.example.com/foo/bar?key=value#fragment(absolute)
Code Reference
This issue affects both the standard and reactive implementations of UrlHandlerFilter, so should be fixed for both classes:
- Standard:
https://github.com/spring-projects/spring-framework/blob/16edf9867a143f95cefadb7b2115dcbbf624e176/spring-web/src/main/java/org/springframework/web/filter/UrlHandlerFilter.java#L315-L323
- Reactive:
https://github.com/spring-projects/spring-framework/blob/16edf9867a143f95cefadb7b2115dcbbf624e176/spring-web/src/main/java/org/springframework/web/filter/reactive/UrlHandlerFilter.java#L291-L297
As an aside, I think the reactive variant should return response.setComplete() instead of Mono.empty()?
Comment From: rstoyanchev
Indeed this is an issue for the query string.
Fragments are not sent to the server, and I don' t believe there is a way to obtain a fragment from the Servlet API. Do you have an actual scenario with fragments, and if so could you provide more details? Note also the RFC says that the original fragment must be used.
Comment From: jamesmissen
No, I noticed the problem with query parameters only. I only mentioned fragments upon inspecting the code. It seemed to only use the path of the original URI to derive the Location header. E.g. for reactive:
https://github.com/spring-projects/spring-framework/blob/16edf9867a143f95cefadb7b2115dcbbf624e176/spring-web/src/main/java/org/springframework/web/filter/reactive/UrlHandlerFilter.java#L269-L270
As such, in the issue I really just meant that everything after a URI's path is ignored, which by definition happens to be query parameters and fragment.
I'm not sure if it is possible, as you say, for Spring to be aware of the fragment. Perhaps there are circumstances depending on the source of the request/exchange object (e.g. maybe some kind of mock request might behave differently?).
I envisioned a fix being something that considers the path and everything after it, rather than just the path. For example, something like using UriComponentsBuilder with replacePath to mutate only the path portion, but keep everything else as is.