Adding options to specify maximum response body size
This commit is contained in:
parent
3b0f01a8ec
commit
d72ef128dd
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue