Skip to content

Instantly share code, notes, and snippets.

@distantcam
Created July 18, 2015 06:50
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save distantcam/06e08761a3c5884949fe to your computer and use it in GitHub Desktop.
Save distantcam/06e08761a3c5884949fe to your computer and use it in GitHub Desktop.
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