Make IResponseCacheStore APIs async

This commit is contained in:
John Luo 2016-09-16 11:39:53 -07:00
parent fbac81a471
commit c30d471c27
6 changed files with 75 additions and 67 deletions

View File

@ -2,13 +2,14 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.ResponseCaching
{
public interface IResponseCacheStore
{
object Get(string key);
void Set(string key, object entry, TimeSpan validFor);
void Remove(string key);
Task<object> GetAsync(string key);
Task SetAsync(string key, object entry, TimeSpan validFor);
Task RemoveAsync(string key);
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
namespace Microsoft.AspNetCore.ResponseCaching.Internal
@ -20,11 +21,11 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal
_cache = cache;
}
public object Get(string key)
public async Task<object> GetAsync(string key)
{
try
{
return CacheEntrySerializer.Deserialize(_cache.Get(key));
return CacheEntrySerializer.Deserialize(await _cache.GetAsync(key));
}
catch
{
@ -32,20 +33,20 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal
}
}
public void Remove(string key)
public async Task RemoveAsync(string key)
{
try
{
_cache.Remove(key);
await _cache.RemoveAsync(key);
}
catch { }
}
public void Set(string key, object entry, TimeSpan validFor)
public async Task SetAsync(string key, object entry, TimeSpan validFor)
{
try
{
_cache.Set(
await _cache.SetAsync(
key,
CacheEntrySerializer.Serialize(entry),
new DistributedCacheEntryOptions()

View File

@ -2,7 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.ResponseCaching.Internal
{
@ -20,17 +22,18 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal
_cache = cache;
}
public object Get(string key)
public Task<object> GetAsync(string key)
{
return _cache.Get(key);
return Task.FromResult(_cache.Get(key));
}
public void Remove(string key)
public Task RemoveAsync(string key)
{
_cache.Remove(key);
return TaskCache.CompletedTask;
}
public void Set(string key, object entry, TimeSpan validFor)
public Task SetAsync(string key, object entry, TimeSpan validFor)
{
_cache.Set(
key,
@ -39,6 +42,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal
{
AbsoluteExpirationRelativeToNow = validFor
});
return TaskCache.CompletedTask;
}
}
}

View File

@ -60,11 +60,7 @@ namespace Microsoft.AspNetCore.ResponseCaching
_options = options.Value;
_policyProvider = policyProvider;
_keyProvider = keyProvider;
_onStartingCallback = state =>
{
OnResponseStarting((ResponseCacheContext)state);
return TaskCache.CompletedTask;
};
_onStartingCallback = state => OnResponseStartingAsync((ResponseCacheContext)state);
}
public async Task Invoke(HttpContext httpContext)
@ -91,10 +87,10 @@ namespace Microsoft.AspNetCore.ResponseCaching
await _next(httpContext);
// If there was no response body, check the response headers now. We can cache things like redirects.
OnResponseStarting(context);
await OnResponseStartingAsync(context);
// Finalize the cache entry
FinalizeCacheBody(context);
await FinalizeCacheBodyAsync(context);
}
finally
{
@ -135,7 +131,7 @@ namespace Microsoft.AspNetCore.ResponseCaching
response.Headers[HeaderNames.Age] = context.CachedEntryAge.TotalSeconds.ToString("F0", CultureInfo.InvariantCulture);
var body = context.CachedResponse.Body ??
((CachedResponseBody)_store.Get(context.CachedResponse.BodyKeyPrefix))?.Body;
((CachedResponseBody) await _store.GetAsync(context.CachedResponse.BodyKeyPrefix))?.Body;
// If the body is not found, something went wrong.
if (body == null)
@ -164,7 +160,7 @@ namespace Microsoft.AspNetCore.ResponseCaching
internal async Task<bool> TryServeFromCacheAsync(ResponseCacheContext context)
{
context.BaseKey = _keyProvider.CreateBaseKey(context);
var cacheEntry = _store.Get(context.BaseKey);
var cacheEntry = await _store.GetAsync(context.BaseKey);
if (cacheEntry is CachedVaryByRules)
{
@ -173,7 +169,7 @@ namespace Microsoft.AspNetCore.ResponseCaching
foreach (var varyKey in _keyProvider.CreateLookupVaryByKeys(context))
{
cacheEntry = _store.Get(varyKey);
cacheEntry = await _store.GetAsync(varyKey);
if (cacheEntry is CachedResponse && await TryServeCachedResponseAsync(context, (CachedResponse)cacheEntry))
{
@ -196,7 +192,7 @@ namespace Microsoft.AspNetCore.ResponseCaching
return false;
}
internal void FinalizeCacheHeaders(ResponseCacheContext context)
internal async Task FinalizeCacheHeadersAsync(ResponseCacheContext context)
{
if (_policyProvider.IsResponseCacheable(context))
{
@ -232,7 +228,7 @@ namespace Microsoft.AspNetCore.ResponseCaching
}
// Always overwrite the CachedVaryByRules to update the expiry information
_store.Set(context.BaseKey, context.CachedVaryByRules, context.CachedResponseValidFor);
await _store.SetAsync(context.BaseKey, context.CachedVaryByRules, context.CachedResponseValidFor);
context.StorageVaryKey = _keyProvider.CreateStorageVaryByKey(context);
}
@ -265,7 +261,7 @@ namespace Microsoft.AspNetCore.ResponseCaching
}
}
internal void FinalizeCacheBody(ResponseCacheContext context)
internal async Task FinalizeCacheBodyAsync(ResponseCacheContext context)
{
if (context.ShouldCacheResponse &&
context.ResponseCacheStream.BufferingEnabled &&
@ -275,32 +271,36 @@ namespace Microsoft.AspNetCore.ResponseCaching
if (context.ResponseCacheStream.BufferedStream.Length >= _options.MinimumSplitBodySize)
{
// Store response and response body separately
_store.Set(context.StorageVaryKey ?? context.BaseKey, context.CachedResponse, context.CachedResponseValidFor);
await _store.SetAsync(context.StorageVaryKey ?? context.BaseKey, context.CachedResponse, context.CachedResponseValidFor);
var cachedResponseBody = new CachedResponseBody()
{
Body = context.ResponseCacheStream.BufferedStream.ToArray()
};
_store.Set(context.CachedResponse.BodyKeyPrefix, cachedResponseBody, context.CachedResponseValidFor);
await _store.SetAsync(context.CachedResponse.BodyKeyPrefix, cachedResponseBody, context.CachedResponseValidFor);
}
else
{
// Store response and response body together
context.CachedResponse.Body = context.ResponseCacheStream.BufferedStream.ToArray();
_store.Set(context.StorageVaryKey ?? context.BaseKey, context.CachedResponse, context.CachedResponseValidFor);
await _store.SetAsync(context.StorageVaryKey ?? context.BaseKey, context.CachedResponse, context.CachedResponseValidFor);
}
}
}
internal void OnResponseStarting(ResponseCacheContext context)
internal Task OnResponseStartingAsync(ResponseCacheContext context)
{
if (!context.ResponseStarted)
{
context.ResponseStarted = true;
context.ResponseTime = _options.SystemClock.UtcNow;
FinalizeCacheHeaders(context);
return FinalizeCacheHeadersAsync(context);
}
else
{
return TaskCache.CompletedTask;
}
}

View File

@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
var middleware = TestUtils.CreateTestMiddleware(store: store, keyProvider: new TestResponseCacheKeyProvider("BaseKey"));
var context = TestUtils.CreateTestContext();
store.Set(
await store.SetAsync(
"BaseKey",
new CachedResponse()
{
@ -68,7 +68,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
var middleware = TestUtils.CreateTestMiddleware(store: store, keyProvider: new TestResponseCacheKeyProvider("BaseKey"));
var context = TestUtils.CreateTestContext();
store.Set(
await store.SetAsync(
"BaseKey",
new CachedVaryByRules(),
TimeSpan.Zero);
@ -84,11 +84,11 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
var middleware = TestUtils.CreateTestMiddleware(store: store, keyProvider: new TestResponseCacheKeyProvider("BaseKey", new[] { "VaryKey", "VaryKey2" }));
var context = TestUtils.CreateTestContext();
store.Set(
await store.SetAsync(
"BaseKey",
new CachedVaryByRules(),
TimeSpan.Zero);
store.Set(
await store.SetAsync(
"BaseKeyVaryKey2",
new CachedResponse()
{
@ -226,7 +226,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
}
[Fact]
public void FinalizeCacheHeaders_DoNotUpdateShouldCacheResponse_IfResponseIsNotCacheable()
public async Task FinalizeCacheHeaders_DoNotUpdateShouldCacheResponse_IfResponseIsNotCacheable()
{
var middleware = TestUtils.CreateTestMiddleware(policyProvider: new ResponseCachePolicyProvider());
var context = TestUtils.CreateTestContext();
@ -234,13 +234,13 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
Assert.False(context.ShouldCacheResponse);
middleware.ShimResponseStream(context);
middleware.FinalizeCacheHeaders(context);
await middleware.FinalizeCacheHeadersAsync(context);
Assert.False(context.ShouldCacheResponse);
}
[Fact]
public void FinalizeCacheHeaders_UpdateShouldCacheResponse_IfResponseIsCacheable()
public async Task FinalizeCacheHeaders_UpdateShouldCacheResponse_IfResponseIsCacheable()
{
var middleware = TestUtils.CreateTestMiddleware(policyProvider: new ResponseCachePolicyProvider());
var context = TestUtils.CreateTestContext();
@ -251,24 +251,24 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
Assert.False(context.ShouldCacheResponse);
middleware.FinalizeCacheHeaders(context);
await middleware.FinalizeCacheHeadersAsync(context);
Assert.True(context.ShouldCacheResponse);
}
[Fact]
public void FinalizeCacheHeaders_DefaultResponseValidity_Is10Seconds()
public async Task FinalizeCacheHeaders_DefaultResponseValidity_Is10Seconds()
{
var middleware = TestUtils.CreateTestMiddleware();
var context = TestUtils.CreateTestContext();
middleware.FinalizeCacheHeaders(context);
await middleware.FinalizeCacheHeadersAsync(context);
Assert.Equal(TimeSpan.FromSeconds(10), context.CachedResponseValidFor);
}
[Fact]
public void FinalizeCacheHeaders_ResponseValidity_UseExpiryIfAvailable()
public async Task FinalizeCacheHeaders_ResponseValidity_UseExpiryIfAvailable()
{
var utcNow = DateTimeOffset.MinValue;
var middleware = TestUtils.CreateTestMiddleware();
@ -277,13 +277,13 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
context.ResponseTime = utcNow;
context.TypedResponseHeaders.Expires = utcNow + TimeSpan.FromSeconds(11);
middleware.FinalizeCacheHeaders(context);
await middleware.FinalizeCacheHeadersAsync(context);
Assert.Equal(TimeSpan.FromSeconds(11), context.CachedResponseValidFor);
}
[Fact]
public void FinalizeCacheHeaders_ResponseValidity_UseMaxAgeIfAvailable()
public async Task FinalizeCacheHeaders_ResponseValidity_UseMaxAgeIfAvailable()
{
var middleware = TestUtils.CreateTestMiddleware();
var context = TestUtils.CreateTestContext();
@ -295,13 +295,13 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
context.ResponseTime = DateTimeOffset.UtcNow;
context.TypedResponseHeaders.Expires = context.ResponseTime + TimeSpan.FromSeconds(11);
middleware.FinalizeCacheHeaders(context);
await middleware.FinalizeCacheHeadersAsync(context);
Assert.Equal(TimeSpan.FromSeconds(12), context.CachedResponseValidFor);
}
[Fact]
public void FinalizeCacheHeaders_ResponseValidity_UseSharedMaxAgeIfAvailable()
public async Task FinalizeCacheHeaders_ResponseValidity_UseSharedMaxAgeIfAvailable()
{
var middleware = TestUtils.CreateTestMiddleware();
var context = TestUtils.CreateTestContext();
@ -314,7 +314,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
context.ResponseTime = DateTimeOffset.UtcNow;
context.TypedResponseHeaders.Expires = context.ResponseTime + TimeSpan.FromSeconds(11);
middleware.FinalizeCacheHeaders(context);
await middleware.FinalizeCacheHeadersAsync(context);
Assert.Equal(TimeSpan.FromSeconds(13), context.CachedResponseValidFor);
}
@ -337,7 +337,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
context.CachedVaryByRules = cachedVaryByRules;
await middleware.TryServeFromCacheAsync(context);
middleware.FinalizeCacheHeaders(context);
await middleware.FinalizeCacheHeadersAsync(context);
Assert.Equal(1, store.SetCount);
Assert.NotSame(cachedVaryByRules, context.CachedVaryByRules);
@ -362,7 +362,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
context.CachedVaryByRules = cachedVaryByRules;
await middleware.TryServeFromCacheAsync(context);
middleware.FinalizeCacheHeaders(context);
await middleware.FinalizeCacheHeadersAsync(context);
// An update to the cache is always made but the entry should be the same
Assert.Equal(1, store.SetCount);
@ -370,7 +370,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
}
[Fact]
public void FinalizeCacheHeaders_DoNotAddDate_IfSpecified()
public async Task FinalizeCacheHeaders_DoNotAddDate_IfSpecified()
{
var utcNow = DateTimeOffset.MinValue;
var middleware = TestUtils.CreateTestMiddleware();
@ -379,13 +379,13 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
Assert.Null(context.TypedResponseHeaders.Date);
middleware.FinalizeCacheHeaders(context);
await middleware.FinalizeCacheHeadersAsync(context);
Assert.Equal(utcNow, context.TypedResponseHeaders.Date);
}
[Fact]
public void FinalizeCacheHeaders_AddsDate_IfNoneSpecified()
public async Task FinalizeCacheHeaders_AddsDate_IfNoneSpecified()
{
var utcNow = DateTimeOffset.MinValue;
var middleware = TestUtils.CreateTestMiddleware();
@ -395,20 +395,20 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
Assert.Equal(utcNow, context.TypedResponseHeaders.Date);
middleware.FinalizeCacheHeaders(context);
await middleware.FinalizeCacheHeadersAsync(context);
Assert.Equal(utcNow, context.TypedResponseHeaders.Date);
}
[Fact]
public void FinalizeCacheHeaders_StoresCachedResponse_InState()
public async Task FinalizeCacheHeaders_StoresCachedResponse_InState()
{
var middleware = TestUtils.CreateTestMiddleware();
var context = TestUtils.CreateTestContext();
Assert.Null(context.CachedResponse);
middleware.FinalizeCacheHeaders(context);
await middleware.FinalizeCacheHeadersAsync(context);
Assert.NotNull(context.CachedResponse);
}
@ -421,7 +421,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
context.HttpContext.Response.Headers[HeaderNames.Vary] = "HeaderB, heaDera";
await middleware.TryServeFromCacheAsync(context);
middleware.FinalizeCacheHeaders(context);
await middleware.FinalizeCacheHeadersAsync(context);
Assert.Equal(new StringValues(new[] { "HEADERA", "HEADERB" }), context.CachedVaryByRules.Headers);
}
@ -444,7 +444,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
context.BaseKey = "BaseKey";
context.CachedResponseValidFor = TimeSpan.FromSeconds(10);
middleware.FinalizeCacheBody(context);
await middleware.FinalizeCacheBodyAsync(context);
Assert.Equal(2, store.SetCount);
}
@ -467,7 +467,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
context.BaseKey = "BaseKey";
context.CachedResponseValidFor = TimeSpan.FromSeconds(10);
middleware.FinalizeCacheBody(context);
await middleware.FinalizeCacheBodyAsync(context);
Assert.Equal(1, store.SetCount);
}
@ -493,7 +493,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
context.BaseKey = "BaseKey";
context.CachedResponseValidFor = TimeSpan.FromSeconds(10);
middleware.FinalizeCacheBody(context);
await middleware.FinalizeCacheBodyAsync(context);
Assert.Equal(1, store.SetCount);
}
@ -517,7 +517,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
context.BaseKey = "BaseKey";
context.CachedResponseValidFor = TimeSpan.FromSeconds(10);
middleware.FinalizeCacheBody(context);
await middleware.FinalizeCacheBodyAsync(context);
Assert.Equal(1, store.SetCount);
}
@ -541,7 +541,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
context.BaseKey = "BaseKey";
context.CachedResponseValidFor = TimeSpan.FromSeconds(10);
middleware.FinalizeCacheBody(context);
await middleware.FinalizeCacheBodyAsync(context);
Assert.Equal(0, store.SetCount);
}
@ -564,7 +564,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
context.BaseKey = "BaseKey";
context.CachedResponseValidFor = TimeSpan.FromSeconds(10);
middleware.FinalizeCacheBody(context);
await middleware.FinalizeCacheBodyAsync(context);
Assert.Equal(1, store.SetCount);
}

View File

@ -168,27 +168,29 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
public int GetCount { get; private set; }
public int SetCount { get; private set; }
public object Get(string key)
public Task<object> GetAsync(string key)
{
GetCount++;
try
{
return _storage[key];
return Task.FromResult(_storage[key]);
}
catch
{
return null;
return Task.FromResult<object>(null);
}
}
public void Remove(string key)
public Task RemoveAsync(string key)
{
return TaskCache.CompletedTask;
}
public void Set(string key, object entry, TimeSpan validFor)
public Task SetAsync(string key, object entry, TimeSpan validFor)
{
SetCount++;
_storage[key] = entry;
return TaskCache.CompletedTask;
}
}
}