X-RateLimit-Limit, Remaining and Reset timing
vickyRathee opened this issue · comments
Any way to send X headers in every call response to tell clients about their limitation? As implemented in asp.core version
"X-RateLimit-Limit": "5000",
"X-RateLimit-Remaining": "4966",
"X-RateLimit-Reset": "1372700873"
@vickyRathee I implemented a custom version of this by overriding the SendAsync
method in a custom throttle handler. That looks like this:
config.MessageHandlers.Add(new CustomThrottlingHandler() //be sure to use the custom handler!
{
Policy = ThrottlePolicy.FromStore(new PolicyConfigurationProvider()),
Repository = new CacheRepository()
});
Then the handler:
public class CustomThrottlingHandler : ThrottlingHandler
{
private const string ANONYMOUS_IDENTITY_KEY = "Anonymous";
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var result = await base.SendAsync(request, cancellationToken);
//if user isn't anonymous then get the remaining counts
var identity = SetIdentity(request);
if (identity?.ClientKey != ANONYMOUS_IDENTITY_KEY)
{
//grab the users identity key and current limit to include in the response header
var identityKey = base.ComputeThrottleKey(identity, RateLimitPeriod.Hour);
var currentLimit = base.Repository.FirstOrDefault(identityKey);
if (currentLimit != null)
{
var rateLimit = base.Policy.Rates[RateLimitPeriod.Hour];
var reset = (int)(currentLimit.Value.Timestamp.AddHours(1).Subtract(new System.DateTime(1970, 1, 1))).TotalSeconds; //unix timestamp
result.Headers.Add("X-RateLimit-Limit", rateLimit.ToString());
result.Headers.Add("X-RateLimit-Remaining", (rateLimit - currentLimit.Value.TotalRequests).ToString());
result.Headers.Add("X-RateLimit-Reset", reset.ToString());
}
}
return result;
}
protected override RequestIdentity SetIdentity(HttpRequestMessage request)
{
return new RequestIdentity()
{
ClientKey = request.Headers.Contains("Authorization")
? request.Headers.GetValues("Authorization").FirstOrDefault()
: ANONYMOUS_IDENTITY_KEY,
ClientIp = base.GetClientIp(request).ToString(),
Endpoint = request.RequestUri.AbsolutePath.ToLowerInvariant()
};
}
Two things to note: 1) This is assuming you're only limiting on an hourly period. 2) It also allows for anonymous users to interact with the API and not get a limit back (may not be useful for every API).
It would be great to support ratelimiting via the new ratelimit standardization proposal.
The proposal was:
- recently presented at the HTTP Workgroup during the IETF Meeting
- implemented by Kong, Envoy Proxy and 3scale
The proposal
Very similar to your current implementation, but header names have no X-
and Reset
is in delta-seconds
instead of timestamp. The rationale for the choice is in FAQ 5.
RateLimit-Limit: 60
RateLimit-Remaining: 50
RateLimit-Reset: 5