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

Commit d72ef12

Browse files
committed
Adding options to specify maximum response body size
1 parent 3b0f01a commit d72ef12

File tree

7 files changed

+212
-338
lines changed

7 files changed

+212
-338
lines changed

src/Microsoft.AspNetCore.ResponseCaching/Internal/ResponseCacheStream.cs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal
1111
internal class ResponseCacheStream : Stream
1212
{
1313
private readonly Stream _innerStream;
14+
private readonly long _maxBufferSize;
1415

15-
public ResponseCacheStream(Stream innerStream)
16+
public ResponseCacheStream(Stream innerStream, long maxBufferSize)
1617
{
1718
_innerStream = innerStream;
19+
_maxBufferSize = maxBufferSize;
1820
}
1921

2022
public MemoryStream BufferedStream { get; } = new MemoryStream();
@@ -38,6 +40,8 @@ public override long Position
3840
public void DisableBuffering()
3941
{
4042
BufferingEnabled = false;
43+
BufferedStream.SetLength(0);
44+
BufferedStream.Capacity = 0;
4145
BufferedStream.Dispose();
4246
}
4347

@@ -77,7 +81,14 @@ public override void Write(byte[] buffer, int offset, int count)
7781

7882
if (BufferingEnabled)
7983
{
80-
BufferedStream.Write(buffer, offset, count);
84+
if (BufferedStream.Length + count > _maxBufferSize)
85+
{
86+
DisableBuffering();
87+
}
88+
else
89+
{
90+
BufferedStream.Write(buffer, offset, count);
91+
}
8192
}
8293
}
8394

@@ -95,7 +106,14 @@ public override async Task WriteAsync(byte[] buffer, int offset, int count, Canc
95106

96107
if (BufferingEnabled)
97108
{
98-
await BufferedStream.WriteAsync(buffer, offset, count, cancellationToken);
109+
if (BufferedStream.Length + count > _maxBufferSize)
110+
{
111+
DisableBuffering();
112+
}
113+
else
114+
{
115+
await BufferedStream.WriteAsync(buffer, offset, count, cancellationToken);
116+
}
99117
}
100118
}
101119

@@ -113,7 +131,14 @@ public override void WriteByte(byte value)
113131

114132
if (BufferingEnabled)
115133
{
116-
BufferedStream.WriteByte(value);
134+
if (BufferedStream.Length + 1 > _maxBufferSize)
135+
{
136+
DisableBuffering();
137+
}
138+
else
139+
{
140+
BufferedStream.WriteByte(value);
141+
}
117142
}
118143
}
119144

src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingContext.cs

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Linq;
88
using System.Text;
99
using System.Threading.Tasks;
10+
using Microsoft.AspNetCore.Builder;
1011
using Microsoft.AspNetCore.Http;
1112
using Microsoft.AspNetCore.Http.Features;
1213
using Microsoft.AspNetCore.Http.Headers;
@@ -25,7 +26,7 @@ internal class ResponseCachingContext
2526

2627
private readonly HttpContext _httpContext;
2728
private readonly IResponseCache _cache;
28-
private readonly ISystemClock _clock;
29+
private readonly ResponseCachingOptions _options;
2930
private readonly ObjectPool<StringBuilder> _builderPool;
3031
private readonly IResponseCachingCacheabilityValidator _cacheabilityValidator;
3132
private readonly IResponseCachingCacheKeyModifier _cacheKeyModifier;
@@ -40,29 +41,19 @@ internal class ResponseCachingContext
4041
private CachedResponse _cachedResponse;
4142
private TimeSpan _cachedResponseValidFor;
4243
internal DateTimeOffset _responseTime;
43-
44-
internal ResponseCachingContext(
45-
HttpContext httpContext,
46-
IResponseCache cache,
47-
ObjectPool<StringBuilder> builderPool,
48-
IResponseCachingCacheabilityValidator cacheabilityValidator,
49-
IResponseCachingCacheKeyModifier cacheKeyModifier)
50-
: this(httpContext, cache, new SystemClock(), builderPool, cacheabilityValidator, cacheKeyModifier)
51-
{
52-
}
5344

