Exemplo n.º 1
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));
        }
Exemplo n.º 2
0
        // 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);
            }
                       ));
        }