Skip to content

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

Merged
merged 17 commits into from
Aug 19, 2022
Merged

rate limit sample #13

merged 17 commits into from
Aug 19, 2022

Conversation

Rick-Anderson
Copy link
Contributor

@Rick-Anderson Rick-Anderson commented Aug 9, 2022

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.

Copy link
Member

@BrennanConroy BrennanConroy left a 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.

@Rick-Anderson

This comment was marked as resolved.

}

public Func<OnRejectedContext, CancellationToken, ValueTask>?
OnRejected { get => _onRejected; }
Copy link
Contributor Author

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.

Copy link
Member

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.

@Rick-Anderson

This comment was marked as off-topic.

}

public Func<OnRejectedContext, CancellationToken, ValueTask>?
OnRejected { get => _onRejected; }

This comment was marked as resolved.

@Rick-Anderson

This comment was marked as resolved.

@wtgodbe

This comment was marked as resolved.

@Rick-Anderson

This comment was marked as resolved.

@Rick-Anderson

This comment was marked as resolved.

@Rick-Anderson

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.

@BrennanConroy
Copy link
Member

BrennanConroy commented Aug 13, 2022

From the browser I get a 200 and the previous response while the output window shows the fail: and callstack.

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

@Rick-Anderson
Copy link
Contributor Author

I assume you're just refreshing the browser page when you get limited?

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 OnRejected. The code hasn't changed and I'm on the same 7.0.100-preview.7.22377.5 version.

}

context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
context?.HttpContext?.RequestServices?.GetService<ILoggerFactory>()?
Copy link
Member

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.

Suggested change
context?.HttpContext?.RequestServices?.GetService<ILoggerFactory>()?
context.HttpContext.RequestServices.GetService<ILoggerFactory>()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image
They all have Dereference of possibly null reference.

Comment on lines 321 to 327
return RateLimitPartition.CreateTokenBucketLimiter("Anon", key =>
new TokenBucketRateLimiterOptions(tokenLimit: 5,
queueProcessingOrder: QueueProcessingOrder.OldestFirst,
queueLimit: 1,
replenishmentPeriod: TimeSpan.FromSeconds(5),
tokensPerPeriod: 1,
autoReplenishment: true));
Copy link
Member

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.

Copy link
Contributor Author

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.

Comment on lines 306 to 307
var accessToken = httpContext?.Features?.Get<IAuthenticateResultFeature>()?
.AuthenticateResult?.Properties?.GetTokenValue("access_token")?.ToString()

This comment was marked as resolved.

@Rick-Anderson

This comment was marked as resolved.

@wtgodbe
Copy link
Member

wtgodbe commented Aug 16, 2022

dotnet/aspnetcore#43320 should fix the behavior you're seeing w/ TaskCanceledException


app.Run();
// </snippet_adm>
#elif JWT
Copy link
Contributor Author

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

@Rick-Anderson
Copy link
Contributor Author

@wtgodbe @BrennanConroy I've just started the rate limiting middleware article so it would be helpful to get this sample reviewed and merged.

Copy link
Member

@wtgodbe wtgodbe left a 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()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

app.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions);

app.UseRateLimiter(new RateLimiterOptions()
.AddNoLimiter(policyName: jwtPolicyName)
Copy link
Member

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; }
Copy link
Member

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)
Copy link
Contributor Author

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.

Comment on lines 221 to 223
//.AddPolicy<string>(completePolicyName,
// new SampleRateLimiterPolicy(NullLogger<SampleRateLimiterPolicy>.Instance,
// (IOptions < MyRateLimitOptions >)myConfigSection))
Copy link
Contributor Author

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?

Copy link
Contributor Author

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.

Copy link
Member

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.

@Rick-Anderson Rick-Anderson merged commit c563496 into main Aug 19, 2022
@Rick-Anderson Rick-Anderson deleted the rl2 branch August 19, 2022 23:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants