예제 #1
0
        internal async static Task UpdateCachedResponseAsync(CacheKey cacheKey,
                                                             HttpResponseMessage cachedResponse,
                                                             HttpResponseMessage serverResponse,
                                                             ICacheStore store)
        {
            TraceWriter.WriteLine("CachingHandler.UpdateCachedResponseAsync - 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.UpdateCachedResponseAsync - CacheControl: " + serverResponse.Headers.CacheControl.ToString(), TraceLevel.Verbose);
                cachedResponse.Headers.CacheControl = serverResponse.Headers.CacheControl;
            }
            else
            {
                TraceWriter.WriteLine("CachingHandler.UpdateCachedResponseAsync - CacheControl missing from server. Applying sliding expiration. Date => " + DateTimeOffset.UtcNow, TraceLevel.Verbose);
            }

            cachedResponse.Headers.Date = DateTimeOffset.UtcNow; // very important
            CheckForCacheCowHeader(cachedResponse);
            await store.AddOrUpdateAsync(cacheKey, cachedResponse);
        }
예제 #2
0
        // TODO: this method is terribly long. Shorten
        protected async 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(await 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());

            cachedResponse = await _cacheStore.GetValueAsync(cacheKey);

            cacheCowHeader.DidNotExist = cachedResponse == null;
            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/PATCH/DELETE validation
            if (request.Method.IsPutPatchOrDelete() && validationResultForCachedResponse.IsIn(
                    ResponseValidationResult.OK, ResponseValidationResult.MustRevalidate))
            {
                ApplyPutPatchDeleteValidationHeaders(request, cacheCowHeader, cachedResponse);
                return(await 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(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(cachedResponse.AddCacheCowHeader(cacheCowHeader));
                    // EXIT !! ____________________________
                }
                else
                {
                    validationResultForCachedResponse = ResponseValidationResult.MustRevalidate; // revalidate
                }
            }

            // cache validation for GET
            else if (validationResultForCachedResponse == ResponseValidationResult.MustRevalidate)
            {
                ApplyGetCacheValidationHeaders(request, cacheCowHeader, cachedResponse);
            }


            // _______________________________ RESPONSE only GET  ___________________________________________

            var serverResponse = await base.SendAsync(request, cancellationToken);

            // HERE IS LATE FOR APPLYING EXCEPTION POLICY !!!

            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());

                await UpdateCachedResponseAsync(cacheKey, cachedResponse, serverResponse, _cacheStore);

                ConsumeAndDisposeResponse(serverResponse);
                return(cachedResponse.AddCacheCowHeader(cacheCowHeader).CopyOtherCacheCowHeaders(serverResponse)); // 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);

                // 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
                CheckForCacheCowHeader(serverResponse);
                await _cacheStore.AddOrUpdateAsync(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());
                await _cacheStore.TryRemoveAsync(cacheKey);

                TraceWriter.WriteLine("{0} - After TryRemoveAsync", TraceLevel.Verbose, request.RequestUri.ToString());

                cacheCowHeader.NotCacheable = true;

                break;
            }
            TraceWriter.WriteLine("{0} - Before returning response",
                                  TraceLevel.Verbose, request.RequestUri.ToString());

            return(serverResponse.AddCacheCowHeader(cacheCowHeader));
        }