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

Add an option to specify the max response size cached #25

Merged
merged 1 commit into from
Sep 2, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal
internal class ResponseCacheStream : Stream
{
private readonly Stream _innerStream;
private readonly long _maxBufferSize;

public ResponseCacheStream(Stream innerStream)
public ResponseCacheStream(Stream innerStream, long maxBufferSize)
{
_innerStream = innerStream;
_maxBufferSize = maxBufferSize;
}

public MemoryStream BufferedStream { get; } = new MemoryStream();
Expand All @@ -38,6 +40,8 @@ public override long Position
public void DisableBuffering()
{
BufferingEnabled = false;
BufferedStream.SetLength(0);
BufferedStream.Capacity = 0;
BufferedStream.Dispose();
}

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

if (BufferingEnabled)
{
BufferedStream.Write(buffer, offset, count);
if (BufferedStream.Length + count > _maxBufferSize)
{
DisableBuffering();
}
else
{
BufferedStream.Write(buffer, offset, count);
}
}
}

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

if (BufferingEnabled)
{
await BufferedStream.WriteAsync(buffer, offset, count, cancellationToken);
if (BufferedStream.Length + count > _maxBufferSize)
{
DisableBuffering();
}
else
{
await BufferedStream.WriteAsync(buffer, offset, count, cancellationToken);
}
}
}

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

if (BufferingEnabled)
{
BufferedStream.WriteByte(value);
if (BufferedStream.Length + 1 > _maxBufferSize)
{
DisableBuffering();
}
else
{
BufferedStream.WriteByte(value);
}
}
}

Expand Down
36 changes: 16 additions & 20 deletions src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http.Headers;
Expand All @@ -25,7 +26,7 @@ internal class ResponseCachingContext

private readonly HttpContext _httpContext;
private readonly IResponseCache _cache;
private readonly ISystemClock _clock;
private readonly ResponseCachingOptions _options;
private readonly ObjectPool<StringBuilder> _builderPool;
private readonly IResponseCachingCacheabilityValidator _cacheabilityValidator;
private readonly IResponseCachingCacheKeyModifier _cacheKeyModifier;
Expand All @@ -40,29 +41,19 @@ internal class ResponseCachingContext
private CachedResponse _cachedResponse;
private TimeSpan _cachedResponseValidFor;
internal DateTimeOffset _responseTime;

internal ResponseCachingContext(
HttpContext httpContext,
IResponseCache cache,
ObjectPool<StringBuilder> builderPool,
IResponseCachingCacheabilityValidator cacheabilityValidator,
IResponseCachingCacheKeyModifier cacheKeyModifier)
: this(httpContext, cache, new SystemClock(), builderPool, cacheabilityValidator, cacheKeyModifier)
{
}

// Internal for testing
internal ResponseCachingContext(
HttpContext httpContext,
IResponseCache cache,
ISystemClock clock,
ResponseCachingOptions options,
ObjectPool<StringBuilder> builderPool,
IResponseCachingCacheabilityValidator cacheabilityValidator,
IResponseCachingCacheKeyModifier cacheKeyModifier)
{
_httpContext = httpContext;
_cache = cache;
_clock = clock;
_options = options;
_builderPool = builderPool;
_cacheabilityValidator = cacheabilityValidator;
_cacheKeyModifier = cacheKeyModifier;
Expand All @@ -74,10 +65,7 @@ internal bool CacheResponse
{
if (_cacheResponse == null)
{
// TODO: apparent age vs corrected age value
var responseAge = _responseTime - ResponseHeaders.Date ?? TimeSpan.Zero;

_cacheResponse = ResponseIsCacheable() && EntryIsFresh(ResponseHeaders, responseAge, verifyAgainstRequest: false);
_cacheResponse = ResponseIsCacheable();
}
return _cacheResponse.Value;
}
Expand Down Expand Up @@ -363,6 +351,14 @@ internal bool ResponseIsCacheable()
return false;
}

