Ejemplo n.º 1
0
        public void Get_Must_Revalidate_Etag_NotModified()
        {
            // setup
            var request           = new HttpRequestMessage(HttpMethod.Get, DummyUrl);
            var responseFromCache = GetOkMessage(true);

            responseFromCache.Headers.ETag            = new EntityTagHeaderValue(ETagValue);
            responseFromCache.Content.Headers.Expires = DateTime.Now.Subtract(TimeSpan.FromSeconds(10));
            var responseFromServer = new HttpResponseMessage(HttpStatusCode.NotModified);

            _messageHandler.Response = responseFromServer;
            _cacheStore.Setup(x => x.GetValueAsync(It.IsAny <CacheKey>())).ReturnsAsync(responseFromCache);
            _cacheStore.Setup(x => x.AddOrUpdateAsync(It.IsAny <CacheKey>(), It.IsAny <HttpResponseMessage>()))
            .Returns(Task.FromResult(true));

            // run
            var            task             = _client.SendAsync(request);
            var            responseReturned = task.Result;
            var            header           = responseReturned.Headers.Single(x => x.Key == CacheCowHeader.Name);
            CacheCowHeader cacheCowHeader   = null;

            CacheCowHeader.TryParse(header.Value.First(), out cacheCowHeader);

            // verify
            Assert.NotNull(cacheCowHeader);
            Assert.Equal(ETagValue, request.Headers.IfNoneMatch.First().Tag);
            Assert.Equal(responseFromCache, responseReturned);
            Assert.Equal(true, cacheCowHeader.CacheValidationApplied);
        }
