Don't allocate the FormFeature eagerly (#6511)

- Expose FormOptions on DefaultHttpContext
- Use those options on DefaultHttpContext when the FormFeature is initialized
This commit is contained in:
David Fowler 2019-01-09 14:44:41 -08:00 committed by GitHub
parent b6bdffe247
commit ea344bf726
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 31 additions and 12 deletions

View File

@ -62,6 +62,8 @@ namespace Microsoft.AspNetCore.Http
_websockets?.Uninitialize();
}
public FormOptions FormOptions { get; set; }
private IItemsFeature ItemsFeature =>
_features.Fetch(ref _features.Cache.Items, _newItemsFeature);

View File

@ -15,8 +15,6 @@ namespace Microsoft.AspNetCore.Http.Features
{
public class FormFeature : IFormFeature
{
private static readonly FormOptions DefaultFormOptions = new FormOptions();
private readonly HttpRequest _request;
private readonly FormOptions _options;
private Task<IFormCollection> _parsedFormTask;
@ -32,7 +30,7 @@ namespace Microsoft.AspNetCore.Http.Features
Form = form;
}
public FormFeature(HttpRequest request)
: this(request, DefaultFormOptions)
: this(request, FormOptions.Default)
{
}

View File

@ -1,4 +1,4 @@
// 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.
using System.IO;
@ -8,6 +8,8 @@ namespace Microsoft.AspNetCore.Http.Features
{
public class FormOptions
{
internal static readonly FormOptions Default = new FormOptions();
public const int DefaultMemoryBufferThreshold = 1024 * 64;
public const int DefaultBufferBodyLengthLimit = 1024 * 1024 * 128;
public const int DefaultMultipartBoundaryLengthLimit = 128;

View File

@ -42,13 +42,12 @@ namespace Microsoft.AspNetCore.Http
_httpContextAccessor.HttpContext = httpContext;
}
var formFeature = new FormFeature(httpContext.Request, _formOptions);
featureCollection.Set<IFormFeature>(formFeature);
httpContext.FormOptions = _formOptions;
return httpContext;
}
private static HttpContext CreateHttpContext(IFeatureCollection featureCollection)
private static DefaultHttpContext CreateHttpContext(IFeatureCollection featureCollection)
{
if (featureCollection is IHttpContextContainer container)
{

View File

@ -6,6 +6,6 @@ namespace Microsoft.AspNetCore.Http
{
public interface IHttpContextContainer
{
HttpContext HttpContext { get; }
DefaultHttpContext HttpContext { get; }
}
}

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Http.Internal
// Lambdas hoisted to static readonly fields to improve inlining https://github.com/dotnet/roslyn/issues/13624
private readonly static Func<IFeatureCollection, IHttpRequestFeature> _nullRequestFeature = f => null;
private readonly static Func<IFeatureCollection, IQueryFeature> _newQueryFeature = f => new QueryFeature(f);
private readonly static Func<HttpRequest, IFormFeature> _newFormFeature = r => new FormFeature(r);
private readonly static Func<DefaultHttpRequest, IFormFeature> _newFormFeature = r => new FormFeature(r, r._context.FormOptions ?? FormOptions.Default);
private readonly static Func<IFeatureCollection, IRequestCookiesFeature> _newRequestCookiesFeature = f => new RequestCookiesFeature(f);
private readonly static Func<IFeatureCollection, IRouteValuesFeature> _newRouteValuesFeature = f => new RouteValuesFeature();
private readonly static Func<HttpContext, IRequestBodyPipeFeature> _newRequestBodyPipeFeature = context => new RequestBodyPipeFeature(context);

View File

@ -29,6 +29,24 @@ namespace Microsoft.AspNetCore.Http.Features
Assert.Same(FormCollection.Empty, formCollection);
}
[Fact]
public async Task FormFeatureReadsOptionsFromDefaultHttpContext()
{
var context = new DefaultHttpContext();
context.Request.ContentType = "application/x-www-form-urlencoded; charset=utf-8";
context.FormOptions = new FormOptions
{
ValueCountLimit = 1
};
var formContent = Encoding.UTF8.GetBytes("foo=bar&baz=2");
context.Request.Body = new NonSeekableReadStream(formContent);
var exception = await Assert.ThrowsAsync<InvalidDataException>(() => context.Request.ReadFormAsync());
Assert.Equal("Form value count limit 1 exceeded.", exception.Message);
}
[Theory]
[InlineData(true)]
[InlineData(false)]
@ -391,7 +409,7 @@ namespace Microsoft.AspNetCore.Http.Features
IFormFeature formFeature = new FormFeature(context.Request, new FormOptions() { BufferBody = bufferRequest, ValueCountLimit = 2 });
context.Features.Set<IFormFeature>(formFeature);
var exception = await Assert.ThrowsAsync<InvalidDataException> (() => context.Request.ReadFormAsync());
var exception = await Assert.ThrowsAsync<InvalidDataException>(() => context.Request.ReadFormAsync());
Assert.Equal("Form value count limit 2 exceeded.", exception.Message);
}
@ -416,7 +434,7 @@ namespace Microsoft.AspNetCore.Http.Features
IFormFeature formFeature = new FormFeature(context.Request, new FormOptions() { BufferBody = bufferRequest, ValueCountLimit = 2 });
context.Features.Set<IFormFeature>(formFeature);
var exception = await Assert.ThrowsAsync<InvalidDataException> (() => context.Request.ReadFormAsync());
var exception = await Assert.ThrowsAsync<InvalidDataException>(() => context.Request.ReadFormAsync());
Assert.Equal("Form value count limit 2 exceeded.", exception.Message);
}

View File

@ -277,7 +277,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
protected HttpResponseHeaders HttpResponseHeaders { get; } = new HttpResponseHeaders();
HttpContext IHttpContextContainer.HttpContext
DefaultHttpContext IHttpContextContainer.HttpContext
{
get
{