Changed RequestSizeLimitAttribute to create an authorization filter rather than a resource filter.

[Fixes #6777] RequestSizeLimit is ignored
This commit is contained in:
Kiran Challa 2017-09-09 12:27:40 -07:00
parent de38922601
commit 06f6de6c11
10 changed files with 332 additions and 92 deletions

View File

@ -199,10 +199,10 @@ namespace Microsoft.Extensions.DependencyInjection
ServiceDescriptor.Singleton<IFilterProvider, DefaultFilterProvider>()); ServiceDescriptor.Singleton<IFilterProvider, DefaultFilterProvider>());
// //
// Resource Filters // RequestSizeLimit filters
// //
services.TryAddTransient<RequestSizeLimitResourceFilter>(); services.TryAddTransient<RequestSizeLimitFilter>();
services.TryAddTransient<DisableRequestSizeLimitResourceFilter>(); services.TryAddTransient<DisableRequestSizeLimitFilter>();
// //
// ModelBinding, Validation // ModelBinding, Validation

View File

@ -14,8 +14,23 @@ namespace Microsoft.AspNetCore.Mvc
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class DisableRequestSizeLimitAttribute : Attribute, IFilterFactory, IOrderedFilter public class DisableRequestSizeLimitAttribute : Attribute, IFilterFactory, IOrderedFilter
{ {
/// <inheritdoc /> /// <summary>
public int Order { get; set; } /// Gets the order value for determining the order of execution of filters. Filters execute in
/// ascending numeric value of the <see cref="Order"/> property.
/// </summary>
/// <remarks>
/// <para>
/// Filters are executed in an ordering determined by an ascending sort of the <see cref="Order"/> property.
/// </para>
/// <para>
/// The default Order for this attribute is 900 because it must run before ValidateAntiForgeryTokenAttribute and
/// after any filter which does authentication or login in order to allow them to behave as expected (ie Unauthenticated or Redirect instead of 400).
/// </para>
/// <para>
/// Look at <see cref="IOrderedFilter.Order"/> for more detailed info.
/// </para>
/// </remarks>
public int Order { get; set; } = 900;
/// <inheritdoc /> /// <inheritdoc />
public bool IsReusable => true; public bool IsReusable => true;
@ -23,7 +38,7 @@ namespace Microsoft.AspNetCore.Mvc
/// <inheritdoc /> /// <inheritdoc />
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{ {
var filter = serviceProvider.GetRequiredService<DisableRequestSizeLimitResourceFilter>(); var filter = serviceProvider.GetRequiredService<DisableRequestSizeLimitFilter>();
return filter; return filter;
} }
} }

View File

@ -14,34 +14,26 @@ namespace Microsoft.AspNetCore.Mvc.Internal
/// A filter that sets <see cref="IHttpMaxRequestBodySizeFeature.MaxRequestBodySize"/> /// A filter that sets <see cref="IHttpMaxRequestBodySizeFeature.MaxRequestBodySize"/>
/// to <c>null</c>. /// to <c>null</c>.
/// </summary> /// </summary>
public class DisableRequestSizeLimitResourceFilter : IResourceFilter, IRequestSizePolicy public class DisableRequestSizeLimitFilter : IAuthorizationFilter, IRequestSizePolicy
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
/// <summary> /// <summary>
/// Creates a new instance of <see cref="DisableRequestSizeLimitResourceFilter"/>. /// Creates a new instance of <see cref="DisableRequestSizeLimitFilter"/>.
/// </summary> /// </summary>
public DisableRequestSizeLimitResourceFilter(ILoggerFactory loggerFactory) public DisableRequestSizeLimitFilter(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<DisableRequestSizeLimitResourceFilter>();
}
/// <inheritdoc />
public int Order { get; set; }
/// <inheritdoc />
public void OnResourceExecuted(ResourceExecutedContext context)
{ {
_logger = loggerFactory.CreateLogger<DisableRequestSizeLimitFilter>();
} }
/// <summary> /// <summary>
/// Sets the <see cref="IHttpMaxRequestBodySizeFeature.MaxRequestBodySize"/> /// Sets the <see cref="IHttpMaxRequestBodySizeFeature.MaxRequestBodySize"/>
/// to <c>null</c>. /// to <c>null</c>.
/// </summary> /// </summary>
/// <param name="context">The <see cref="ResourceExecutingContext"/>.</param> /// <param name="context">The <see cref="AuthorizationFilterContext"/>.</param>
/// <remarks>If <see cref="IHttpMaxRequestBodySizeFeature"/> is not enabled or is read-only, /// <remarks>If <see cref="IHttpMaxRequestBodySizeFeature"/> is not enabled or is read-only,
/// the <see cref="DisableRequestSizeLimitAttribute"/> is not applied.</remarks> /// the <see cref="DisableRequestSizeLimitAttribute"/> is not applied.</remarks>
public void OnResourceExecuting(ResourceExecutingContext context) public void OnAuthorization(AuthorizationFilterContext context)
{ {
if (context == null) if (context == null)
{ {

View File

@ -14,32 +14,27 @@ namespace Microsoft.AspNetCore.Mvc.Internal
/// A filter that sets the <see cref="IHttpMaxRequestBodySizeFeature.MaxRequestBodySize"/> /// A filter that sets the <see cref="IHttpMaxRequestBodySizeFeature.MaxRequestBodySize"/>
/// to the specified <see cref="Bytes"/>. /// to the specified <see cref="Bytes"/>.
/// </summary> /// </summary>
public class RequestSizeLimitResourceFilter : IResourceFilter, IRequestSizePolicy public class RequestSizeLimitFilter : IAuthorizationFilter, IRequestSizePolicy
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
/// <summary> /// <summary>
/// Creates a new instance of <see cref="RequestSizeLimitResourceFilter"/>. /// Creates a new instance of <see cref="RequestSizeLimitFilter"/>.
/// </summary> /// </summary>
public RequestSizeLimitResourceFilter(ILoggerFactory loggerFactory) public RequestSizeLimitFilter(ILoggerFactory loggerFactory)
{ {
_logger = loggerFactory.CreateLogger<RequestSizeLimitResourceFilter>(); _logger = loggerFactory.CreateLogger<RequestSizeLimitFilter>();
} }
public long Bytes { get; set; } public long Bytes { get; set; }
/// <inheritdoc />
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
/// <summary> /// <summary>
/// Sets the <see cref="IHttpMaxRequestBodySizeFeature.MaxRequestBodySize"/> to <see cref="Bytes"/>. /// Sets the <see cref="IHttpMaxRequestBodySizeFeature.MaxRequestBodySize"/> to <see cref="Bytes"/>.
/// </summary> /// </summary>
/// <param name="context">The <see cref="ResourceExecutingContext"/>.</param> /// <param name="context">The <see cref="AuthorizationFilterContext"/>.</param>
/// <remarks>If <see cref="IHttpMaxRequestBodySizeFeature"/> is not enabled or is read-only, /// <remarks>If <see cref="IHttpMaxRequestBodySizeFeature"/> is not enabled or is read-only,
/// the <see cref="RequestSizeLimitAttribute"/> is not applied.</remarks> /// the <see cref="RequestSizeLimitAttribute"/> is not applied.</remarks>
public void OnResourceExecuting(ResourceExecutingContext context) public void OnAuthorization(AuthorizationFilterContext context)
{ {
if (context == null) if (context == null)
{ {

View File

@ -25,8 +25,23 @@ namespace Microsoft.AspNetCore.Mvc
_bytes = bytes; _bytes = bytes;
} }
/// <inheritdoc /> /// <summary>
public int Order { get; set; } /// Gets the order value for determining the order of execution of filters. Filters execute in
/// ascending numeric value of the <see cref="Order"/> property.
/// </summary>
/// <remarks>
/// <para>
/// Filters are executed in an ordering determined by an ascending sort of the <see cref="Order"/> property.
/// </para>
/// <para>
/// The default Order for this attribute is 900 because it must run before ValidateAntiForgeryTokenAttribute and
/// after any filter which does authentication or login in order to allow them to behave as expected (ie Unauthenticated or Redirect instead of 400).
/// </para>
/// <para>
/// Look at <see cref="IOrderedFilter.Order"/> for more detailed info.
/// </para>
/// </remarks>
public int Order { get; set; } = 900;
/// <inheritdoc /> /// <inheritdoc />
public bool IsReusable => true; public bool IsReusable => true;
@ -34,7 +49,7 @@ namespace Microsoft.AspNetCore.Mvc
/// <inheritdoc /> /// <inheritdoc />
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{ {
var filter = serviceProvider.GetRequiredService<RequestSizeLimitResourceFilter>(); var filter = serviceProvider.GetRequiredService<RequestSizeLimitFilter>();
filter.Bytes = _bytes; filter.Bytes = _bytes;
return filter; return filter;
} }

View File

@ -1,12 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging.Testing; using Microsoft.Extensions.Logging.Testing;
@ -14,20 +12,20 @@ using Xunit;
namespace Microsoft.AspNetCore.Mvc.Internal namespace Microsoft.AspNetCore.Mvc.Internal
{ {
public class DisableRequestSizeLimitResourceFilterTest public class DisableRequestSizeLimitFilterTest
{ {
[Fact] [Fact]
public void SetsMaxRequestBodySizeToNull() public void SetsMaxRequestBodySizeToNull()
{ {
// Arrange // Arrange
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitResourceFilter(NullLoggerFactory.Instance); var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(NullLoggerFactory.Instance);
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter }); var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature(); var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
resourceExecutingContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize); authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
// Act // Act
disableRequestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext); disableRequestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
// Assert // Assert
Assert.Null(httpMaxRequestBodySize.MaxRequestBodySize); Assert.Null(httpMaxRequestBodySize.MaxRequestBodySize);
@ -37,16 +35,17 @@ namespace Microsoft.AspNetCore.Mvc.Internal
public void SkipsWhenOverridden() public void SkipsWhenOverridden()
{ {
// Arrange // Arrange
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitResourceFilter(NullLoggerFactory.Instance); var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(NullLoggerFactory.Instance);
var disableRequestSizeLimitResourceFilterFinal = new DisableRequestSizeLimitResourceFilter(NullLoggerFactory.Instance); var disableRequestSizeLimitResourceFilterFinal = new DisableRequestSizeLimitFilter(NullLoggerFactory.Instance);
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter, disableRequestSizeLimitResourceFilterFinal }); var authorizationFilterContext = CreateauthorizationFilterContext(
new IFilterMetadata[] { disableRequestSizeLimitResourceFilter, disableRequestSizeLimitResourceFilterFinal });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature(); var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
resourceExecutingContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize); authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
// Act // Act
disableRequestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext); disableRequestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
disableRequestSizeLimitResourceFilterFinal.OnResourceExecuting(resourceExecutingContext); disableRequestSizeLimitResourceFilterFinal.OnAuthorization(authorizationFilterContext);
// Assert // Assert
Assert.Null(httpMaxRequestBodySize.MaxRequestBodySize); Assert.Null(httpMaxRequestBodySize.MaxRequestBodySize);
@ -60,11 +59,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var sink = new TestSink(); var sink = new TestSink();
var loggerFactory = new TestLoggerFactory(sink, enabled: true); var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitResourceFilter(loggerFactory); var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(loggerFactory);
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter }); var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
// Act // Act
disableRequestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext); disableRequestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
// Assert // Assert
var write = Assert.Single(sink.Writes); var write = Assert.Single(sink.Writes);
@ -79,15 +78,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var sink = new TestSink(); var sink = new TestSink();
var loggerFactory = new TestLoggerFactory(sink, enabled: true); var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitResourceFilter(loggerFactory); var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(loggerFactory);
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter }); var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature(); var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
httpMaxRequestBodySize.IsReadOnly = true; httpMaxRequestBodySize.IsReadOnly = true;
resourceExecutingContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize); authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
// Act // Act
disableRequestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext); disableRequestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
// Assert // Assert
var write = Assert.Single(sink.Writes); var write = Assert.Single(sink.Writes);
@ -101,26 +100,23 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var sink = new TestSink(); var sink = new TestSink();
var loggerFactory = new TestLoggerFactory(sink, enabled: true); var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitResourceFilter(loggerFactory); var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(loggerFactory);
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter }); var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature(); var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
resourceExecutingContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize); authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
// Act // Act
disableRequestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext); disableRequestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
// Assert // Assert
var write = Assert.Single(sink.Writes); var write = Assert.Single(sink.Writes);
Assert.Equal($"The request body size limit has been disabled.", write.State.ToString()); Assert.Equal($"The request body size limit has been disabled.", write.State.ToString());
} }
private static ResourceExecutingContext CreateResourceExecutingContext(IFilterMetadata[] filters) private static AuthorizationFilterContext CreateauthorizationFilterContext(IFilterMetadata[] filters)
{ {
return new ResourceExecutingContext( return new AuthorizationFilterContext(CreateActionContext(), filters);
CreateActionContext(),
filters,
new List<IValueProviderFactory>());
} }
private static ActionContext CreateActionContext() private static ActionContext CreateActionContext()

View File

@ -1,12 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging.Testing; using Microsoft.Extensions.Logging.Testing;
@ -14,21 +12,21 @@ using Xunit;
namespace Microsoft.AspNetCore.Mvc.Internal namespace Microsoft.AspNetCore.Mvc.Internal
{ {
public class RequestSizeLimitResourceFilterTest public class RequestSizeLimitFilterTest
{ {
[Fact] [Fact]
public void SetsMaxRequestBodySize() public void SetsMaxRequestBodySize()
{ {
// Arrange // Arrange
var requestSizeLimitResourceFilter = new RequestSizeLimitResourceFilter(NullLoggerFactory.Instance); var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(NullLoggerFactory.Instance);
requestSizeLimitResourceFilter.Bytes = 12345; requestSizeLimitResourceFilter.Bytes = 12345;
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { requestSizeLimitResourceFilter }); var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature(); var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
resourceExecutingContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize); authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
// Act // Act
requestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext); requestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
// Assert // Assert
Assert.Equal(12345, httpMaxRequestBodySize.MaxRequestBodySize); Assert.Equal(12345, httpMaxRequestBodySize.MaxRequestBodySize);
@ -38,18 +36,19 @@ namespace Microsoft.AspNetCore.Mvc.Internal
public void SkipsWhenOverridden() public void SkipsWhenOverridden()
{ {
// Arrange // Arrange
var requestSizeLimitResourceFilter = new RequestSizeLimitResourceFilter(NullLoggerFactory.Instance); var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(NullLoggerFactory.Instance);
requestSizeLimitResourceFilter.Bytes = 12345; requestSizeLimitResourceFilter.Bytes = 12345;
var requestSizeLimitResourceFilterFinal = new RequestSizeLimitResourceFilter(NullLoggerFactory.Instance); var requestSizeLimitResourceFilterFinal = new RequestSizeLimitFilter(NullLoggerFactory.Instance);
requestSizeLimitResourceFilterFinal.Bytes = 0; requestSizeLimitResourceFilterFinal.Bytes = 0;
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { requestSizeLimitResourceFilter, requestSizeLimitResourceFilterFinal }); var authorizationFilterContext = CreateauthorizationFilterContext(
new IFilterMetadata[] { requestSizeLimitResourceFilter, requestSizeLimitResourceFilterFinal });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature(); var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
resourceExecutingContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize); authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
// Act // Act
requestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext); requestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
requestSizeLimitResourceFilterFinal.OnResourceExecuting(resourceExecutingContext); requestSizeLimitResourceFilterFinal.OnAuthorization(authorizationFilterContext);
// Assert // Assert
Assert.Equal(0, httpMaxRequestBodySize.MaxRequestBodySize); Assert.Equal(0, httpMaxRequestBodySize.MaxRequestBodySize);
@ -63,12 +62,12 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var sink = new TestSink(); var sink = new TestSink();
var loggerFactory = new TestLoggerFactory(sink, enabled: true); var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var requestSizeLimitResourceFilter = new RequestSizeLimitResourceFilter(loggerFactory); var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(loggerFactory);
requestSizeLimitResourceFilter.Bytes = 12345; requestSizeLimitResourceFilter.Bytes = 12345;
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { requestSizeLimitResourceFilter }); var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
// Act // Act
requestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext); requestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
// Assert // Assert
var write = Assert.Single(sink.Writes); var write = Assert.Single(sink.Writes);
@ -83,16 +82,16 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var sink = new TestSink(); var sink = new TestSink();
var loggerFactory = new TestLoggerFactory(sink, enabled: true); var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var requestSizeLimitResourceFilter = new RequestSizeLimitResourceFilter(loggerFactory); var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(loggerFactory);
requestSizeLimitResourceFilter.Bytes = 12345; requestSizeLimitResourceFilter.Bytes = 12345;
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { requestSizeLimitResourceFilter }); var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature(); var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
httpMaxRequestBodySize.IsReadOnly = true; httpMaxRequestBodySize.IsReadOnly = true;
resourceExecutingContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize); authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
// Act // Act
requestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext); requestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
// Assert // Assert
var write = Assert.Single(sink.Writes); var write = Assert.Single(sink.Writes);
@ -106,27 +105,24 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var sink = new TestSink(); var sink = new TestSink();
var loggerFactory = new TestLoggerFactory(sink, enabled: true); var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var requestSizeLimitResourceFilter = new RequestSizeLimitResourceFilter(loggerFactory); var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(loggerFactory);
requestSizeLimitResourceFilter.Bytes = 12345; requestSizeLimitResourceFilter.Bytes = 12345;
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { requestSizeLimitResourceFilter }); var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature(); var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
resourceExecutingContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize); authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
// Act // Act
requestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext); requestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
// Assert // Assert
var write = Assert.Single(sink.Writes); var write = Assert.Single(sink.Writes);
Assert.Equal($"The maximum request body size has been set to 12345.", write.State.ToString()); Assert.Equal($"The maximum request body size has been set to 12345.", write.State.ToString());
} }
private static ResourceExecutingContext CreateResourceExecutingContext(IFilterMetadata[] filters) private static AuthorizationFilterContext CreateauthorizationFilterContext(IFilterMetadata[] filters)
{ {
return new ResourceExecutingContext( return new AuthorizationFilterContext(CreateActionContext(), filters);
CreateActionContext(),
filters,
new List<IValueProviderFactory>());
} }
private static ActionContext CreateActionContext() private static ActionContext CreateActionContext()

View File

@ -0,0 +1,90 @@
// 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;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class RequestSizeLimitTest : IClassFixture<MvcTestFixture<BasicWebSite.StartupRequestLimitSize>>
{
// Some tests require comparing the actual response body against an expected response baseline
// so they require a reference to the assembly on which the resources are located, in order to
// make the tests less verbose, we get a reference to the assembly with the resources and we
// use it on all the rest of the tests.
private static readonly Assembly _resourcesAssembly = typeof(BasicTests).GetTypeInfo().Assembly;
public RequestSizeLimitTest(MvcTestFixture<BasicWebSite.StartupRequestLimitSize> fixture)
{
Client = fixture.Client;
}
public HttpClient Client { get; }
[Fact]
public async Task RequestSizeLimitCheckHappens_BeforeAntiforgeryTokenValidation()
{
// Arrange
var request = new HttpRequestMessage();
var kvps = new List<KeyValuePair<string, string>>();
kvps.Add(new KeyValuePair<string, string>("SampleString", new string('p', 1024)));
kvps.Add(new KeyValuePair<string, string>("RequestVerificationToken", "invalid-data"));
// Act
var response = await Client.PostAsync(
"RequestSizeLimit/RequestSizeLimitCheckBeforeAntiforgeryValidation",
new FormUrlEncodedContent(kvps));
// Assert
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
var result = await response.Content.ReadAsStringAsync();
Assert.Contains(
"InvalidOperationException: Request content size is greater than the limit size",
result);
}
[Fact]
public async Task AntiforgeryTokenValidationHappens_AfterRequestSizeLimitCheck()
{
// Arrange
var request = new HttpRequestMessage();
var kvps = new List<KeyValuePair<string, string>>();
kvps.Add(new KeyValuePair<string, string>("SampleString", "string"));
kvps.Add(new KeyValuePair<string, string>("RequestVerificationToken", "invalid-data"));
// Act
var response = await Client.PostAsync(
"RequestSizeLimit/RequestSizeLimitCheckBeforeAntiforgeryValidation",
new FormUrlEncodedContent(kvps));
// Assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
[Fact]
public async Task DisableRequestSizeLimitOnAction_OverridesControllerLevelSettings()
{
// Arrange
var expected = $"{{\"sampleInt\":10,\"sampleString\":\"{new string('p', 1024)}\"}}";
var request = new HttpRequestMessage();
request.Method = HttpMethod.Post;
request.Content = new StringContent(expected, Encoding.UTF8, "text/json");
request.RequestUri = new Uri("http://localhost/RequestSizeLimit/DisableRequestSizeLimit");
// Act
var response = await Client.SendAsync(request);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var actual = await response.Content.ReadAsStringAsync();
Assert.Equal(expected, actual);
}
}
}

View File

@ -0,0 +1,37 @@
// 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;
using BasicWebSite.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace BasicWebSite.Controllers
{
[RequestSizeLimit(500)]
public class RequestSizeLimitController : Controller
{
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult RequestSizeLimitCheckBeforeAntiforgeryValidation(Product product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return Json(product);
}
[HttpPost]
[DisableRequestSizeLimit]
public IActionResult DisableRequestSizeLimit([FromBody] Product product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return Json(product);
}
}
}

View File

@ -0,0 +1,104 @@
// 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;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.DependencyInjection;
namespace BasicWebSite
{
public class StartupRequestLimitSize
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();
app.Use((httpContext, next) =>
{
var testHttpMaxRequestBodySizeFeature = new TestHttpMaxRequestBodySizeFeature();
httpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(
testHttpMaxRequestBodySizeFeature);
httpContext.Request.Body = new RequestBodySizeCheckingStream(
httpContext.Request.Body,
testHttpMaxRequestBodySizeFeature);
return next();
});
app.UseMvcWithDefaultRoute();
}
private class RequestBodySizeCheckingStream : Stream
{
private readonly Stream _innerStream;
private readonly IHttpMaxRequestBodySizeFeature _maxRequestBodySizeFeature;
public RequestBodySizeCheckingStream(
Stream innerStream,
IHttpMaxRequestBodySizeFeature maxRequestBodySizeFeature)
{
_innerStream = innerStream;
_maxRequestBodySizeFeature = maxRequestBodySizeFeature;
}
public override bool CanRead => _innerStream.CanRead;
public override bool CanSeek => _innerStream.CanSeek;
public override bool CanWrite => _innerStream.CanWrite;
public override long Length => _innerStream.Length;
public override long Position
{
get { return _innerStream.Position; }
set { _innerStream.Position = value; }
}
public override void Flush()
{
_innerStream.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
if (_maxRequestBodySizeFeature.MaxRequestBodySize != null
&& _innerStream.Length > _maxRequestBodySizeFeature.MaxRequestBodySize)
{
throw new InvalidOperationException("Request content size is greater than the limit size");
}
return _innerStream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return _innerStream.Seek(offset, origin);
}
public override void SetLength(long value)
{
_innerStream.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
_innerStream.Write(buffer, offset, count);
}
}
private class TestHttpMaxRequestBodySizeFeature : IHttpMaxRequestBodySizeFeature
{
public bool IsReadOnly => false;
public long? MaxRequestBodySize { get; set; }
}
}
}