Ejemplo n.º 2
0
        public void Get_Stale_ApplyValidation_NotModified()
        {
            // setup
            var request           = new HttpRequestMessage(HttpMethod.Get, DummyUrl);
            var responseFromCache = GetOkMessage();

            responseFromCache.Content.Headers.Expires      = DateTimeOffset.Now.AddDays(-1);
            responseFromCache.Content.Headers.LastModified = DateTimeOffset.Now.AddDays(-2);
            var responseFromServer = GetOkMessage();

            responseFromServer.StatusCode = HttpStatusCode.NotModified;

            _messageHandler.Response = responseFromServer;
            _cacheStore.Expect(x => x.TryGetValue(Arg <CacheKey> .Is.Anything,
                                                  out Arg <HttpResponseMessage> .Out(responseFromCache).Dummy)).Return(true);

            _mockRepository.ReplayAll();

            // run
            var            task             = _client.SendAsync(request);
            var            responseReturned = task.Result;
            var            header           = responseReturned.Headers.Single(x => x.Key == CacheCowHeader.Name);
            CacheCowHeader cacheCowHeader   = null;

            CacheCowHeader.TryParse(header.Value.First(), out cacheCowHeader);

            // verify
            _mockRepository.VerifyAll();
            Assert.IsNotNull(cacheCowHeader);
            Assert.AreSame(responseFromCache, responseReturned);
            Assert.AreEqual(true, cacheCowHeader.WasStale);
            Assert.AreEqual(true, cacheCowHeader.CacheValidationApplied);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Needs to run before action runs
        /// </summary>
        /// <param name="context"></param>
        /// <returns>Whether continue or not. If false then the action MUST not run</returns>
        public async Task <bool> Before(HttpContext context)
        {
            _cacheCowHeader        = new CacheCowHeader();
            _cacheValidated        = null;
            _isRequestCacheable    = _validator.IsCacheable(context.Request);
            _cacheValidationStatus = context.Request.GetCacheValidationStatus();
            if (_cacheValidationStatus != CacheValidationStatus.None)
            {
                var timedETag = await _cacheDirectiveProvider.QueryAsync(context);

                _cacheCowHeader.QueryMadeAndSuccessful = timedETag != null;
                _cacheValidated = ApplyCacheValidation(timedETag, _cacheValidationStatus, context);
                _cacheCowHeader.ValidationApplied = true;
                if (_cacheValidated ?? false)
                {
                    _cacheCowHeader.ShortCircuited    = true;
                    _cacheCowHeader.ValidationMatched = HttpMethods.IsGet(context.Request.Method); // NOTE: In GET match result in short-circuit and in PUT the opposite
                    context.Response.Headers.Add(CacheCowHeader.Name, _cacheCowHeader.ToString());
                    // the response would have been set and no need to run the rest of the pipeline
                    return(false);
                }
            }

            _stream = context.Response.Body;
            context.Response.Body = new MemoryStream();

            return(true);
        }
Ejemplo n.º 4
0
        public void Get_NoMustRevalidate_NoMustRevalidateByDefault_Expires_GetFromCache()
        {
            // setup
            var request      = new HttpRequestMessage(HttpMethod.Get, DummyUrl);
            var lastModified = DateTimeOffset.UtcNow.AddHours(-1);

            lastModified = lastModified.AddMilliseconds(1000 - lastModified.Millisecond);
            var responseFromCache = GetOkMessage(false);            // NOTE !!

            _cachingHandler.MustRevalidateByDefault        = false; // NOTE!!
            responseFromCache.Content.Headers.LastModified = lastModified;
            var responseFromServer = GetOkMessage();

            responseFromCache.Content.Headers.Expires = DateTime.Now.Subtract(TimeSpan.FromSeconds(10));

            _messageHandler.Response = responseFromServer;
            _cacheStore.Setup(x => x.GetValueAsync(It.IsAny <CacheKey>())).ReturnsAsync(responseFromCache);


            // run
            var            task             = _client.SendAsync(request);
            var            responseReturned = task.Result;
            var            header           = responseReturned.Headers.Single(x => x.Key == CacheCowHeader.Name);
            CacheCowHeader cacheCowHeader   = null;

            CacheCowHeader.TryParse(header.Value.First(), out cacheCowHeader);

            // verify
            Assert.NotNull(cacheCowHeader);
            Assert.Equal(responseFromCache, responseReturned);
            Assert.Equal(true, cacheCowHeader.WasStale);
        }
Ejemplo n.º 5
0
        public void Get_OK_But_Not_In_Cache_To_Insert_In_Cache()
        {
            // setup
            var request  = new HttpRequestMessage(HttpMethod.Get, DummyUrl);
            var response = GetOkMessage();

            _messageHandler.Response = response;
            _cacheStore.Expect(x => x.TryGetValue(Arg <CacheKey> .Is.Anything,
                                                  out Arg <HttpResponseMessage> .Out(null).Dummy)).Return(false);
            _cacheStore.Expect(x => x.AddOrUpdate(Arg <CacheKey> .Is.Anything,
                                                  Arg <HttpResponseMessage> .Is.Same(response)));

            _mockRepository.ReplayAll();

            // run
            var            task             = _client.SendAsync(request);
            var            responseReturned = task.Result;
            var            header           = responseReturned.Headers.Single(x => x.Key == CacheCowHeader.Name);
            CacheCowHeader cacheCowHeader   = null;

            CacheCowHeader.TryParse(header.Value.First(), out cacheCowHeader);
            // verify
            _mockRepository.VerifyAll();

            Assert.IsNotNull(cacheCowHeader);
            Assert.AreEqual(true, cacheCowHeader.DidNotExist);
        }
Ejemplo n.º 6
0
        public void Get_Must_Revalidate_Etag_NotModified()
        {
            // setup
            var request           = new HttpRequestMessage(HttpMethod.Get, DummyUrl);
            var responseFromCache = GetOkMessage(true);

            responseFromCache.Headers.ETag            = new EntityTagHeaderValue(ETagValue);
            responseFromCache.Content.Headers.Expires = DateTime.Now.Subtract(TimeSpan.FromSeconds(10));
            var responseFromServer = new HttpResponseMessage(HttpStatusCode.NotModified);

            _messageHandler.Response = responseFromServer;
            _cacheStore.Expect(x => x.TryGetValue(Arg <CacheKey> .Is.Anything,
                                                  out Arg <HttpResponseMessage> .Out(responseFromCache).Dummy)).Return(true);

            _mockRepository.ReplayAll();

            // run
            var            task             = _client.SendAsync(request);
            var            responseReturned = task.Result;
            var            header           = responseReturned.Headers.Single(x => x.Key == CacheCowHeader.Name);
            CacheCowHeader cacheCowHeader   = null;

            CacheCowHeader.TryParse(header.Value.First(), out cacheCowHeader);

            // verify
            _mockRepository.VerifyAll();
            Assert.IsNotNull(cacheCowHeader);
            Assert.AreEqual(ETagValue, request.Headers.IfNoneMatch.First().Tag);
            Assert.AreSame(responseFromCache, responseReturned);
            Assert.AreEqual(true, cacheCowHeader.CacheValidationApplied);
        }
Ejemplo n.º 7
0
        public void Get_Must_Revalidate_NoCache_InRequest()
        {
            // setup
            var request = new HttpRequestMessage(HttpMethod.Get, DummyUrl);

            request.Headers.CacheControl = new CacheControlHeaderValue {
                NoCache = true
            };
            var responseFromCache  = GetOkMessage();
            var responseFromServer = GetOkMessage();

            _messageHandler.Response = responseFromServer;
            _cacheStore.Setup(x => x.GetValueAsync(It.IsAny <CacheKey>())).ReturnsAsync(responseFromCache);
            _cacheStore.Setup(x => x.AddOrUpdateAsync(It.IsAny <CacheKey>(),
                                                      It.Is <HttpResponseMessage>(r => r == responseFromServer))).Returns(Task.FromResult(true));

            // run
            var            task             = _client.SendAsync(request);
            var            responseReturned = task.Result;
            var            header           = responseReturned.Headers.Single(x => x.Key == CacheCowHeader.Name);
            CacheCowHeader cacheCowHeader   = null;

            CacheCowHeader.TryParse(header.Value.First(), out cacheCowHeader);

            // verify
            Assert.NotNull(cacheCowHeader);
            Assert.Equal(responseFromServer, responseReturned);
            Assert.Equal(true, cacheCowHeader.CacheValidationApplied);
        }
Ejemplo n.º 8
0
        public void Get_Must_Revalidate_Expires_NotModified()
        {
            // setup
            var request      = new HttpRequestMessage(HttpMethod.Get, DummyUrl);
            var lastModified = DateTimeOffset.UtcNow.AddHours(-1);

            lastModified = lastModified.AddMilliseconds(1000 - lastModified.Millisecond);
            var responseFromCache = GetOkMessage(true);

            responseFromCache.Content.Headers.LastModified = lastModified;
            responseFromCache.Content.Headers.Expires      = DateTime.Now.Subtract(TimeSpan.FromSeconds(10));

            var responseFromServer = new HttpResponseMessage(HttpStatusCode.NotModified);

            _messageHandler.Response = responseFromServer;
            _cacheStore.Expect(x => x.TryGetValue(Arg <CacheKey> .Is.Anything, out Arg <HttpResponseMessage> .Out(responseFromCache).Dummy)).Return(true);
            _cacheStore.Expect(x => x.AddOrUpdate(Arg <CacheKey> .Is.Anything, Arg <HttpResponseMessage> .Is.Anything));

            _mockRepository.ReplayAll();

            // run
            var            task             = _client.SendAsync(request);
            var            responseReturned = task.Result;
            var            header           = responseReturned.Headers.Single(x => x.Key == CacheCowHeader.Name);
            CacheCowHeader cacheCowHeader   = null;

            CacheCowHeader.TryParse(header.Value.First(), out cacheCowHeader);

            // verify
            _mockRepository.VerifyAll();
            Assert.IsNotNull(cacheCowHeader);
            Assert.AreEqual(lastModified.ToString(), request.Headers.IfModifiedSince.Value.ToString());
            Assert.AreSame(responseFromCache, responseReturned);
            Assert.AreEqual(true, cacheCowHeader.CacheValidationApplied);
        }
Ejemplo n.º 9
0
        public async override Task OnActionExecutingAsync(HttpActionContext context, CancellationToken cancellationToken)
        {
            await base.OnActionExecutingAsync(context, cancellationToken);

            var cacheabilityValidator = (ICacheabilityValidator)context.ControllerContext.Configuration.DependencyResolver.GetService(typeof(ICacheabilityValidator))
                                        ?? new DefaultCacheabilityValidator();
            var cacheDirectiveProvider = context.ControllerContext.Configuration.DependencyResolver.GetCacheDirectiveProvider(ViewModelType);

            var cacheCowHeader = new CacheCowHeader();

            context.Request.Properties[CacheCowHeaderKey] = cacheCowHeader;
            bool?cacheValidated        = null;
            bool isRequestCacheable    = cacheabilityValidator.IsCacheable(context.Request);
            var  cacheValidationStatus = context.Request.GetCacheValidationStatus();

            if (cacheValidationStatus != CacheValidationStatus.None)
            {
                var timedETag = await cacheDirectiveProvider.QueryAsync(context);

                cacheCowHeader.QueryMadeAndSuccessful = timedETag != null;
                cacheValidated = ApplyCacheValidation(timedETag, cacheValidationStatus, new ContextUnifier(context));
                context.Request.Properties.Add(CacheValidatedKey, cacheValidated);
                cacheCowHeader.ValidationApplied = true;
                if (cacheValidated ?? false)
                {
                    cacheCowHeader.ShortCircuited    = true;
                    cacheCowHeader.ValidationMatched = HttpMethod.Get == context.Request.Method; // NOTE: In GET match result in short-circuit and in PUT the opposite
                    context.Response.Headers.Add(CacheCowHeader.Name, cacheCowHeader.ToString());
                    // the response would have been set and no need to run the rest of the pipeline
                    return;
                }
            }
        }
Ejemplo n.º 10
0
        public void Get_NotModified_With_Stale_Client_Cache_Shall_Update_Date_Header()
        {
            // setup
            var request = new HttpRequestMessage(HttpMethod.Get, DummyUrl);

            var responseFromCache = GetOkMessage(false);

            responseFromCache.Headers.Date = DateTimeOffset.UtcNow.AddHours(-1);
            responseFromCache.Headers.CacheControl.MaxAge = TimeSpan.FromSeconds(10);

            var responseFromServer = new HttpResponseMessage(HttpStatusCode.NotModified)
            {
                Content = new ByteArrayContent(new byte[256])
            };

            _messageHandler.Response = responseFromServer;
            _cacheStore.Setup(x => x.GetValueAsync(It.IsAny <CacheKey>())).ReturnsAsync(responseFromCache);
            _cacheStore.Setup(x => x.AddOrUpdateAsync(It.IsAny <CacheKey>(), It.Is <HttpResponseMessage>(r => DateTimeOffset.UtcNow - r.Headers.Date.Value <= TimeSpan.FromSeconds(1))))
            .Returns(Task.FromResult(0));


            // run
            var            responseReturned = _client.SendAsync(request).Result;
            var            header           = responseReturned.Headers.Single(x => x.Key == CacheCowHeader.Name);
            CacheCowHeader cacheCowHeader;

            CacheCowHeader.TryParse(header.Value.First(), out cacheCowHeader);


            // verify
            Assert.NotNull(cacheCowHeader);
            Assert.Equal(true, cacheCowHeader.CacheValidationApplied);
            Assert.Equal(true, cacheCowHeader.WasStale);
        }
Ejemplo n.º 11
0
        public void Get_Must_Revalidate_Expires_Modified()
        {
            // setup
            var request      = new HttpRequestMessage(HttpMethod.Get, DummyUrl);
            var lastModified = DateTimeOffset.UtcNow.AddHours(-1);

            lastModified = lastModified.AddMilliseconds(1000 - lastModified.Millisecond);
            var responseFromCache = GetOkMessage(true);

            responseFromCache.Content.Headers.LastModified = lastModified;
            var responseFromServer = GetOkMessage();

            responseFromCache.Content.Headers.Expires = DateTime.Now.Subtract(TimeSpan.FromSeconds(10));

            _messageHandler.Response = responseFromServer;
            _cacheStore.Setup(x => x.GetValueAsync(It.IsAny <CacheKey>())).ReturnsAsync(responseFromCache);
            _cacheStore.Setup(x => x.AddOrUpdateAsync(It.IsAny <CacheKey>(),
                                                      It.Is <HttpResponseMessage>(r => r == responseFromServer))).Returns(Task.FromResult(true));


            // run
            var            task             = _client.SendAsync(request);
            var            responseReturned = task.Result;
            var            header           = responseReturned.Headers.Single(x => x.Key == CacheCowHeader.Name);
            CacheCowHeader cacheCowHeader   = null;

            CacheCowHeader.TryParse(header.Value.First(), out cacheCowHeader);

            // verify
            Assert.NotNull(cacheCowHeader);
            Assert.Equal(responseFromServer, responseReturned);
            Assert.Equal(true, cacheCowHeader.CacheValidationApplied);
        }
Ejemplo n.º 12
0
        public void ParseTest_Successful()
        {
            CacheCowHeader header;
            var            result = CacheCowHeader.TryParse("1.0;was-stale=true;not-cacheable=false;retrieved-from-cache=true;", out header);

            Assert.AreEqual(true, result);
            Assert.AreEqual("1.0", header.Version);
            Assert.AreEqual(true, header.WasStale);
            Assert.AreEqual(true, header.RetrievedFromCache);
            Assert.AreEqual(false, header.NotCacheable);
            Assert.AreEqual(null, header.DidNotExist);
            Assert.AreEqual(null, header.CacheValidationApplied);
        }
        public static HttpResponseMessage AddCacheCowHeader(this HttpResponseMessage response,
                                                            CacheCowHeader header)
        {
            var previousCacheCowHeader = response.Headers.GetCacheCowHeader();

            if (previousCacheCowHeader != null)
            {
                TraceWriter.WriteLine("WARNING: Already had this header: {0} NOw setting this: {1}", TraceLevel.Warning, previousCacheCowHeader, header);
                response.Headers.Remove(CacheCowHeader.Name);
            }

            response.Headers.Add(CacheCowHeader.Name, header.ToString());
            return(response);
        }
Ejemplo n.º 14
0
        public void ToStringTest_Successful()
        {
            var cacheCowHeader = new CacheCowHeader()
            {
                CacheValidationApplied = true,
                DidNotExist            = false
            };

            var s = cacheCowHeader.ToString();

            Console.WriteLine(s);
            Assert.IsTrue(s.StartsWith(_version));
            Assert.IsTrue(s.IndexOf(CacheCowHeader.ExtensionNames.CacheValidationApplied + "=true") > 0);
            Assert.IsTrue(s.IndexOf(CacheCowHeader.ExtensionNames.DidNotExist + "=false") > 0);
        }
Ejemplo n.º 15
0
        private static void DoCacheValidationForGet(HttpRequestMessage request, CacheCowHeader cacheCowHeader,
                                                    HttpResponseMessage cachedResponse)
        {
            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"));
            }
        }
        public void Get_NotModified_With_Stale_Client_Cache_Shall_Update_Date_Header()
        {
            // setup
            var request = new HttpRequestMessage(HttpMethod.Get, DummyUrl);

            var responseFromCache = GetOkMessage(false);

            responseFromCache.Headers.Date = DateTimeOffset.UtcNow.AddHours(-1);
            responseFromCache.Headers.CacheControl.MaxAge = TimeSpan.FromSeconds(10);

            var responseFromServer = new HttpResponseMessage(HttpStatusCode.NotModified)
            {
                Content = new ByteArrayContent(new byte[256])
            };

            _messageHandler.Response = responseFromServer;
            _cacheStore.Expect(
                x => x.TryGetValue(Arg <CacheKey> .Is.Anything, out Arg <HttpResponseMessage> .Out(responseFromCache).Dummy))
            .Return(true);
            _cacheStore.Expect(
                x =>
                x.AddOrUpdate(Arg <CacheKey> .Is.Anything,
                              Arg <HttpResponseMessage> .Matches(
                                  r => DateTimeOffset.UtcNow - r.Headers.Date.Value <= TimeSpan.FromSeconds(1))));

            _mockRepository.ReplayAll();


            // run
            var            task             = _client.SendAsync(request);
            var            responseReturned = task.Result;
            var            header           = responseReturned.Headers.Single(x => x.Key == CacheCowHeader.Name);
            CacheCowHeader cacheCowHeader;

            CacheCowHeader.TryParse(header.Value.First(), out cacheCowHeader);


            // verify
            _mockRepository.VerifyAll();
            Assert.IsNotNull(cacheCowHeader);
            Assert.AreEqual(true, cacheCowHeader.CacheValidationApplied);
            Assert.AreEqual(true, cacheCowHeader.WasStale);
        }
Ejemplo n.º 17
0
 private void DoPutValidation(HttpRequestMessage request, CacheCowHeader cacheCowHeader,
                              HttpResponseMessage cachedResponse)
 {
     // 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"));
         }
     }
 }
Ejemplo n.º 18
0
        public void Get_Stale_And_In_Cache_To_Get_From_Cache()
        {
            // setup
            var request  = new HttpRequestMessage(HttpMethod.Get, DummyUrl);
            var response = GetOkMessage();

            _messageHandler.Response = response;
            _cacheStore.Setup(x => x.GetValueAsync(It.IsAny <CacheKey>())).ReturnsAsync(response);


            // run
            var            task             = _client.SendAsync(request);
            var            responseReturned = task.Result;
            var            header           = responseReturned.Headers.Single(x => x.Key == CacheCowHeader.Name);
            CacheCowHeader cacheCowHeader   = null;

            CacheCowHeader.TryParse(header.Value.First(), out cacheCowHeader);

            // verify
            Assert.NotNull(cacheCowHeader);
            Assert.Equal(response, responseReturned);
            Assert.Equal(true, cacheCowHeader.RetrievedFromCache);
        }
Ejemplo n.º 19
0
        public void Get_OK_But_Not_In_Cache_To_Insert_In_Cache()
        {
            // setup
            var request  = new HttpRequestMessage(HttpMethod.Get, DummyUrl);
            var response = GetOkMessage();

            _messageHandler.Response = response;
            _cacheStore.Setup(x => x.GetValueAsync(It.IsAny <CacheKey>())).ReturnsAsync((HttpResponseMessage)null);
            _cacheStore.Setup(x => x.AddOrUpdateAsync(It.IsAny <CacheKey>(),
                                                      It.Is <HttpResponseMessage>(y => y == response))).Returns(Task.FromResult(response));

            // run
            var            task             = _client.SendAsync(request);
            var            responseReturned = task.Result;
            var            header           = responseReturned.Headers.Single(x => x.Key == CacheCowHeader.Name);
            CacheCowHeader cacheCowHeader   = null;

            CacheCowHeader.TryParse(header.Value.First(), out cacheCowHeader);

            // verify
            Assert.NotNull(cacheCowHeader);
            Assert.Equal(true, cacheCowHeader.DidNotExist);
        }
Ejemplo n.º 20
0
        public void Get_Stale_ApplyValidation_NotModified()
        {
            // setup
            var request           = new HttpRequestMessage(HttpMethod.Get, DummyUrl);
            var responseFromCache = GetOkMessage();
            var then = DateTimeOffset.UtcNow.AddMilliseconds(-1);

            responseFromCache.Headers.Date                 = then;
            responseFromCache.Content.Headers.Expires      = DateTimeOffset.Now.AddDays(-1);
            responseFromCache.Content.Headers.LastModified = DateTimeOffset.Now.AddDays(-2);
            var responseFromServer = GetOkMessage();

            responseFromServer.StatusCode = HttpStatusCode.NotModified;

            _messageHandler.Response = responseFromServer;
            _cacheStore.Setup(x => x.GetValueAsync(It.IsAny <CacheKey>())).ReturnsAsync(responseFromCache);
            _cacheStore.Setup(x => x.AddOrUpdateAsync(It.IsAny <CacheKey>(), It.Is <HttpResponseMessage>(r => r == responseFromCache)))
            .Returns(Task.FromResult(false));



            // run
            var            task             = _client.SendAsync(request);
            var            responseReturned = task.Result;
            var            header           = responseReturned.Headers.Single(x => x.Key == CacheCowHeader.Name);
            CacheCowHeader cacheCowHeader   = null;

            CacheCowHeader.TryParse(header.Value.First(), out cacheCowHeader);

            // verify
            Assert.NotNull(cacheCowHeader);
            Assert.Equal(responseFromCache, responseReturned);
            Assert.Equal(true, cacheCowHeader.WasStale);
            Assert.Equal(true, cacheCowHeader.CacheValidationApplied);
            Assert.NotEqual(then, responseFromCache.Headers.Date);
        }
Ejemplo n.º 21
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));
        }
 public static HttpResponseMessage AddCacheCowHeader(this HttpResponseMessage response,
                                                     CacheCowHeader header)
 {
     response.Headers.Add(CacheCowHeader.Name, header.ToString());
     return(response);
 }
Ejemplo n.º 23
0
        // TODO: this method is terribly long. Shorten
        protected override Task <HttpResponseMessage> SendAsync(HttpRequestMessage request,
                                                                CancellationToken cancellationToken)
        {
            var cacheCowHeader = new CacheCowHeader();
            var 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;
            var 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
                if (validationResultForCachedResponse == ResponseValidationResult.Stale)
                {
                    cacheCowHeader.WasStale = true;
                    var isFreshOrStaleAcceptable = IsFreshOrStaleAcceptable(cachedResponse, request);
                    if (isFreshOrStaleAcceptable.HasValue && isFreshOrStaleAcceptable.Value) // similar to OK
                    {
                        return(TaskHelpers.FromResult(cachedResponse.AddCacheCowHeader(cacheCowHeader)));
                    }
                    validationResultForCachedResponse = ResponseValidationResult.MustRevalidate; // revalidate
                }

                // cache validation for GET
                else if (validationResultForCachedResponse == ResponseValidationResult.MustRevalidate)
                {
                    DoCacheValidationForGet(request, cacheCowHeader, cachedResponse);
                }
            }
            catch (Exception ex)
            {
                if (ExceptionHandler == null)
                {
                    throw;
                }
                ExceptionHandler(ex);
                Trace.TraceWarning("Exception was swallowed in CacheCow: " + ex);
                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);
            }));
        }
Ejemplo n.º 24
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="context"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
        {
            var  cacheCowHeader        = new CacheCowHeader();
            bool?cacheValidated        = null;
            bool isRequestCacheable    = _validator.IsCacheable(context.HttpContext.Request);
            var  cacheValidationStatus = context.HttpContext.Request.GetCacheValidationStatus();

            if (cacheValidationStatus != CacheValidationStatus.None)
            {
                var timedETag = await CacheDirectiveProvider.QueryAsync(context);

                cacheValidated = ApplyCacheValidation(timedETag, cacheValidationStatus, context);
                cacheCowHeader.ValidationApplied = true;
                if (cacheValidated ?? false)
                {
                    cacheCowHeader.ShortCircuited    = true;
                    cacheCowHeader.ValidationMatched = true;
                    context.HttpContext.Response.Headers.Add(CacheCowHeader.Name, cacheCowHeader.ToString());
                    // the response would have been set and no need to run the rest of the pipeline
                    return;
                }
            }

            context.HttpContext.Items[StreamName] = context.HttpContext.Response.Body;
            context.HttpContext.Response.Body     = new MemoryStream();

            var execCtx = await next(); // _______________________________________________________________________________

            var  ms          = context.HttpContext.Response.Body as MemoryStream;
            bool mustReflush = ms != null && ms.Length > 0;

            context.HttpContext.Response.Body = (Stream)context.HttpContext.Items[StreamName];

            try
            {
                if (HttpMethods.IsGet(context.HttpContext.Request.Method))
                {
                    context.HttpContext.Response.Headers.Add(HeaderNames.Vary, string.Join(";", CacheDirectiveProvider.GetVaryHeaders(context.HttpContext)));
                    var cacheControl        = CacheDirectiveProvider.GetCacheControl(context.HttpContext, this.ConfiguredExpiry);
                    var isResponseCacheable = _validator.IsCacheable(context.HttpContext.Response);
                    if (!cacheControl.NoStore && isResponseCacheable) // _______ is cacheable
                    {
                        var or = execCtx.Result as ObjectResult;
                        TimedEntityTagHeaderValue tet = null;
                        if (or != null && or.Value != null)
                        {
                            tet = CacheDirectiveProvider.Extract(or.Value);
                        }

                        if (cacheValidated == null && // could not validate
                            tet != null &&
                            cacheValidationStatus != CacheValidationStatus.None)    // can only do GET validation, PUT is already impacted backend stores
                        {
                            cacheValidated = ApplyCacheValidation(tet, cacheValidationStatus, context);
                            cacheCowHeader.ValidationApplied = true;
                            // the response would have been set and no need to run the rest of the pipeline

                            if (cacheValidated ?? false)
                            {
                                cacheCowHeader.ValidationMatched = true;
                                context.HttpContext.Response.Headers.Add(CacheCowHeader.Name, cacheCowHeader.ToString());
                                return;
                            }
                        }

                        if (tet != null)
                        {
                            context.HttpContext.Response.ApplyTimedETag(tet);
                        }
                    }

                    if (!isRequestCacheable || !isResponseCacheable)
                    {
                        context.HttpContext.Response.MakeNonCacheable();
                    }
                    else
                    {
                        context.HttpContext.Response.Headers[HttpHeaderNames.CacheControl] = cacheControl.ToString();
                    }

                    context.HttpContext.Response.Headers.Add(CacheCowHeader.Name, cacheCowHeader.ToString());
                }
            }
            finally
            {
                if (mustReflush)
                {
                    ms.Position = 0;
                    ms.CopyTo(context.HttpContext.Response.Body);
                }
            }
        }
Ejemplo n.º 25
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);
            }
                       ));
        }