private static async Task <ResponseHandlerResult> WriteResponseInCacheAsync(HttpResponseBase httpResponse, CacheItem cacheItem)
        {
            await cacheItem.Response.WriteResponseAsync(httpResponse);

            httpResponse.Headers.Set("Last-Modified", cacheItem.CachedUtcTimestamp.ToHttpDate());

            return(ResponseHandlerResult.ResponseWritten());
        }
Example #2
0
        public async Task <ResponseHandlerResult> HandleResponseAsync(HttpContextBase context, IResponse suggestedResponse, ICache cache, string cacheKey)
        {
            context.ThrowIfNull("context");
            suggestedResponse.ThrowIfNull("suggestedResponse");

            await new CacheResponse(suggestedResponse).WriteResponseAsync(context.Response);

            return(ResponseHandlerResult.ResponseWritten());
        }
 public void SetUp()
 {
     _handler = new NonCacheableResponseHandler();
     _httpRequest = MockRepository.GenerateMock<HttpRequestBase>();
     _httpCachePolicyBase = MockRepository.GenerateMock<HttpCachePolicyBase>();
     _httpResponse = MockRepository.GenerateMock<HttpResponseBase>();
     _httpResponse.Stub(arg => arg.BinaryWrite(Arg<byte[]>.Is.Anything)).WhenCalled(arg => _responseWritten = true);
     _httpResponse.Stub(arg => arg.Cache).Return(_httpCachePolicyBase);
     _cachePolicy = MockRepository.GenerateMock<ICachePolicy>();
     _cachePolicy.Stub(arg => arg.Clone()).Return(_cachePolicy);
     _response = MockRepository.GenerateMock<IResponse>();
     _response.Stub(arg => arg.CachePolicy).Return(_cachePolicy);
     _response.Stub(arg => arg.Cookies).Return(Enumerable.Empty<Cookie>());
     _response.Stub(arg => arg.GetContent()).Return(new byte[0]);
     _response.Stub(arg => arg.Headers).Return(Enumerable.Empty<Header>());
     _response.Stub(arg => arg.StatusCode).Return(new StatusAndSubStatusCode(HttpStatusCode.OK));
     _cache = MockRepository.GenerateMock<ICache>();
     _result = _handler.HandleResponse(_httpRequest, _httpResponse, _response, _cache, "key");
 }
        public async Task <ResponseHandlerResult> HandleResponseAsync(HttpContextBase context, IResponse suggestedResponse, ICache cache, string cacheKey)
        {
            context.ThrowIfNull("context");
            suggestedResponse.ThrowIfNull("suggestedResponse");

            StatusAndSubStatusCode statusCode = suggestedResponse.StatusCode;

            if (!_statusCodes.Contains(statusCode))
            {
                return(ResponseHandlerResult.ResponseNotHandled());
            }

            AcceptHeader[] acceptHeaders = AcceptHeader.ParseMany(context.Request.Headers["Accept"]).ToArray();

            if (acceptHeaders.Any() && !acceptHeaders.Any(arg => arg.MediaTypeMatches("text/html")))
            {
                return(ResponseHandlerResult.ResponseNotHandled());
            }

            const string format   = @"<!DOCTYPE html>
<html>
	<head>
		<title>{0}</title>
		<style>h1 {{ margin: 0; padding: 0; }}</style>
	</head>
	<body>
		<h1>{0}</h1>
		<hr/>
		HTTP {1}{2}
	</body>
</html>";
            Response     response = new Response(statusCode)
                                    .TextHtml()
                                    .Content(String.Format(format, statusCode.StatusDescription, statusCode.StatusCode, statusCode.SubStatusCode == 0 ? "" : "." + statusCode.SubStatusCode));

            response.CachePolicy.NoClientCaching();

            await new CacheResponse(response).WriteResponseAsync(context.Response);

            context.Response.TrySkipIisCustomErrors = true;

            return(ResponseHandlerResult.ResponseWritten());
        }
        public async Task <ResponseHandlerResult> HandleResponseAsync(HttpContextBase context, IResponse suggestedResponse, ICache cache, string cacheKey)
        {
            context.ThrowIfNull("context");
            suggestedResponse.ThrowIfNull("suggestedResponse");

            StatusAndSubStatusCode statusCode = suggestedResponse.StatusCode;

            if (!_statusCodes.Contains(statusCode))
            {
                return(ResponseHandlerResult.ResponseNotHandled());
            }

            AcceptHeader[] acceptHeaders = AcceptHeader.ParseMany(context.Request.Headers["Accept"]).ToArray();

            if (acceptHeaders.Any() && !acceptHeaders.Any(arg => arg.MediaTypeMatches("text/plain")))
            {
                return(ResponseHandlerResult.ResponseNotHandled());
            }

            string content = String.Format(
                "HTTP {0}{1} {2}",
                statusCode.StatusCode,
                statusCode.SubStatusCode == 0 ? "" : "." + statusCode.SubStatusCode.ToString(CultureInfo.InvariantCulture),
                statusCode.StatusDescription.Length > 0 ? String.Format("({0})", statusCode.StatusDescription) : "");
            Response response = new Response(statusCode)
                                .TextPlain()
                                .Content(content);

            response.CachePolicy.NoClientCaching();

            await new CacheResponse(response).WriteResponseAsync(context.Response);

            context.Response.TrySkipIisCustomErrors = true;

            return(ResponseHandlerResult.ResponseWritten());
        }
        public async Task <ResponseHandlerResult> HandleResponseAsync(HttpContextBase context, IResponse suggestedResponse, ICache cache, string cacheKey)
        {
            context.ThrowIfNull("context");
            suggestedResponse.ThrowIfNull("suggestedResponse");

            if (!suggestedResponse.CachePolicy.HasPolicy || cache == null || cacheKey == null)
            {
                return(ResponseHandlerResult.ResponseNotHandled());
            }

            CacheItem cacheItem = await cache.GetAsync(cacheKey);

            string responseETag = suggestedResponse.CachePolicy.ETag;

            #region If-Match precondition header

            IfMatchHeader[] ifMatchHeaders = IfMatchHeader.ParseMany(context.Request.Headers["If-Match"]).ToArray();

            // Only consider If-Match headers if response status code is 2xx or 412
            if (ifMatchHeaders.Any() && ((suggestedResponse.StatusCode.StatusCode >= 200 && suggestedResponse.StatusCode.StatusCode <= 299) || suggestedResponse.StatusCode.StatusCode == 412))
            {
                // Return 412 if no If-Match header matches the response ETag
                // Return 412 if an "If-Match: *" header is present and the response has no ETag
                if (ifMatchHeaders.All(arg => arg.EntityTag.Value != responseETag) ||
                    (responseETag == null && ifMatchHeaders.Any(arg => arg.EntityTag.Value == "*")))
                {
                    return(await WriteResponseAsync(context.Response, new Response().PreconditionFailed()));
                }
            }

            #endregion

            #region If-None-Match precondition header

            IfNoneMatchHeader[] ifNoneMatchHeaders = IfNoneMatchHeader.ParseMany(context.Request.Headers["If-None-Match"]).ToArray();

            if (ifNoneMatchHeaders.Any())
            {
                // Return 304 if an If-None-Match header matches the response ETag and the request method was GET or HEAD
                // Return 304 if an "If-None-Match: *" header is present, the response has an ETag and the request method was GET or HEAD
                // Return 412 if an "If-None-Match: *" header is present, the response has an ETag and the request method was not GET or HEAD
                if (ifNoneMatchHeaders.Any(arg => arg.EntityTag.Value == responseETag) ||
                    (ifNoneMatchHeaders.Any(arg => arg.EntityTag.Value == "*") && responseETag != null))
                {
                    if (String.Equals(context.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase) || String.Equals(context.Request.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase))
                    {
                        if (cacheItem != null)
                        {
                            cacheItem.Response.CachePolicy.Apply(context.Response.Cache);
                        }
                        else
                        {
                            suggestedResponse.CachePolicy.Apply(context.Response.Cache);
                        }

                        return(await WriteResponseAsync(context.Response, new Response().NotModified()));
                    }

                    return(await WriteResponseAsync(context.Response, new Response().PreconditionFailed()));
                }
            }

            #endregion

            #region If-Modified-Since precondition header

            IfModifiedSinceHeader ifModifiedSinceHeader = IfModifiedSinceHeader.Parse(context.Request.Headers["If-Modified-Since"]);
            bool validIfModifiedSinceHttpDate           = ifModifiedSinceHeader != null && ifModifiedSinceHeader.HttpDate <= _systemClock.UtcDateTime;

            // Only consider an If-Modified-Since header if response status code is 200 and the HTTP-date is valid
            if (suggestedResponse.StatusCode.ParsedStatusCode == HttpStatusCode.OK && validIfModifiedSinceHttpDate)
            {
                // Return 304 if the response was cached before the HTTP-date
                if (cacheItem != null && cacheItem.CachedUtcTimestamp < ifModifiedSinceHeader.HttpDate)
                {
                    return(await WriteResponseAsync(context.Response, new Response().NotModified()));
                }
            }

            #endregion

            #region If-Unmodified-Since precondition header

            IfUnmodifiedSinceHeader ifUnmodifiedSinceHeader = IfUnmodifiedSinceHeader.Parse(context.Request.Headers["If-Unmodified-Since"]);
            bool validIfUnmodifiedSinceHttpDate             = ifUnmodifiedSinceHeader != null && ifUnmodifiedSinceHeader.HttpDate <= _systemClock.UtcDateTime;

            // Only consider an If-Unmodified-Since header if response status code is 2xx or 412 and the HTTP-date is valid
            if (((suggestedResponse.StatusCode.StatusCode >= 200 && suggestedResponse.StatusCode.StatusCode <= 299) || suggestedResponse.StatusCode.StatusCode == 412) && validIfUnmodifiedSinceHttpDate)
            {
                // Return 412 if the previous response was removed from the cache or was cached again at a later time
                if (cacheItem == null || cacheItem.CachedUtcTimestamp >= ifUnmodifiedSinceHeader.HttpDate)
                {
                    return(await WriteResponseAsync(context.Response, new Response().PreconditionFailed()));
                }
            }

            #endregion

            #region No server caching

            // Do not cache the response when the response sends a non-cacheable status code, or when an Authorization header is present
            if (!_cacheableStatusCodes.Contains(suggestedResponse.StatusCode) || context.Request.Headers["Authorization"] != null)
            {
                return(await WriteResponseAsync(context.Response, suggestedResponse));
            }

            CacheControlHeader cacheControlHeader = CacheControlHeader.Parse(context.Request.Headers["Cache-Control"]);

            // Do not cache the response if a "Cache-Control: no-cache" or "Cache-Control: no-store" header is present
            if (cacheControlHeader != null && (cacheControlHeader.NoCache || cacheControlHeader.NoStore))
            {
                return(await WriteResponseAsync(context.Response, suggestedResponse));
            }

            IEnumerable <PragmaHeader> pragmaHeader = PragmaHeader.ParseMany(context.Request.Headers["Pragma"]);

            // Do not cache the response if a "Pragma: no-cache" header is present
            if (pragmaHeader.Any(arg => String.Equals(arg.Name, "no-cache", StringComparison.OrdinalIgnoreCase)))
            {
                return(await WriteResponseAsync(context.Response, suggestedResponse));
            }

            #endregion

            // Return 504 if the response has not been cached but the client is requesting to receive only a cached response
            if (cacheItem == null && cacheControlHeader != null && cacheControlHeader.OnlyIfCached)
            {
                return(await WriteResponseAsync(context.Response, new Response().GatewayTimeout()));
            }

            if (cacheItem != null)
            {
                // Write the cached response if no Cache-Control header is present
                // Write the cached response if a "Cache-Control: max-age" header is validated
                // Write the cached response if a "Cache-Control: max-stale" header is validated
                // Write the cached response if a "Cache-Control: min-fresh" header is validated
                if (cacheControlHeader == null ||
                    _systemClock.UtcDateTime - cacheItem.CachedUtcTimestamp <= cacheControlHeader.MaxAge ||
                    cacheControlHeader.OnlyIfCached ||
                    cacheItem.ExpiresUtcTimestamp == null ||
                    _systemClock.UtcDateTime - cacheItem.ExpiresUtcTimestamp.Value <= cacheControlHeader.MaxStale ||
                    cacheItem.ExpiresUtcTimestamp.Value - _systemClock.UtcDateTime < cacheControlHeader.MinFresh)
                {
                    return(await WriteResponseInCacheAsync(context.Response, cacheItem));
                }
            }

            bool cacheOnServer = suggestedResponse.CachePolicy.AllowsServerCaching;
            var  cacheResponse = new CacheResponse(suggestedResponse);

            if (cacheOnServer)
            {
                DateTime expirationUtcTimestamp = suggestedResponse.CachePolicy.ServerCacheExpirationUtcTimestamp != null
                                        ? suggestedResponse.CachePolicy.ServerCacheExpirationUtcTimestamp.Value
                                        : _systemClock.UtcDateTime + suggestedResponse.CachePolicy.ServerCacheMaxAge.Value;

                await cache.AddAsync(cacheKey, cacheResponse, expirationUtcTimestamp);
            }

            return(await WriteResponseAsync(context.Response, cacheResponse));
        }
        private static async Task <ResponseHandlerResult> WriteResponseAsync(HttpResponseBase httpResponse, CacheResponse cacheResponse)
        {
            await cacheResponse.WriteResponseAsync(httpResponse);

            return(ResponseHandlerResult.ResponseWritten());
        }
 public void SetUp()
 {
     _result = ResponseHandlerResult.ResponseWritten();
 }
 public void SetUp()
 {
     _response = MockRepository.GenerateMock<IResponse>();
     _result = ResponseHandlerResult.ResponseSuggested(_response);
 }
 public void SetUp()
 {
     _result = ResponseHandlerResult.ResponseNotHandled();
 }