diff --git a/src/Microsoft.AspNetCore.ResponseCaching/Internal/MemoryResponseCache.cs b/src/Microsoft.AspNetCore.ResponseCaching/Internal/MemoryResponseCache.cs index 4a855e2..f2509c8 100644 --- a/src/Microsoft.AspNetCore.ResponseCaching/Internal/MemoryResponseCache.cs +++ b/src/Microsoft.AspNetCore.ResponseCaching/Internal/MemoryResponseCache.cs @@ -24,7 +24,7 @@ public MemoryResponseCache(IMemoryCache cache) public Task GetAsync(string key) { var entry = _cache.Get(key); - + var memoryCachedResponse = entry as MemoryCachedResponse; if (memoryCachedResponse != null) { diff --git a/src/Microsoft.AspNetCore.ResponseCaching/Internal/ResponseCachingContext.cs b/src/Microsoft.AspNetCore.ResponseCaching/Internal/ResponseCachingContext.cs index 4fc7292..eeed0d9 100644 --- a/src/Microsoft.AspNetCore.ResponseCaching/Internal/ResponseCachingContext.cs +++ b/src/Microsoft.AspNetCore.ResponseCaching/Internal/ResponseCachingContext.cs @@ -5,7 +5,6 @@ using System.IO; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Http.Headers; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; @@ -13,16 +12,14 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal { public class ResponseCachingContext { - private static readonly CacheControlHeaderValue EmptyCacheControl = new CacheControlHeaderValue(); - - private RequestHeaders _requestHeaders; - private ResponseHeaders _responseHeaders; - private CacheControlHeaderValue _requestCacheControl; - private CacheControlHeaderValue _responseCacheControl; private DateTimeOffset? _responseDate; private bool _parsedResponseDate; private DateTimeOffset? _responseExpires; private bool _parsedResponseExpires; + private TimeSpan? _responseSharedMaxAge; + private bool _parsedResponseSharedMaxAge; + private TimeSpan? _responseMaxAge; + private bool _parsedResponseMaxAge; internal ResponseCachingContext(HttpContext httpContext, ILogger logger) { @@ -58,85 +55,79 @@ internal ResponseCachingContext(HttpContext httpContext, ILogger logger) internal IHttpSendFileFeature OriginalSendFileFeature { get; set; } - internal ResponseHeaders CachedResponseHeaders { get; set; } + internal IHeaderDictionary CachedResponseHeaders { get; set; } - internal RequestHeaders TypedRequestHeaders - { - get - { - if (_requestHeaders == null) - { - _requestHeaders = HttpContext.Request.GetTypedHeaders(); - } - return _requestHeaders; - } - } - - internal ResponseHeaders TypedResponseHeaders + internal DateTimeOffset? ResponseDate { get { - if (_responseHeaders == null) + if (!_parsedResponseDate) { - _responseHeaders = HttpContext.Response.GetTypedHeaders(); + _parsedResponseDate = true; + DateTimeOffset date; + if (HeaderUtilities.TryParseDate(HttpContext.Response.Headers[HeaderNames.Date], out date)) + { + _responseDate = date; + } + else + { + _responseDate = null; + } } - return _responseHeaders; + return _responseDate; } - } - - internal CacheControlHeaderValue RequestCacheControlHeaderValue - { - get + set { - if (_requestCacheControl == null) - { - _requestCacheControl = TypedRequestHeaders.CacheControl ?? EmptyCacheControl; - } - return _requestCacheControl; + // Don't reparse the response date again if it's explicitly set + _parsedResponseDate = true; + _responseDate = value; } } - internal CacheControlHeaderValue ResponseCacheControlHeaderValue + internal DateTimeOffset? ResponseExpires { get { - if (_responseCacheControl == null) + if (!_parsedResponseExpires) { - _responseCacheControl = TypedResponseHeaders.CacheControl ?? EmptyCacheControl; + _parsedResponseExpires = true; + DateTimeOffset expires; + if (HeaderUtilities.TryParseDate(HttpContext.Response.Headers[HeaderNames.Expires], out expires)) + { + _responseExpires = expires; + } + else + { + _responseExpires = null; + } } - return _responseCacheControl; + return _responseExpires; } } - internal DateTimeOffset? ResponseDate + internal TimeSpan? ResponseSharedMaxAge { get { - if (!_parsedResponseDate) + if (!_parsedResponseSharedMaxAge) { - _parsedResponseDate = true; - _responseDate = TypedResponseHeaders.Date; + _parsedResponseSharedMaxAge = true; + HeaderUtilities.TryParseSeconds(HttpContext.Response.Headers[HeaderNames.CacheControl], CacheControlHeaderValue.SharedMaxAgeString, out _responseSharedMaxAge); } - return _responseDate; - } - set - { - // Don't reparse the response date again if it's explicitly set - _parsedResponseDate = true; - _responseDate = value; + return _responseSharedMaxAge; } } - internal DateTimeOffset? ResponseExpires + internal TimeSpan? ResponseMaxAge { get { - if (!_parsedResponseExpires) + if (!_parsedResponseMaxAge) { - _parsedResponseExpires = true; - _responseExpires = TypedResponseHeaders.Expires; + _parsedResponseMaxAge = true; + HeaderUtilities.TryParseSeconds(HttpContext.Response.Headers[HeaderNames.CacheControl], CacheControlHeaderValue.MaxAgeString, out _responseMaxAge); } - return _responseExpires; + return _responseMaxAge; } } } diff --git a/src/Microsoft.AspNetCore.ResponseCaching/Internal/ResponseCachingPolicyProvider.cs b/src/Microsoft.AspNetCore.ResponseCaching/Internal/ResponseCachingPolicyProvider.cs index 0072546..c37fcac 100644 --- a/src/Microsoft.AspNetCore.ResponseCaching/Internal/ResponseCachingPolicyProvider.cs +++ b/src/Microsoft.AspNetCore.ResponseCaching/Internal/ResponseCachingPolicyProvider.cs @@ -10,8 +10,6 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal { public class ResponseCachingPolicyProvider : IResponseCachingPolicyProvider { - private static readonly CacheControlHeaderValue EmptyCacheControl = new CacheControlHeaderValue(); - public virtual bool IsRequestCacheable(ResponseCachingContext context) { // Verify the method @@ -32,7 +30,7 @@ public virtual bool IsRequestCacheable(ResponseCachingContext context) // Verify request cache-control parameters if (!StringValues.IsNullOrEmpty(request.Headers[HeaderNames.CacheControl])) { - if (context.RequestCacheControlHeaderValue.NoCache) + if (HeaderUtilities.ContainsCacheDirective(request.Headers[HeaderNames.CacheControl], CacheControlHeaderValue.NoCacheString)) { context.Logger.LogRequestWithNoCacheNotCacheable(); return false; @@ -42,13 +40,10 @@ public virtual bool IsRequestCacheable(ResponseCachingContext context) { // Support for legacy HTTP 1.0 cache directive var pragmaHeaderValues = request.Headers[HeaderNames.Pragma]; - foreach (var directive in pragmaHeaderValues) + if (HeaderUtilities.ContainsCacheDirective(request.Headers[HeaderNames.Pragma], CacheControlHeaderValue.NoCacheString)) { - if (string.Equals("no-cache", directive, StringComparison.OrdinalIgnoreCase)) - { - context.Logger.LogRequestWithPragmaNoCacheNotCacheable(); - return false; - } + context.Logger.LogRequestWithPragmaNoCacheNotCacheable(); + return false; } } @@ -57,22 +52,25 @@ public virtual bool IsRequestCacheable(ResponseCachingContext context) public virtual bool IsResponseCacheable(ResponseCachingContext context) { + var responseCacheControlHeader = context.HttpContext.Response.Headers[HeaderNames.CacheControl]; + // Only cache pages explicitly marked with public - if (!context.ResponseCacheControlHeaderValue.Public) + if (!HeaderUtilities.ContainsCacheDirective(responseCacheControlHeader, CacheControlHeaderValue.PublicString)) { context.Logger.LogResponseWithoutPublicNotCacheable(); return false; } // Check no-store - if (context.RequestCacheControlHeaderValue.NoStore || context.ResponseCacheControlHeaderValue.NoStore) + if (HeaderUtilities.ContainsCacheDirective(context.HttpContext.Request.Headers[HeaderNames.CacheControl], CacheControlHeaderValue.NoStoreString) + || HeaderUtilities.ContainsCacheDirective(responseCacheControlHeader, CacheControlHeaderValue.NoStoreString)) { context.Logger.LogResponseWithNoStoreNotCacheable(); return false; } // Check no-cache - if (context.ResponseCacheControlHeaderValue.NoCache) + if (HeaderUtilities.ContainsCacheDirective(responseCacheControlHeader, CacheControlHeaderValue.NoCacheString)) { context.Logger.LogResponseWithNoCacheNotCacheable(); return false; @@ -96,7 +94,7 @@ public virtual bool IsResponseCacheable(ResponseCachingContext context) } // Check private - if (context.ResponseCacheControlHeaderValue.Private) + if (HeaderUtilities.ContainsCacheDirective(responseCacheControlHeader, CacheControlHeaderValue.PrivateString)) { context.Logger.LogResponseWithPrivateNotCacheable(); return false; @@ -112,8 +110,8 @@ public virtual bool IsResponseCacheable(ResponseCachingContext context) // Check response freshness if (!context.ResponseDate.HasValue) { - if (!context.ResponseCacheControlHeaderValue.SharedMaxAge.HasValue && - !context.ResponseCacheControlHeaderValue.MaxAge.HasValue && + if (!context.ResponseSharedMaxAge.HasValue && + !context.ResponseMaxAge.HasValue && context.ResponseTime.Value >= context.ResponseExpires) { context.Logger.LogExpirationExpiresExceeded(context.ResponseTime.Value, context.ResponseExpires.Value); @@ -125,22 +123,20 @@ public virtual bool IsResponseCacheable(ResponseCachingContext context) var age = context.ResponseTime.Value - context.ResponseDate.Value; // Validate shared max age - var sharedMaxAge = context.ResponseCacheControlHeaderValue.SharedMaxAge; - if (age >= sharedMaxAge) + if (age >= context.ResponseSharedMaxAge) { - context.Logger.LogExpirationSharedMaxAgeExceeded(age, sharedMaxAge.Value); + context.Logger.LogExpirationSharedMaxAgeExceeded(age, context.ResponseSharedMaxAge.Value); return false; } - else if (!sharedMaxAge.HasValue) + else if (!context.ResponseSharedMaxAge.HasValue) { // Validate max age - var maxAge = context.ResponseCacheControlHeaderValue.MaxAge; - if (age >= maxAge) + if (age >= context.ResponseMaxAge) { - context.Logger.LogExpirationMaxAgeExceeded(age, maxAge.Value); + context.Logger.LogExpirationMaxAgeExceeded(age, context.ResponseMaxAge.Value); return false; } - else if (!maxAge.HasValue) + else if (!context.ResponseMaxAge.HasValue) { // Validate expiration if (context.ResponseTime.Value >= context.ResponseExpires) @@ -158,44 +154,53 @@ public virtual bool IsResponseCacheable(ResponseCachingContext context) public virtual bool IsCachedEntryFresh(ResponseCachingContext context) { var age = context.CachedEntryAge.Value; - var cachedControlHeaders = context.CachedResponseHeaders.CacheControl ?? EmptyCacheControl; + var cachedCacheControlHeaders = context.CachedResponseHeaders[HeaderNames.CacheControl]; + var requestCacheControlHeaders = context.HttpContext.Request.Headers[HeaderNames.CacheControl]; // Add min-fresh requirements - var minFresh = context.RequestCacheControlHeaderValue.MinFresh; - if (minFresh.HasValue) + TimeSpan? minFresh; + if (HeaderUtilities.TryParseSeconds(requestCacheControlHeaders, CacheControlHeaderValue.MinFreshString, out minFresh)) { age += minFresh.Value; context.Logger.LogExpirationMinFreshAdded(minFresh.Value); } // Validate shared max age, this overrides any max age settings for shared caches - var sharedMaxAge = cachedControlHeaders.SharedMaxAge; - if (age >= sharedMaxAge) + TimeSpan? cachedSharedMaxAge; + HeaderUtilities.TryParseSeconds(cachedCacheControlHeaders, CacheControlHeaderValue.SharedMaxAgeString, out cachedSharedMaxAge); + + if (age >= cachedSharedMaxAge) { // shared max age implies must revalidate - context.Logger.LogExpirationSharedMaxAgeExceeded(age, sharedMaxAge.Value); + context.Logger.LogExpirationSharedMaxAgeExceeded(age, cachedSharedMaxAge.Value); return false; } - else if (!sharedMaxAge.HasValue) + else if (!cachedSharedMaxAge.HasValue) { - var cachedMaxAge = cachedControlHeaders.MaxAge; - var requestMaxAge = context.RequestCacheControlHeaderValue.MaxAge; + TimeSpan? requestMaxAge; + HeaderUtilities.TryParseSeconds(requestCacheControlHeaders, CacheControlHeaderValue.MaxAgeString, out requestMaxAge); + + TimeSpan? cachedMaxAge; + HeaderUtilities.TryParseSeconds(cachedCacheControlHeaders, CacheControlHeaderValue.MaxAgeString, out cachedMaxAge); + var lowestMaxAge = cachedMaxAge < requestMaxAge ? cachedMaxAge : requestMaxAge ?? cachedMaxAge; // Validate max age if (age >= lowestMaxAge) { // Must revalidate - if (cachedControlHeaders.MustRevalidate) + if (HeaderUtilities.ContainsCacheDirective(cachedCacheControlHeaders, CacheControlHeaderValue.MustRevalidateString)) { context.Logger.LogExpirationMustRevalidate(age, lowestMaxAge.Value); return false; } + TimeSpan? requestMaxStale; + HeaderUtilities.TryParseSeconds(requestCacheControlHeaders, CacheControlHeaderValue.MaxStaleString, out requestMaxStale); + // Request allows stale values - var maxStaleLimit = context.RequestCacheControlHeaderValue.MaxStaleLimit; - if (maxStaleLimit.HasValue && age - lowestMaxAge < maxStaleLimit) + if (requestMaxStale.HasValue && age - lowestMaxAge < requestMaxStale) { - context.Logger.LogExpirationMaxStaleSatisfied(age, lowestMaxAge.Value, maxStaleLimit.Value); + context.Logger.LogExpirationMaxStaleSatisfied(age, lowestMaxAge.Value, requestMaxStale.Value); return true; } @@ -205,11 +210,11 @@ public virtual bool IsCachedEntryFresh(ResponseCachingContext context) else if (!cachedMaxAge.HasValue && !requestMaxAge.HasValue) { // Validate expiration - var responseTime = context.ResponseTime.Value; - var expires = context.CachedResponseHeaders.Expires; - if (responseTime >= expires) + DateTimeOffset expires; + if (HeaderUtilities.TryParseDate(context.CachedResponseHeaders[HeaderNames.Expires], out expires) && + context.ResponseTime.Value >= expires) { - context.Logger.LogExpirationExpiresExceeded(responseTime, expires.Value); + context.Logger.LogExpirationExpiresExceeded(context.ResponseTime.Value, expires); return false; } } diff --git a/src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingMiddleware.cs b/src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingMiddleware.cs index dc01df4..a15353a 100644 --- a/src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingMiddleware.cs +++ b/src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingMiddleware.cs @@ -2,11 +2,11 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Globalization; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Http.Headers; using Microsoft.AspNetCore.ResponseCaching.Internal; using Microsoft.Extensions.Internal; using Microsoft.Extensions.Logging; @@ -119,7 +119,7 @@ internal async Task TryServeCachedResponseAsync(ResponseCachingContext con } context.CachedResponse = cachedResponse; - context.CachedResponseHeaders = new ResponseHeaders(cachedResponse.Headers); + context.CachedResponseHeaders = cachedResponse.Headers; context.ResponseTime = _options.SystemClock.UtcNow; var cachedEntryAge = context.ResponseTime.Value - context.CachedResponse.Created; context.CachedEntryAge = cachedEntryAge > TimeSpan.Zero ? cachedEntryAge : TimeSpan.Zero; @@ -198,7 +198,7 @@ internal async Task TryServeFromCacheAsync(ResponseCachingContext context) } } - if (context.RequestCacheControlHeaderValue.OnlyIfCached) + if (HeaderUtilities.ContainsCacheDirective(context.HttpContext.Request.Headers[HeaderNames.CacheControl], CacheControlHeaderValue.OnlyIfCachedString)) { _logger.LogGatewayTimeoutServed(); context.HttpContext.Response.StatusCode = StatusCodes.Status504GatewayTimeout; @@ -219,8 +219,8 @@ internal async Task FinalizeCacheHeadersAsync(ResponseCachingContext context) var response = context.HttpContext.Response; var varyHeaders = new StringValues(response.Headers.GetCommaSeparatedValues(HeaderNames.Vary)); var varyQueryKeys = new StringValues(context.HttpContext.Features.Get()?.VaryByQueryKeys); - context.CachedResponseValidFor = context.ResponseCacheControlHeaderValue.SharedMaxAge ?? - context.ResponseCacheControlHeaderValue.MaxAge ?? + context.CachedResponseValidFor = context.ResponseSharedMaxAge ?? + context.ResponseMaxAge ?? (context.ResponseExpires - context.ResponseTime.Value) ?? DefaultExpirationTimeSpan; @@ -256,7 +256,7 @@ internal async Task FinalizeCacheHeadersAsync(ResponseCachingContext context) { context.ResponseDate = context.ResponseTime.Value; // Setting the date on the raw response headers. - context.TypedResponseHeaders.Date = context.ResponseDate; + context.HttpContext.Response.Headers[HeaderNames.Date] = HeaderUtilities.FormatDate(context.ResponseDate.Value); } // Store the response on the state @@ -266,7 +266,7 @@ internal async Task FinalizeCacheHeadersAsync(ResponseCachingContext context) StatusCode = context.HttpContext.Response.StatusCode }; - foreach (var header in context.TypedResponseHeaders.Headers) + foreach (var header in context.HttpContext.Response.Headers) { if (!string.Equals(header.Key, HeaderNames.Age, StringComparison.OrdinalIgnoreCase)) { @@ -282,7 +282,7 @@ internal async Task FinalizeCacheHeadersAsync(ResponseCachingContext context) internal async Task FinalizeCacheBodyAsync(ResponseCachingContext context) { - var contentLength = context.TypedResponseHeaders.ContentLength; + var contentLength = context.HttpContext.Response.ContentLength; if (context.ShouldCacheResponse && context.ResponseCachingStream.BufferingEnabled) { var bufferStream = context.ResponseCachingStream.GetBufferStream(); @@ -355,23 +355,27 @@ internal static void UnshimResponseStream(ResponseCachingContext context) internal static bool ContentIsNotModified(ResponseCachingContext context) { var cachedResponseHeaders = context.CachedResponseHeaders; - var ifNoneMatchHeader = context.TypedRequestHeaders.IfNoneMatch; + var ifNoneMatchHeader = context.HttpContext.Request.Headers[HeaderNames.IfNoneMatch]; - if (ifNoneMatchHeader != null) + if (!StringValues.IsNullOrEmpty(ifNoneMatchHeader)) { - if (ifNoneMatchHeader.Count == 1 && ifNoneMatchHeader[0].Equals(EntityTagHeaderValue.Any)) + if (ifNoneMatchHeader.Count == 1 && string.Equals(ifNoneMatchHeader[0], EntityTagHeaderValue.Any.Tag, StringComparison.OrdinalIgnoreCase)) { context.Logger.LogNotModifiedIfNoneMatchStar(); return true; } - if (cachedResponseHeaders.ETag != null) + EntityTagHeaderValue eTag; + IList ifNoneMatchEtags; + if (!StringValues.IsNullOrEmpty(cachedResponseHeaders[HeaderNames.ETag]) + && EntityTagHeaderValue.TryParse(cachedResponseHeaders[HeaderNames.ETag], out eTag) + && EntityTagHeaderValue.TryParseList(ifNoneMatchHeader, out ifNoneMatchEtags)) { - foreach (var tag in ifNoneMatchHeader) + foreach (var requestETag in ifNoneMatchEtags) { - if (cachedResponseHeaders.ETag.Compare(tag, useStrongComparison: false)) + if (eTag.Compare(requestETag, useStrongComparison: false)) { - context.Logger.LogNotModifiedIfNoneMatchMatched(tag); + context.Logger.LogNotModifiedIfNoneMatchMatched(requestETag); return true; } } @@ -379,13 +383,21 @@ internal static bool ContentIsNotModified(ResponseCachingContext context) } else { - var ifUnmodifiedSince = context.TypedRequestHeaders.IfUnmodifiedSince; - if (ifUnmodifiedSince != null) + var ifUnmodifiedSince = context.HttpContext.Request.Headers[HeaderNames.IfUnmodifiedSince]; + if (!StringValues.IsNullOrEmpty(ifUnmodifiedSince)) { - var lastModified = cachedResponseHeaders.LastModified ?? cachedResponseHeaders.Date; - if (lastModified <= ifUnmodifiedSince) + DateTimeOffset modified; + if (!HeaderUtilities.TryParseDate(cachedResponseHeaders[HeaderNames.LastModified], out modified) && + !HeaderUtilities.TryParseDate(cachedResponseHeaders[HeaderNames.Date], out modified)) { - context.Logger.LogNotModifiedIfUnmodifiedSinceSatisfied(lastModified.Value, ifUnmodifiedSince.Value); + return false; + } + + DateTimeOffset unmodifiedSince; + if (HeaderUtilities.TryParseDate(ifUnmodifiedSince, out unmodifiedSince) && + modified <= unmodifiedSince) + { + context.Logger.LogNotModifiedIfUnmodifiedSinceSatisfied(modified, unmodifiedSince); return true; } } diff --git a/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingMiddlewareTests.cs b/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingMiddlewareTests.cs index fb74241..bced86f 100644 --- a/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingMiddlewareTests.cs +++ b/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingMiddlewareTests.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Headers; using Microsoft.AspNetCore.ResponseCaching.Internal; using Microsoft.Extensions.Logging.Testing; using Microsoft.Extensions.Primitives; @@ -23,10 +22,10 @@ public async Task TryServeFromCacheAsync_OnlyIfCached_Serves504() var sink = new TestSink(); var middleware = TestUtils.CreateTestMiddleware(testSink: sink, cache: cache, keyProvider: new TestResponseCachingKeyProvider()); var context = TestUtils.CreateTestContext(); - context.TypedRequestHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Request.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { OnlyIfCached = true - }; + }.ToString(); Assert.True(await middleware.TryServeFromCacheAsync(context)); Assert.Equal(StatusCodes.Status504GatewayTimeout, context.HttpContext.Response.StatusCode); @@ -149,7 +148,7 @@ public void ContentIsNotModified_NotConditionalRequest_False() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()); + context.CachedResponseHeaders = new HeaderDictionary(); Assert.False(ResponseCachingMiddleware.ContentIsNotModified(context)); Assert.Empty(sink.Writes); @@ -161,22 +160,22 @@ public void ContentIsNotModified_IfUnmodifiedSince_FallsbackToDateHeader() var utcNow = DateTimeOffset.UtcNow; var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()); + context.CachedResponseHeaders = new HeaderDictionary(); - context.TypedRequestHeaders.IfUnmodifiedSince = utcNow; + context.HttpContext.Request.Headers[HeaderNames.IfUnmodifiedSince] = HeaderUtilities.FormatDate(utcNow); // Verify modifications in the past succeeds - context.CachedResponseHeaders.Date = utcNow - TimeSpan.FromSeconds(10); + context.CachedResponseHeaders[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow - TimeSpan.FromSeconds(10)); Assert.True(ResponseCachingMiddleware.ContentIsNotModified(context)); Assert.Equal(1, sink.Writes.Count); // Verify modifications at present succeeds - context.CachedResponseHeaders.Date = utcNow; + context.CachedResponseHeaders[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow); Assert.True(ResponseCachingMiddleware.ContentIsNotModified(context)); Assert.Equal(2, sink.Writes.Count); // Verify modifications in the future fails - context.CachedResponseHeaders.Date = utcNow + TimeSpan.FromSeconds(10); + context.CachedResponseHeaders[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow + TimeSpan.FromSeconds(10)); Assert.False(ResponseCachingMiddleware.ContentIsNotModified(context)); // Verify logging @@ -192,25 +191,25 @@ public void ContentIsNotModified_IfUnmodifiedSince_LastModifiedOverridesDateHead var utcNow = DateTimeOffset.UtcNow; var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()); + context.CachedResponseHeaders = new HeaderDictionary(); - context.TypedRequestHeaders.IfUnmodifiedSince = utcNow; + context.HttpContext.Request.Headers[HeaderNames.IfUnmodifiedSince] = HeaderUtilities.FormatDate(utcNow); // Verify modifications in the past succeeds - context.CachedResponseHeaders.Date = utcNow + TimeSpan.FromSeconds(10); - context.CachedResponseHeaders.LastModified = utcNow - TimeSpan.FromSeconds(10); + context.CachedResponseHeaders[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow + TimeSpan.FromSeconds(10)); + context.CachedResponseHeaders[HeaderNames.LastModified] = HeaderUtilities.FormatDate(utcNow - TimeSpan.FromSeconds(10)); Assert.True(ResponseCachingMiddleware.ContentIsNotModified(context)); Assert.Equal(1, sink.Writes.Count); // Verify modifications at present - context.CachedResponseHeaders.Date = utcNow + TimeSpan.FromSeconds(10); - context.CachedResponseHeaders.LastModified = utcNow; + context.CachedResponseHeaders[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow + TimeSpan.FromSeconds(10)); + context.CachedResponseHeaders[HeaderNames.LastModified] = HeaderUtilities.FormatDate(utcNow); Assert.True(ResponseCachingMiddleware.ContentIsNotModified(context)); Assert.Equal(2, sink.Writes.Count); // Verify modifications in the future fails - context.CachedResponseHeaders.Date = utcNow - TimeSpan.FromSeconds(10); - context.CachedResponseHeaders.LastModified = utcNow + TimeSpan.FromSeconds(10); + context.CachedResponseHeaders[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow - TimeSpan.FromSeconds(10)); + context.CachedResponseHeaders[HeaderNames.LastModified] = HeaderUtilities.FormatDate(utcNow + TimeSpan.FromSeconds(10)); Assert.False(ResponseCachingMiddleware.ContentIsNotModified(context)); // Verify logging @@ -226,13 +225,13 @@ public void ContentIsNotModified_IfNoneMatch_Overrides_IfUnmodifiedSince_ToTrue( var utcNow = DateTimeOffset.UtcNow; var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()); + context.CachedResponseHeaders = new HeaderDictionary(); // This would fail the IfUnmodifiedSince checks - context.TypedRequestHeaders.IfUnmodifiedSince = utcNow; - context.CachedResponseHeaders.LastModified = utcNow + TimeSpan.FromSeconds(10); + context.HttpContext.Request.Headers[HeaderNames.IfUnmodifiedSince] = HeaderUtilities.FormatDate(utcNow); + context.CachedResponseHeaders[HeaderNames.LastModified] = HeaderUtilities.FormatDate(utcNow + TimeSpan.FromSeconds(10)); - context.TypedRequestHeaders.IfNoneMatch = new List(new[] { EntityTagHeaderValue.Any }); + context.HttpContext.Request.Headers[HeaderNames.IfNoneMatch] = EntityTagHeaderValue.Any.ToString(); Assert.True(ResponseCachingMiddleware.ContentIsNotModified(context)); TestUtils.AssertLoggedMessages( sink.Writes, @@ -245,13 +244,13 @@ public void ContentIsNotModified_IfNoneMatch_Overrides_IfUnmodifiedSince_ToFalse var utcNow = DateTimeOffset.UtcNow; var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()); + context.CachedResponseHeaders = new HeaderDictionary(); // This would pass the IfUnmodifiedSince checks - context.TypedRequestHeaders.IfUnmodifiedSince = utcNow; - context.CachedResponseHeaders.LastModified = utcNow - TimeSpan.FromSeconds(10); + context.HttpContext.Request.Headers[HeaderNames.IfUnmodifiedSince] = HeaderUtilities.FormatDate(utcNow); + context.CachedResponseHeaders[HeaderNames.LastModified] = HeaderUtilities.FormatDate(utcNow - TimeSpan.FromSeconds(10)); - context.TypedRequestHeaders.IfNoneMatch = new List(new[] { new EntityTagHeaderValue("\"E1\"") }); + context.HttpContext.Request.Headers[HeaderNames.IfNoneMatch] = "\"E1\""; Assert.False(ResponseCachingMiddleware.ContentIsNotModified(context)); Assert.Empty(sink.Writes); } @@ -261,9 +260,8 @@ public void ContentIsNotModified_IfNoneMatch_AnyWithoutETagInResponse_False() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()); - - context.TypedRequestHeaders.IfNoneMatch = new List(new[] { new EntityTagHeaderValue("\"E1\"") }); + context.CachedResponseHeaders = new HeaderDictionary(); + context.HttpContext.Request.Headers[HeaderNames.IfNoneMatch] = "\"E1\""; Assert.False(ResponseCachingMiddleware.ContentIsNotModified(context)); Assert.Empty(sink.Writes); @@ -289,12 +287,9 @@ public void ContentIsNotModified_IfNoneMatch_ExplicitWithMatch_True(EntityTagHe { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()) - { - ETag = responseETag - }; - - context.TypedRequestHeaders.IfNoneMatch = new List(new[] { requestETag }); + context.CachedResponseHeaders = new HeaderDictionary(); + context.CachedResponseHeaders[HeaderNames.ETag] = responseETag.ToString(); + context.HttpContext.Request.Headers[HeaderNames.IfNoneMatch] = requestETag.ToString(); Assert.True(ResponseCachingMiddleware.ContentIsNotModified(context)); TestUtils.AssertLoggedMessages( @@ -307,17 +302,29 @@ public void ContentIsNotModified_IfNoneMatch_ExplicitWithoutMatch_False() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()) - { - ETag = new EntityTagHeaderValue("\"E2\"") - }; - - context.TypedRequestHeaders.IfNoneMatch = new List(new[] { new EntityTagHeaderValue("\"E1\"") }); + context.CachedResponseHeaders = new HeaderDictionary(); + context.CachedResponseHeaders[HeaderNames.ETag] = "\"E2\""; + context.HttpContext.Request.Headers[HeaderNames.IfNoneMatch] = "\"E1\""; Assert.False(ResponseCachingMiddleware.ContentIsNotModified(context)); Assert.Empty(sink.Writes); } + [Fact] + public void ContentIsNotModified_IfNoneMatch_MatchesAtLeastOneValue_True() + { + var sink = new TestSink(); + var context = TestUtils.CreateTestContext(sink); + context.CachedResponseHeaders = new HeaderDictionary(); + context.CachedResponseHeaders[HeaderNames.ETag] = "\"E2\""; + context.HttpContext.Request.Headers[HeaderNames.IfNoneMatch] = new string[] { "\"E0\", \"E1\"", "\"E1\", \"E2\"" }; + + Assert.True(ResponseCachingMiddleware.ContentIsNotModified(context)); + TestUtils.AssertLoggedMessages( + sink.Writes, + LoggedMessage.NotModifiedIfNoneMatchMatched); + } + [Fact] public async Task FinalizeCacheHeaders_DoNotUpdateShouldCacheResponse_IfResponseIsNotCacheable() { @@ -340,10 +347,10 @@ public async Task FinalizeCacheHeaders_UpdateShouldCacheResponse_IfResponseIsCac var sink = new TestSink(); var middleware = TestUtils.CreateTestMiddleware(testSink: sink, policyProvider: new ResponseCachingPolicyProvider()); var context = TestUtils.CreateTestContext(); - context.TypedResponseHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true - }; + }.ToString(); Assert.False(context.ShouldCacheResponse); @@ -375,7 +382,7 @@ public async Task FinalizeCacheHeaders_ResponseValidity_UseExpiryIfAvailable() var context = TestUtils.CreateTestContext(); context.ResponseTime = utcNow; - context.TypedResponseHeaders.Expires = utcNow + TimeSpan.FromSeconds(11); + context.HttpContext.Response.Headers[HeaderNames.Expires] = HeaderUtilities.FormatDate(utcNow + TimeSpan.FromSeconds(11)); await middleware.FinalizeCacheHeadersAsync(context); @@ -389,12 +396,12 @@ public async Task FinalizeCacheHeaders_ResponseValidity_UseMaxAgeIfAvailable() var sink = new TestSink(); var middleware = TestUtils.CreateTestMiddleware(testSink: sink); var context = TestUtils.CreateTestContext(); - context.TypedResponseHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(12) - }; + }.ToString(); - context.TypedResponseHeaders.Expires = context.ResponseTime + TimeSpan.FromSeconds(11); + context.HttpContext.Response.Headers[HeaderNames.Expires] = HeaderUtilities.FormatDate(context.ResponseTime.Value + TimeSpan.FromSeconds(11)); await middleware.FinalizeCacheHeadersAsync(context); @@ -408,13 +415,13 @@ public async Task FinalizeCacheHeaders_ResponseValidity_UseSharedMaxAgeIfAvailab var sink = new TestSink(); var middleware = TestUtils.CreateTestMiddleware(testSink: sink); var context = TestUtils.CreateTestContext(); - context.TypedResponseHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(12), SharedMaxAge = TimeSpan.FromSeconds(13) - }; + }.ToString(); - context.TypedResponseHeaders.Expires = context.ResponseTime + TimeSpan.FromSeconds(11); + context.HttpContext.Response.Headers[HeaderNames.Expires] = HeaderUtilities.FormatDate(context.ResponseTime.Value + TimeSpan.FromSeconds(11)); await middleware.FinalizeCacheHeadersAsync(context); @@ -538,11 +545,11 @@ public async Task FinalizeCacheHeaders_DoNotAddDate_IfSpecified() var context = TestUtils.CreateTestContext(); context.ResponseTime = utcNow; - Assert.Null(context.TypedResponseHeaders.Date); + Assert.True(StringValues.IsNullOrEmpty(context.HttpContext.Response.Headers[HeaderNames.Date])); await middleware.FinalizeCacheHeadersAsync(context); - Assert.Equal(utcNow, context.TypedResponseHeaders.Date); + Assert.Equal(HeaderUtilities.FormatDate(utcNow), context.HttpContext.Response.Headers[HeaderNames.Date]); Assert.Empty(sink.Writes); } @@ -553,14 +560,14 @@ public async Task FinalizeCacheHeaders_AddsDate_IfNoneSpecified() var sink = new TestSink(); var middleware = TestUtils.CreateTestMiddleware(testSink: sink); var context = TestUtils.CreateTestContext(); - context.TypedResponseHeaders.Date = utcNow; + context.HttpContext.Response.Headers[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow); context.ResponseTime = utcNow + TimeSpan.FromSeconds(10); - Assert.Equal(utcNow, context.TypedResponseHeaders.Date); + Assert.Equal(HeaderUtilities.FormatDate(utcNow), context.HttpContext.Response.Headers[HeaderNames.Date]); await middleware.FinalizeCacheHeadersAsync(context); - Assert.Equal(utcNow, context.TypedResponseHeaders.Date); + Assert.Equal(HeaderUtilities.FormatDate(utcNow), context.HttpContext.Response.Headers[HeaderNames.Date]); Assert.Empty(sink.Writes); } diff --git a/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingPolicyProviderTests.cs b/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingPolicyProviderTests.cs index 36fbb6a..02a068e 100644 --- a/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingPolicyProviderTests.cs +++ b/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingPolicyProviderTests.cs @@ -88,10 +88,10 @@ public void IsRequestCacheable_NoCache_NotAllowed() var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Request.Method = HttpMethods.Get; - context.TypedRequestHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Request.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { NoCache = true - }; + }.ToString(); Assert.False(new ResponseCachingPolicyProvider().IsRequestCacheable(context)); TestUtils.AssertLoggedMessages( @@ -105,10 +105,10 @@ public void IsRequestCacheable_NoStore_Allowed() var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Request.Method = HttpMethods.Get; - context.TypedRequestHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Request.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { NoStore = true - }; + }.ToString(); Assert.True(new ResponseCachingPolicyProvider().IsRequestCacheable(context)); Assert.Empty(sink.Writes); @@ -158,10 +158,10 @@ public void IsResponseCacheable_Public_Allowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.TypedResponseHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true - }; + }.ToString(); Assert.True(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); Assert.Empty(sink.Writes); @@ -172,11 +172,11 @@ public void IsResponseCacheable_NoCache_NotAllowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.TypedResponseHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true, NoCache = true - }; + }.ToString(); Assert.False(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); TestUtils.AssertLoggedMessages( @@ -189,14 +189,14 @@ public void IsResponseCacheable_RequestNoStore_NotAllowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.TypedRequestHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Request.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { NoStore = true - }; - context.TypedResponseHeaders.CacheControl = new CacheControlHeaderValue() + }.ToString(); + context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true - }; + }.ToString(); Assert.False(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); TestUtils.AssertLoggedMessages( @@ -209,11 +209,11 @@ public void IsResponseCacheable_ResponseNoStore_NotAllowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.TypedResponseHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true, NoStore = true - }; + }.ToString(); Assert.False(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); TestUtils.AssertLoggedMessages( @@ -226,10 +226,10 @@ public void IsResponseCacheable_SetCookieHeader_NotAllowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.TypedResponseHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true - }; + }.ToString(); context.HttpContext.Response.Headers[HeaderNames.SetCookie] = "cookieName=cookieValue"; Assert.False(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); @@ -243,10 +243,10 @@ public void IsResponseCacheable_VaryHeaderByStar_NotAllowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.TypedResponseHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true - }; + }.ToString(); context.HttpContext.Response.Headers[HeaderNames.Vary] = "*"; Assert.False(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); @@ -260,11 +260,11 @@ public void IsResponseCacheable_Private_NotAllowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.TypedResponseHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true, Private = true - }; + }.ToString(); Assert.False(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); TestUtils.AssertLoggedMessages( @@ -279,10 +279,10 @@ public void IsResponseCacheable_SuccessStatusCodes_Allowed(int statusCode) var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.StatusCode = statusCode; - context.TypedResponseHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true - }; + }.ToString(); Assert.True(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); Assert.Empty(sink.Writes); @@ -342,10 +342,10 @@ public void IsResponseCacheable_NonSuccessStatusCodes_NotAllowed(int statusCode) var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.StatusCode = statusCode; - context.TypedResponseHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true - }; + }.ToString(); Assert.False(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); TestUtils.AssertLoggedMessages( @@ -359,13 +359,13 @@ public void IsResponseCacheable_NoExpiryRequirements_IsAllowed() var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.StatusCode = StatusCodes.Status200OK; - context.TypedResponseHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true - }; + }.ToString(); var utcNow = DateTimeOffset.UtcNow; - context.TypedResponseHeaders.Date = utcNow; + context.HttpContext.Response.Headers[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow); context.ResponseTime = DateTimeOffset.MaxValue; Assert.True(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); @@ -378,14 +378,14 @@ public void IsResponseCacheable_AtExpiry_NotAllowed() var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.StatusCode = StatusCodes.Status200OK; - context.TypedResponseHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true - }; + }.ToString(); var utcNow = DateTimeOffset.UtcNow; - context.TypedResponseHeaders.Expires = utcNow; + context.HttpContext.Response.Headers[HeaderNames.Expires] = HeaderUtilities.FormatDate(utcNow); - context.TypedResponseHeaders.Date = utcNow; + context.HttpContext.Response.Headers[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow); context.ResponseTime = utcNow; Assert.False(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); @@ -401,13 +401,13 @@ public void IsResponseCacheable_MaxAgeOverridesExpiry_ToAllowed() var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.StatusCode = StatusCodes.Status200OK; - context.TypedResponseHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true, MaxAge = TimeSpan.FromSeconds(10) - }; - context.TypedResponseHeaders.Expires = utcNow; - context.TypedResponseHeaders.Date = utcNow; + }.ToString(); + context.HttpContext.Response.Headers[HeaderNames.Expires] = HeaderUtilities.FormatDate(utcNow); + context.HttpContext.Response.Headers[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow); context.ResponseTime = utcNow + TimeSpan.FromSeconds(9); Assert.True(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); @@ -421,13 +421,13 @@ public void IsResponseCacheable_MaxAgeOverridesExpiry_ToNotAllowed() var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.StatusCode = StatusCodes.Status200OK; - context.TypedResponseHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true, MaxAge = TimeSpan.FromSeconds(10) - }; - context.TypedResponseHeaders.Expires = utcNow; - context.TypedResponseHeaders.Date = utcNow; + }.ToString(); + context.HttpContext.Response.Headers[HeaderNames.Expires] = HeaderUtilities.FormatDate(utcNow); + context.HttpContext.Response.Headers[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow); context.ResponseTime = utcNow + TimeSpan.FromSeconds(10); Assert.False(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); @@ -443,13 +443,13 @@ public void IsResponseCacheable_SharedMaxAgeOverridesMaxAge_ToAllowed() var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.StatusCode = StatusCodes.Status200OK; - context.TypedResponseHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true, MaxAge = TimeSpan.FromSeconds(10), SharedMaxAge = TimeSpan.FromSeconds(15) - }; - context.TypedResponseHeaders.Date = utcNow; + }.ToString(); + context.HttpContext.Response.Headers[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow); context.ResponseTime = utcNow + TimeSpan.FromSeconds(11); Assert.True(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); @@ -463,13 +463,13 @@ public void IsResponseCacheable_SharedMaxAgeOverridesMaxAge_ToNotAllowed() var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.StatusCode = StatusCodes.Status200OK; - context.TypedResponseHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true, MaxAge = TimeSpan.FromSeconds(10), SharedMaxAge = TimeSpan.FromSeconds(5) - }; - context.TypedResponseHeaders.Date = utcNow; + }.ToString(); + context.HttpContext.Response.Headers[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow); context.ResponseTime = utcNow + TimeSpan.FromSeconds(5); Assert.False(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); @@ -486,7 +486,7 @@ public void IsCachedEntryFresh_NoCachedCacheControl_FallsbackToEmptyCacheControl var context = TestUtils.CreateTestContext(sink); context.ResponseTime = DateTimeOffset.MaxValue; context.CachedEntryAge = TimeSpan.MaxValue; - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()); + context.CachedResponseHeaders = new HeaderDictionary(); Assert.True(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); Assert.Empty(sink.Writes); @@ -500,13 +500,11 @@ public void IsCachedEntryFresh_NoExpiryRequirements_IsFresh() var context = TestUtils.CreateTestContext(sink); context.ResponseTime = DateTimeOffset.MaxValue; context.CachedEntryAge = TimeSpan.MaxValue; - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()) + context.CachedResponseHeaders = new HeaderDictionary(); + context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { - CacheControl = new CacheControlHeaderValue() - { - Public = true - } - }; + Public = true + }.ToString(); Assert.True(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); Assert.Empty(sink.Writes); @@ -520,14 +518,12 @@ public void IsCachedEntryFresh_AtExpiry_IsNotFresh() var context = TestUtils.CreateTestContext(sink); context.ResponseTime = utcNow; context.CachedEntryAge = TimeSpan.Zero; - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()) + context.CachedResponseHeaders = new HeaderDictionary(); + context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { - CacheControl = new CacheControlHeaderValue() - { - Public = true - }, - Expires = utcNow - }; + Public = true + }.ToString(); + context.CachedResponseHeaders[HeaderNames.Expires] = HeaderUtilities.FormatDate(utcNow); Assert.False(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); TestUtils.AssertLoggedMessages( @@ -543,15 +539,13 @@ public void IsCachedEntryFresh_MaxAgeOverridesExpiry_ToFresh() var context = TestUtils.CreateTestContext(sink); context.CachedEntryAge = TimeSpan.FromSeconds(9); context.ResponseTime = utcNow + context.CachedEntryAge; - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()) + context.CachedResponseHeaders = new HeaderDictionary(); + context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { - CacheControl = new CacheControlHeaderValue() - { - Public = true, - MaxAge = TimeSpan.FromSeconds(10) - }, - Expires = utcNow - }; + Public = true, + MaxAge = TimeSpan.FromSeconds(10) + }.ToString(); + context.HttpContext.Response.Headers[HeaderNames.Expires] = HeaderUtilities.FormatDate(utcNow); Assert.True(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); Assert.Empty(sink.Writes); @@ -565,15 +559,13 @@ public void IsCachedEntryFresh_MaxAgeOverridesExpiry_ToNotFresh() var context = TestUtils.CreateTestContext(sink); context.CachedEntryAge = TimeSpan.FromSeconds(10); context.ResponseTime = utcNow + context.CachedEntryAge; - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()) + context.CachedResponseHeaders = new HeaderDictionary(); + context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { - CacheControl = new CacheControlHeaderValue() - { - Public = true, - MaxAge = TimeSpan.FromSeconds(10) - }, - Expires = utcNow - }; + Public = true, + MaxAge = TimeSpan.FromSeconds(10) + }.ToString(); + context.HttpContext.Response.Headers[HeaderNames.Expires] = HeaderUtilities.FormatDate(utcNow); Assert.False(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); TestUtils.AssertLoggedMessages( @@ -589,16 +581,14 @@ public void IsCachedEntryFresh_SharedMaxAgeOverridesMaxAge_ToFresh() var context = TestUtils.CreateTestContext(sink); context.CachedEntryAge = TimeSpan.FromSeconds(11); context.ResponseTime = utcNow + context.CachedEntryAge; - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()) + context.CachedResponseHeaders = new HeaderDictionary(); + context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { - CacheControl = new CacheControlHeaderValue() - { - Public = true, - MaxAge = TimeSpan.FromSeconds(10), - SharedMaxAge = TimeSpan.FromSeconds(15) - }, - Expires = utcNow - }; + Public = true, + MaxAge = TimeSpan.FromSeconds(10), + SharedMaxAge = TimeSpan.FromSeconds(15) + }.ToString(); + context.CachedResponseHeaders[HeaderNames.Expires] = HeaderUtilities.FormatDate(utcNow); Assert.True(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); Assert.Empty(sink.Writes); @@ -612,16 +602,14 @@ public void IsCachedEntryFresh_SharedMaxAgeOverridesMaxAge_ToNotFresh() var context = TestUtils.CreateTestContext(sink); context.CachedEntryAge = TimeSpan.FromSeconds(5); context.ResponseTime = utcNow + context.CachedEntryAge; - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()) + context.CachedResponseHeaders = new HeaderDictionary(); + context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { - CacheControl = new CacheControlHeaderValue() - { - Public = true, - MaxAge = TimeSpan.FromSeconds(10), - SharedMaxAge = TimeSpan.FromSeconds(5) - }, - Expires = utcNow - }; + Public = true, + MaxAge = TimeSpan.FromSeconds(10), + SharedMaxAge = TimeSpan.FromSeconds(5) + }.ToString(); + context.CachedResponseHeaders[HeaderNames.Expires] = HeaderUtilities.FormatDate(utcNow); Assert.False(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); TestUtils.AssertLoggedMessages( @@ -634,18 +622,16 @@ public void IsCachedEntryFresh_MinFreshReducesFreshness_ToNotFresh() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.TypedRequestHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Request.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { MinFresh = TimeSpan.FromSeconds(2) - }; - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()) + }.ToString(); + context.CachedResponseHeaders = new HeaderDictionary(); + context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { - CacheControl = new CacheControlHeaderValue() - { - MaxAge = TimeSpan.FromSeconds(10), - SharedMaxAge = TimeSpan.FromSeconds(5) - } - }; + MaxAge = TimeSpan.FromSeconds(10), + SharedMaxAge = TimeSpan.FromSeconds(5) + }.ToString(); context.CachedEntryAge = TimeSpan.FromSeconds(3); Assert.False(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); @@ -660,17 +646,15 @@ public void IsCachedEntryFresh_RequestMaxAgeRestrictAge_ToNotFresh() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.TypedRequestHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Request.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(5) - }; - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()) + }.ToString(); + context.CachedResponseHeaders = new HeaderDictionary(); + context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { - CacheControl = new CacheControlHeaderValue() - { - MaxAge = TimeSpan.FromSeconds(10), - } - }; + MaxAge = TimeSpan.FromSeconds(10), + }.ToString(); context.CachedEntryAge = TimeSpan.FromSeconds(5); Assert.False(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); @@ -684,19 +668,17 @@ public void IsCachedEntryFresh_MaxStaleOverridesFreshness_ToFresh() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.TypedRequestHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Request.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(5), MaxStale = true, // This value must be set to true in order to specify MaxStaleLimit MaxStaleLimit = TimeSpan.FromSeconds(2) - }; - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()) + }.ToString(); + context.CachedResponseHeaders = new HeaderDictionary(); + context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { - CacheControl = new CacheControlHeaderValue() - { - MaxAge = TimeSpan.FromSeconds(5), - } - }; + MaxAge = TimeSpan.FromSeconds(5), + }.ToString(); context.CachedEntryAge = TimeSpan.FromSeconds(6); Assert.True(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); @@ -710,19 +692,17 @@ public void IsCachedEntryFresh_MaxStaleOverridesFreshness_ButStillNotFresh() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.TypedRequestHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Request.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(5), MaxStale = true, // This value must be set to true in order to specify MaxStaleLimit MaxStaleLimit = TimeSpan.FromSeconds(1) - }; - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()) + }.ToString(); + context.CachedResponseHeaders = new HeaderDictionary(); + context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { - CacheControl = new CacheControlHeaderValue() - { - MaxAge = TimeSpan.FromSeconds(5), - } - }; + MaxAge = TimeSpan.FromSeconds(5), + }.ToString(); context.CachedEntryAge = TimeSpan.FromSeconds(6); Assert.False(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); @@ -736,20 +716,18 @@ public void IsCachedEntryFresh_MustRevalidateOverridesRequestMaxStale_ToNotFresh { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); - context.TypedRequestHeaders.CacheControl = new CacheControlHeaderValue() + context.HttpContext.Request.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(5), MaxStale = true, // This value must be set to true in order to specify MaxStaleLimit MaxStaleLimit = TimeSpan.FromSeconds(2) - }; - context.CachedResponseHeaders = new ResponseHeaders(new HeaderDictionary()) + }.ToString(); + context.CachedResponseHeaders = new HeaderDictionary(); + context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { - CacheControl = new CacheControlHeaderValue() - { - MaxAge = TimeSpan.FromSeconds(5), - MustRevalidate = true - } - }; + MaxAge = TimeSpan.FromSeconds(5), + MustRevalidate = true + }.ToString(); context.CachedEntryAge = TimeSpan.FromSeconds(6); Assert.False(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context));