-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Conversation
/// </summary> | ||
/// <param name="bytes">The maximum request body size accepted by the application.</param> | ||
/// <param name="disabled">Indicates whether this attribute is disabled.</param> | ||
public RequestSizeLimitAttribute(long bytes, bool disabled) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This always requires both parameters, correct? I don't think you ever want to set both parameters for this attribute. Get rid of the constructor and only use Property setters?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e.g. [RequestSizeLimit(Bytes=10)], [RequestSizeLimit(Disabled=true)]. Not sure what happens for [RequestSizeLimit] though, I assume it would default to 0, false, which would be weird.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the limit is not specified, [RequestSizeLimit]
, is it reasonable to set Disabled
to true
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, what does Disabled
do?
This sounds like a totally different attribute
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
if (Disabled == false) | ||
{ | ||
var requestLength = context.HttpContext.Request.Body.Length; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be setting the value on https://github.com/aspnet/HttpAbstractions/blob/dev/src/Microsoft.AspNetCore.Http.Features/IHttpMaxRequestBodySizeFeature.cs
This isn't for us to implement
/// the request body size is greater than the specified limit. | ||
/// </summary> | ||
/// <param name="context">The <see cref="ResourceExecutingContext"/>.</param> | ||
public void OnResourceExecuting(ResourceExecutingContext context) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should support scoping, meaning that the filter closest to the action is the one to act.
Look at:
https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.ViewFeatures/ViewFeatures/IAntiforgeryPolicy.cs
https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Internal/ValidateAntiforgeryTokenAuthorizationFilter.cs
The general pattern here is:
- Define a policy interface (
IRequestSizePolicy
) - Define filter attributes that implement that policy interface (
RequestSizeLimitAttribute
,DisableRequestSizeLimitAttribute
). - Each of these filter attributes checks if it's the last
IRequestSizePolicy
incontext.Filters
and skip itself if it isn't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there be two different classes, one for the filter and one for the attribute like your example? https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.ViewFeatures/ValidateAntiForgeryTokenAttribute.cs.
I was following the pattern of ConsumesAttribute which implements Attribute and IResourceFilter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there should be 3.
The two attributes, and then the policy interface.
When we have a separation between filter and attribute - it's usually because DI is involved
@@ -554,5 +554,53 @@ public FiltersTest(MvcTestFixture<FiltersWebSite.Startup> fixture) | |||
$"CurrentCulture:{expected},CurrentUICulture:{expected}", | |||
await response.Content.ReadAsStringAsync()); | |||
} | |||
|
|||
[Fact] | |||
public async Task RequestSizeLimit_ThrowsForInvalidSize() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make a new class for these tests and don't put them in FiltersWebSite
. That thing is a mess and we plan to just nuke it from orbit some day.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If all we do is set a feature, do we need functional tests for this? Wouldn't unit tests suffice?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It actually might be the case that we have to do unit tests. The test server may not implement this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I tried and was unable to figure out how to initialize the feature for the functional tests. Updating the PR with just unit tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have tests that use kestrel/httpsys? or just TestServer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We only use testserver
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to find a way to test this with a real server.
public bool IsReusable => true; | ||
|
||
/// <inheritdoc /> | ||
public int Order { get; set; } = 1000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems high. This must apply before model binding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resource filters are always before model binding.
@jbagga - leave this at 0 (unset) unless you have a good reason to do otherwise.
if (IsClosestRequestSizePolicy(context.Filters)) | ||
{ | ||
var maxRequestBodySizeFeature = context.HttpContext.Features.Get<IHttpMaxRequestBodySizeFeature>(); | ||
maxRequestBodySizeFeature.MaxRequestBodySize = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check for null feature
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You also need to check IsReadOnly and not try to change the value. Possibly log that the setting could not be applied. This would happen if the request body had already started reading, such as being buffered.
It would also make sense to log if the feature was missing and the attribute couldn't be applied.
|
||
private bool IsClosestRequestSizePolicy(IList<IFilterMetadata> filters) | ||
{ | ||
// Determine if this instance is the 'effective' antiforgery policy. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
antiforgery?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah update this 👍
using Microsoft.AspNetCore.Http.Features; | ||
using Microsoft.AspNetCore.Mvc.Filters; | ||
|
||
namespace Microsoft.AspNetCore.Mvc.Core |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Microsoft.AspNetCore.Mvc
The general rule of thumb is that anything I user will have to type in a controller belongs in Microsoft.AspNetCore.Mvc
public class DisableRequestSizeLimitAttribute : Attribute, IRequestSizePolicy, IOrderedFilter, IResourceFilter | ||
{ | ||
/// <inheritdoc /> | ||
public bool IsReusable => true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this, it's not needed
|
||
namespace Microsoft.AspNetCore.Mvc.Core | ||
{ | ||
public class DisableRequestSizeLimitAttribute : Attribute, IRequestSizePolicy, IOrderedFilter, IResourceFilter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docs
/// <summary> | ||
/// A marker interface for filters which define a policy for maximum size for the request body. | ||
/// </summary> | ||
public interface IRequestSizePolicy : IFilterMetadata |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
|
||
using Microsoft.AspNetCore.Mvc.Filters; | ||
|
||
namespace Microsoft.AspNetCore.Mvc.Core |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be .Mvc
as well
using Microsoft.AspNetCore.Http.Features; | ||
using Microsoft.AspNetCore.Mvc.Filters; | ||
|
||
namespace Microsoft.AspNetCore.Mvc.Core |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.Mvc
{ | ||
/// <summary> | ||
/// A filter that specifies the request body size limit. | ||
/// </summary> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might help here to put some links to in the docs to the http feature
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The feature interfaces aren't really meant for direct user consumption. putting it in the docs here would be more confusing than the current description.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] | ||
public class RequestSizeLimitAttribute : Attribute, IRequestSizePolicy, IOrderedFilter, IResourceFilter | ||
{ | ||
private long _bytes; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
readonly
} | ||
|
||
/// <inheritdoc /> | ||
public bool IsReusable => true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not needed
public bool IsReusable => true; | ||
|
||
/// <inheritdoc /> | ||
public int Order { get; set; } = 1000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
set to 0
if (IsClosestRequestSizePolicy(context.Filters)) | ||
{ | ||
var maxRequestBodySizeFeature = context.HttpContext.Features.Get<IHttpMaxRequestBodySizeFeature>(); | ||
maxRequestBodySizeFeature.MaxRequestBodySize = _bytes; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comments apply here, we need to check for null.
@Tratcher - any ideas on what a reasonable behavior is when a server doesn't support this feature?
I could see throwing, but the problem will be that we're going to encourage users to use things like TestServer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might want to document that this will no op when the server doesn't support it
@@ -3,7 +3,6 @@ | |||
|
|||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😻
using Microsoft.AspNetCore.Routing; | ||
using Xunit; | ||
|
||
namespace Microsoft.AspNetCore.Mvc.Core |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
update the namespace of these tests as well
|
||
namespace Microsoft.AspNetCore.Mvc.Core | ||
{ | ||
public class RequestSizeLimitAttributeTests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test not Tests ( yeah, I went there. 😲
using System.Linq; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.AspNetCore.Mvc.Core; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LEL?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Design of this is looking overall right. 👍
throw new ArgumentNullException(nameof(context)); | ||
} | ||
|
||
if (IsClosestRequestSizePolicy(context.Filters)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's pretty inexpensive to set IHttpMaxRequestBodySizeFeature.MaxRequestBodySize on both of our servers. Wouldn't it be simpler to just greedily set the property instead of checking IsClosestRequestSizePolicy each time?
This is assuming that the "closest" filter has its OnResourceExecuting method called last which I'm unsure about.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They use this pattern elsewhere. it's better not to run the same functionality twice, especially if it starts logging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better to just run a single policy. These things tend to sprout more features over time so the clean semantics are important
|
||
/// <summary> | ||
/// As an <see cref="IResourceFilter"/>, this filter looks at the request and rejects it before going ahead if | ||
/// the request body size is greater than the specified limit. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: there is no specified limit in this case.
Don't worry about my IsClosestRequestSizePolicy comment
} | ||
|
||
[Fact] | ||
public void LogsFeatureNotFound() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We haven't normally written unit tests for the logging when it's simple like this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can I remove all logging unit tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You've already written them, what's the harm in keeping them? Unless they need to churn do to other API changes.
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.AspNetCore.Mvc.Filters; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rynowak is being stingy with 😺 and this is the money stop
0340a6c
to
e56b4e0
Compare
/// <summary> | ||
/// The request body size limit. | ||
/// </summary> | ||
public long Bytes { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did y'all discuss making this a constructor parameter vs property earlier? This seems odd to me as a property because [RequestSizeLimit]
will set it to 0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, now that there's a single parameter then making it part of the constructor would be fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK please make that change. Other than that this looks good to me.
Have you tested this out manually (given that we can't functional test it)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I did some sandbox testing. Updated the PR with the constructor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll just look at added documentation next time through
} | ||
|
||
[Fact] | ||
public void DisableRequestSizeLimitAttribute_LogsMaxRequestBodySizeSetToNull() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Has nothing to do w/ DisableRequestSizeLimitAttribute
; just call this LogsMaxRequestBodySizeSetToNull
} | ||
|
||
[Fact] | ||
public void RequestSizeLimitAttribute_LogsMaxRequestBodySizeSet() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Has nothing to do w/ RequestSizeLimitAttribute
; just call this LogsMaxRequestBodySizeSet
{ | ||
private readonly long _bytes; | ||
|
||
public RequestSizeLimitAttribute(long bytes) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+
doc comments (e.g. parameter description similar to what you had for the Bytes
property)
namespace Microsoft.AspNetCore.Mvc | ||
{ | ||
/// <summary> | ||
/// Sets the <see cref="IHttpMaxRequestBodySizeFeature.MaxRequestBodySize"/> to <c>null</c>. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hadn't seen @Tratcher's comment on the feature when I made my earlier common on the inconsistent <see/>
versus descriptive text. Best to consistently describe what's going on here in the public classes. But, leave what you've got in the .Internal
classes alone.
The feature interfaces aren't really meant for direct user consumption. putting it in the docs here would be more confusing than the current description.
Looks great! |
namespace Microsoft.AspNetCore.Mvc | ||
{ | ||
/// <summary> | ||
/// Sets the request body size limit to <c>null</c>. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weren't you going to change this to say it disabled the size limit?
2225bcd
to
d977ad4
Compare
Addresses #6352
cc @Tratcher @rynowak