public virtual void Save(string path, string message) { logger.Log("INFO", $"Saving message to {path}."); saveOrReadStorage.Save(path, message); cacheStorage.AddOrUpdate(path, message); logger.Log("INFO", $"Saved message to {path}."); }
internal static void UpdateCachedResponse(CacheKey cacheKey, HttpResponseMessage cachedResponse, HttpResponseMessage serverResponse, ICacheStore store) { TraceWriter.WriteLine("CachingHandler.UpdateCachedResponse - response: " + serverResponse.Headers, TraceLevel.Verbose); // update only if server had a cachecontrol. // TODO: merge CacheControl headers instead of replace if ((serverResponse.Headers.CacheControl != null) && !serverResponse.Headers.CacheControl.NoCache) // added to cover issue #139 { TraceWriter.WriteLine( "CachingHandler.UpdateCachedResponse - CacheControl: " + serverResponse.Headers.CacheControl, TraceLevel.Verbose); cachedResponse.Headers.CacheControl = serverResponse.Headers.CacheControl; } else { TraceWriter.WriteLine( "CachingHandler.UpdateCachedResponse - CacheControl missing from server. Applying sliding expiration. Date => " + DateTimeOffset.UtcNow, TraceLevel.Verbose); } cachedResponse.Headers.Date = DateTimeOffset.UtcNow; // very important store.AddOrUpdate(cacheKey, cachedResponse); }
internal static void UpdateCachedResponse(CacheKey cacheKey, HttpResponseMessage cachedResponse, HttpResponseMessage serverResponse, ICacheStore store) { // update only if server had a cachecontrol. // TODO: merge CacheControl headers instead of replace if (serverResponse.Headers.CacheControl != null) { cachedResponse.Headers.CacheControl = serverResponse.Headers.CacheControl; cachedResponse.Headers.Date = DateTimeOffset.UtcNow; // very important store.AddOrUpdate(cacheKey, cachedResponse); } }
internal static void UpdateCachedResponse(CacheKey cacheKey, HttpResponseMessage cachedResponse, HttpResponseMessage serverResponse, ICacheStore store) { TraceWriter.WriteLine("CachingHandler.UpdateCachedResponse - response: " + serverResponse.Headers.ToString(), TraceLevel.Verbose); // update only if server had a cachecontrol. // TODO: merge CacheControl headers instead of replace if (serverResponse.Headers.CacheControl != null && (!serverResponse.Headers.CacheControl.NoCache)) // added to cover issue #139 { TraceWriter.WriteLine("CachingHandler.UpdateCachedResponse - CacheControl: " + serverResponse.Headers.CacheControl.ToString(), TraceLevel.Verbose); cachedResponse.Headers.CacheControl = serverResponse.Headers.CacheControl; cachedResponse.Headers.Date = DateTimeOffset.UtcNow; // very important store.AddOrUpdate(cacheKey, cachedResponse); } }
internal static void UpdateCachedResponse(CacheKey cacheKey, HttpResponseMessage cachedResponse, HttpResponseMessage serverResponse, ICacheStore store) { TraceWriter.WriteLine("CachingHandler.UpdateCachedResponse - response: " + serverResponse.Headers.ToString(), TraceLevel.Verbose); // update only if server had a cachecontrol. // TODO: merge CacheControl headers instead of replace if (serverResponse.Headers.CacheControl != null && (!serverResponse.Headers.CacheControl.NoCache)) // added to cover issue #139 { TraceWriter.WriteLine("CachingHandler.UpdateCachedResponse - CacheControl: " + serverResponse.Headers.CacheControl.ToString(), TraceLevel.Verbose); cachedResponse.Headers.CacheControl = serverResponse.Headers.CacheControl; } else { TraceWriter.WriteLine("CachingHandler.UpdateCachedResponse - CacheControl missing from server. Applying sliding expiration. Date => " + DateTimeOffset.UtcNow, TraceLevel.Verbose); } cachedResponse.Headers.Date = DateTimeOffset.UtcNow; // very important store.AddOrUpdate(cacheKey, cachedResponse); }
// TODO: this method is terribly long. Shorten protected override Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var cacheCowHeader = new CacheCowHeader(); string uri = request.RequestUri.ToString(); TraceWriter.WriteLine("{0} - Starting SendAsync", TraceLevel.Verbose, request.RequestUri.ToString()); // check if needs to be ignored if (_ignoreRequestRules(request)) { return(base.SendAsync(request, cancellationToken)); // EXIT !! _________________ } IEnumerable <string> varyHeaders; if (!VaryHeaderStore.TryGetValue(uri, out varyHeaders)) { varyHeaders = DefaultVaryHeaders; } var cacheKey = new CacheKey(uri, request.Headers.Where(x => varyHeaders.Any(y => y.Equals(x.Key, StringComparison.CurrentCultureIgnoreCase))) .SelectMany(z => z.Value) ); // get from cache and verify response HttpResponseMessage cachedResponse; ResponseValidationResult validationResultForCachedResponse = ResponseValidationResult.NotExist; try { TraceWriter.WriteLine("{0} - Before TryGetValue", TraceLevel.Verbose, request.RequestUri.ToString()); cacheCowHeader.DidNotExist = !_cacheStore.TryGetValue(cacheKey, out cachedResponse); TraceWriter.WriteLine("{0} - After TryGetValue: DidNotExist => {1}", TraceLevel.Verbose, request.RequestUri.ToString(), cacheCowHeader.DidNotExist); if (!cacheCowHeader.DidNotExist.Value) // so if it EXISTS in cache { TraceWriter.WriteLine("{0} - Existed in the cache. CacheControl Headers => {1}", TraceLevel.Verbose, request.RequestUri.ToString(), cachedResponse.Headers.CacheControl.ToString()); cachedResponse.RequestMessage = request; validationResultForCachedResponse = ResponseValidator(cachedResponse); } TraceWriter.WriteLine("{0} - After ResponseValidator => {1}", TraceLevel.Verbose, request.RequestUri, validationResultForCachedResponse); // PUT validation if (request.Method == HttpMethod.Put && validationResultForCachedResponse.IsIn( ResponseValidationResult.OK, ResponseValidationResult.MustRevalidate)) { DoPutValidation(request, cacheCowHeader, cachedResponse); return(base.SendAsync(request, cancellationToken)); // EXIT !! _____________________________ } // here onward is only GET only. See if cache OK and if it is then return if (validationResultForCachedResponse == ResponseValidationResult.OK) { cacheCowHeader.RetrievedFromCache = true; return(TaskHelpers.FromResult(cachedResponse.AddCacheCowHeader(cacheCowHeader))); // EXIT !! ____________________________ } // if stale else if (validationResultForCachedResponse == ResponseValidationResult.Stale) { cacheCowHeader.WasStale = true; var isFreshOrStaleAcceptable = IsFreshOrStaleAcceptable(cachedResponse, request); if (isFreshOrStaleAcceptable.HasValue && isFreshOrStaleAcceptable.Value) // similar to OK { // TODO: CONSUME AND RELEASE Response !!! return(TaskHelpers.FromResult(cachedResponse.AddCacheCowHeader(cacheCowHeader))); // EXIT !! ____________________________ } else { validationResultForCachedResponse = ResponseValidationResult.MustRevalidate; // revalidate } } // cache validation for GET else if (validationResultForCachedResponse == ResponseValidationResult.MustRevalidate) { DoCacheValidationForGet(request, cacheCowHeader, cachedResponse); } } catch (Exception ex) { if (ExceptionHandler == null) { throw; } else { ExceptionHandler(ex); Trace.TraceWarning("Exception was swallowed in CacheCow: " + ex.ToString()); return(base.SendAsync(request, cancellationToken)); } } // _______________________________ RESPONSE only GET ___________________________________________ return(base.SendAsync(request, cancellationToken) .ContinueWith( tt => { // HERE IS LATE FOR APPLYING EXCEPTION POLICY !!! var serverResponse = tt.Result; TraceWriter.WriteLine("{0} - After getting response", TraceLevel.Verbose, request.RequestUri.ToString()); if (request.Method != HttpMethod.Get) // only interested here if it is a GET - this line really never called - only GET gets here { return serverResponse; } // in case of MustRevalidate with result 304 if (validationResultForCachedResponse == ResponseValidationResult.MustRevalidate && serverResponse.StatusCode == HttpStatusCode.NotModified) { TraceWriter.WriteLine("{0} - Got 304 from the server and ResponseValidationResult.MustRevalidate", TraceLevel.Verbose, request.RequestUri.ToString()); cachedResponse.RequestMessage = request; cacheCowHeader.RetrievedFromCache = true; TraceWriter.WriteLine("{0} - NotModified", TraceLevel.Verbose, request.RequestUri.ToString()); UpdateCachedResponse(cacheKey, cachedResponse, serverResponse, _cacheStore); ConsumeAndDisposeResponse(serverResponse); return cachedResponse.AddCacheCowHeader(cacheCowHeader); // EXIT !! _______________ } var validationResult = ResponseValidator(serverResponse); switch (validationResult) { case ResponseValidationResult.MustRevalidate: case ResponseValidationResult.OK: TraceWriter.WriteLine("{0} - ResponseValidationResult.OK or MustRevalidate", TraceLevel.Verbose, request.RequestUri.ToString()); // prepare ResponseStoragePreparationRules(serverResponse); TraceWriter.WriteLine("{0} - Before AddOrUpdate", TraceLevel.Verbose, request.RequestUri.ToString()); // re-create cacheKey with real server accept // if there is a vary header, store it if (serverResponse.Headers.Vary != null) { varyHeaders = serverResponse.Headers.Vary.Select(x => x).ToArray(); IEnumerable <string> temp; if (!VaryHeaderStore.TryGetValue(uri, out temp)) { VaryHeaderStore.AddOrUpdate(uri, varyHeaders); } } // create real cacheKey with correct Vary headers cacheKey = new CacheKey(uri, request.Headers.Where(x => varyHeaders.Any(y => y.Equals(x.Key, StringComparison.CurrentCultureIgnoreCase))) .SelectMany(z => z.Value) ); // store the cache _cacheStore.AddOrUpdate(cacheKey, serverResponse); TraceWriter.WriteLine("{0} - After AddOrUpdate", TraceLevel.Verbose, request.RequestUri.ToString()); break; default: TraceWriter.WriteLine("{0} - ResponseValidationResult. Other", TraceLevel.Verbose, request.RequestUri.ToString()); TraceWriter.WriteLine("{0} - Before TryRemove", TraceLevel.Verbose, request.RequestUri.ToString()); _cacheStore.TryRemove(cacheKey); TraceWriter.WriteLine("{0} - After AddOrUpdate", TraceLevel.Verbose, request.RequestUri.ToString()); cacheCowHeader.NotCacheable = true; break; } TraceWriter.WriteLine("{0} - Before returning response", TraceLevel.Verbose, request.RequestUri.ToString()); return serverResponse.AddCacheCowHeader(cacheCowHeader); })); }
// TODO: this method is terribly long. Shorten protected override Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var cacheCowHeader = new CacheCowHeader(); string uri = request.RequestUri.ToString(); TraceWriter.WriteLine("{0} - Starting", TraceLevel.Verbose, request.RequestUri.ToString()); // check if needs to be ignored if (_ignoreRequestRules(request)) { return(base.SendAsync(request, cancellationToken)); // EXIT !! _________________ } IEnumerable <string> varyHeaders; if (!VaryHeaderStore.TryGetValue(uri, out varyHeaders)) { varyHeaders = DefaultVaryHeaders; } var cacheKey = new CacheKey(uri, request.Headers.Where(x => varyHeaders.Any(y => y.Equals(x.Key, StringComparison.CurrentCultureIgnoreCase))) .SelectMany(z => z.Value) ); // get from cache and verify response HttpResponseMessage cachedResponse; ResponseValidationResult validationResultForCachedResponse = ResponseValidationResult.NotExist; TraceWriter.WriteLine("{0} - Before TryGetValue", TraceLevel.Verbose, request.RequestUri.ToString()); cacheCowHeader.DidNotExist = !_cacheStore.TryGetValue(cacheKey, out cachedResponse); TraceWriter.WriteLine("{0} - After TryGetValue", TraceLevel.Verbose, request.RequestUri.ToString()); if (!cacheCowHeader.DidNotExist.Value) // so if it EXISTS in cache { cachedResponse.RequestMessage = request; validationResultForCachedResponse = ResponseValidator(cachedResponse); } TraceWriter.WriteLine("{0} - After ResponseValidator {1}", TraceLevel.Verbose, request.RequestUri, validationResultForCachedResponse); // PUT validation if (request.Method == HttpMethod.Put && validationResultForCachedResponse.IsIn( ResponseValidationResult.OK, ResponseValidationResult.MustRevalidate)) { // add headers for a cache validation. First check ETag since is better if (UseConditionalPut) { cacheCowHeader.CacheValidationApplied = true; if (cachedResponse.Headers.ETag != null) { request.Headers.Add(HttpHeaderNames.IfMatch, cachedResponse.Headers.ETag.ToString()); } else if (cachedResponse.Content.Headers.LastModified != null) { request.Headers.Add(HttpHeaderNames.IfUnmodifiedSince, cachedResponse.Content.Headers.LastModified.Value.ToString("r")); } } return(base.SendAsync(request, cancellationToken)); // EXIT !! _____________________________ } // here onward is only GET only. See if cache OK and if it is then return if (validationResultForCachedResponse == ResponseValidationResult.OK) { cacheCowHeader.RetrievedFromCache = true; return(TaskHelpers.FromResult(cachedResponse.AddCacheCowHeader(cacheCowHeader))); // EXIT !! ____________________________ } // if stale else if (validationResultForCachedResponse == ResponseValidationResult.Stale) { cacheCowHeader.WasStale = true; var isFreshOrStaleAcceptable = IsFreshOrStaleAcceptable(cachedResponse, request); if (isFreshOrStaleAcceptable.HasValue && isFreshOrStaleAcceptable.Value) // similar to OK { // TODO: CONSUME AND RELEASE Response !!! return(TaskHelpers.FromResult(cachedResponse.AddCacheCowHeader(cacheCowHeader))); // EXIT !! ____________________________ } else { validationResultForCachedResponse = ResponseValidationResult.MustRevalidate; // revalidate } } // cache validation for GET else if (validationResultForCachedResponse == ResponseValidationResult.MustRevalidate) { cacheCowHeader.CacheValidationApplied = true; cacheCowHeader.WasStale = true; // add headers for a cache validation. First check ETag since is better if (cachedResponse.Headers.ETag != null) { request.Headers.Add(HttpHeaderNames.IfNoneMatch, cachedResponse.Headers.ETag.ToString()); } else if (cachedResponse.Content.Headers.LastModified != null) { request.Headers.Add(HttpHeaderNames.IfModifiedSince, cachedResponse.Content.Headers.LastModified.Value.ToString("r")); } } // _______________________________ RESPONSE only GET ___________________________________________ return(base.SendAsync(request, cancellationToken) .ContinueWith( tt => { var serverResponse = tt.Result; TraceWriter.WriteLine("{0} - After getting response", TraceLevel.Verbose, request.RequestUri.ToString()); if (request.Method != HttpMethod.Get) // only interested here if it is a GET - this line really never called - only GET gets here { return serverResponse; } // in case of MustRevalidate with result 304 if (validationResultForCachedResponse == ResponseValidationResult.MustRevalidate && serverResponse.StatusCode == HttpStatusCode.NotModified) { cachedResponse.RequestMessage = request; cacheCowHeader.RetrievedFromCache = true; TraceWriter.WriteLine("{0} - NotModified", TraceLevel.Verbose, request.RequestUri.ToString()); ConsumeAndDisposeResponse(serverResponse); return cachedResponse.AddCacheCowHeader(cacheCowHeader); // EXIT !! _______________ } var validationResult = ResponseValidator(serverResponse); switch (validationResult) { case ResponseValidationResult.MustRevalidate: case ResponseValidationResult.OK: TraceWriter.WriteLine("{0} - ResponseValidationResult.OK or MustRevalidate", TraceLevel.Verbose, request.RequestUri.ToString()); // prepare ResponseStoragePreparationRules(serverResponse); TraceWriter.WriteLine("{0} - Before AddOrUpdate", TraceLevel.Verbose, request.RequestUri.ToString()); // store the cache _cacheStore.AddOrUpdate(cacheKey, serverResponse); TraceWriter.WriteLine("{0} - Before AddOrUpdate", TraceLevel.Verbose, request.RequestUri.ToString()); // if there is a vary header, store it if (serverResponse.Headers.Vary != null) { VaryHeaderStore.AddOrUpdate(uri, serverResponse.Headers.Vary.Select(x => x).ToArray()); } break; default: TraceWriter.WriteLine("{0} - ResponseValidationResult. Other", TraceLevel.Verbose, request.RequestUri.ToString()); TraceWriter.WriteLine("{0} - Before TryRemove", TraceLevel.Verbose, request.RequestUri.ToString()); _cacheStore.TryRemove(cacheKey); TraceWriter.WriteLine("{0} - After AddOrUpdate", TraceLevel.Verbose, request.RequestUri.ToString()); cacheCowHeader.NotCacheable = true; break; } TraceWriter.WriteLine("{0} - Before returning response", TraceLevel.Verbose, request.RequestUri.ToString()); return serverResponse.AddCacheCowHeader(cacheCowHeader); } )); }