From 411681e8d62d954825ff34d5443e3245ade93e38 Mon Sep 17 00:00:00 2001 From: John Luo Date: Fri, 2 Sep 2016 16:03:07 -0700 Subject: [PATCH] Add options to configure case sensitivity of request paths --- .../ResponseCachingContext.cs | 10 ++--- .../ResponseCachingOptions.cs | 7 ++- .../ResponseCachingContextTests.cs | 43 ++++++++++++++++++- 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingContext.cs b/src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingContext.cs index 2733172..142ded2 100644 --- a/src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingContext.cs +++ b/src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingContext.cs @@ -44,7 +44,7 @@ internal class ResponseCachingContext // Internal for testing internal ResponseCachingContext( - HttpContext httpContext, + HttpContext httpContext, IResponseCache cache, ResponseCachingOptions options, ObjectPool builderPool, @@ -149,11 +149,11 @@ internal string CreateCacheKey(CachedVaryBy varyBy) .Append(KeyDelimiter); } - // Default key + // Default key builder .Append(request.Method.ToUpperInvariant()) .Append(KeyDelimiter) - .Append(request.Path.Value.ToUpperInvariant()); + .Append(_options.CaseSensitivePaths ? request.Path.Value : request.Path.Value.ToUpperInvariant()); // Vary by headers if (varyBy?.Headers.Count > 0) @@ -337,7 +337,7 @@ internal bool ResponseIsCacheable() } // TODO: public MAY override the cacheability checks for private and status codes - + // Check private if (ResponseCacheControl.Private) { @@ -365,7 +365,7 @@ internal bool ResponseIsCacheable() internal bool EntryIsFresh(ResponseHeaders responseHeaders, TimeSpan age, bool verifyAgainstRequest) { var responseCacheControl = responseHeaders.CacheControl ?? EmptyCacheControl; - + // Add min-fresh requirements if (verifyAgainstRequest) { diff --git a/src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingOptions.cs b/src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingOptions.cs index 753b9b6..fefe65c 100644 --- a/src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingOptions.cs +++ b/src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingOptions.cs @@ -9,10 +9,15 @@ namespace Microsoft.AspNetCore.Builder public class ResponseCachingOptions { /// - /// The largest cacheable size for the response body in bytes. + /// The largest cacheable size for the response body in bytes. The default is set to 1 MB. /// public long MaximumCachedBodySize { get; set; } = 1024 * 1024; + /// + /// true if request paths are case-sensitive; otherwise false. The default is to treat paths as case-insensitive. + /// + public bool CaseSensitivePaths { get; set; } = false; + /// /// For testing purposes only. /// diff --git a/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingContextTests.cs b/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingContextTests.cs index 2662c56..1227eb9 100644 --- a/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingContextTests.cs +++ b/test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingContextTests.cs @@ -280,6 +280,34 @@ public void CreateCacheKey_CacheKeyModifier_AddsPrefix() })); } + [Fact] + public void CreateCacheKey_CaseInsensitivePath_NormalizesPath() + { + var httpContext = new DefaultHttpContext(); + httpContext.Request.Method = "GET"; + httpContext.Request.Path = "/Path"; + var context = CreateTestContext(httpContext, new ResponseCachingOptions() + { + CaseSensitivePaths = false + }); + + Assert.Equal($"GET{KeyDelimiter}/PATH", context.CreateCacheKey()); + } + + [Fact] + public void CreateCacheKey_CaseSensitivePath_PreservesPathCase() + { + var httpContext = new DefaultHttpContext(); + httpContext.Request.Method = "GET"; + httpContext.Request.Path = "/Path"; + var context = CreateTestContext(httpContext, new ResponseCachingOptions() + { + CaseSensitivePaths = true + }); + + Assert.Equal($"GET{KeyDelimiter}/Path", context.CreateCacheKey()); + } + [Fact] public void ResponseIsCacheable_NoPublic_NotAllowed() { @@ -832,6 +860,16 @@ private static ResponseCachingContext CreateTestContext(HttpContext httpContext) { return CreateTestContext( httpContext, + new ResponseCachingOptions(), + new NoopCacheKeyModifier(), + new NoopCacheabilityValidator()); + } + + private static ResponseCachingContext CreateTestContext(HttpContext httpContext, ResponseCachingOptions options) + { + return CreateTestContext( + httpContext, + options, new NoopCacheKeyModifier(), new NoopCacheabilityValidator()); } @@ -840,6 +878,7 @@ private static ResponseCachingContext CreateTestContext(HttpContext httpContext, { return CreateTestContext( httpContext, + new ResponseCachingOptions(), cacheKeyModifier, new NoopCacheabilityValidator()); } @@ -848,19 +887,21 @@ private static ResponseCachingContext CreateTestContext(HttpContext httpContext, { return CreateTestContext( httpContext, + new ResponseCachingOptions(), new NoopCacheKeyModifier(), cacheabilityValidator); } private static ResponseCachingContext CreateTestContext( HttpContext httpContext, + ResponseCachingOptions options, IResponseCachingCacheKeyModifier cacheKeyModifier, IResponseCachingCacheabilityValidator cacheabilityValidator) { return new ResponseCachingContext( httpContext, new TestResponseCache(), - new ResponseCachingOptions(), + options, new DefaultObjectPool(new StringBuilderPooledObjectPolicy()), cacheabilityValidator, cacheKeyModifier);