-
Notifications
You must be signed in to change notification settings - Fork 160
rate limit sample #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
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 probably also want a sample showing custom IRateLimiterPolicy<T>
implementation and usage.
Co-authored-by: Brennan <[email protected]>
This comment was marked as resolved.
This comment was marked as resolved.
fundamentals/middleware/rate-limit/WebRateLimitAuth/SampleRateLimiterPolicy.cs
Show resolved
Hide resolved
} | ||
|
||
public Func<OnRejectedContext, CancellationToken, ValueTask>? | ||
OnRejected { get => _onRejected; } |
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.
Is it OK to have OnRejected
here and also in RateLimiterOptions
directly?
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
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.
Right, policy OnRejected will win.
This comment was marked as off-topic.
This comment was marked as off-topic.
fundamentals/middleware/rate-limit/WebRateLimitAuth/SampleRateLimiterPolicy.cs
Show resolved
Hide resolved
} | ||
|
||
public Func<OnRejectedContext, CancellationToken, ValueTask>? | ||
OnRejected { get => _onRejected; } |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
Co-authored-by: Kahbazi <[email protected]>
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
var builder = WebApplication.CreateBuilder(args); | ||
var app = builder.Build(); | ||
|
||
static string GetTicks() => DateTime.Now.Ticks.ToString().Substring(14); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
I assume you're just refreshing the browser page when you get limited? This would cause the request to be aborted by the client. Like Will said, we might want to change the behavior here. MVC often swallows the OCE from client aborts and just makes sure the request is aborted. https://source.dot.net/#Microsoft.AspNetCore.Mvc.Core/FileResultHelper.cs,47 |
Yes, I didn't see that behavior when testing with JMeter on an Azure app. After getting that exception in the output window for several days, I can't reproduce that now and get the 429 response I set in |
} | ||
|
||
context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests; | ||
context?.HttpContext?.RequestServices?.GetService<ILoggerFactory>()? |
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 don't think theses null checks are necessary.
context?.HttpContext?.RequestServices?.GetService<ILoggerFactory>()? | |
context.HttpContext.RequestServices.GetService<ILoggerFactory>() |
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.
return RateLimitPartition.CreateTokenBucketLimiter("Anon", key => | ||
new TokenBucketRateLimiterOptions(tokenLimit: 5, | ||
queueProcessingOrder: QueueProcessingOrder.OldestFirst, | ||
queueLimit: 1, | ||
replenishmentPeriod: TimeSpan.FromSeconds(5), | ||
tokensPerPeriod: 1, | ||
autoReplenishment: 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.
How about to use a NoLimiter
here? an unauthenticated user could call this endpoint so many times and block all unauthenticated enpoints for everyone.
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.
That's a good point and something I'll call out. A DOS attack is something to consider on all limiters and with no limiter. I think the simplest samples I just added will have hard coded limits, the last two sample will use configuration.
var accessToken = httpContext?.Features?.Get<IAuthenticateResultFeature>()? | ||
.AuthenticateResult?.Properties?.GetTokenValue("access_token")?.ToString() |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
dotnet/aspnetcore#43320 should fix the behavior you're seeing w/ |
|
||
app.Run(); | ||
// </snippet_adm> | ||
#elif JWT |
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 also include JWT Auth handler, so it is clear in the sample that where does the "access_token" come form.
builder.Services.AddAuthentication().AddJwt(...);
@Kahbazi I made a new sample for this and removed GetTokenValue("access_token")
from the other sample. This one is easy to test with dotnet user-jwts
@wtgodbe @BrennanConroy I've just started the rate limiting middleware article so it would be helpful to get this sample reviewed and merged. |
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.
Seems good other than the 2 API change reactions - it'll also be good to add examples of controllers (using the new attributes) once RC1 comes out.
|
||
static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000"); | ||
|
||
app.UseRateLimiter(new RateLimiterOptions() |
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.
Long term I'd prefer using AddRateLimiter
w/ services: https://github.com/dotnet/aspnetcore/blob/4cf483ee7a1cb470b8b2290a174ad34f450a5eab/src/Middleware/RateLimiting/src/RateLimiterServiceCollectionExtensions.cs#L20
app.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions); | ||
|
||
app.UseRateLimiter(new RateLimiterOptions() | ||
.AddNoLimiter(policyName: jwtPolicyName) |
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.
What's the intention here? Adding a no limiter is functionally equivalent to not adding any endpoint limiter for this endpoint - the global limiter will still run. We're removing AddNoLimiter
in rc1 (we are also adding an attribute that allows you to disable rate limiting entirely for an endpoint, including the global limiter)
} | ||
|
||
public Func<OnRejectedContext, CancellationToken, ValueTask>? | ||
OnRejected { get => _onRejected; } |
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.
Right, policy OnRejected will win.
private readonly MyRateLimitOptions _options; | ||
|
||
public SampleRateLimiterPolicy(ILogger<SampleRateLimiterPolicy> logger, | ||
IOptions<MyRateLimitOptions> options) |
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.
@Kahbazi I'm now passing in IOptions
so I can use config for setting the values.
//.AddPolicy<string>(completePolicyName, | ||
// new SampleRateLimiterPolicy(NullLogger<SampleRateLimiterPolicy>.Instance, | ||
// (IOptions < MyRateLimitOptions >)myConfigSection)) |
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.
@Kahbazi how do I pass IOptions<MyRateLimitOptions>
here?
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.
@Kahbazi Is there any advantage to showing new SampleRateLimiterPolicy()
? Maybe we should just use .AddPolicy<string, SampleRateLimiterPolicy>(helloPolicy)
like we do on the next line.
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.
how do I pass IOptions here?
When you have the MyRateLimitOptions
instance, you can create it with Microsoft.Extensions.Options.Options.Create<MyRateLimitOptions>(options)
.
@Kahbazi Is there any advantage to showing new SampleRateLimiterPolicy()? Maybe we should just use .AddPolicy<string, SampleRateLimiterPolicy>(helloPolicy) like we do on the next line.
Yeah, I agree. Having just .AddPolicy<string, SampleRateLimiterPolicy>(helloPolicy)
for sample is enough I think.
Contributes to dotnet/AspNetCore.Docs#26515
All the supporting MVC and Razor Pages (RP) files were merged in #12 to make it easier to review the relevant code.
When dotnet/aspnetcore#43053 makes it to a preview release, sample will be updated.
Sample lives in this repository. Once code is approved, I'll start working on the doc.