5445
// Internal for testing
5546
internal ResponseCachingContext(
5647
HttpContext httpContext,
5748
IResponseCache cache,
58-
ISystemClock clock,
49+
ResponseCachingOptions options,
5950
ObjectPool<StringBuilder> builderPool,
6051
IResponseCachingCacheabilityValidator cacheabilityValidator,
6152
IResponseCachingCacheKeyModifier cacheKeyModifier)
6253
{
6354
_httpContext = httpContext;
6455
_cache = cache;
65-
_clock = clock;
56+
_options = options;
6657
_builderPool = builderPool;
6758
_cacheabilityValidator = cacheabilityValidator;
6859
_cacheKeyModifier = cacheKeyModifier;
@@ -74,10 +65,7 @@ internal bool CacheResponse
7465
{
7566
if (_cacheResponse == null)
7667
{
77-
// TODO: apparent age vs corrected age value
78-
var responseAge = _responseTime - ResponseHeaders.Date ?? TimeSpan.Zero;
79-
80-
_cacheResponse = ResponseIsCacheable() && EntryIsFresh(ResponseHeaders, responseAge, verifyAgainstRequest: false);
68+
_cacheResponse = ResponseIsCacheable();
8169
}
8270
return _cacheResponse.Value;
8371
}
@@ -363,6 +351,14 @@ internal bool ResponseIsCacheable()
363351
return false;
364352
}
365353

354+
// Check response freshness
355+
// TODO: apparent age vs corrected age value
356+
var responseAge = _responseTime - ResponseHeaders.Date ?? TimeSpan.Zero;
357+
if (!EntryIsFresh(ResponseHeaders, responseAge, verifyAgainstRequest: false))
358+
{
359+
return false;
360+
}
361+
366362
return true;
367363
}
368364

@@ -433,7 +429,7 @@ internal async Task<bool> TryServeFromCacheAsync()
433429
var cachedResponse = cacheEntry as CachedResponse;
434430
var cachedResponseHeaders = new ResponseHeaders(cachedResponse.Headers);
435431

436-
_responseTime = _clock.UtcNow;
432+
_responseTime = _options.SystemClock.UtcNow;
437433
var age = _responseTime - cachedResponse.Created;
438434
age = age > TimeSpan.Zero ? age : TimeSpan.Zero;
439435

@@ -607,7 +603,7 @@ internal void OnResponseStarting()
607603
if (!ResponseStarted)
608604
{
609605
ResponseStarted = true;
610-
_responseTime = _clock.UtcNow;
606+
_responseTime = _options.SystemClock.UtcNow;
611607

612608
FinalizeCachingHeaders();
613609
}
@@ -619,7 +615,7 @@ internal void ShimResponseStream()
619615

620616
// Shim response stream
621617
OriginalResponseStream = _httpContext.Response.Body;
622-
ResponseCacheStream = new ResponseCacheStream(OriginalResponseStream);
618+
ResponseCacheStream = new ResponseCacheStream(OriginalResponseStream, _options.MaximumCachedBodySize);
623619
_httpContext.Response.Body = ResponseCacheStream;
624620

625621
// Shim IHttpSendFileFeature

src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingExtensions.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using Microsoft.AspNetCore.ResponseCaching;
6+
using Microsoft.Extensions.Options;
67

78
namespace Microsoft.AspNetCore.Builder
89
{
@@ -17,5 +18,19 @@ public static IApplicationBuilder UseResponseCaching(this IApplicationBuilder ap
1718

1819
return app.UseMiddleware<ResponseCachingMiddleware>();
1920
}
21+
22+
public static IApplicationBuilder UseResponseCaching(this IApplicationBuilder app, ResponseCachingOptions options)
23+
{
24+
if (app == null)
25+
{
26+
throw new ArgumentNullException(nameof(app));
27+
}
28+
if (options == null)
29+
{
30+
throw new ArgumentNullException(nameof(options));
31+
}
32+
33+
return app.UseMiddleware<ResponseCachingMiddleware>(Options.Create(options));
34+
}
2035
}
2136
}

