Introduce optimizeLocations flag for resource location filtering on startup
This flag is off by default since it requires jar files with directory entries. Closes gh-27624
This commit is contained in:
@@ -55,6 +55,8 @@ public class ResourceHandlerRegistration {
|
||||
|
||||
private boolean useLastModified = true;
|
||||
|
||||
private boolean optimizeLocations = false;
|
||||
|
||||
|
||||
/**
|
||||
* Create a {@link ResourceHandlerRegistration} instance.
|
||||
@@ -130,15 +132,33 @@ public class ResourceHandlerRegistration {
|
||||
/**
|
||||
* Set whether the {@link Resource#lastModified()} information should be used to drive HTTP responses.
|
||||
* <p>This configuration is set to {@code true} by default.
|
||||
* @param useLastModified whether the "last modified" resource information should be used.
|
||||
* @param useLastModified whether the "last modified" resource information should be used
|
||||
* @return the same {@link ResourceHandlerRegistration} instance, for chained method invocation
|
||||
* @since 5.3
|
||||
* @see ResourceHttpRequestHandler#setUseLastModified
|
||||
*/
|
||||
public ResourceHandlerRegistration setUseLastModified(boolean useLastModified) {
|
||||
this.useLastModified = useLastModified;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to optimize the specified locations through an existence check on startup,
|
||||
* filtering non-existing directories upfront so that they do not have to be checked
|
||||
* on every resource access.
|
||||
* <p>The default is {@code false}, for defensiveness against zip files without directory
|
||||
* entries which are unable to expose the existence of a directory upfront. Switch this flag to
|
||||
* {@code true} for optimized access in case of a consistent jar layout with directory entries.
|
||||
* @param optimizeLocations whether to optimize the locations through an existence check on startup
|
||||
* @return the same {@link ResourceHandlerRegistration} instance, for chained method invocation
|
||||
* @since 5.3.13
|
||||
* @see ResourceHttpRequestHandler#setOptimizeLocations
|
||||
*/
|
||||
public ResourceHandlerRegistration setOptimizeLocations(boolean optimizeLocations) {
|
||||
this.optimizeLocations = optimizeLocations;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a chain of resource resolvers and transformers to use. This
|
||||
* can be useful, for example, to apply a version strategy to resource URLs.
|
||||
@@ -204,6 +224,7 @@ public class ResourceHandlerRegistration {
|
||||
handler.setCacheSeconds(this.cachePeriod);
|
||||
}
|
||||
handler.setUseLastModified(this.useLastModified);
|
||||
handler.setOptimizeLocations(this.optimizeLocations);
|
||||
return handler;
|
||||
}
|
||||
|
||||
|
||||
@@ -140,11 +140,13 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
@Nullable
|
||||
private UrlPathHelper urlPathHelper;
|
||||
|
||||
private boolean useLastModified = true;
|
||||
|
||||
private boolean optimizeLocations = false;
|
||||
|
||||
@Nullable
|
||||
private StringValueResolver embeddedValueResolver;
|
||||
|
||||
private boolean useLastModified = true;
|
||||
|
||||
|
||||
public ResourceHttpRequestHandler() {
|
||||
super(HttpMethod.GET.name(), HttpMethod.HEAD.name());
|
||||
@@ -185,13 +187,13 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
/**
|
||||
* Return the configured {@code List} of {@code Resource} locations including
|
||||
* both String-based locations provided via
|
||||
* {@link #setLocationValues(List) setLocationValues} and pre-resolved {@code Resource}
|
||||
* locations provided via {@link #setLocations(List) setLocations}.
|
||||
* {@link #setLocationValues(List) setLocationValues} and pre-resolved
|
||||
* {@code Resource} locations provided via {@link #setLocations(List) setLocations}.
|
||||
* <p>Note that the returned list is fully initialized only after
|
||||
* initialization via {@link #afterPropertiesSet()}.
|
||||
* <p><strong>Note:</strong> As of 5.3.11 the list of locations is filtered
|
||||
* to exclude those that don't actually exist and therefore the list returned
|
||||
* from this method may be a subset of all given locations.
|
||||
* <p><strong>Note:</strong> As of 5.3.11 the list of locations may be filtered to
|
||||
* exclude those that don't actually exist and therefore the list returned from this
|
||||
* method may be a subset of all given locations. See {@link #setOptimizeLocations}.
|
||||
* @see #setLocationValues
|
||||
* @see #setLocations
|
||||
*/
|
||||
@@ -293,7 +295,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
/**
|
||||
* Return the configured content negotiation manager.
|
||||
* @since 4.3
|
||||
* @deprecated as of 5.2.4.
|
||||
* @deprecated as of 5.2.4
|
||||
*/
|
||||
@Nullable
|
||||
@Deprecated
|
||||
@@ -303,7 +305,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
|
||||
/**
|
||||
* Add mappings between file extensions, extracted from the filename of a
|
||||
* static {@link Resource}, and corresponding media type to set on the
|
||||
* static {@link Resource}, and corresponding media type to set on the
|
||||
* response.
|
||||
* <p>Use of this method is typically not necessary since mappings are
|
||||
* otherwise determined via
|
||||
@@ -361,9 +363,16 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
return this.urlPathHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEmbeddedValueResolver(StringValueResolver resolver) {
|
||||
this.embeddedValueResolver = resolver;
|
||||
/**
|
||||
* Set whether we should look at the {@link Resource#lastModified()} when
|
||||
* serving resources and use this information to drive {@code "Last-Modified"}
|
||||
* HTTP response headers.
|
||||
* <p>This option is enabled by default and should be turned off if the metadata
|
||||
* of the static files should be ignored.
|
||||
* @since 5.3
|
||||
*/
|
||||
public void setUseLastModified(boolean useLastModified) {
|
||||
this.useLastModified = useLastModified;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -376,18 +385,35 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether we should look at the {@link Resource#lastModified()}
|
||||
* when serving resources and use this information to drive {@code "Last-Modified"}
|
||||
* HTTP response headers.
|
||||
* <p>This option is enabled by default and should be turned off if the metadata of
|
||||
* the static files should be ignored.
|
||||
* @param useLastModified whether to use the resource last-modified information.
|
||||
* @since 5.3
|
||||
* Set whether to optimize the specified locations through an existence
|
||||
* check on startup, filtering non-existing directories upfront so that
|
||||
* they do not have to be checked on every resource access.
|
||||
* <p>The default is {@code false}, for defensiveness against zip files
|
||||
* without directory entries which are unable to expose the existence of
|
||||
* a directory upfront. Switch this flag to {@code true} for optimized
|
||||
* access in case of a consistent jar layout with directory entries.
|
||||
* @since 5.3.13
|
||||
*/
|
||||
public void setUseLastModified(boolean useLastModified) {
|
||||
this.useLastModified = useLastModified;
|
||||
public void setOptimizeLocations(boolean optimizeLocations) {
|
||||
this.optimizeLocations = optimizeLocations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to optimize the specified locations through an existence
|
||||
* check on startup, filtering non-existing directories upfront so that
|
||||
* they do not have to be checked on every resource access.
|
||||
* @since 5.3.13
|
||||
*/
|
||||
public boolean isOptimizeLocations() {
|
||||
return this.optimizeLocations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEmbeddedValueResolver(StringValueResolver resolver) {
|
||||
this.embeddedValueResolver = resolver;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
resolveResourceLocations();
|
||||
@@ -449,8 +475,8 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
if (location.equals("/") && !(resource instanceof ServletContextResource)) {
|
||||
throw new IllegalStateException(
|
||||
"The String-based location \"/\" should be relative to the web application root " +
|
||||
"but resolved to a Resource of type: " + resource.getClass() + ". " +
|
||||
"If this is intentional, please pass it as a pre-configured Resource via setLocations.");
|
||||
"but resolved to a Resource of type: " + resource.getClass() + ". " +
|
||||
"If this is intentional, please pass it as a pre-configured Resource via setLocations.");
|
||||
}
|
||||
result.add(resource);
|
||||
if (charset != null) {
|
||||
@@ -463,7 +489,9 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
}
|
||||
|
||||
result.addAll(this.locationResources);
|
||||
result = result.stream().filter(Resource::exists).collect(Collectors.toList());
|
||||
if (isOptimizeLocations()) {
|
||||
result = result.stream().filter(Resource::exists).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
this.locationsToUse.clear();
|
||||
this.locationsToUse.addAll(result);
|
||||
@@ -508,6 +536,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes a resource request.
|
||||
* <p>Checks for the existence of the requested resource in the configured list of locations.
|
||||
|
||||
@@ -311,7 +311,7 @@ public class ResourceHttpRequestHandlerTests {
|
||||
assertThat(this.response.getContentAsString()).isEqualTo("h1 { color:red; }");
|
||||
}
|
||||
|
||||
@Test // gh-27538
|
||||
@Test // gh-27538, gh-27624
|
||||
public void filterNonExistingLocations() throws Exception {
|
||||
List<Resource> inputLocations = Arrays.asList(
|
||||
new ClassPathResource("test/", getClass()),
|
||||
@@ -321,6 +321,7 @@ public class ResourceHttpRequestHandlerTests {
|
||||
ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler();
|
||||
handler.setServletContext(new MockServletContext());
|
||||
handler.setLocations(inputLocations);
|
||||
handler.setOptimizeLocations(true);
|
||||
handler.afterPropertiesSet();
|
||||
|
||||
List<Resource> actual = handler.getLocations();
|
||||
|
||||
Reference in New Issue
Block a user