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); }
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; }
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; } }
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); }
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); }