stefanprodan / WebApiThrottle

ASP.NET Web API rate limiter for IIS and Owin hosting

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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:

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