Skip to content

Instantly share code, notes, and snippets.

@PeteGoo
Created January 16, 2013 08:58
Show Gist options
  • Save PeteGoo/4545689 to your computer and use it in GitHub Desktop.
Save PeteGoo/4545689 to your computer and use it in GitHub Desktop.
using CacheCow.Client.FileCacheStore;
namespace Caching.WpfApp {
public class PeopleProxy {
public async Task<IEnumerable<Person>> GetAll() {
var handler = new CachingHandler(
new FileStore("c:\\temp\\cache.cow")) {
InnerHandler = new HttpClientHandler()
};
HttpClient client = new HttpClient(handler) {
BaseAddress = new Uri("http://ipv4.fiddler/Caching.WebApplication/"),
};
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("api/people");
if (!response.IsSuccessStatusCode) {
// maybe you'll want to throw a real exception type
throw new Exception("Could not fetch people");
}
return await response.Content.ReadAsAsync<IEnumerable<Person>>();
}
}
}
GET http://127.0.0.1/Caching.WebApplication/api/people HTTP/1.1
Accept: application/json
Host: 127.0.0.1
Connection: Keep-Alive
HTTP/1.1 200 OK
Cache-Control: max-age=20
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Wed, 16 Jan 2013 08:56:53 GMT
Content-Length: 161
[{"id":1,"firstName":"Peter","lastName":"Goodman","favouriteMovie":"The Big Lebowski"},{"id":2,"firstName":"Foo","lastName":"Bar","favouriteMovie":"The Grinch"}]
@PeteGoo
Copy link
Author

PeteGoo commented Jan 16, 2013

When I issue the client request, the response is properly handled and subsequent calls are served from the cache.

However after 20 seconds I would have expected the cache to be evicted but it is not and all subsequent calls are served from the cache even after an app restart.

Looking at the cache.cow code at CachingHandler Line 174

It would seem that this should be a "<" as a stale value is not acceptable if the request has no cache-control header.

Or am I getting this all wrong?

@aliostad
Copy link

Thanks Peter. Let me have a look at this tonight. Might very well be a bug.
Some unit tests are missing in this area which I need to add. Do you mind creating an issue against CacheCow (so there is a history) and just add this gist link to it?

My expectation here is for handler to change the call to a conditional GET and in case 304, gets it from cache.

@PeteGoo
Copy link
Author

PeteGoo commented Jan 16, 2013

@aliostad Done. I think I've figured out what it should be and fired off a pull request. Feel free to bounce it if you think it's wrong or there's a better way.

Pete

@aliostad
Copy link

I merged the code but I think it is still not right. Will need to work on it.

This needs to fall into the Must-Revalidate which is missing in your server's response.

@aliostad
Copy link

OK I rushed the merge, there was no need to change the code. The code is doing as it should do so I have reverted the changes.

Have a look here:http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4
The point here is that a cache implementation MAY choose to use the stale value - as CacheCow is doing. Server usually prevents this behaviour by sending must-revalidate.

In your case, response from the server did not have must-revalidate so CacheCow is free to serve stale response from cache. You can change this behaviour however by setting ResponseStoragePreparationRules and in there in addition to default work, set the response.Headers.CacheControl.MustRevalidate (make sure CacheControl header has a value and is not null):

    ResponseStoragePreparationRules = (response) =>
            {
                // 14.9.3
                // If a response includes both an Expires header and a max-age directive, 
                // the max-age directive overrides the Expires header, even if the Expires header is more restrictive.
                if(response.Content.Headers.Expires!=null &&
                    (response.Headers.CacheControl.MaxAge != null || response.Headers.CacheControl.SharedMaxAge!=null))
                {
                    response.Content.Headers.Expires = null;
                }
                response.Headers.CacheControl.MustRevalidate = true;
            };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment