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]

Comment From: wilkinsona

Gitter discussion.

Comment From: QuantumXiecao

This difference between @AutoConfigureMockMvc and MockMvcBuilder is that: spring boot's SpringBootMockMvcBuilderCustomizer would add some filters including ResourceUrlEncodingFilter to MockMvcBuilder which would directly leads to the LookupPathIndexException. You can set @AutoConfigureMockMvc addFilters=false to solve this. @schosin @wilkinsona

Comment From: schosin

As stated in the gitter discussion, the point of swapping to @AutoConfigureMockMvc was to add a custom filter. While the application works just fine when running it, ResourceUrlEncodingFilter seems to break tests that call the url "" when HttpServletResponse#encodeURL is invoked, with the SIOOBE being thrown when that same method is called again in an error handler.

If calls to "" aren't allowed and are rewritten to the requestURI "/" when running the actual application, shouldn't the same be happening when using mockMvc? That difference seems to be the root cause for this issue.

Comment From: rstoyanchev

Thanks for the sample.

In 5.2.4, paths without a leading slash were disallowed in MockMvc to avoid ambiguity (#24556). In 6.0.4, empty path was excluded (#28823), but it leads to issues in some places. The request path should have a leading "/", and rather than allowing or rejecting an empty path, we can use "/" instead.