src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingMiddleware.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Text;
66
using System.Threading.Tasks;
7+
using Microsoft.AspNetCore.Builder;
78
using Microsoft.AspNetCore.Http;
89
using Microsoft.Extensions.ObjectPool;
910
using Microsoft.Extensions.Options;
@@ -20,13 +21,15 @@ public class ResponseCachingMiddleware
2021

2122
private readonly RequestDelegate _next;
2223
private readonly IResponseCache _cache;
24+
private readonly ResponseCachingOptions _options;
2325
private readonly ObjectPool<StringBuilder> _builderPool;
2426
private readonly IResponseCachingCacheabilityValidator _cacheabilityValidator;
2527
private readonly IResponseCachingCacheKeyModifier _cacheKeyModifier;
2628

2729
public ResponseCachingMiddleware(
28-
RequestDelegate next,
30+
RequestDelegate next,
2931
IResponseCache cache,
32+
IOptions<ResponseCachingOptions> options,
3033
ObjectPoolProvider poolProvider,
3134
IResponseCachingCacheabilityValidator cacheabilityValidator,
3235
IResponseCachingCacheKeyModifier cacheKeyModifier)
@@ -39,6 +42,10 @@ public ResponseCachingMiddleware(
3942
{
4043
throw new ArgumentNullException(nameof(cache));
4144
}
45+
if (options == null)
46+
{
47+
throw new ArgumentNullException(nameof(options));
48+
}
4249
if (poolProvider == null)
4350
{
4451
throw new ArgumentNullException(nameof(poolProvider));
@@ -54,6 +61,7 @@ public ResponseCachingMiddleware(
5461

5562
_next = next;
5663
_cache = cache;
64+
_options = options.Value;
5765
_builderPool = poolProvider.CreateStringBuilderPool();
5866
_cacheabilityValidator = cacheabilityValidator;
5967
_cacheKeyModifier = cacheKeyModifier;
@@ -64,6 +72,7 @@ public async Task Invoke(HttpContext context)
6472
var cachingContext = new ResponseCachingContext(
6573
context,
6674
_cache,
75+
_options,
6776
_builderPool,
6877
_cacheabilityValidator,
6978
_cacheKeyModifier);
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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.ComponentModel;
5+
using Microsoft.AspNetCore.ResponseCaching.Internal;
6+
7+
namespace Microsoft.AspNetCore.Builder
8+
{
9+
public class ResponseCachingOptions
10+
{
11+
/// <summary>
12+
/// The largest cacheable size for the response body in bytes.
13+
/// </summary>
14+
public long MaximumCachedBodySize { get; set; } = 1024 * 1024;
15+
16+
/// <summary>
17+
/// For testing purposes only.
18+
/// </summary>
19+
[EditorBrowsable(EditorBrowsableState.Never)]
20+
internal ISystemClock SystemClock { get; set; } = new SystemClock();
21+
}
22+
}

test/Microsoft.AspNetCore.ResponseCaching.Tests/ResponseCachingContextTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Text;
77
using System.Threading;
88
using System.Threading.Tasks;
9+
using Microsoft.AspNetCore.Builder;
910
using Microsoft.AspNetCore.Http;
1011
using Microsoft.AspNetCore.Http.Features;
1112
using Microsoft.AspNetCore.Http.Headers;
@@ -859,7 +860,7 @@ private static ResponseCachingContext CreateTestContext(
859860
return new ResponseCachingContext(
860861
httpContext,
861862
new TestResponseCache(),
862-
new SystemClock(),
863+
new ResponseCachingOptions(),
863864
new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy()),
864865
cacheabilityValidator,
865866
cacheKeyModifier);

0 commit comments

Comments
 (0)