Created
July 18, 2015 06:50
-
-
Save distantcam/06e08761a3c5884949fe to your computer and use it in GitHub Desktop.
Akavache and ETags - http://cam.macfar.land/blog/akavache-and-etags/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class AkavacheExtensions | |
{ | |
public static IObservable<string> GetOrFetchWithETag(this IBlobCache cache, string url) | |
{ | |
var result = | |
// Get from cache | |
cache.GetObject<string>(url) | |
// Cached values are true | |
.Select(x => Tuple.Create(x, true)) | |
// Turn exceptions into false | |
.Catch(Observable.Return(Tuple.Create("", false))) | |
// If true, return an observable with the result, else an empty observable. | |
.SelectMany(x => x.Item2 ? Observable.Return(x.Item1) : Observable.Empty<string>()); | |
var fetch = | |
// Get the ETag from cache | |
cache.GetObject<string>("etag-" + url) | |
// Exceptions => Blank ETag | |
.Catch(Observable.Return("")) | |
// Call our web method | |
.SelectMany(etag => GetFromWeb(url, etag) | |
// Invalidate the old and add the new etag to the cache | |
.SelectMany(x => cache.InvalidateObject<string>("etag-" + url).Select(_ => x)) | |
.SelectMany(x => cache.InsertObject("etag-" + url, x.Item1).Select(_ => x)) | |
// Invalidate the old and add the new data to the cache | |
.SelectMany(x => cache.InvalidateObject<string>(url).Select(_ => x)) | |
.SelectMany(x => cache.InsertObject(url, x.Item2).Select(_ => x))) | |
// Select the data from the tuple | |
.Select(x => x.Item2); | |
return result | |
.Concat(fetch) | |
.Replay() | |
.RefCount(); | |
} | |
private static HttpClient CreateWebClient() | |
{ | |
var handler = new HttpClientHandler(); | |
if (handler.SupportsAutomaticDecompression) | |
{ | |
handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; | |
} | |
var client = new HttpClient(handler); | |
return client; | |
} | |
private static IObservable<Tuple<string, string>> GetFromWeb(string url, string etag) | |
{ | |
return Observable.Create<Tuple<string, string>>(async observer => | |
{ | |
using (var client = CreateWebClient()) | |
{ | |
var request = new HttpRequestMessage | |
{ | |
Method = HttpMethod.Get, | |
RequestUri = new Uri(url) | |
}; | |
if (!string.IsNullOrEmpty(etag)) | |
{ | |
request.Headers.TryAddWithoutValidation("If-None-Match", etag); | |
} | |
var response = await client.SendAsync(request) | |
.ConfigureAwait(false); | |
if (!response.IsSuccessStatusCode && | |
response.StatusCode != HttpStatusCode.NotModified) | |
{ | |
observer.OnError(new HttpRequestException( | |
"Status code: " + response.StatusCode)); | |
} | |
else if (response.IsSuccessStatusCode) | |
{ | |
var data = await response.Content.ReadAsStringAsync() | |
.ConfigureAwait(false); | |
observer.OnNext(Tuple.Create(response.Headers.ETag.Tag, data)); | |
} | |
} | |
observer.OnCompleted(); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment