Wednesday, June 24, 2009

WebKit/Safari Application Cache Work-Around

If you are using offline Application Cache W3C and Safari in the initial 4.0 release of WebKit/Safari you might run into a strange problem, let me explain.

Initial Download

When you first download the site into your application cache everything works fine.  From Fiddler (one of my all time favorite tools) you can see the “stuff” that gets sent over the wire.

image

As you can see it includes an ETag and Last-Modified date even though we have additional headers to ignore the cache.  At least that’s how IIS 7.0 serves up the content.  I added the following custom response headers to make sure these files are aren’t cached.

image

When you Update Your Application Cache

Now the problem, when you update your manifest file all the offline files should be re-downloaded and stored into the cache.  When the resource is requested, you will see the following traffic within Fiddler.
 image

Drilling down into one of these requests, it becomes fairly obvious where the problem is:

image

When WebKit/Safari attempts to download a resource it sends up an If-Modified-Since and a If-None-Match header.  IIS looks at the files and says, Nope, the file hasn’t changed, so I’ll *help* you out by returning a 304 Not Modified status code.  WebKit/Safari says, nope, that’s not good enough, I’m going to request it again.  As you see with the initial Fiddler trace, this will continue as long as you let it.  If you clear the cache in the browser and make the request.  It won’t send the headers to check for “304 Non-Modified” so it will work, but I don’t think you want to have your users having to clear the cache everything you download an updated.

The Solution

I searched forever (well at least 5 minutes) on how to disable 304 checking and the only thing that was even close was to create an HttpModule to remove the headers.  That’s what I ended up doing.

Obviously this is C# code that is running within IIS.  If you’ve followed this far, chances are you get the gist of what I’m doing here.  The code and solution isn’t the important thing but understanding what needs to be done is.

public class RemoveCacheTagModule : IHttpModule
{
    public void Dispose()
    {
        throw new NotImplementedException();
    }

    public void Init(HttpApplication context)
    {           
        context.PreSendRequestContent += new EventHandler(context_PreSendRequestContent);        
    }

    void context_PreSendRequestContent(object sender, EventArgs e)
    {           
        HttpContext.Current.Response.Headers.Remove("ETag");
        HttpContext.Current.Response.Headers.Remove("Last-Modified");
    }
}

 

With the HttpModule installed and running Fiddler you can now see that the headers no longer contain a Last-Modifed or ETag so when WebKit/Safari makes the request for the resource IIS has no choice but to return the resource with a status code of 200.

image

Hope that saves someone a bit of time.

-ec

1 comment:

  1. I'm an IIS noob; how do you get that C# code running to remove those headers?

    Thanks!

    ReplyDelete