Skip to content
This repository was archived by the owner on Nov 22, 2018. It is now read-only.

Commit a40cc88

Browse files
committed
IResponseCache adapter and support for vary by header
1 parent 8e7eb5d commit a40cc88

15 files changed

+516
-39
lines changed

samples/ResponseCachingSample/Startup.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,22 @@ public class Startup
1515
{
1616
public void ConfigureServices(IServiceCollection services)
1717
{
18-
services.AddMemoryCache();
18+
services.AddDistributedResponseCache();
1919
}
2020

2121
public void Configure(IApplicationBuilder app)
2222
{
2323
app.UseResponseCaching();
2424
app.Run(async (context) =>
2525
{
26+
// These settings should be configured by context.Response.Cache.*
2627
context.Response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue()
2728
{
2829
Public = true,
29-
MaxAge = TimeSpan.FromSeconds(10)
30+
MaxAge = TimeSpan.FromSeconds(10)
3031
};
32+
context.Response.Headers["Vary"] = new string[] { "Accept-Encoding", "Non-Existent" };
33+
3134
await context.Response.WriteAsync("Hello World! " + DateTime.UtcNow);
3235
});
3336
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
namespace Microsoft.AspNetCore.ResponseCaching
5+
{
6+
public interface IResponseCache
7+
{
8+
object Get(string key);
9+
// TODO: Set expiry policy in the underlying cache?
10+
void Set(string key, object entry);
11+
void Remove(string key);
12+
}
13+
}

src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingEntry.cs renamed to src/Microsoft.AspNetCore.ResponseCaching/Internal/CachedResponse.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33

44
using Microsoft.AspNetCore.Http;
55

6-
namespace Microsoft.AspNetCore.ResponseCaching
6+
namespace Microsoft.AspNetCore.ResponseCaching.Internal
77
{
8-
internal class ResponseCachingEntry
8+
internal class CachedResponse
99
{
10-
public int StatusCode { get; set; }
10+
internal int StatusCode { get; set; }
11+
1112
internal IHeaderDictionary Headers { get; set; } = new HeaderDictionary();
13+
1214
internal byte[] Body { get; set; }
1315
}
1416
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.Extensions.Primitives;
5+
6+
namespace Microsoft.AspNetCore.ResponseCaching.Internal
7+
{
8+
internal class CachedVaryBy
9+
{
10+
internal StringValues Headers { get; set; }
11+
}
12+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.IO;
6+
using Microsoft.AspNetCore.Http;
7+
8+
namespace Microsoft.AspNetCore.ResponseCaching.Internal
9+
{
10+
internal static class DefaultResponseCacheSerializer
11+
{
12+
private const int FormatVersion = 1;
13+
14+
public static object Deserialize(byte[] serializedEntry)
15+
{
16+
using (var memory = new MemoryStream(serializedEntry))
17+
{
18+
using (var reader = new BinaryReader(memory))
19+
{
20+
return Read(reader);
21+
}
22+
}
23+
}
24+
25+
public static byte[] Serialize(object entry)
26+
{
27+
using (var memory = new MemoryStream())
28+
{
29+
using (var writer = new BinaryWriter(memory))
30+
{
31+
Write(writer, entry);
32+
writer.Flush();
33+
return memory.ToArray();
34+
}
35+
}
36+
}
37+
38+
// Serialization Format
39+
// Format version (int)
40+
// Type (string)
41+
// Type-dependent data (see CachedResponse and CachedVaryBy)
42+
public static object Read(BinaryReader reader)
43+
{
44+
if (reader == null)
45+
{
46+
throw new ArgumentNullException(nameof(reader));
47+
}
48+
49+
if (reader.ReadInt32() != FormatVersion)
50+
{
51+
return null;
52+
}
53+
54+
var type = reader.ReadString();
55+
56+
if (string.Equals(nameof(CachedResponse), type))
57+
{
58+
var cachedResponse = ReadCachedResponse(reader);
59+
return cachedResponse;
60+
}
61+
else if (string.Equals(nameof(CachedVaryBy), type))
62+
{
63+
var cachedResponse = ReadCachedVaryBy(reader);
64+
return cachedResponse;
65+
}
66+
67+
// Unable to read as CachedResponse or CachedVaryBy
68+
return null;
69+
}
70+
71+
// Serialization Format
72+
// Status code (int)
73+
// Header count (int)
74+
// Header(s)
75+
// Key (string)
76+
// Value (string)
77+
// Body length (int)
78+
// Body (byte[])
79+
private static CachedResponse ReadCachedResponse(BinaryReader reader)
80+
{
81+
var statusCode = reader.ReadInt32();
82+
var headerCount = reader.ReadInt32();
83+
var headers = new HeaderDictionary();
84+
for (var index = 0; index < headerCount; index++)
85+
{
86+
var key = reader.ReadString();
87+
var value = reader.ReadString();
88+
headers[key] = value;
89+
}
90+
var bodyLength = reader.ReadInt32();
91+
var body = reader.ReadBytes(bodyLength);
92+
93+
return new CachedResponse { StatusCode = statusCode, Headers = headers, Body = body };
94+
}
95+
96+
// Serialization Format
97+
// Headers (comma separated string)
98+
private static CachedVaryBy ReadCachedVaryBy(BinaryReader reader)
99+
{
100+
var headers = reader.ReadString().Split(',');
101+
102+
return new CachedVaryBy { Headers = headers };
103+
}
104+
105+
// See serialization format above
106+
public static void Write(BinaryWriter writer, object entry)
107+
{
108+
if (writer == null)
109+
{
110+
throw new ArgumentNullException(nameof(writer));
111+
}
112+
113+
if (entry == null)
114+
{
115+
throw new ArgumentNullException(nameof(entry));
116+
}
117+
118+
writer.Write(FormatVersion);
119+
120+
if (entry is CachedResponse)
121+
{
122+
WriteCachedResponse(writer, entry as CachedResponse);
123+
}
124+
else if (entry is CachedVaryBy)
125+
{
126+
WriteCachedVaryBy(writer, entry as CachedVaryBy);
127+
}
128+
else
129+
{
130+
throw new NotSupportedException($"Unrecognized entry format for {nameof(entry)}.");
131+
}
132+
}
133+
134+
// See serialization format above
135+
private static void WriteCachedResponse(BinaryWriter writer, CachedResponse entry)
136+
{
137+
writer.Write(nameof(CachedResponse));
138+
writer.Write(entry.StatusCode);
139+
writer.Write(entry.Headers.Count);
140+
foreach (var header in entry.Headers)
141+
{
142+
writer.Write(header.Key);
143+
writer.Write(header.Value);
144+
}
145+
146+
writer.Write(entry.Body.Length);
147+
writer.Write(entry.Body);
148+
}
149+
150+
// See serialization format above
151+
private static void WriteCachedVaryBy(BinaryWriter writer, CachedVaryBy entry)
152+
{
153+
writer.Write(nameof(CachedVaryBy));
154+
writer.Write(entry.Headers);
155+
}
156+
}
157+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using Microsoft.Extensions.Caching.Distributed;
6+
7+
namespace Microsoft.AspNetCore.ResponseCaching.Internal
8+
{
9+
internal class DistributedResponseCache : IResponseCache
10+
{
11+
private readonly IDistributedCache _cache;
12+
13+
public DistributedResponseCache(IDistributedCache cache)
14+
{
15+
if (cache == null)
16+
{
17+
throw new ArgumentNullException(nameof(cache));
18+
}
19+
20+
_cache = cache;
21+
}
22+
23+
public object Get(string key)
24+
{
25+
try
26+
{
27+
return DefaultResponseCacheSerializer.Deserialize(_cache.Get(key));
28+
}
29+
catch
30+
{
31+
// TODO: Log error
32+
return null;
33+
}
34+
}
35+
36+
public void Remove(string key)
37+
{
38+
try
39+
{
40+
_cache.Remove(key);
41+
}
42+
catch
43+
{
44+
// TODO: Log error
45+
}
46+
}
47+
48+
public void Set(string key, object entry)
49+
{
50+
try
51+
{
52+
_cache.Set(key, DefaultResponseCacheSerializer.Serialize(entry));
53+
}
54+
catch
55+
{
56+
// TODO: Log error
57+
}
58+
}
59+
}
60+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using Microsoft.Extensions.Caching.Memory;
6+
7+
namespace Microsoft.AspNetCore.ResponseCaching.Internal
8+
{
9+
internal class MemoryResponseCache : IResponseCache
10+
{
11+
private readonly IMemoryCache _cache;
12+
13+
public MemoryResponseCache(IMemoryCache cache)
14+
{
15+
if (cache == null)
16+
{
17+
throw new ArgumentNullException(nameof(cache));
18+
}
19+
20+
_cache = cache;
21+
}
22+
23+
public object Get(string key)
24+
{
25+
return _cache.Get(key);
26+
}
27+
28+
public void Remove(string key)
29+
{
30+
_cache.Remove(key);
31+
}
32+
33+
public void Set(string key, object entry)
34+
{
35+
_cache.Set(key, entry);
36+
}
37+
}
38+
}

0 commit comments

Comments
 (0)