Merge branch 'rel/2.0.0' into dev
This commit is contained in:
commit
d5430992d3
|
|
@ -0,0 +1,88 @@
|
||||||
|
// 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 Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.ResponseCaching.Internal
|
||||||
|
{
|
||||||
|
internal static class CacheEntryHelpers
|
||||||
|
{
|
||||||
|
|
||||||
|
internal static long EstimateCachedResponseSize(CachedResponse cachedResponse)
|
||||||
|
{
|
||||||
|
if (cachedResponse == null)
|
||||||
|
{
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
checked
|
||||||
|
{
|
||||||
|
// StatusCode
|
||||||
|
long size = sizeof(int);
|
||||||
|
|
||||||
|
// Headers
|
||||||
|
if (cachedResponse.Headers != null)
|
||||||
|
{
|
||||||
|
foreach (var item in cachedResponse.Headers)
|
||||||
|
{
|
||||||
|
size += item.Key.Length * sizeof(char) + EstimateStringValuesSize(item.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Body
|
||||||
|
if (cachedResponse.Body != null)
|
||||||
|
{
|
||||||
|
size += cachedResponse.Body.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static long EstimateCachedVaryByRulesySize(CachedVaryByRules cachedVaryByRules)
|
||||||
|
{
|
||||||
|
if (cachedVaryByRules == null)
|
||||||
|
{
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
checked
|
||||||
|
{
|
||||||
|
var size = 0L;
|
||||||
|
|
||||||
|
// VaryByKeyPrefix
|
||||||
|
if (!string.IsNullOrEmpty(cachedVaryByRules.VaryByKeyPrefix))
|
||||||
|
{
|
||||||
|
size = cachedVaryByRules.VaryByKeyPrefix.Length * sizeof(char);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Headers
|
||||||
|
size += EstimateStringValuesSize(cachedVaryByRules.Headers);
|
||||||
|
|
||||||
|
// QueryKeys
|
||||||
|
size += EstimateStringValuesSize(cachedVaryByRules.QueryKeys);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static long EstimateStringValuesSize(StringValues stringValues)
|
||||||
|
{
|
||||||
|
checked
|
||||||
|
{
|
||||||
|
var size = 0L;
|
||||||
|
|
||||||
|
for (var i = 0; i < stringValues.Count; i++)
|
||||||
|
{
|
||||||
|
var stringValue = stringValues[i];
|
||||||
|
if (!string.IsNullOrEmpty(stringValue))
|
||||||
|
{
|
||||||
|
size += stringValues[i].Length * sizeof(char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -67,7 +67,8 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal
|
||||||
},
|
},
|
||||||
new MemoryCacheEntryOptions
|
new MemoryCacheEntryOptions
|
||||||
{
|
{
|
||||||
AbsoluteExpirationRelativeToNow = validFor
|
AbsoluteExpirationRelativeToNow = validFor,
|
||||||
|
Size = CacheEntryHelpers.EstimateCachedResponseSize(cachedResponse)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -77,7 +78,8 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal
|
||||||
entry,
|
entry,
|
||||||
new MemoryCacheEntryOptions
|
new MemoryCacheEntryOptions
|
||||||
{
|
{
|
||||||
AbsoluteExpirationRelativeToNow = validFor
|
AbsoluteExpirationRelativeToNow = validFor,
|
||||||
|
Size = CacheEntryHelpers.EstimateCachedVaryByRulesySize(entry as CachedVaryByRules)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.ResponseCaching.Internal;
|
using Microsoft.AspNetCore.ResponseCaching.Internal;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
@ -26,6 +27,24 @@ namespace Microsoft.AspNetCore.ResponseCaching
|
||||||
private readonly IResponseCachingKeyProvider _keyProvider;
|
private readonly IResponseCachingKeyProvider _keyProvider;
|
||||||
|
|
||||||
public ResponseCachingMiddleware(
|
public ResponseCachingMiddleware(
|
||||||
|
RequestDelegate next,
|
||||||
|
IOptions<ResponseCachingOptions> options,
|
||||||
|
ILoggerFactory loggerFactory,
|
||||||
|
IResponseCachingPolicyProvider policyProvider,
|
||||||
|
IResponseCachingKeyProvider keyProvider)
|
||||||
|
: this(
|
||||||
|
next,
|
||||||
|
options,
|
||||||
|
loggerFactory,
|
||||||
|
policyProvider,
|
||||||
|
new MemoryResponseCache(new MemoryCache(new MemoryCacheOptions
|
||||||
|
{
|
||||||
|
SizeLimit = options.Value.SizeLimit
|
||||||
|
})), keyProvider)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
// for testing
|
||||||
|
internal ResponseCachingMiddleware(
|
||||||
RequestDelegate next,
|
RequestDelegate next,
|
||||||
IOptions<ResponseCachingOptions> options,
|
IOptions<ResponseCachingOptions> options,
|
||||||
ILoggerFactory loggerFactory,
|
ILoggerFactory loggerFactory,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,11 @@ namespace Microsoft.AspNetCore.ResponseCaching
|
||||||
{
|
{
|
||||||
public class ResponseCachingOptions
|
public class ResponseCachingOptions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The size limit for the response cache middleware in bytes. The default is set to 100 MB.
|
||||||
|
/// </summary>
|
||||||
|
public long SizeLimit { get; set; } = 100 * 1024 * 1024;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The largest cacheable size for the response body in bytes. The default is set to 64 MB.
|
/// The largest cacheable size for the response body in bytes. The default is set to 64 MB.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
services.AddMemoryCache();
|
services.AddMemoryCache();
|
||||||
services.TryAdd(ServiceDescriptor.Singleton<IResponseCachingPolicyProvider, ResponseCachingPolicyProvider>());
|
services.TryAdd(ServiceDescriptor.Singleton<IResponseCachingPolicyProvider, ResponseCachingPolicyProvider>());
|
||||||
services.TryAdd(ServiceDescriptor.Singleton<IResponseCachingKeyProvider, ResponseCachingKeyProvider>());
|
services.TryAdd(ServiceDescriptor.Singleton<IResponseCachingKeyProvider, ResponseCachingKeyProvider>());
|
||||||
services.TryAdd(ServiceDescriptor.Singleton<IResponseCache, MemoryResponseCache>());
|
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.ResponseCaching.Internal;
|
using Microsoft.AspNetCore.ResponseCaching.Internal;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.Logging.Testing;
|
using Microsoft.Extensions.Logging.Testing;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
@ -823,6 +824,38 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
|
||||||
LoggedMessage.ResponseNotCached);
|
LoggedMessage.ResponseNotCached);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task FinalizeCacheBody_DoNotCache_IfSizeTooBig()
|
||||||
|
{
|
||||||
|
var sink = new TestSink();
|
||||||
|
var middleware = TestUtils.CreateTestMiddleware(
|
||||||
|
testSink: sink,
|
||||||
|
keyProvider: new TestResponseCachingKeyProvider("BaseKey"),
|
||||||
|
cache: new MemoryResponseCache(new MemoryCache(new MemoryCacheOptions
|
||||||
|
{
|
||||||
|
SizeLimit = 100
|
||||||
|
})));
|
||||||
|
var context = TestUtils.CreateTestContext();
|
||||||
|
|
||||||
|
context.ShouldCacheResponse = true;
|
||||||
|
middleware.ShimResponseStream(context);
|
||||||
|
|
||||||
|
await context.HttpContext.Response.WriteAsync(new string('0', 101));
|
||||||
|
|
||||||
|
context.CachedResponse = new CachedResponse() { Headers = new HeaderDictionary() };
|
||||||
|
context.CachedResponseValidFor = TimeSpan.FromSeconds(10);
|
||||||
|
|
||||||
|
await middleware.FinalizeCacheBodyAsync(context);
|
||||||
|
|
||||||
|
// The response cached message will be logged but the adding of the entry will no-op
|
||||||
|
TestUtils.AssertLoggedMessages(
|
||||||
|
sink.Writes,
|
||||||
|
LoggedMessage.ResponseCached);
|
||||||
|
|
||||||
|
// The entry cannot be retrieved
|
||||||
|
Assert.False(await middleware.TryServeFromCacheAsync(context));
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AddResponseCachingFeature_SecondInvocation_Throws()
|
public void AddResponseCachingFeature_SecondInvocation_Throws()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue