示例#1
0
		public static bool TryParse(string value, out CacheCowHeader cacheCowHeader)
		{
			cacheCowHeader = null;

			if (value == null)
				return false;

			if (value == string.Empty)
				return false;

			cacheCowHeader = new CacheCowHeader();
			var chunks = value.Split(new []{";"}, StringSplitOptions.None);
			cacheCowHeader.Version = chunks[0];

			for (int i = 1; i < chunks.Length; i++)
			{
				cacheCowHeader.WasStale = cacheCowHeader.WasStale ?? ParseNameValue(chunks[i], ExtensionNames.WasStale);
				cacheCowHeader.CacheValidationApplied = cacheCowHeader.CacheValidationApplied ?? ParseNameValue(chunks[i], ExtensionNames.CacheValidationApplied);
				cacheCowHeader.NotCacheable = cacheCowHeader.NotCacheable ?? ParseNameValue(chunks[i], ExtensionNames.NotCacheable);
				cacheCowHeader.DidNotExist = cacheCowHeader.DidNotExist ?? ParseNameValue(chunks[i], ExtensionNames.DidNotExist);
				cacheCowHeader.RetrievedFromCache = cacheCowHeader.RetrievedFromCache ?? ParseNameValue(chunks[i], ExtensionNames.RetrievedFromCache);
			}

			return true;
		}
示例#2
0
        public static bool TryParse(string value, out CacheCowHeader cacheCowHeader)
        {
            cacheCowHeader = null;

            if (value == null)
            {
                return(false);
            }

            if (value == string.Empty)
            {
                return(false);
            }

            cacheCowHeader = new CacheCowHeader();
            var chunks = value.Split(new[] { ";" }, StringSplitOptions.None);

            cacheCowHeader.Version = chunks[0];

            for (int i = 1; i < chunks.Length; i++)
            {
                cacheCowHeader.WasStale = cacheCowHeader.WasStale ?? ParseNameValue(chunks[i], ExtensionNames.WasStale);
                cacheCowHeader.CacheValidationApplied = cacheCowHeader.CacheValidationApplied ?? ParseNameValue(chunks[i], ExtensionNames.CacheValidationApplied);
                cacheCowHeader.NotCacheable           = cacheCowHeader.NotCacheable ?? ParseNameValue(chunks[i], ExtensionNames.NotCacheable);
                cacheCowHeader.DidNotExist            = cacheCowHeader.DidNotExist ?? ParseNameValue(chunks[i], ExtensionNames.DidNotExist);
                cacheCowHeader.RetrievedFromCache     = cacheCowHeader.RetrievedFromCache ?? ParseNameValue(chunks[i], ExtensionNames.RetrievedFromCache);
            }

            return(true);
        }
示例#3
0
        public static CacheCowHeader GetCacheCowHeader(this HttpResponseHeaders headers)
        {
            CacheCowHeader header         = null;
            var            cacheCowHeader = headers.Where(x => x.Key == CacheCowHeader.Name).FirstOrDefault();

            if (cacheCowHeader.Value.Count() > 0)
            {
                var last = cacheCowHeader.Value.Last();
                CacheCowHeader.TryParse(last, out header);
            }

            return(header);
        }
        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);
        }
示例#5
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"));
            }
        }
示例#6
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"));
         }
     }
 }
示例#7
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 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);
                });
        }
示例#8
0
        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;
                _cacheStore.TryRemove(cacheKey);
                TraceWriter.WriteLine("{0} - after TryRemove ", TraceLevel.Verbose, request.RequestUri.ToString());

            }

            // cache validation for GET
            else if(validationResultForCachedResponse == ResponseValidationResult.MustRevalidate)
            {
                cacheCowHeader.CacheValidationApplied = 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 =>
                //serverResponse =>
                    {
                        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());
                            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);
                                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);
                    }
                );
        }
		public static HttpResponseMessage AddCacheCowHeader(this HttpResponseMessage response,
			CacheCowHeader header)
		{
			response.Headers.Add(CacheCowHeader.Name, header.ToString());
			return response;
		}