ŁukaszBachman ŁukaszBachman - 19 days ago 6
Java Question

Should I manually return 304 response for cached requests (ETag) in Restlet?

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.

Answer

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.

Comments