I added a custom caching behavior to my application, similar to what Thierry has proposed in this article. For every CSS, JS and HTML file server in my static content I am adding the two following headers:
// Added by me
ETag: "0c635aa7113262fac7606da2432e00f5" // md5(last_mod_date_of_file)
Cache-Control: max-age=31536000 // one year
// Automatically added by Restlet (probably Directory class?)
Date: Wed, 09 Nov 2016 11:50:53 GMT
Expires: Wed, 09 Nov 2016 12:00:53 GMT
Last-Modified: Wed, 09 Nov 2016 17:30:56 GMT
This works fine, however I have noticed that after deploying the code on test server and hitting F5
in Chrome, I fetch the entire body of response once again (with HTTP 200 returned).
I've noticed that those requests are using proper headers too:
Cache-Control:max-age=0
If-Modified-Since: Wed, 09 Nov 2016 17:30:56 GMT
If-None-Match: "0c635aa7113262fac7606da2432e00f5"
My question is, should I do any manual verifications of If-None-Match
header in my server filter and return 304 response then? Or is that handled by Restlet?
Note: what's a bit strange in this issue is the fact that it seemed to work properly on my local development environment. I'm also a little bit confused as to why Expires
is set by Restlet to a date before Last-Modified
. I'll try to debug if this is the root of evil, but it doesn't invalidate my question about manual setting of 304 status and checking ETags on the server.
Ok, so I've been able to figure this out and I'm posting answers below.
Should I do any manual verification of If-None-Match header in my server filter and return 304 response then?
No, you don't have to do that yourself manually. This is automatically being handled by Restlet (DirectoryServerResource
takes care of that).
What was the problem then?
The problem was indeed with the Last-Modified
header being set to a future date. This has happened because my production server was in UTC-8
Time Zone, whereas I'm developing in UTC+1
.
How did I fix it?
It required getting acquainted with Restlet API, but the solution was trivial then. I made sure that when my application is started it reads File Last Modified
property of my application directory from Operating System, as this is the value I wanted to use in Last-Modified
header.
Now, you can't just set this header on a response
in a Filter
, as the automatic handling of HTTP caching headers happens before that in mentioned DirectoryServerResource
class. So the solution is the following:
Create a class which extends DSR (giving you all the automatic caching handling for free) and modify its handle()
method so that Last-Modified
header is set before this logic kicks in:
public class WebAssetsResource extends DirectoryServerResource {
@Override
public Representation handle() {
Date desiredDate = ...; // I read this from File System
getInfo().setModificationDate(desiredDate);
return super.handle(); // Automatic logic will use desired date
}
}
Now, make sure that your newly created resource is used by custom Directory
class.
public class CachedWebAssetsDirectory extends Directory {
public CachedWebAssetsDirectory(Context context, Reference rootLocalReference) {
super(context, rootLocalReference);
setTargetClass(WebAssetsResource.class); // Needed so that Restlet will use our implementation of a ServerResource to serve static files
}
}
After that you can use CachedWebAssetsDirectory
as you wish, building any custom filters on top of that.