Fix ResourceUrlProvider handler auto-detection

Prior to this commit, `ResourceUrlProvider` would listen and consider
all `ContextRefreshedEvent` and use the given context to detect
`SimpleUrlHandlerMapping`.

This could lead to situations where a `ResourceUrlProvider` uses another
application context than its own (in a parent/child context setup) and
detect the wrong set of handlers.

Because `ResourceUrlProvider` locks itself once the auto-detection is
done, we need to ensure that it considers only events sent by its
application context.

Fixes gh-26561
This commit is contained in:
Brian Clozel
2021-02-17 22:04:39 +01:00
parent 49ccd7ad9c
commit 947387b4cd
4 changed files with 109 additions and 24 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,6 +44,7 @@ import static org.mockito.Mockito.mock;
*
* @author Jeremy Grelle
* @author Rossen Stoyanchev
* @author Brian Clozel
*/
public class ResourceUrlProviderTests {
@@ -57,7 +58,7 @@ public class ResourceUrlProviderTests {
@BeforeEach
public void setUp() throws Exception {
void setUp() throws Exception {
this.locations.add(new ClassPathResource("test/", getClass()));
this.locations.add(new ClassPathResource("testalternatepath/", getClass()));
this.handler.setServletContext(new MockServletContext());
@@ -69,13 +70,13 @@ public class ResourceUrlProviderTests {
@Test
public void getStaticResourceUrl() {
void getStaticResourceUrl() {
String url = this.urlProvider.getForLookupPath("/resources/foo.css");
assertThat(url).isEqualTo("/resources/foo.css");
}
@Test // SPR-13374
public void getStaticResourceUrlRequestWithQueryOrHash() {
void getStaticResourceUrlRequestWithQueryOrHash() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContextPath("/");
request.setRequestURI("/");
@@ -90,7 +91,7 @@ public class ResourceUrlProviderTests {
}
@Test // SPR-16526
public void getStaticResourceWithMissingContextPath() {
void getStaticResourceWithMissingContextPath() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContextPath("/contextpath-longer-than-request-path");
request.setRequestURI("/contextpath-longer-than-request-path/style.css");
@@ -100,7 +101,7 @@ public class ResourceUrlProviderTests {
}
@Test
public void getFingerprintedResourceUrl() {
void getFingerprintedResourceUrl() {
Map<String, VersionStrategy> versionStrategyMap = new HashMap<>();
versionStrategyMap.put("/**", new ContentVersionStrategy());
VersionResourceResolver versionResolver = new VersionResourceResolver();
@@ -116,7 +117,7 @@ public class ResourceUrlProviderTests {
}
@Test // SPR-12647
public void bestPatternMatch() throws Exception {
void bestPatternMatch() throws Exception {
ResourceHttpRequestHandler otherHandler = new ResourceHttpRequestHandler();
otherHandler.setLocations(this.locations);
Map<String, VersionStrategy> versionStrategyMap = new HashMap<>();
@@ -138,7 +139,7 @@ public class ResourceUrlProviderTests {
@Test // SPR-12592
@SuppressWarnings("resource")
public void initializeOnce() throws Exception {
void initializeOnce() throws Exception {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setServletContext(new MockServletContext());
context.register(HandlerMappingConfiguration.class);
@@ -149,8 +150,30 @@ public class ResourceUrlProviderTests {
assertThat(urlProviderBean.isAutodetect()).isFalse();
}
@Test
void initializeOnCurrentContext() {
AnnotationConfigWebApplicationContext parentContext = new AnnotationConfigWebApplicationContext();
parentContext.setServletContext(new MockServletContext());
parentContext.register(ParentHandlerMappingConfiguration.class);
AnnotationConfigWebApplicationContext childContext = new AnnotationConfigWebApplicationContext();
childContext.setParent(parentContext);
childContext.setServletContext(new MockServletContext());
childContext.register(HandlerMappingConfiguration.class);
parentContext.refresh();
childContext.refresh();
ResourceUrlProvider parentUrlProvider = parentContext.getBean(ResourceUrlProvider.class);
assertThat(parentUrlProvider.getHandlerMap()).isEmpty();
assertThat(parentUrlProvider.isAutodetect()).isTrue();
ResourceUrlProvider childUrlProvider = childContext.getBean(ResourceUrlProvider.class);
assertThat(childUrlProvider.getHandlerMap()).containsOnlyKeys("/resources/**");
assertThat(childUrlProvider.isAutodetect()).isFalse();
}
@Test // SPR-16296
public void getForLookupPathShouldNotFailIfPathContainsDoubleSlashes() {
void getForLookupPathShouldNotFailIfPathContainsDoubleSlashes() {
// given
ResourceResolver mockResourceResolver = mock(ResourceResolver.class);
given(mockResourceResolver.resolveUrlPath(any(), any(), any())).willReturn("some-path");
@@ -185,4 +208,14 @@ public class ResourceUrlProviderTests {
}
}
@Configuration
@SuppressWarnings({"unused", "WeakerAccess"})
static class ParentHandlerMappingConfiguration {
@Bean
public ResourceUrlProvider resourceUrlProvider() {
return new ResourceUrlProvider();
}
}
}