-
Notifications
You must be signed in to change notification settings - Fork 38.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support ETag generation on ResourceHttpRequestHandler #29031
Comments
When debugging locally, it seems that If this is the problem, could this simply be the result of a bad configuration where the |
You've obviously spent a lot of time analyzing this issue, thanks for that. Unfortunately it's still hard to understand the core problem. The problem might be in multiple places, a documentation issue or both. Often, a simple reproducer is unbeatable! Thanks! |
Thanks for the feedback @bclozel. I've added a simple reproducer here: https://github.com/skjelmo/spring-unexpected-304 Opening http://localhost:8080/index.html should provide necessary values from Sending a curl request with the If you modify However, if you only modify the (Remember to replace header values)
The core problem seems to be that The existing test |
304 Not Modified
when combining If-Modified-Since
and If-None-Match
304 Not Modified
when combining If-Modified-Since
and If-None-Match
Thanks for the analysis and the sample application, I think I understand now what's going on. The core problem here is that you expect the resource handling (
Resource handling is a separate feature provided by Spring MVC. This is a complete solution for serving static resources from a Spring MVC application. This includes managing conditional and caching headers. What happens here is that the resource handling only considers the caching aspects using the information it's got: the last modified resource metadata and the request headers. The handler is called first, as the filter at this point only wrapped the response. The handler here is in a position to skip entirely the writing of the resource to the response, saving CPU and memory. At this point, there is no ETag information available. Once that's done, the In order to get the expected behavior here, we need to be in a position to have both the last modified information and a calculated ETag for the resource. Of course, calculating that hash is an opinion on its own. In a Spring MVC application, this can be provided at the controller level (calling In your case, this In general, I think that the |
Thank you for the thorough response @bclozel! You are correct in that we expected the resource handling and We have not yet been able to handle both
However, we are curious how a
CSS- and JavaScript-files are generated by webpack with a content-hash in their filename. They are included by Webpack in an @Configuration
public class WebConfig extends WebMvcConfigurationSupport {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/path_to_resources/**")
.addResourceLocations("/path_to_resources/");
}
@Override
protected void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("forward:/path_to_resources/html/index.html");
}
} Is it perhaps possible to configure a handler that handles both |
Sorry for the late reply. I think we could consider an enhancement where we could configure on the In the meantime, I think the easiest way to handle the index page is a code snippet similar to this: @GetMapping("/")
@ResponseBody
public Resource index(WebRequest request) throws IOException {
ClassPathResource indexPage = new ClassPathResource("static/index.html");
String eTag = DigestUtils.md5DigestAsHex(indexPage.getInputStream());
long lastModified = indexPage.lastModified();
if (request.checkNotModified(eTag, lastModified)) {
return null;
}
return indexPage;
} I'm repurposing this issue as an enhancement. |
304 Not Modified
when combining If-Modified-Since
and If-None-Match
Thanks for implementing the enhancement @bclozel 🙌 🥳! |
This commit replicates the ETag generation option now available on `ResourceHttpRequestHandler` but for its WebFlux counterpart. See gh-29031
This commit surfaces the ETag generation feature for both `ResourceHttpRequestHandler` and `ResourceWebHandler` on their respective `ResourceHandlerRegistration` for easier configuration. See gh-29031
Affects: 5.3.21
Stack overflow question
Expected behaviour:
If-None-Match
has precedence whenIf-None-Match
is used in combination withIf-Modified-Since
.The function
checkNotModified
inorg.springframework.web.context.request.ServletWebRequest
references the expected order of precedence with the following comment:checkNotModified methods
Observed behaviour
The server returns
304 Not Modified
for an invalid etag when both theIf-None-Match
andIf-Modified-Since
-header is set.The response is updated with status
NOT MODIFIED
whencheckNotModified
is called fromhandleRequest
inorg.springframework.web.servlet.resource.ResourceHttpRequestHandler
andlastModifiedTimestamp
indicates that the resources is not modified.The response is not updated again when
checkNotModified
is called fromupdateResponse
inorg.springframework.web.filter.ShallowEtagHeaderFilter
with an etag value indicating that the resource is modified.Test coverage
checkNotModified
is covered by the testIfNoneMatchAndIfNotModifiedSinceShouldNotMatchWhenDifferentETag
. However, this test covers the case wherecheckNotModified
is called with botheTag
andlastModifiedTimestamp
. In the case described,checkNotModified
is called sequentially by overloaded methods. The overloaded method call withlastModifiedTimestamp
alters the response status to304 Not Modified
, and it seems that it makes isEligibleForEtag in ShallowEtagHeaderFilter return false if the status code is304
, and the call tocheckNotModified
from updateResponse in shallowEtagHeaderFilter does not update the response from304
to200 OK
if the etag has changed.I currently see no test in ShallowEtagHeaderFilterTests asserting that the response code is
200 OK
if a response has an invalid etag and response code304 Not Modified
.Problem
After rollback to an earlier deployment, clients will send a
lastModifiedTimestamp
newer than the content on the server and an invalid etag. SinceIf-None-Match
doesn't have precedence as expected, the client recieves a304 NOT MODIFIED
when a200 OK
was expected.Work-around
Problem can be mitigated by not using
If-Modified-Since
. E.g. utilizingsetUseLastModified(false)
on a resource handler.Reproduction
Send a
GET
-request with an invalid etag andlastModifiedTimestamp
in the future.The text was updated successfully, but these errors were encountered: