Skip to content
This repository has been archived by the owner on Nov 1, 2023. It is now read-only.

X-RateLimit-Limit, Remaining and Reset timing #126

Open
vickyRathee opened this issue Feb 10, 2019 · 2 comments
Open

X-RateLimit-Limit, Remaining and Reset timing #126

vickyRathee opened this issue Feb 10, 2019 · 2 comments

Comments

@vickyRathee
Copy link

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"
@graham-saunders
Copy link

graham-saunders commented Feb 12, 2019

@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).

@ioggstream
Copy link

ioggstream commented Jan 22, 2020

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

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants