Skip to content

Rate limiting middleware - Statistics about rate limiters #44140

Open
@maartenba

Description

@maartenba

Is there an existing issue for this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

The ASP.NET Core rate limiting middleware is great, but "limited" in terms of what you an communicate with your users. Let's start with some code that you can write today in .NET 7:

builder.Services.AddRateLimiter(options =>
{
    options.OnRejected = async (context, token) =>
    {
        context.HttpContext.Response.StatusCode = 429;
        if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
        {
            await context.HttpContext.Response.WriteAsync(
                $"Too many requests. Please try again after {retryAfter.TotalMinutes} minute(s). " +
                $"Read more about our rate limits at https://example.org/docs/ratelimiting.", cancellationToken: token);
        }
        else
        {
            await context.HttpContext.Response.WriteAsync(
                "Too many requests. Please try again later. " +
                "Read more about our rate limits at https://example.org/docs/ratelimiting.", cancellationToken: token);
        }
    };

    // ...
});

When rate limits are triggered, a response is returned that tells the user they are rate limited, where to find more information, and (if the MetadataName.RetryAfter data is available), when to retry.

This is quite limited. There's no access to which rate limiter fired, and what its statistics are.

Additionally, the current RateLimitLease is only accessible when rate limiting is fired - not for every request. If you would want to return statistics about your limits (e.g. like GitHub does), you'll find it impossible to get statistics about the current lease in a custom middleware that can write out these additional headers.

Describe the solution you'd like

Here's a middleware that has access to some of data that I'd want to access:

public class RateLimitStatisticsMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IOptions<RateLimiterOptions> _options;

    public RateLimitStatisticsMiddleware(RequestDelegate next, IOptions<RateLimiterOptions> options)
    {
        _next = next;
        _options = options;
    }

    public Task Invoke(HttpContext context)
    {
        // Note: This should also work on endpoint limiters, but those are not available.
        // There is no current "rate limit context" of sorts. Including the policy name etc.
        var globalLimiter = _options.Value.GlobalLimiter;
        if (globalLimiter != null)
        {
            var statistics = globalLimiter.GetStatistics(context);
            if (statistics != null)
            {
                // Note: It would be great to be able to get the TokenLimit from the "current rate limiter context"
                context.Response.Headers.Add("X-Rate-Limit-Limit", "20");

                // Note: It would be great to be able to get the Window from the "current rate limiter context"
                context.Response.Headers.Add("X-Rate-Limit-Reset", DateTimeOffset.UtcNow.ToString("O"));

                // This one is there today
                context.Response.Headers.Add("X-Rate-Limit-Remaining", statistics.CurrentAvailablePermits.ToString());
            }
        }

        return _next(context);
    }
}

The dream scenario for a better ASP.NET Core rate limiter middleware would be to:

  • Have access to more statistics about the rate limiter (which policy fired, what partition, what are the limiter's options so I can emit a message that says "you can only make 10 requests per minute", ...) in the rejected callbacks.
  • Have a feature on the current HttpContext that gives access to the current rate limit context, so these details can also be returned on successful requests, or added to telemetry.

Additional context

Most rate limiters in System.Threading.RateLimiting don't provide additional statistics. This feature will need changes in both ASP.NET Core and the .NET framework.

Metadata

Metadata

Assignees

Labels

area-middlewareIncludes: URL rewrite, redirect, response cache/compression, session, and other general middlewaresarea-networkingIncludes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractionsfeature-rate-limitWork related to use of rate limit primitives

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions