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:
parent
b6bdffe247
commit
ea344bf726
|
|
@ -62,6 +62,8 @@ namespace Microsoft.AspNetCore.Http
|
||||||
_websockets?.Uninitialize();
|
_websockets?.Uninitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FormOptions FormOptions { get; set; }
|
||||||
|
|
||||||
private IItemsFeature ItemsFeature =>
|
private IItemsFeature ItemsFeature =>
|
||||||
_features.Fetch(ref _features.Cache.Items, _newItemsFeature);
|
_features.Fetch(ref _features.Cache.Items, _newItemsFeature);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ namespace Microsoft.AspNetCore.Http.Features
|
||||||
{
|
{
|
||||||
public class FormFeature : IFormFeature
|
public class FormFeature : IFormFeature
|
||||||
{
|
{
|
||||||
private static readonly FormOptions DefaultFormOptions = new FormOptions();
|
|
||||||
|
|
||||||
private readonly HttpRequest _request;
|
private readonly HttpRequest _request;
|
||||||
private readonly FormOptions _options;
|
private readonly FormOptions _options;
|
||||||
private Task<IFormCollection> _parsedFormTask;
|
private Task<IFormCollection> _parsedFormTask;
|
||||||
|
|
@ -32,7 +30,7 @@ namespace Microsoft.AspNetCore.Http.Features
|
||||||
Form = form;
|
Form = form;
|
||||||
}
|
}
|
||||||
public FormFeature(HttpRequest request)
|
public FormFeature(HttpRequest request)
|
||||||
: this(request, DefaultFormOptions)
|
: this(request, FormOptions.Default)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
@ -8,6 +8,8 @@ namespace Microsoft.AspNetCore.Http.Features
|
||||||
{
|
{
|
||||||
public class FormOptions
|
public class FormOptions
|
||||||
{
|
{
|
||||||
|
internal static readonly FormOptions Default = new FormOptions();
|
||||||
|
|
||||||
public const int DefaultMemoryBufferThreshold = 1024 * 64;
|
public const int DefaultMemoryBufferThreshold = 1024 * 64;
|
||||||
public const int DefaultBufferBodyLengthLimit = 1024 * 1024 * 128;
|
public const int DefaultBufferBodyLengthLimit = 1024 * 1024 * 128;
|
||||||
public const int DefaultMultipartBoundaryLengthLimit = 128;
|
public const int DefaultMultipartBoundaryLengthLimit = 128;
|
||||||
|
|
|
||||||
|
|
@ -42,13 +42,12 @@ namespace Microsoft.AspNetCore.Http
|
||||||
_httpContextAccessor.HttpContext = httpContext;
|
_httpContextAccessor.HttpContext = httpContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
var formFeature = new FormFeature(httpContext.Request, _formOptions);
|
httpContext.FormOptions = _formOptions;
|
||||||
featureCollection.Set<IFormFeature>(formFeature);
|
|
||||||
|
|
||||||
return httpContext;
|
return httpContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpContext CreateHttpContext(IFeatureCollection featureCollection)
|
private static DefaultHttpContext CreateHttpContext(IFeatureCollection featureCollection)
|
||||||
{
|
{
|
||||||
if (featureCollection is IHttpContextContainer container)
|
if (featureCollection is IHttpContextContainer container)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,6 @@ namespace Microsoft.AspNetCore.Http
|
||||||
{
|
{
|
||||||
public interface IHttpContextContainer
|
public interface IHttpContextContainer
|
||||||
{
|
{
|
||||||
HttpContext HttpContext { get; }
|
DefaultHttpContext HttpContext { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
// 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, IHttpRequestFeature> _nullRequestFeature = f => null;
|
||||||
private readonly static Func<IFeatureCollection, IQueryFeature> _newQueryFeature = f => new QueryFeature(f);
|
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, IRequestCookiesFeature> _newRequestCookiesFeature = f => new RequestCookiesFeature(f);
|
||||||
private readonly static Func<IFeatureCollection, IRouteValuesFeature> _newRouteValuesFeature = f => new RouteValuesFeature();
|
private readonly static Func<IFeatureCollection, IRouteValuesFeature> _newRouteValuesFeature = f => new RouteValuesFeature();
|
||||||
private readonly static Func<HttpContext, IRequestBodyPipeFeature> _newRequestBodyPipeFeature = context => new RequestBodyPipeFeature(context);
|
private readonly static Func<HttpContext, IRequestBodyPipeFeature> _newRequestBodyPipeFeature = context => new RequestBodyPipeFeature(context);
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,24 @@ namespace Microsoft.AspNetCore.Http.Features
|
||||||
Assert.Same(FormCollection.Empty, formCollection);
|
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]
|
[Theory]
|
||||||
[InlineData(true)]
|
[InlineData(true)]
|
||||||
[InlineData(false)]
|
[InlineData(false)]
|
||||||
|
|
@ -391,7 +409,7 @@ namespace Microsoft.AspNetCore.Http.Features
|
||||||
IFormFeature formFeature = new FormFeature(context.Request, new FormOptions() { BufferBody = bufferRequest, ValueCountLimit = 2 });
|
IFormFeature formFeature = new FormFeature(context.Request, new FormOptions() { BufferBody = bufferRequest, ValueCountLimit = 2 });
|
||||||
context.Features.Set<IFormFeature>(formFeature);
|
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);
|
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 });
|
IFormFeature formFeature = new FormFeature(context.Request, new FormOptions() { BufferBody = bufferRequest, ValueCountLimit = 2 });
|
||||||
context.Features.Set<IFormFeature>(formFeature);
|
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);
|
Assert.Equal("Form value count limit 2 exceeded.", exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -277,7 +277,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
||||||
|
|
||||||
protected HttpResponseHeaders HttpResponseHeaders { get; } = new HttpResponseHeaders();
|
protected HttpResponseHeaders HttpResponseHeaders { get; } = new HttpResponseHeaders();
|
||||||
|
|
||||||
HttpContext IHttpContextContainer.HttpContext
|
DefaultHttpContext IHttpContextContainer.HttpContext
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue