private string GetResponseMessage(RateLimitOptions option)
        {
            var message = string.IsNullOrEmpty(option.QuotaExceededMessage)
                ? $"API calls quota exceeded! maximum admitted {option.RateLimitRule.Limit} per {option.RateLimitRule.Period}."
                : option.QuotaExceededMessage;

            return(message);
        }
Example #2
0
        public Task SaveRateLimitCounter(ClientRequestIdentity requestIdentity, RateLimitOptions option, RateLimitCounter counter, TimeSpan expirationTime)
        {
            var counterId = ComputeCounterKey(requestIdentity, option);

            // stores: id (string) - timestamp (datetime) - total_requests (long)
            _rateLimitStore.SetAsync(counterId, counter, expirationTime);
            return(Task.CompletedTask);
        }
        public bool IsWhitelisted(ClientRequestIdentity requestIdentity, RateLimitOptions option)
        {
            if (option.ClientWhitelist.Contains(requestIdentity.ClientId))
            {
                return(true);
            }

            return(false);
        }
 public ClientRateLimitMiddleware(RequestDelegate next,
                                  ILogger <ClientRateLimitMiddleware> logger,
                                  IRateLimitStore rateLimitStore, IOptions <RateLimitOptions> options)
 {
     _next           = next;
     _rateLimitStore = rateLimitStore;
     _processor      = new RateLimitProcessor(rateLimitStore);
     _logger         = logger;
     _options        = options.Value;
 }
Example #5
0
        public string ComputeCounterKey(ClientRequestIdentity requestIdentity, RateLimitOptions options)
        {
            var key     = $"{options.RateLimitCounterPrefix}_{requestIdentity.ClientId}_{options.RateLimitRule.Period}_{requestIdentity.HttpVerb}_{requestIdentity.Path}";
            var idBytes = Encoding.UTF8.GetBytes(key);

            byte[] hashBytes;
            using (var algorithm = SHA1.Create())
            {
                hashBytes = algorithm.ComputeHash(idBytes);
            }
            return(BitConverter.ToString(hashBytes).Replace("-", string.Empty));
        }
        public Task ReturnQuotaExceededResponse(HttpContext httpContext, RateLimitOptions option, string retryAfter)
        {
            var message = this.GetResponseMessage(option);

            if (!option.DisableRateLimitHeaders)
            {
                httpContext.Response.Headers["Retry-After"] = retryAfter;
            }

            httpContext.Response.StatusCode = option.HttpStatusCode;
            return(httpContext.Response.WriteAsync(message));
        }
        public ClientRequestIdentity SetIdentity(HttpContext httpContext, RateLimitOptions option)
        {
            try
            {
                var clientId = "client";
                if (httpContext.Request.Headers.Keys.Contains(option.ClientIdHeader))
                {
                    clientId = httpContext.Request.Headers[option.ClientIdHeader].FirstOrDefault();
                }

                return(new ClientRequestIdentity(
                           clientId,
                           httpContext.Request.Path.ToString().ToLowerInvariant(),
                           httpContext.Request.Method.ToLowerInvariant()
                           ));
            }
            catch (System.Exception)
            {
                throw;
            }
        }
Example #8
0
        public RateLimitHeaders GetRateLimitHeaders(HttpContext context, ClientRequestIdentity requestIdentity, RateLimitOptions option)
        {
            var rule = option.RateLimitRule;
            RateLimitHeaders headers = null;
            var counterId            = ComputeCounterKey(requestIdentity, option);
            var entry = _rateLimitStore.Get(counterId);

            if (entry.HasValue)
            {
                headers = new RateLimitHeaders(context, rule.Period,
                                               (rule.Limit - entry.Value.TotalRequests).ToString(),
                                               (entry.Value.Timestamp + ConvertToTimeSpan(rule.Period)).ToUniversalTime().ToString("o", DateTimeFormatInfo.InvariantInfo)
                                               );
            }
            else
            {
                headers = new RateLimitHeaders(context,
                                               rule.Period,
                                               rule.Limit.ToString(),
                                               (DateTime.UtcNow + ConvertToTimeSpan(rule.Period)).ToUniversalTime().ToString("o", DateTimeFormatInfo.InvariantInfo));
            }

            return(headers);
        }
Example #9
0
        public async Task <RateLimitCounter> ProcessRequest(ClientRequestIdentity requestIdentity, RateLimitOptions option)
        {
            RateLimitCounter counter = new RateLimitCounter(DateTime.UtcNow, 1);
            var rule = option.RateLimitRule;

            var counterId = ComputeCounterKey(requestIdentity, option);

            // serial reads and writes
            lock (_processLocker)
            {
                var entry = _rateLimitStore.Get(counterId);
                if (entry.HasValue)
                {
                    // entry has not expired
                    if (entry.Value.Timestamp + TimeSpan.FromSeconds(rule.PeriodTimespan) >= DateTime.UtcNow)
                    {
                        // increment request count
                        var totalRequests = entry.Value.TotalRequests + 1;

                        // deep copy
                        counter = new RateLimitCounter(entry.Value.Timestamp, totalRequests);
                    }
                }
            }

            if (counter.TotalRequests > rule.Limit)
            {
                var retryAfter = RetryAfterFrom(counter.Timestamp, rule);
                if (retryAfter > 0)
                {
                    var expirationTime = rule.PeriodTimespan;
                    await _rateLimitStore.SetAsync(counterId, counter, TimeSpan.FromSeconds(expirationTime));
                }
                else
                {
                    await _rateLimitStore.RemoveAsync(counterId);
                }
            }
            else
            {
                var expirationTime = ConvertToTimeSpan(rule.Period);
                await _rateLimitStore.SetAsync(counterId, counter, expirationTime);
            }

            return(counter);
        }