Description
Affects: 6.0.4
After adding a FilterRegistrationBean into my project, I swapped from MockMvcBuilder to Spring Boots @AutoConfigureMockMvc
to have that filter applied in tests. That project also uses Thymeleaf so a ResourceUrlEncodingFilter is added as well by Spring Boot.
A existing test started breaking. It tested a @RequestMapping("")
controller, which caused ResourceUrlEncodingFilter
to throw a LookupPathIndexException
[1] because the behaviour changed from requestUri returing ""
instead of "/"
.
When an ExceptionHandler
also calls HttpServletResponse#encodeURL
(rendering a HTML error page with @{...} from thymeleaf), indexLookupPath
will already have the value of -1 and instead will throw a StringIndexOutOfBoundsException
in resolveUrlPath [2].
Here's a project demonstrating the problem as well as the differences between MockMvcBuilder
without ResourceUrlEncodingFilter
, MockMvcBuilder
with ResourceUrlEncodingFilter
and @AutoConfigureMockMvc
:
https://github.com/schosin/AutoConfigureMockMvcErrorApplication
To see the StringIndexOutOfBoundsException
, enable the profile errorhandling in application.properties.
[1] LookupPathIndexException
Caused by: org.springframework.web.servlet.resource.ResourceUrlEncodingFilter$LookupPathIndexException: Failed to find lookupPath '/' within requestUri ''. This could be because the path has invalid encoded characters or isn't normalized.
at org.springframework.web.servlet.resource.ResourceUrlEncodingFilter$ResourceUrlEncodingRequestWrapper.initLookupPath(ResourceUrlEncodingFilter.java:102)
at org.springframework.web.servlet.resource.ResourceUrlEncodingFilter$ResourceUrlEncodingRequestWrapper.setAttribute(ResourceUrlEncodingFilter.java:89)
at org.springframework.web.servlet.resource.ResourceUrlProviderExposingInterceptor.preHandle(ResourceUrlProviderExposingInterceptor.java:53)
[2] IndexOutOfBoundsException
Caused by: java.lang.StringIndexOutOfBoundsException: Range [-1, 13) out of bounds for length 13
at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:55) ~[na:na]
at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:52) ~[na:na]
at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:213) ~[na:na]
at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:210) ~[na:na]
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:98) ~[na:na]
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckFromToIndex(Preconditions.java:112) ~[na:na]
at java.base/jdk.internal.util.Preconditions.checkFromToIndex(Preconditions.java:349) ~[na:na]
at java.base/java.lang.String.checkBoundsBeginEnd(String.java:4602) ~[na:na]
at java.base/java.lang.String.substring(String.java:2715) ~[na:na]
at org.springframework.web.servlet.resource.ResourceUrlEncodingFilter$ResourceUrlEncodingRequestWrapper.resolveUrlPath(ResourceUrlEncodingFilter.java:125) ~[spring-webmvc-6.0.4.jar:6.0.4]
at org.springframework.web.servlet.resource.ResourceUrlEncodingFilter$ResourceUrlEncodingResponseWrapper.encodeURL(ResourceUrlEncodingFilter.java:159) ~[spring-webmvc-6.0.4.jar:6.0.4]
at org.thymeleaf.web.servlet.JakartaServletWebExchange.transformURL(JakartaServletWebExchange.java:124) ~[thymeleaf-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at org.thymeleaf.linkbuilder.StandardLinkBuilder.processLink(StandardLinkBuilder.java:547) ~[thymeleaf-3.1.1.RELEASE.jar:3.1.1.RELEASE]