// Check response freshness
// TODO: apparent age vs corrected age value
var responseAge = _responseTime - ResponseHeaders.Date ?? TimeSpan.Zero;
if (!EntryIsFresh(ResponseHeaders, responseAge, verifyAgainstRequest: false))
{
return false;
}

return true;
}

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

_responseTime = _clock.UtcNow;
_responseTime = _options.SystemClock.UtcNow;
var age = _responseTime - cachedResponse.Created;
age = age > TimeSpan.Zero ? age : TimeSpan.Zero;

Expand Down Expand Up @@ -607,7 +603,7 @@ internal void OnResponseStarting()
if (!ResponseStarted)
{
ResponseStarted = true;
_responseTime = _clock.UtcNow;
_responseTime = _options.SystemClock.UtcNow;

FinalizeCachingHeaders();
}
Expand All @@ -619,7 +615,7 @@ internal void ShimResponseStream()

// Shim response stream
OriginalResponseStream = _httpContext.Response.Body;
ResponseCacheStream = new ResponseCacheStream(OriginalResponseStream);
ResponseCacheStream = new ResponseCacheStream(OriginalResponseStream, _options.MaximumCachedBodySize);
_httpContext.Response.Body = ResponseCacheStream;

// Shim IHttpSendFileFeature
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using Microsoft.AspNetCore.ResponseCaching;
using Microsoft.Extensions.Options;

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

return app.UseMiddleware<ResponseCachingMiddleware>();
}

public static IApplicationBuilder UseResponseCaching(this IApplicationBuilder app, ResponseCachingOptions options)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}

return app.UseMiddleware<ResponseCachingMiddleware>(Options.Create(options));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options;
Expand All @@ -20,13 +21,15 @@ public class ResponseCachingMiddleware

private readonly RequestDelegate _next;
private readonly IResponseCache _cache;
private readonly ResponseCachingOptions _options;
private readonly ObjectPool<StringBuilder> _builderPool;
private readonly IResponseCachingCacheabilityValidator _cacheabilityValidator;
private readonly IResponseCachingCacheKeyModifier _cacheKeyModifier;

public ResponseCachingMiddleware(
RequestDelegate next,
RequestDelegate next,
IResponseCache cache,
IOptions<ResponseCachingOptions> options,
ObjectPoolProvider poolProvider,
IResponseCachingCacheabilityValidator cacheabilityValidator,
IResponseCachingCacheKeyModifier cacheKeyModifier)
Expand All @@ -39,6 +42,10 @@ public ResponseCachingMiddleware(
{
throw new ArgumentNullException(nameof(cache));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (poolProvider == null)
{
throw new ArgumentNullException(nameof(poolProvider));
Expand All @@ -54,6 +61,7 @@ public ResponseCachingMiddleware(

_next = next;
_cache = cache;
_options = options.Value;
_builderPool = poolProvider.CreateStringBuilderPool();
_cacheabilityValidator = cacheabilityValidator;
_cacheKeyModifier = cacheKeyModifier;
Expand All @@ -64,6 +72,7 @@ public async Task Invoke(HttpContext context)
var cachingContext = new ResponseCachingContext(
context,
_cache,
_options,
_builderPool,
_cacheabilityValidator,
_cacheKeyModifier);
Expand Down
22 changes: 22 additions & 0 deletions src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// 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.ComponentModel;
using Microsoft.AspNetCore.ResponseCaching.Internal;

namespace Microsoft.AspNetCore.Builder
{
public class ResponseCachingOptions
{
/// <summary>
/// The largest cacheable size for the response body in bytes.
/// </summary>
public long MaximumCachedBodySize { get; set; } = 1024 * 1024;

/// <summary>
/// For testing purposes only.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
internal ISystemClock SystemClock { get; set; } = new SystemClock();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http.Headers;
Expand Down Expand Up @@ -859,7 +860,7 @@ private static ResponseCachingContext CreateTestContext(
return new ResponseCachingContext(
httpContext,
new TestResponseCache(),
new SystemClock(),
new ResponseCachingOptions(),
new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy()),
cacheabilityValidator,
cacheKeyModifier);
Expand Down
Loading