-
-
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"}] |
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.
@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
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.
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;
};
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?