From 4f61c65931de17c1d4a7312c1017ba9368a646df Mon Sep 17 00:00:00 2001 From: John Luo Date: Tue, 23 Aug 2016 17:11:18 -0700 Subject: [PATCH] Update no-store behaviour --- .../ResponseCachingContext.cs | 4 +- .../ResponseCachingContextTests.cs | 45 +++++++++-- .../ResponseCachingTests.cs | 76 +++++++++++++++++++ 3 files changed, 116 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingContext.cs b/src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingContext.cs index 4eb18dc0af..54f6384d4b 100644 --- a/src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingContext.cs +++ b/src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingContext.cs @@ -212,7 +212,7 @@ namespace Microsoft.AspNetCore.ResponseCaching // TODO: no-cache requests can be retrieved upon validation with origin if (!string.IsNullOrEmpty(request.Headers[HeaderNames.CacheControl])) { - if (RequestCacheControl.NoCache || RequestCacheControl.NoStore) + if (RequestCacheControl.NoCache) { return false; } @@ -244,7 +244,7 @@ namespace Microsoft.AspNetCore.ResponseCaching } // Check no-store - if (ResponseCacheControl.NoStore) + if (RequestCacheControl.NoStore || ResponseCacheControl.NoStore) { return false; } diff --git a/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingContextTests.cs b/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingContextTests.cs index 067d6c91b5..beeff86721 100644 --- a/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingContextTests.cs +++ b/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingContextTests.cs @@ -57,20 +57,34 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests Assert.False(context.RequestIsCacheable()); } - [Theory] - [InlineData("no-cache")] - [InlineData("no-store")] - [InlineData("no-cache, no-store")] - public void RequestIsCacheable_ExplicitDisablingDirectives_NotAllowed(string directive) + [Fact] + public void RequestIsCacheable_NoCache_NotAllowed() { var httpContext = new DefaultHttpContext(); httpContext.Request.Method = "GET"; - httpContext.Request.Headers[HeaderNames.CacheControl] = directive; + httpContext.Request.GetTypedHeaders().CacheControl = new CacheControlHeaderValue() + { + NoCache = true + }; var context = new ResponseCachingContext(httpContext, new TestResponseCache()); Assert.False(context.RequestIsCacheable()); } + [Fact] + public void RequestIsCacheable_NoStore_Allowed() + { + var httpContext = new DefaultHttpContext(); + httpContext.Request.Method = "GET"; + httpContext.Request.GetTypedHeaders().CacheControl = new CacheControlHeaderValue() + { + NoStore = true + }; + var context = new ResponseCachingContext(httpContext, new TestResponseCache()); + + Assert.True(context.RequestIsCacheable()); + } + [Fact] public void RequestIsCacheable_LegacyDirectives_NotAllowed() { @@ -162,7 +176,24 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests } [Fact] - public void ResponseIsCacheable_NoStore_NotAllowed() + public void ResponseIsCacheable_RequestNoStore_NotAllowed() + { + var httpContext = new DefaultHttpContext(); + httpContext.Request.GetTypedHeaders().CacheControl = new CacheControlHeaderValue() + { + NoStore = true + }; + httpContext.Response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue() + { + Public = true + }; + var context = new ResponseCachingContext(httpContext, new TestResponseCache()); + + Assert.False(context.ResponseIsCacheable()); + } + + [Fact] + public void ResponseIsCacheable_ResponseNoStore_NotAllowed() { var httpContext = new DefaultHttpContext(); httpContext.Response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue() diff --git a/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingTests.cs b/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingTests.cs index c21c2dab11..9c700e6917 100644 --- a/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingTests.cs +++ b/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingTests.cs @@ -347,6 +347,82 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests } } + [Fact] + public async void ServesCachedContentIfSubsequentRequestContainsNoStore() + { + var builder = CreateBuilderWithResponseCaching( + async (context) => + { + var uniqueId = Guid.NewGuid().ToString(); + var headers = context.Response.GetTypedHeaders(); + headers.CacheControl = new CacheControlHeaderValue() + { + Public = true, + MaxAge = TimeSpan.FromSeconds(10) + }; + headers.Date = DateTimeOffset.UtcNow; + headers.Headers["X-Value"] = uniqueId; + await context.Response.WriteAsync(uniqueId); + }); + + using (var server = new TestServer(builder)) + { + var client = server.CreateClient(); + var initialResponse = await client.GetAsync(""); + client.DefaultRequestHeaders.CacheControl = new System.Net.Http.Headers.CacheControlHeaderValue() + { + NoStore = true + }; + var subsequentResponse = await client.GetAsync(""); + + initialResponse.EnsureSuccessStatusCode(); + subsequentResponse.EnsureSuccessStatusCode(); + + foreach (var header in initialResponse.Headers) + { + Assert.Equal(initialResponse.Headers.GetValues(header.Key), subsequentResponse.Headers.GetValues(header.Key)); + } + Assert.True(subsequentResponse.Headers.Contains(HeaderNames.Age)); + Assert.Equal(await initialResponse.Content.ReadAsStringAsync(), await subsequentResponse.Content.ReadAsStringAsync()); + } + } + + [Fact] + public async void ServesFreshContentIfInitialRequestContainsNoStore() + { + var builder = CreateBuilderWithResponseCaching( + async (context) => + { + var uniqueId = Guid.NewGuid().ToString(); + var headers = context.Response.GetTypedHeaders(); + headers.CacheControl = new CacheControlHeaderValue() + { + Public = true, + MaxAge = TimeSpan.FromSeconds(10) + }; + headers.Date = DateTimeOffset.UtcNow; + headers.Headers["X-Value"] = uniqueId; + await context.Response.WriteAsync(uniqueId); + }); + + using (var server = new TestServer(builder)) + { + var client = server.CreateClient(); + client.DefaultRequestHeaders.CacheControl = new System.Net.Http.Headers.CacheControlHeaderValue() + { + NoStore = true + }; + var initialResponse = await client.GetAsync(""); + var subsequentResponse = await client.GetAsync(""); + + initialResponse.EnsureSuccessStatusCode(); + subsequentResponse.EnsureSuccessStatusCode(); + + Assert.False(subsequentResponse.Headers.Contains(HeaderNames.Age)); + Assert.NotEqual(await initialResponse.Content.ReadAsStringAsync(), await subsequentResponse.Content.ReadAsStringAsync()); + } + } + private static IWebHostBuilder CreateBuilderWithResponseCaching(RequestDelegate requestDelegate) => CreateBuilderWithResponseCaching(app => { }, requestDelegate);