Make DistributedResponseCacheStore internal (#61)
Remove DistributedResponseCacheStore and move all interfaces to the Internal namespace
This commit is contained in:
parent
24385e74c4
commit
aa52e66585
|
|
@ -15,7 +15,7 @@ namespace ResponseCachingSample
|
|||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddDistributedResponseCacheStore();
|
||||
services.AddMemoryResponseCacheStore();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
|
|
@ -23,7 +23,6 @@ namespace ResponseCachingSample
|
|||
app.UseResponseCache();
|
||||
app.Run(async (context) =>
|
||||
{
|
||||
// These settings should be configured by context.Response.Cache.*
|
||||
context.Response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue()
|
||||
{
|
||||
Public = true,
|
||||
|
|
|
|||
|
|
@ -18,29 +18,9 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
}
|
||||
|
||||
services.AddMemoryCache();
|
||||
services.AddResponseCacheServices();
|
||||
services.TryAdd(ServiceDescriptor.Singleton<IResponseCacheStore, MemoryResponseCacheStore>());
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddDistributedResponseCacheStore(this IServiceCollection services)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
|
||||
services.AddDistributedMemoryCache();
|
||||
services.AddResponseCacheServices();
|
||||
services.TryAdd(ServiceDescriptor.Singleton<IResponseCacheStore, DistributedResponseCacheStore>());
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private static IServiceCollection AddResponseCacheServices(this IServiceCollection services)
|
||||
{
|
||||
services.TryAdd(ServiceDescriptor.Singleton<IResponseCachePolicyProvider, ResponseCachePolicyProvider>());
|
||||
services.TryAdd(ServiceDescriptor.Singleton<IResponseCacheKeyProvider, ResponseCacheKeyProvider>());
|
||||
services.TryAdd(ServiceDescriptor.Singleton<IResponseCacheStore, MemoryResponseCacheStore>());
|
||||
|
||||
return services;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ using System;
|
|||
using System.IO;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.ResponseCaching
|
||||
namespace Microsoft.AspNetCore.ResponseCaching.Internal
|
||||
{
|
||||
public class CachedResponse : IResponseCacheEntry
|
||||
{
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.AspNetCore.ResponseCaching
|
||||
namespace Microsoft.AspNetCore.ResponseCaching.Internal
|
||||
{
|
||||
public class CachedVaryByRules : IResponseCacheEntry
|
||||
{
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
// 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 System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
|
||||
namespace Microsoft.AspNetCore.ResponseCaching.Internal
|
||||
{
|
||||
public class DistributedResponseCacheStore : IResponseCacheStore
|
||||
{
|
||||
private readonly IDistributedCache _cache;
|
||||
|
||||
public DistributedResponseCacheStore(IDistributedCache cache)
|
||||
{
|
||||
if (cache == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(cache));
|
||||
}
|
||||
|
||||
_cache = cache;
|
||||
}
|
||||
|
||||
public async Task<IResponseCacheEntry> GetAsync(string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ResponseCacheEntrySerializer.Deserialize(await _cache.GetAsync(key));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SetAsync(string key, IResponseCacheEntry entry, TimeSpan validFor)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _cache.SetAsync(
|
||||
key,
|
||||
ResponseCacheEntrySerializer.Serialize(entry),
|
||||
new DistributedCacheEntryOptions()
|
||||
{
|
||||
AbsoluteExpirationRelativeToNow = validFor
|
||||
});
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -54,7 +54,6 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal
|
|||
|
||||
private static unsafe string GenerateGuidString(FastGuid guid)
|
||||
{
|
||||
|
||||
// stackalloc to allocate array on stack rather than heap
|
||||
char* charBuffer = stackalloc char[13];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.ResponseCaching
|
||||
namespace Microsoft.AspNetCore.ResponseCaching.Internal
|
||||
{
|
||||
public interface IResponseCacheEntry
|
||||
{
|
||||
|
|
@ -5,7 +5,7 @@ using System.Collections.Generic;
|
|||
|
||||
namespace Microsoft.AspNetCore.ResponseCaching.Internal
|
||||
{
|
||||
internal interface IResponseCacheKeyProvider
|
||||
public interface IResponseCacheKeyProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a base key for a response cache entry.
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.ResponseCaching
|
||||
namespace Microsoft.AspNetCore.ResponseCaching.Internal
|
||||
{
|
||||
public interface IResponseCachePolicyProvider
|
||||
{
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.ResponseCaching
|
||||
namespace Microsoft.AspNetCore.ResponseCaching.Internal
|
||||
{
|
||||
public interface IResponseCacheStore
|
||||
{
|
||||
|
|
@ -6,10 +6,9 @@ using System.IO;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Http.Headers;
|
||||
using Microsoft.AspNetCore.ResponseCaching.Internal;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Microsoft.AspNetCore.ResponseCaching
|
||||
namespace Microsoft.AspNetCore.ResponseCaching.Internal
|
||||
{
|
||||
public class ResponseCacheContext
|
||||
{
|
||||
|
|
@ -1,249 +0,0 @@
|
|||
// 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 System.IO;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.ResponseCaching.Internal
|
||||
{
|
||||
internal static class ResponseCacheEntrySerializer
|
||||
{
|
||||
private const int FormatVersion = 1;
|
||||
|
||||
internal static IResponseCacheEntry Deserialize(byte[] serializedEntry)
|
||||
{
|
||||
if (serializedEntry == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var memory = new MemoryStream(serializedEntry))
|
||||
{
|
||||
using (var reader = new BinaryReader(memory))
|
||||
{
|
||||
return Read(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static byte[] Serialize(IResponseCacheEntry entry)
|
||||
{
|
||||
using (var memory = new MemoryStream())
|
||||
{
|
||||
using (var writer = new BinaryWriter(memory))
|
||||
{
|
||||
Write(writer, entry);
|
||||
writer.Flush();
|
||||
return memory.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Serialization Format
|
||||
// Format version (int)
|
||||
// Type (char: 'R' for CachedResponse, 'V' for CachedVaryByRules)
|
||||
// Type-dependent data (see CachedResponse and CachedVaryByRules)
|
||||
private static IResponseCacheEntry Read(BinaryReader reader)
|
||||
{
|
||||
if (reader == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(reader));
|
||||
}
|
||||
|
||||
if (reader.ReadInt32() != FormatVersion)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var type = reader.ReadChar();
|
||||
|
||||
if (type == 'R')
|
||||
{
|
||||
return ReadCachedResponse(reader);
|
||||
}
|
||||
else if (type == 'V')
|
||||
{
|
||||
return ReadCachedVaryByRules(reader);
|
||||
}
|
||||
|
||||
// Unable to read as CachedResponse or CachedVaryByRules
|
||||
return null;
|
||||
}
|
||||
|
||||
// Serialization Format
|
||||
// Creation time - UtcTicks (long)
|
||||
// Status code (int)
|
||||
// Header count (int)
|
||||
// Header(s)
|
||||
// Key (string)
|
||||
// ValueCount (int)
|
||||
// Value(s)
|
||||
// Value (string)
|
||||
// BodyLength (int)
|
||||
// Body (byte[])
|
||||
private static CachedResponse ReadCachedResponse(BinaryReader reader)
|
||||
{
|
||||
var created = new DateTimeOffset(reader.ReadInt64(), TimeSpan.Zero);
|
||||
var statusCode = reader.ReadInt32();
|
||||
var headerCount = reader.ReadInt32();
|
||||
var headers = new HeaderDictionary();
|
||||
for (var index = 0; index < headerCount; index++)
|
||||
{
|
||||
var key = reader.ReadString();
|
||||
var headerValueCount = reader.ReadInt32();
|
||||
if (headerValueCount > 1)
|
||||
{
|
||||
var headerValues = new string[headerValueCount];
|
||||
for (var valueIndex = 0; valueIndex < headerValueCount; valueIndex++)
|
||||
{
|
||||
headerValues[valueIndex] = reader.ReadString();
|
||||
}
|
||||
headers[key] = headerValues;
|
||||
}
|
||||
else if (headerValueCount == 1)
|
||||
{
|
||||
headers[key] = reader.ReadString();
|
||||
}
|
||||
}
|
||||
|
||||
var bodyLength = reader.ReadInt32();
|
||||
var bodyBytes = reader.ReadBytes(bodyLength);
|
||||
|
||||
return new CachedResponse
|
||||
{
|
||||
Created = created,
|
||||
StatusCode = statusCode,
|
||||
Headers = headers,
|
||||
Body = new MemoryStream(bodyBytes, writable: false)
|
||||
};
|
||||
}
|
||||
|
||||
// Serialization Format
|
||||
// VaryKeyPrefix (string)
|
||||
// Headers count
|
||||
// Header(s) (comma separated string)
|
||||
// QueryKey count
|
||||
// QueryKey(s) (comma separated string)
|
||||
private static CachedVaryByRules ReadCachedVaryByRules(BinaryReader reader)
|
||||
{
|
||||
var varyKeyPrefix = reader.ReadString();
|
||||
|
||||
var headerCount = reader.ReadInt32();
|
||||
var headers = new string[headerCount];
|
||||
for (var index = 0; index < headerCount; index++)
|
||||
{
|
||||
headers[index] = reader.ReadString();
|
||||
}
|
||||
var queryKeysCount = reader.ReadInt32();
|
||||
var queryKeys = new string[queryKeysCount];
|
||||
for (var index = 0; index < queryKeysCount; index++)
|
||||
{
|
||||
queryKeys[index] = reader.ReadString();
|
||||
}
|
||||
|
||||
return new CachedVaryByRules { VaryByKeyPrefix = varyKeyPrefix, Headers = headers, QueryKeys = queryKeys };
|
||||
}
|
||||
|
||||
// See serialization format above
|
||||
private static void Write(BinaryWriter writer, IResponseCacheEntry entry)
|
||||
{
|
||||
if (writer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(writer));
|
||||
}
|
||||
|
||||
if (entry == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(entry));
|
||||
}
|
||||
|
||||
writer.Write(FormatVersion);
|
||||
|
||||
if (entry is CachedResponse)
|
||||
{
|
||||
writer.Write('R');
|
||||
WriteCachedResponse(writer, (CachedResponse)entry);
|
||||
}
|
||||
else if (entry is CachedVaryByRules)
|
||||
{
|
||||
writer.Write('V');
|
||||
WriteCachedVaryByRules(writer, (CachedVaryByRules)entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException($"Unrecognized entry type for {nameof(entry)}.");
|
||||
}
|
||||
}
|
||||
|
||||
// See serialization format above
|
||||
private static void WriteCachedResponse(BinaryWriter writer, CachedResponse entry)
|
||||
{
|
||||
writer.Write(entry.Created.UtcTicks);
|
||||
writer.Write(entry.StatusCode);
|
||||
writer.Write(entry.Headers.Count);
|
||||
foreach (var header in entry.Headers)
|
||||
{
|
||||
writer.Write(header.Key);
|
||||
writer.Write(header.Value.Count);
|
||||
foreach (var headerValue in header.Value)
|
||||
{
|
||||
writer.Write(headerValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.Body.CanSeek)
|
||||
{
|
||||
if (entry.Body.Length > int.MaxValue)
|
||||
{
|
||||
throw new NotSupportedException($"{nameof(entry.Body)} is too large to serialized.");
|
||||
}
|
||||
|
||||
var bodyLength = (int)entry.Body.Length;
|
||||
var bodyBytes = new byte[bodyLength];
|
||||
var bytesRead = entry.Body.Read(bodyBytes, 0, bodyLength);
|
||||
|
||||
if (bytesRead != bodyLength)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to fully read {nameof(entry.Body)}.");
|
||||
}
|
||||
|
||||
writer.Write(bodyLength);
|
||||
writer.Write(bodyBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
entry.Body.CopyTo(stream);
|
||||
|
||||
if (stream.Length > int.MaxValue)
|
||||
{
|
||||
throw new NotSupportedException($"{nameof(entry.Body)} is too large to serialized.");
|
||||
}
|
||||
|
||||
var bodyLength = (int)stream.Length;
|
||||
writer.Write(bodyLength);
|
||||
writer.Write(stream.ToArray());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// See serialization format above
|
||||
private static void WriteCachedVaryByRules(BinaryWriter writer, CachedVaryByRules varyByRules)
|
||||
{
|
||||
writer.Write(varyByRules.VaryByKeyPrefix);
|
||||
|
||||
writer.Write(varyByRules.Headers.Count);
|
||||
foreach (var header in varyByRules.Headers)
|
||||
{
|
||||
writer.Write(header);
|
||||
}
|
||||
|
||||
writer.Write(varyByRules.QueryKeys.Count);
|
||||
foreach (var queryKey in varyByRules.QueryKeys)
|
||||
{
|
||||
writer.Write(queryKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ using Microsoft.Extensions.Primitives;
|
|||
|
||||
namespace Microsoft.AspNetCore.ResponseCaching.Internal
|
||||
{
|
||||
internal class ResponseCacheKeyProvider : IResponseCacheKeyProvider
|
||||
public class ResponseCacheKeyProvider : IResponseCacheKeyProvider
|
||||
{
|
||||
// Use the record separator for delimiting components of the cache key to avoid possible collisions
|
||||
private static readonly char KeyDelimiter = '\x1e';
|
||||
|
|
@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal
|
|||
private readonly ObjectPool<StringBuilder> _builderPool;
|
||||
private readonly ResponseCacheOptions _options;
|
||||
|
||||
internal ResponseCacheKeyProvider(ObjectPoolProvider poolProvider, IOptions<ResponseCacheOptions> options)
|
||||
public ResponseCacheKeyProvider(ObjectPoolProvider poolProvider, IOptions<ResponseCacheOptions> options)
|
||||
{
|
||||
if (poolProvider == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Http;
|
|||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Microsoft.AspNetCore.ResponseCaching
|
||||
namespace Microsoft.AspNetCore.ResponseCaching.Internal
|
||||
{
|
||||
public class ResponseCachePolicyProvider : IResponseCachePolicyProvider
|
||||
{
|
||||
|
|
@ -30,20 +30,9 @@ namespace Microsoft.AspNetCore.ResponseCaching
|
|||
|
||||
public ResponseCacheMiddleware(
|
||||
RequestDelegate next,
|
||||
IResponseCacheStore store,
|
||||
IOptions<ResponseCacheOptions> options,
|
||||
IResponseCachePolicyProvider policyProvider,
|
||||
ObjectPoolProvider poolProvider)
|
||||
:this (next, store, options, policyProvider, new ResponseCacheKeyProvider(poolProvider, options))
|
||||
{
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal ResponseCacheMiddleware(
|
||||
RequestDelegate next,
|
||||
IResponseCacheStore store,
|
||||
IOptions<ResponseCacheOptions> options,
|
||||
IResponseCachePolicyProvider policyProvider,
|
||||
IResponseCacheKeyProvider keyProvider)
|
||||
{
|
||||
if (next == null)
|
||||
|
|
|
|||
|
|
@ -1,189 +0,0 @@
|
|||
// 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 System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.ResponseCaching.Internal;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.ResponseCaching.Tests
|
||||
{
|
||||
public class CacheEntrySerializerTests
|
||||
{
|
||||
[Fact]
|
||||
public void Serialize_NullObject_Throws()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>(() => ResponseCacheEntrySerializer.Serialize(null));
|
||||
}
|
||||
|
||||
private class UnknownResponseCacheEntry : IResponseCacheEntry
|
||||
{
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serialize_UnknownObject_Throws()
|
||||
{
|
||||
Assert.Throws<NotSupportedException>(() => ResponseCacheEntrySerializer.Serialize(new UnknownResponseCacheEntry()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_NullObject_ReturnsNull()
|
||||
{
|
||||
Assert.Null(ResponseCacheEntrySerializer.Deserialize(null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RoundTrip_CachedResponseWithBody_Succeeds()
|
||||
{
|
||||
var headers = new HeaderDictionary();
|
||||
headers["keyA"] = "valueA";
|
||||
headers["keyB"] = "valueB";
|
||||
var body = Encoding.ASCII.GetBytes("Hello world");
|
||||
var cachedResponse = new CachedResponse()
|
||||
{
|
||||
Created = DateTimeOffset.UtcNow,
|
||||
StatusCode = StatusCodes.Status200OK,
|
||||
Body = new SegmentReadStream(new List<byte[]>(new[] { body }), body.Length),
|
||||
Headers = headers
|
||||
};
|
||||
|
||||
AssertCachedResponseEqual(cachedResponse, (CachedResponse)ResponseCacheEntrySerializer.Deserialize(ResponseCacheEntrySerializer.Serialize(cachedResponse)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RoundTrip_CachedResponseWithMultivalueHeaders_Succeeds()
|
||||
{
|
||||
var headers = new HeaderDictionary();
|
||||
headers["keyA"] = new StringValues(new[] { "ValueA", "ValueB" });
|
||||
var body = Encoding.ASCII.GetBytes("Hello world");
|
||||
var cachedResponse = new CachedResponse()
|
||||
{
|
||||
Created = DateTimeOffset.UtcNow,
|
||||
StatusCode = StatusCodes.Status200OK,
|
||||
Body = new SegmentReadStream(new List<byte[]>(new[] { body }), body.Length),
|
||||
Headers = headers
|
||||
};
|
||||
|
||||
AssertCachedResponseEqual(cachedResponse, (CachedResponse)ResponseCacheEntrySerializer.Deserialize(ResponseCacheEntrySerializer.Serialize(cachedResponse)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RoundTrip_CachedResponseWithEmptyHeaders_Succeeds()
|
||||
{
|
||||
var headers = new HeaderDictionary();
|
||||
headers["keyA"] = StringValues.Empty;
|
||||
var body = Encoding.ASCII.GetBytes("Hello world");
|
||||
var cachedResponse = new CachedResponse()
|
||||
{
|
||||
Created = DateTimeOffset.UtcNow,
|
||||
StatusCode = StatusCodes.Status200OK,
|
||||
Body = new SegmentReadStream(new List<byte[]>(new[] { body }), body.Length),
|
||||
Headers = headers
|
||||
};
|
||||
|
||||
AssertCachedResponseEqual(cachedResponse, (CachedResponse)ResponseCacheEntrySerializer.Deserialize(ResponseCacheEntrySerializer.Serialize(cachedResponse)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RoundTrip_CachedVaryByRule_EmptyRules_Succeeds()
|
||||
{
|
||||
var cachedVaryByRule = new CachedVaryByRules()
|
||||
{
|
||||
VaryByKeyPrefix = FastGuid.NewGuid().IdString
|
||||
};
|
||||
|
||||
AssertCachedVaryByRuleEqual(cachedVaryByRule, (CachedVaryByRules)ResponseCacheEntrySerializer.Deserialize(ResponseCacheEntrySerializer.Serialize(cachedVaryByRule)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RoundTrip_CachedVaryByRule_HeadersOnly_Succeeds()
|
||||
{
|
||||
var headers = new[] { "headerA", "headerB" };
|
||||
var cachedVaryByRule = new CachedVaryByRules()
|
||||
{
|
||||
VaryByKeyPrefix = FastGuid.NewGuid().IdString,
|
||||
Headers = headers
|
||||
};
|
||||
|
||||
AssertCachedVaryByRuleEqual(cachedVaryByRule, (CachedVaryByRules)ResponseCacheEntrySerializer.Deserialize(ResponseCacheEntrySerializer.Serialize(cachedVaryByRule)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RoundTrip_CachedVaryByRule_QueryKeysOnly_Succeeds()
|
||||
{
|
||||
var queryKeys = new[] { "queryA", "queryB" };
|
||||
var cachedVaryByRule = new CachedVaryByRules()
|
||||
{
|
||||
VaryByKeyPrefix = FastGuid.NewGuid().IdString,
|
||||
QueryKeys = queryKeys
|
||||
};
|
||||
|
||||
AssertCachedVaryByRuleEqual(cachedVaryByRule, (CachedVaryByRules)ResponseCacheEntrySerializer.Deserialize(ResponseCacheEntrySerializer.Serialize(cachedVaryByRule)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RoundTrip_CachedVaryByRule_HeadersAndQueryKeys_Succeeds()
|
||||
{
|
||||
var headers = new[] { "headerA", "headerB" };
|
||||
var queryKeys = new[] { "queryA", "queryB" };
|
||||
var cachedVaryByRule = new CachedVaryByRules()
|
||||
{
|
||||
VaryByKeyPrefix = FastGuid.NewGuid().IdString,
|
||||
Headers = headers,
|
||||
QueryKeys = queryKeys
|
||||
};
|
||||
|
||||
AssertCachedVaryByRuleEqual(cachedVaryByRule, (CachedVaryByRules)ResponseCacheEntrySerializer.Deserialize(ResponseCacheEntrySerializer.Serialize(cachedVaryByRule)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deserialize_InvalidEntries_ReturnsNull()
|
||||
{
|
||||
var headers = new[] { "headerA", "headerB" };
|
||||
var cachedVaryByRule = new CachedVaryByRules()
|
||||
{
|
||||
VaryByKeyPrefix = FastGuid.NewGuid().IdString,
|
||||
Headers = headers
|
||||
};
|
||||
var serializedEntry = ResponseCacheEntrySerializer.Serialize(cachedVaryByRule);
|
||||
Array.Reverse(serializedEntry);
|
||||
|
||||
Assert.Null(ResponseCacheEntrySerializer.Deserialize(serializedEntry));
|
||||
}
|
||||
|
||||
private static void AssertCachedResponseEqual(CachedResponse expected, CachedResponse actual)
|
||||
{
|
||||
Assert.NotNull(actual);
|
||||
Assert.NotNull(expected);
|
||||
Assert.Equal(expected.Created, actual.Created);
|
||||
Assert.Equal(expected.StatusCode, actual.StatusCode);
|
||||
Assert.Equal(expected.Headers.Count, actual.Headers.Count);
|
||||
foreach (var expectedHeader in expected.Headers)
|
||||
{
|
||||
Assert.Equal(expectedHeader.Value, actual.Headers[expectedHeader.Key]);
|
||||
}
|
||||
|
||||
Assert.Equal(expected.Body.Length, actual.Body.Length);
|
||||
var bodyLength = (int)expected.Body.Length;
|
||||
var expectedBytes = new byte[bodyLength];
|
||||
var actualBytes = new byte[bodyLength];
|
||||
expected.Body.Position = 0; // Rewind
|
||||
Assert.Equal(bodyLength, expected.Body.Read(expectedBytes, 0, bodyLength));
|
||||
Assert.Equal(bodyLength, actual.Body.Read(actualBytes, 0, bodyLength));
|
||||
Assert.True(expectedBytes.SequenceEqual(actualBytes));
|
||||
}
|
||||
|
||||
private static void AssertCachedVaryByRuleEqual(CachedVaryByRules expected, CachedVaryByRules actual)
|
||||
{
|
||||
Assert.NotNull(actual);
|
||||
Assert.NotNull(expected);
|
||||
Assert.Equal(expected.VaryByKeyPrefix, actual.VaryByKeyPrefix);
|
||||
Assert.Equal(expected.Headers, actual.Headers);
|
||||
Assert.Equal(expected.QueryKeys, actual.QueryKeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Headers;
|
||||
using Microsoft.AspNetCore.ResponseCaching.Internal;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Xunit;
|
||||
|
||||
|
|
|
|||
|
|
@ -81,19 +81,6 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
|
|||
app.UseResponseCache(options);
|
||||
app.Run(requestDelegate);
|
||||
});
|
||||
|
||||
// Test with DistributedResponseCacheStore
|
||||
yield return new WebHostBuilder()
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddDistributedResponseCacheStore();
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
configureDelegate(app);
|
||||
app.UseResponseCache(options);
|
||||
app.Run(requestDelegate);
|
||||
});
|
||||
}
|
||||
|
||||
internal static ResponseCacheMiddleware CreateTestMiddleware(
|
||||
|
|
@ -121,9 +108,9 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
|
|||
|
||||
return new ResponseCacheMiddleware(
|
||||
httpContext => TaskCache.CompletedTask,
|
||||
store,
|
||||
Options.Create(options),
|
||||
policyProvider,
|
||||
store,
|
||||
keyProvider);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue