// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.ResponseCaching.Internal; using Microsoft.Extensions.Logging.Testing; using Microsoft.Net.Http.Headers; using Xunit; namespace Microsoft.AspNetCore.ResponseCaching.Tests { public class ResponseCachingPolicyProviderTests { public static TheoryData CacheableMethods { get { return new TheoryData { HttpMethods.Get, HttpMethods.Head }; } } [Theory] [MemberData(nameof(CacheableMethods))] public void AttemptResponseCaching_CacheableMethods_Allowed(string method) { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Request.Method = method; Assert.True(new ResponseCachingPolicyProvider().AttemptResponseCaching(context)); Assert.Empty(sink.Writes); } public static TheoryData NonCacheableMethods { get { return new TheoryData { HttpMethods.Post, HttpMethods.Put, HttpMethods.Delete, HttpMethods.Trace, HttpMethods.Connect, HttpMethods.Options, "", null }; } } [Theory] [MemberData(nameof(NonCacheableMethods))] public void AttemptResponseCaching_UncacheableMethods_NotAllowed(string method) { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Request.Method = method; Assert.False(new ResponseCachingPolicyProvider().AttemptResponseCaching(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.RequestMethodNotCacheable); } [Fact] public void AttemptResponseCaching_AuthorizationHeaders_NotAllowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Request.Method = HttpMethods.Get; context.HttpContext.Request.Headers[HeaderNames.Authorization] = "Basic plaintextUN:plaintextPW"; Assert.False(new ResponseCachingPolicyProvider().AttemptResponseCaching(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.RequestWithAuthorizationNotCacheable); } [Fact] public void AllowCacheStorage_NoStore_Allowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Request.Method = HttpMethods.Get; context.HttpContext.Request.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { NoStore = true }.ToString(); Assert.True(new ResponseCachingPolicyProvider().AllowCacheLookup(context)); Assert.Empty(sink.Writes); } [Fact] public void AllowCacheLookup_NoCache_NotAllowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Request.Method = HttpMethods.Get; context.HttpContext.Request.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { NoCache = true }.ToString(); Assert.False(new ResponseCachingPolicyProvider().AllowCacheLookup(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.RequestWithNoCacheNotCacheable); } [Fact] public void AllowCacheLookup_LegacyDirectives_NotAllowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Request.Method = HttpMethods.Get; context.HttpContext.Request.Headers[HeaderNames.Pragma] = "no-cache"; Assert.False(new ResponseCachingPolicyProvider().AllowCacheLookup(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.RequestWithPragmaNoCacheNotCacheable); } [Fact] public void AllowCacheLookup_LegacyDirectives_OverridenByCacheControl() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Request.Method = HttpMethods.Get; context.HttpContext.Request.Headers[HeaderNames.Pragma] = "no-cache"; context.HttpContext.Request.Headers[HeaderNames.CacheControl] = "max-age=10"; Assert.True(new ResponseCachingPolicyProvider().AllowCacheLookup(context)); Assert.Empty(sink.Writes); } [Fact] public void AllowCacheStorage_NoStore_NotAllowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Request.Method = HttpMethods.Get; context.HttpContext.Request.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { NoStore = true }.ToString(); Assert.False(new ResponseCachingPolicyProvider().AllowCacheStorage(context)); Assert.Empty(sink.Writes); } [Fact] public void IsResponseCacheable_NoPublic_NotAllowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); Assert.False(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.ResponseWithoutPublicNotCacheable); } [Fact] public void IsResponseCacheable_Public_Allowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true }.ToString(); Assert.True(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); Assert.Empty(sink.Writes); } [Fact] public void IsResponseCacheable_NoCache_NotAllowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true, NoCache = true }.ToString(); Assert.False(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.ResponseWithNoCacheNotCacheable); } [Fact] public void IsResponseCacheable_ResponseNoStore_NotAllowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true, NoStore = true }.ToString(); Assert.False(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.ResponseWithNoStoreNotCacheable); } [Fact] public void IsResponseCacheable_SetCookieHeader_NotAllowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); 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)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.ResponseWithSetCookieNotCacheable); } [Fact] public void IsResponseCacheable_VaryHeaderByStar_NotAllowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true }.ToString(); context.HttpContext.Response.Headers[HeaderNames.Vary] = "*"; Assert.False(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.ResponseWithVaryStarNotCacheable); } [Fact] public void IsResponseCacheable_Private_NotAllowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true, Private = true }.ToString(); Assert.False(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.ResponseWithPrivateNotCacheable); } [Theory] [InlineData(StatusCodes.Status200OK)] public void IsResponseCacheable_SuccessStatusCodes_Allowed(int statusCode) { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.StatusCode = statusCode; context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true }.ToString(); Assert.True(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); Assert.Empty(sink.Writes); } [Theory] [InlineData(StatusCodes.Status100Continue)] [InlineData(StatusCodes.Status101SwitchingProtocols)] [InlineData(StatusCodes.Status102Processing)] [InlineData(StatusCodes.Status201Created)] [InlineData(StatusCodes.Status202Accepted)] [InlineData(StatusCodes.Status203NonAuthoritative)] [InlineData(StatusCodes.Status204NoContent)] [InlineData(StatusCodes.Status205ResetContent)] [InlineData(StatusCodes.Status206PartialContent)] [InlineData(StatusCodes.Status207MultiStatus)] [InlineData(StatusCodes.Status208AlreadyReported)] [InlineData(StatusCodes.Status226IMUsed)] [InlineData(StatusCodes.Status300MultipleChoices)] [InlineData(StatusCodes.Status301MovedPermanently)] [InlineData(StatusCodes.Status302Found)] [InlineData(StatusCodes.Status303SeeOther)] [InlineData(StatusCodes.Status304NotModified)] [InlineData(StatusCodes.Status305UseProxy)] [InlineData(StatusCodes.Status306SwitchProxy)] [InlineData(StatusCodes.Status307TemporaryRedirect)] [InlineData(StatusCodes.Status308PermanentRedirect)] [InlineData(StatusCodes.Status400BadRequest)] [InlineData(StatusCodes.Status401Unauthorized)] [InlineData(StatusCodes.Status402PaymentRequired)] [InlineData(StatusCodes.Status403Forbidden)] [InlineData(StatusCodes.Status404NotFound)] [InlineData(StatusCodes.Status405MethodNotAllowed)] [InlineData(StatusCodes.Status406NotAcceptable)] [InlineData(StatusCodes.Status407ProxyAuthenticationRequired)] [InlineData(StatusCodes.Status408RequestTimeout)] [InlineData(StatusCodes.Status409Conflict)] [InlineData(StatusCodes.Status410Gone)] [InlineData(StatusCodes.Status411LengthRequired)] [InlineData(StatusCodes.Status412PreconditionFailed)] [InlineData(StatusCodes.Status413RequestEntityTooLarge)] [InlineData(StatusCodes.Status414RequestUriTooLong)] [InlineData(StatusCodes.Status415UnsupportedMediaType)] [InlineData(StatusCodes.Status416RequestedRangeNotSatisfiable)] [InlineData(StatusCodes.Status417ExpectationFailed)] [InlineData(StatusCodes.Status418ImATeapot)] [InlineData(StatusCodes.Status419AuthenticationTimeout)] [InlineData(StatusCodes.Status421MisdirectedRequest)] [InlineData(StatusCodes.Status422UnprocessableEntity)] [InlineData(StatusCodes.Status423Locked)] [InlineData(StatusCodes.Status424FailedDependency)] [InlineData(StatusCodes.Status426UpgradeRequired)] [InlineData(StatusCodes.Status428PreconditionRequired)] [InlineData(StatusCodes.Status429TooManyRequests)] [InlineData(StatusCodes.Status431RequestHeaderFieldsTooLarge)] [InlineData(StatusCodes.Status451UnavailableForLegalReasons)] [InlineData(StatusCodes.Status500InternalServerError)] [InlineData(StatusCodes.Status501NotImplemented)] [InlineData(StatusCodes.Status502BadGateway)] [InlineData(StatusCodes.Status503ServiceUnavailable)] [InlineData(StatusCodes.Status504GatewayTimeout)] [InlineData(StatusCodes.Status505HttpVersionNotsupported)] [InlineData(StatusCodes.Status506VariantAlsoNegotiates)] [InlineData(StatusCodes.Status507InsufficientStorage)] [InlineData(StatusCodes.Status508LoopDetected)] [InlineData(StatusCodes.Status510NotExtended)] [InlineData(StatusCodes.Status511NetworkAuthenticationRequired)] public void IsResponseCacheable_NonSuccessStatusCodes_NotAllowed(int statusCode) { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.StatusCode = statusCode; context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true }.ToString(); Assert.False(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.ResponseWithUnsuccessfulStatusCodeNotCacheable); } [Fact] public void IsResponseCacheable_NoExpiryRequirements_IsAllowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.StatusCode = StatusCodes.Status200OK; context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true }.ToString(); var utcNow = DateTimeOffset.UtcNow; context.HttpContext.Response.Headers[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow); context.ResponseTime = DateTimeOffset.MaxValue; Assert.True(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); Assert.Empty(sink.Writes); } [Fact] public void IsResponseCacheable_AtExpiry_NotAllowed() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.StatusCode = StatusCodes.Status200OK; context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true }.ToString(); var utcNow = DateTimeOffset.UtcNow; context.HttpContext.Response.Headers[HeaderNames.Expires] = HeaderUtilities.FormatDate(utcNow); context.HttpContext.Response.Headers[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow); context.ResponseTime = utcNow; Assert.False(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.ExpirationExpiresExceeded); } [Fact] public void IsResponseCacheable_MaxAgeOverridesExpiry_ToAllowed() { var utcNow = DateTimeOffset.UtcNow; var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.StatusCode = StatusCodes.Status200OK; context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true, MaxAge = TimeSpan.FromSeconds(10) }.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)); Assert.Empty(sink.Writes); } [Fact] public void IsResponseCacheable_MaxAgeOverridesExpiry_ToNotAllowed() { var utcNow = DateTimeOffset.UtcNow; var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.StatusCode = StatusCodes.Status200OK; context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true, MaxAge = TimeSpan.FromSeconds(10) }.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)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.ExpirationMaxAgeExceeded); } [Fact] public void IsResponseCacheable_SharedMaxAgeOverridesMaxAge_ToAllowed() { var utcNow = DateTimeOffset.UtcNow; var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.StatusCode = StatusCodes.Status200OK; context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true, MaxAge = TimeSpan.FromSeconds(10), SharedMaxAge = TimeSpan.FromSeconds(15) }.ToString(); context.HttpContext.Response.Headers[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow); context.ResponseTime = utcNow + TimeSpan.FromSeconds(11); Assert.True(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); Assert.Empty(sink.Writes); } [Fact] public void IsResponseCacheable_SharedMaxAgeOverridesMaxAge_ToNotAllowed() { var utcNow = DateTimeOffset.UtcNow; var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Response.StatusCode = StatusCodes.Status200OK; context.HttpContext.Response.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true, MaxAge = TimeSpan.FromSeconds(10), SharedMaxAge = TimeSpan.FromSeconds(5) }.ToString(); context.HttpContext.Response.Headers[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow); context.ResponseTime = utcNow + TimeSpan.FromSeconds(5); Assert.False(new ResponseCachingPolicyProvider().IsResponseCacheable(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.ExpirationSharedMaxAgeExceeded); } [Fact] public void IsCachedEntryFresh_NoCachedCacheControl_FallsbackToEmptyCacheControl() { var utcNow = DateTimeOffset.UtcNow; var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.ResponseTime = DateTimeOffset.MaxValue; context.CachedEntryAge = TimeSpan.MaxValue; context.CachedResponseHeaders = new HeaderDictionary(); Assert.True(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); Assert.Empty(sink.Writes); } [Fact] public void IsCachedEntryFresh_NoExpiryRequirements_IsFresh() { var utcNow = DateTimeOffset.UtcNow; var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.ResponseTime = DateTimeOffset.MaxValue; context.CachedEntryAge = TimeSpan.MaxValue; context.CachedResponseHeaders = new HeaderDictionary(); context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true }.ToString(); Assert.True(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); Assert.Empty(sink.Writes); } [Fact] public void IsCachedEntryFresh_AtExpiry_IsNotFresh() { var utcNow = DateTimeOffset.UtcNow; var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.ResponseTime = utcNow; context.CachedEntryAge = TimeSpan.Zero; context.CachedResponseHeaders = new HeaderDictionary(); context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true }.ToString(); context.CachedResponseHeaders[HeaderNames.Expires] = HeaderUtilities.FormatDate(utcNow); Assert.False(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.ExpirationExpiresExceeded); } [Fact] public void IsCachedEntryFresh_MaxAgeOverridesExpiry_ToFresh() { var utcNow = DateTimeOffset.UtcNow; var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.CachedEntryAge = TimeSpan.FromSeconds(9); context.ResponseTime = utcNow + context.CachedEntryAge; context.CachedResponseHeaders = new HeaderDictionary(); context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { 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); } [Fact] public void IsCachedEntryFresh_MaxAgeOverridesExpiry_ToNotFresh() { var utcNow = DateTimeOffset.UtcNow; var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.CachedEntryAge = TimeSpan.FromSeconds(10); context.ResponseTime = utcNow + context.CachedEntryAge; context.CachedResponseHeaders = new HeaderDictionary(); context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { Public = true, MaxAge = TimeSpan.FromSeconds(10) }.ToString(); context.HttpContext.Response.Headers[HeaderNames.Expires] = HeaderUtilities.FormatDate(utcNow); Assert.False(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.ExpirationMaxAgeExceeded); } [Fact] public void IsCachedEntryFresh_SharedMaxAgeOverridesMaxAge_ToFresh() { var utcNow = DateTimeOffset.UtcNow; var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.CachedEntryAge = TimeSpan.FromSeconds(11); context.ResponseTime = utcNow + context.CachedEntryAge; context.CachedResponseHeaders = new HeaderDictionary(); context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { 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); } [Fact] public void IsCachedEntryFresh_SharedMaxAgeOverridesMaxAge_ToNotFresh() { var utcNow = DateTimeOffset.UtcNow; var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.CachedEntryAge = TimeSpan.FromSeconds(5); context.ResponseTime = utcNow + context.CachedEntryAge; context.CachedResponseHeaders = new HeaderDictionary(); context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { 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( sink.Writes, LoggedMessage.ExpirationSharedMaxAgeExceeded); } [Fact] public void IsCachedEntryFresh_MinFreshReducesFreshness_ToNotFresh() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Request.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { MinFresh = TimeSpan.FromSeconds(2) }.ToString(); context.CachedResponseHeaders = new HeaderDictionary(); context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(10), SharedMaxAge = TimeSpan.FromSeconds(5) }.ToString(); context.CachedEntryAge = TimeSpan.FromSeconds(3); Assert.False(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.ExpirationMinFreshAdded, LoggedMessage.ExpirationSharedMaxAgeExceeded); } [Fact] public void IsCachedEntryFresh_RequestMaxAgeRestrictAge_ToNotFresh() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Request.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(5) }.ToString(); context.CachedResponseHeaders = new HeaderDictionary(); context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(10), }.ToString(); context.CachedEntryAge = TimeSpan.FromSeconds(5); Assert.False(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.ExpirationMaxAgeExceeded); } [Fact] public void IsCachedEntryFresh_MaxStaleOverridesFreshness_ToFresh() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); 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) }.ToString(); context.CachedResponseHeaders = new HeaderDictionary(); context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(5), }.ToString(); context.CachedEntryAge = TimeSpan.FromSeconds(6); Assert.True(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.ExpirationMaxStaleSatisfied); } [Fact] public void IsCachedEntryFresh_MaxStaleInfiniteOverridesFreshness_ToFresh() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); context.HttpContext.Request.Headers[HeaderNames.CacheControl] = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(5), MaxStale = true // No value specified means a MaxStaleLimit of infinity }.ToString(); context.CachedResponseHeaders = new HeaderDictionary(); context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(5), }.ToString(); context.CachedEntryAge = TimeSpan.FromSeconds(6); Assert.True(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.ExpirationInfiniteMaxStaleSatisfied); } [Fact] public void IsCachedEntryFresh_MaxStaleOverridesFreshness_ButStillNotFresh() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); 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) }.ToString(); context.CachedResponseHeaders = new HeaderDictionary(); context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(5), }.ToString(); context.CachedEntryAge = TimeSpan.FromSeconds(6); Assert.False(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.ExpirationMaxAgeExceeded); } [Fact] public void IsCachedEntryFresh_MustRevalidateOverridesRequestMaxStale_ToNotFresh() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); 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) }.ToString(); context.CachedResponseHeaders = new HeaderDictionary(); context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(5), MustRevalidate = true }.ToString(); context.CachedEntryAge = TimeSpan.FromSeconds(6); Assert.False(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.ExpirationMustRevalidate); } [Fact] public void IsCachedEntryFresh_ProxyRevalidateOverridesRequestMaxStale_ToNotFresh() { var sink = new TestSink(); var context = TestUtils.CreateTestContext(sink); 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) }.ToString(); context.CachedResponseHeaders = new HeaderDictionary(); context.CachedResponseHeaders[HeaderNames.CacheControl] = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(5), MustRevalidate = true }.ToString(); context.CachedEntryAge = TimeSpan.FromSeconds(6); Assert.False(new ResponseCachingPolicyProvider().IsCachedEntryFresh(context)); TestUtils.AssertLoggedMessages( sink.Writes, LoggedMessage.ExpirationMustRevalidate); } } }