Adding options to specify maximum response body size

This commit is contained in:
John Luo 2016-08-31 17:33:01 -07:00
parent 3b0f01a8ec
commit d72ef128dd
7 changed files with 255 additions and 381 deletions

View File

@ -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();
@ -38,6 +40,8 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal
public void DisableBuffering()
{
BufferingEnabled = false;
BufferedStream.SetLength(0);
BufferedStream.Capacity = 0;
BufferedStream.Dispose();
}
@ -77,7 +81,14 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal
if (BufferingEnabled)
{
BufferedStream.Write(buffer, offset, count);
if (BufferedStream.Length + count > _maxBufferSize)
{
DisableBuffering();
}
else
{
BufferedStream.Write(buffer, offset, count);
}
}
}
@ -95,7 +106,14 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal
if (BufferingEnabled)
{
await BufferedStream.WriteAsync(buffer, offset, count, cancellationToken);
if (BufferedStream.Length + count > _maxBufferSize)
{
DisableBuffering();
}
else
{
await BufferedStream.WriteAsync(buffer, offset, count, cancellationToken);
}
}
}
@ -113,7 +131,14 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal
if (BufferingEnabled)
{
BufferedStream.WriteByte(value);
if (BufferedStream.Length + 1 > _maxBufferSize)
{
DisableBuffering();
}
else
{
BufferedStream.WriteByte(value);
}
}
}

View File

@ -7,6 +7,7 @@ using System.Globalization;
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;
@ -25,7 +26,7 @@ namespace Microsoft.AspNetCore.ResponseCaching
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;
@ -40,29 +41,19 @@ namespace Microsoft.AspNetCore.ResponseCaching
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;
@ -74,10 +65,7 @@ namespace Microsoft.AspNetCore.ResponseCaching
{
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;
}
@ -363,6 +351,14 @@ namespace Microsoft.AspNetCore.ResponseCaching
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;
}
@ -433,7 +429,7 @@ namespace Microsoft.AspNetCore.ResponseCaching
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;
@ -607,7 +603,7 @@ namespace Microsoft.AspNetCore.ResponseCaching
if (!ResponseStarted)
{
ResponseStarted = true;
_responseTime = _clock.UtcNow;
_responseTime = _options.SystemClock.UtcNow;
FinalizeCachingHeaders();
}
@ -619,7 +615,7 @@ namespace Microsoft.AspNetCore.ResponseCaching
// Shim response stream
OriginalResponseStream = _httpContext.Response.Body;
ResponseCacheStream = new ResponseCacheStream(OriginalResponseStream);
ResponseCacheStream = new ResponseCacheStream(OriginalResponseStream, _options.MaximumCachedBodySize);
_httpContext.Response.Body = ResponseCacheStream;
// Shim IHttpSendFileFeature

View File

@ -3,6 +3,7 @@
using System;
using Microsoft.AspNetCore.ResponseCaching;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Builder
{
@ -17,5 +18,19 @@ namespace Microsoft.AspNetCore.Builder
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));
}
}
}

View File

@ -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;
@ -20,13 +21,15 @@ namespace Microsoft.AspNetCore.ResponseCaching
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)
@ -39,6 +42,10 @@ namespace Microsoft.AspNetCore.ResponseCaching
{
throw new ArgumentNullException(nameof(cache));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (poolProvider == null)
{
throw new ArgumentNullException(nameof(poolProvider));
@ -54,6 +61,7 @@ namespace Microsoft.AspNetCore.ResponseCaching
_next = next;
_cache = cache;
_options = options.Value;
_builderPool = poolProvider.CreateStringBuilderPool();
_cacheabilityValidator = cacheabilityValidator;
_cacheKeyModifier = cacheKeyModifier;
@ -64,6 +72,7 @@ namespace Microsoft.AspNetCore.ResponseCaching
var cachingContext = new ResponseCachingContext(
context,
_cache,
_options,
_builderPool,
_cacheabilityValidator,
_cacheKeyModifier);

View File

@ -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();
}
}

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
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;
@ -859,7 +860,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
return new ResponseCachingContext(
httpContext,
new TestResponseCache(),
new SystemClock(),
new ResponseCachingOptions(),
new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy()),
cacheabilityValidator,
cacheKeyModifier);

View File

@ -21,19 +21,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
[Fact]
public async void ServesCachedContent_IfAvailable()
{
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
await context.Response.WriteAsync(uniqueId);
});
var builder = CreateBuilderWithResponseCaching();
using (var server = new TestServer(builder))
{
@ -48,19 +36,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
[Fact]
public async void ServesFreshContent_IfNotAvailable()
{
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
await context.Response.WriteAsync(uniqueId);
});
var builder = CreateBuilderWithResponseCaching();
using (var server = new TestServer(builder))
{
@ -77,17 +53,8 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
{
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
context.Response.Headers[HeaderNames.Vary] = HeaderNames.From;
await context.Response.WriteAsync(uniqueId);
await DefaultRequestDelegate(context);
});
using (var server = new TestServer(builder))
@ -106,17 +73,8 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
{
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
context.Response.Headers[HeaderNames.Vary] = HeaderNames.From;
await context.Response.WriteAsync(uniqueId);
await DefaultRequestDelegate(context);
});
using (var server = new TestServer(builder))
@ -136,17 +94,8 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
{
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
context.GetResponseCachingFeature().VaryByParams = "param";
await context.Response.WriteAsync(uniqueId);
await DefaultRequestDelegate(context);
});
using (var server = new TestServer(builder))
@ -162,28 +111,10 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
[Fact]
public async void ServesCachedContent_IfVaryByParamsExplicit_Matches_ParamNameCaseInsensitive()
{
var builder = CreateBuilderWithResponseCaching(
app =>
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
app.Use(async (context, next) =>
{
context.Features.Set<IHttpSendFileFeature>(new DummySendFileFeature());
await next.Invoke();
});
},
async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
context.GetResponseCachingFeature().VaryByParams = new[] { "ParamA", "paramb" };
await context.Response.WriteAsync(uniqueId);
await DefaultRequestDelegate(context);
});
using (var server = new TestServer(builder))
@ -199,28 +130,10 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
[Fact]
public async void ServesCachedContent_IfVaryByParamsStar_Matches_ParamNameCaseInsensitive()
{
var builder = CreateBuilderWithResponseCaching(
app =>
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
app.Use(async (context, next) =>
{
context.Features.Set<IHttpSendFileFeature>(new DummySendFileFeature());
await next.Invoke();
});
},
async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
context.GetResponseCachingFeature().VaryByParams = new[] { "*" };
await context.Response.WriteAsync(uniqueId);
await DefaultRequestDelegate(context);
});
using (var server = new TestServer(builder))
@ -238,17 +151,8 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
{
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
context.GetResponseCachingFeature().VaryByParams = new[] { "ParamB", "ParamA" };
await context.Response.WriteAsync(uniqueId);
await DefaultRequestDelegate(context);
});
using (var server = new TestServer(builder))
@ -266,17 +170,8 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
{
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
context.GetResponseCachingFeature().VaryByParams = new[] { "*" };
await context.Response.WriteAsync(uniqueId);
await DefaultRequestDelegate(context);
});
using (var server = new TestServer(builder))
@ -294,17 +189,8 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
{
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
context.GetResponseCachingFeature().VaryByParams = "param";
await context.Response.WriteAsync(uniqueId);
await DefaultRequestDelegate(context);
});
using (var server = new TestServer(builder))
@ -322,17 +208,8 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
{
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
context.GetResponseCachingFeature().VaryByParams = new[] { "ParamA", "ParamB" };
await context.Response.WriteAsync(uniqueId);
await DefaultRequestDelegate(context);
});
using (var server = new TestServer(builder))
@ -350,17 +227,8 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
{
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
context.GetResponseCachingFeature().VaryByParams = new[] { "*" };
await context.Response.WriteAsync(uniqueId);
await DefaultRequestDelegate(context);
});
using (var server = new TestServer(builder))
@ -376,19 +244,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
[Fact]
public async void ServesFreshContent_IfRequestRequirements_NotMet()
{
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
await context.Response.WriteAsync(uniqueId);
});
var builder = CreateBuilderWithResponseCaching();
using (var server = new TestServer(builder))
{
@ -407,19 +263,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
[Fact]
public async void Serves504_IfOnlyIfCachedHeader_IsSpecified()
{
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
await context.Response.WriteAsync(uniqueId);
});
var builder = CreateBuilderWithResponseCaching();
using (var server = new TestServer(builder))
{
@ -441,17 +285,8 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
{
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
headers.Headers[HeaderNames.SetCookie] = "cookieName=cookieValue";
await context.Response.WriteAsync(uniqueId);
var headers = context.Response.Headers[HeaderNames.SetCookie] = "cookieName=cookieValue";
await DefaultRequestDelegate(context);
});
using (var server = new TestServer(builder))
@ -480,28 +315,14 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
[Fact]
public async void ServesCachedContent_IfIHttpSendFileFeature_NotUsed()
{
var builder = CreateBuilderWithResponseCaching(
app =>
var builder = CreateBuilderWithResponseCaching(app =>
{
app.Use(async (context, next) =>
{
app.Use(async (context, next) =>
{
context.Features.Set<IHttpSendFileFeature>(new DummySendFileFeature());
await next.Invoke();
});
},
async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
await context.Response.WriteAsync(uniqueId);
context.Features.Set<IHttpSendFileFeature>(new DummySendFileFeature());
await next.Invoke();
});
});
using (var server = new TestServer(builder))
{
@ -527,17 +348,8 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
},
async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
await context.Features.Get<IHttpSendFileFeature>().SendFileAsync("dummy", 0, 0, CancellationToken.None);
await context.Response.WriteAsync(uniqueId);
await DefaultRequestDelegate(context);
});
using (var server = new TestServer(builder))
@ -553,20 +365,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
[Fact]
public async void ServesCachedContent_IfSubsequentRequest_ContainsNoStore()
{
var builder = CreateBuilderWithResponseCaching(
async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
await context.Response.WriteAsync(uniqueId);
});
var builder = CreateBuilderWithResponseCaching();
using (var server = new TestServer(builder))
{
@ -585,20 +384,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
[Fact]
public async void ServesFreshContent_IfInitialRequestContains_NoStore()
{
var builder = CreateBuilderWithResponseCaching(
async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
await context.Response.WriteAsync(uniqueId);
});
var builder = CreateBuilderWithResponseCaching();
using (var server = new TestServer(builder))
{
@ -614,6 +400,116 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
}
}
[Fact]
public async void Serves304_IfIfModifiedSince_Satisfied()
{
var builder = CreateBuilderWithResponseCaching();
using (var server = new TestServer(builder))
{
var client = server.CreateClient();
var initialResponse = await client.GetAsync("");
client.DefaultRequestHeaders.IfUnmodifiedSince = DateTimeOffset.MaxValue;
var subsequentResponse = await client.GetAsync("");
initialResponse.EnsureSuccessStatusCode();
Assert.Equal(System.Net.HttpStatusCode.NotModified, subsequentResponse.StatusCode);
}
}
[Fact]
public async void ServesCachedContent_IfIfModifiedSince_NotSatisfied()
{
var builder = CreateBuilderWithResponseCaching();
using (var server = new TestServer(builder))
{
var client = server.CreateClient();
var initialResponse = await client.GetAsync("");
client.DefaultRequestHeaders.IfUnmodifiedSince = DateTimeOffset.MinValue;
var subsequentResponse = await client.GetAsync("");
await AssertResponseCachedAsync(initialResponse, subsequentResponse);
}
}
[Fact]
public async void Serves304_IfIfNoneMatch_Satisfied()
{
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
var headers = context.Response.GetTypedHeaders().ETag = new EntityTagHeaderValue("\"E1\"");
await DefaultRequestDelegate(context);
});
using (var server = new TestServer(builder))
{
var client = server.CreateClient();
var initialResponse = await client.GetAsync("");
client.DefaultRequestHeaders.IfNoneMatch.Add(new System.Net.Http.Headers.EntityTagHeaderValue("\"E1\""));
var subsequentResponse = await client.GetAsync("");
initialResponse.EnsureSuccessStatusCode();
Assert.Equal(System.Net.HttpStatusCode.NotModified, subsequentResponse.StatusCode);
}
}
[Fact]
public async void ServesCachedContent_IfIfNoneMatch_NotSatisfied()
{
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
var headers = context.Response.GetTypedHeaders().ETag = new EntityTagHeaderValue("\"E1\"");
await DefaultRequestDelegate(context);
});
using (var server = new TestServer(builder))
{
var client = server.CreateClient();
var initialResponse = await client.GetAsync("");
client.DefaultRequestHeaders.IfNoneMatch.Add(new System.Net.Http.Headers.EntityTagHeaderValue("\"E2\""));
var subsequentResponse = await client.GetAsync("");
await AssertResponseCachedAsync(initialResponse, subsequentResponse);
}
}
[Fact]
public async void ServesCachedContent_IfBodySize_IsCacheable()
{
var builder = CreateBuilderWithResponseCaching(new ResponseCachingOptions()
{
MaximumCachedBodySize = 100
});
using (var server = new TestServer(builder))
{
var client = server.CreateClient();
var initialResponse = await client.GetAsync("");
var subsequentResponse = await client.GetAsync("");
await AssertResponseCachedAsync(initialResponse, subsequentResponse);
}
}
[Fact]
public async void ServesFreshContent_IfBodySize_IsNotCacheable()
{
var builder = CreateBuilderWithResponseCaching(new ResponseCachingOptions()
{
MaximumCachedBodySize = 1
});
using (var server = new TestServer(builder))
{
var client = server.CreateClient();
var initialResponse = await client.GetAsync("");
var subsequentResponse = await client.GetAsync("/different");
await AssertResponseNotCachedAsync(initialResponse, subsequentResponse);
}
}
private static async Task AssertResponseCachedAsync(HttpResponseMessage initialResponse, HttpResponseMessage subsequentResponse)
{
initialResponse.EnsureSuccessStatusCode();
@ -636,126 +532,36 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
Assert.NotEqual(await initialResponse.Content.ReadAsStringAsync(), await subsequentResponse.Content.ReadAsStringAsync());
}
[Fact]
public async void Serves304_IfIfModifiedSince_Satisfied()
private static RequestDelegate DefaultRequestDelegate = async (context) =>
{
var builder = CreateBuilderWithResponseCaching(async (context) =>
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
await context.Response.WriteAsync(uniqueId);
});
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
await context.Response.WriteAsync(uniqueId);
};
using (var server = new TestServer(builder))
{
var client = server.CreateClient();
var initialResponse = await client.GetAsync("");
client.DefaultRequestHeaders.IfUnmodifiedSince = DateTimeOffset.MaxValue;
var subsequentResponse = await client.GetAsync("");
private static IWebHostBuilder CreateBuilderWithResponseCaching() =>
CreateBuilderWithResponseCaching(app => { }, new ResponseCachingOptions(), DefaultRequestDelegate);
initialResponse.EnsureSuccessStatusCode();
Assert.Equal(System.Net.HttpStatusCode.NotModified, subsequentResponse.StatusCode);
}
}
[Fact]
public async void ServesCachedContent_IfIfModifiedSince_NotSatisfied()
{
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
await context.Response.WriteAsync(uniqueId);
});
using (var server = new TestServer(builder))
{
var client = server.CreateClient();
var initialResponse = await client.GetAsync("");
client.DefaultRequestHeaders.IfUnmodifiedSince = DateTimeOffset.MinValue;
var subsequentResponse = await client.GetAsync("");
await AssertResponseCachedAsync(initialResponse, subsequentResponse);
}
}
[Fact]
public async void Serves304_IfIfNoneMatch_Satisfied()
{
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
headers.ETag = new EntityTagHeaderValue("\"E1\"");
await context.Response.WriteAsync(uniqueId);
});
using (var server = new TestServer(builder))
{
var client = server.CreateClient();
var initialResponse = await client.GetAsync("");
client.DefaultRequestHeaders.IfNoneMatch.Add(new System.Net.Http.Headers.EntityTagHeaderValue("\"E1\""));
var subsequentResponse = await client.GetAsync("");
initialResponse.EnsureSuccessStatusCode();
Assert.Equal(System.Net.HttpStatusCode.NotModified, subsequentResponse.StatusCode);
}
}
[Fact]
public async void ServesCachedContent_IfIfNoneMatch_NotSatisfied()
{
var builder = CreateBuilderWithResponseCaching(async (context) =>
{
var uniqueId = Guid.NewGuid().ToString();
var headers = context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
headers.Date = DateTimeOffset.UtcNow;
headers.Headers["X-Value"] = uniqueId;
headers.ETag = new EntityTagHeaderValue("\"E1\"");
await context.Response.WriteAsync(uniqueId);
});
using (var server = new TestServer(builder))
{
var client = server.CreateClient();
var initialResponse = await client.GetAsync("");
client.DefaultRequestHeaders.IfNoneMatch.Add(new System.Net.Http.Headers.EntityTagHeaderValue("\"E2\""));
var subsequentResponse = await client.GetAsync("");
await AssertResponseCachedAsync(initialResponse, subsequentResponse);
}
}
private static IWebHostBuilder CreateBuilderWithResponseCaching(ResponseCachingOptions options) =>
CreateBuilderWithResponseCaching(app => { }, options, DefaultRequestDelegate);
private static IWebHostBuilder CreateBuilderWithResponseCaching(RequestDelegate requestDelegate) =>
CreateBuilderWithResponseCaching(app => { }, requestDelegate);
CreateBuilderWithResponseCaching(app => { }, new ResponseCachingOptions(), requestDelegate);
private static IWebHostBuilder CreateBuilderWithResponseCaching(Action<IApplicationBuilder> configureDelegate, RequestDelegate requestDelegate)
private static IWebHostBuilder CreateBuilderWithResponseCaching(Action<IApplicationBuilder> configureDelegate) =>
CreateBuilderWithResponseCaching(configureDelegate, new ResponseCachingOptions(), DefaultRequestDelegate);
private static IWebHostBuilder CreateBuilderWithResponseCaching(Action<IApplicationBuilder> configureDelegate, RequestDelegate requestDelegate) =>
CreateBuilderWithResponseCaching(configureDelegate, new ResponseCachingOptions(), requestDelegate);
private static IWebHostBuilder CreateBuilderWithResponseCaching(Action<IApplicationBuilder> configureDelegate, ResponseCachingOptions options, RequestDelegate requestDelegate)
{
return new WebHostBuilder()
.ConfigureServices(services =>
@ -765,7 +571,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Tests
.Configure(app =>
{
configureDelegate(app);
app.UseResponseCaching();
app.UseResponseCaching(options);
app.Run(requestDelegate);
});
}