public RateLimitHeaders GetRateLimitHeaders(ClientRequestIdentity requestIdentity, RateLimitRule rule) { var headers = new RateLimitHeaders(); var counterId = ComputeCounterKey(requestIdentity, rule); var entry = _counterStore.Get(counterId); if (entry.HasValue) { headers.Reset = (entry.Value.Timestamp + ConvertToTimeSpan(rule.Period)).ToUniversalTime() .ToString("o", DateTimeFormatInfo.InvariantInfo); headers.Limit = rule.Period; headers.Remaining = (rule.Limit - entry.Value.TotalRequests).ToString(); } else { headers.Reset = (DateTime.UtcNow + ConvertToTimeSpan(rule.Period)).ToUniversalTime() .ToString("o", DateTimeFormatInfo.InvariantInfo); headers.Limit = rule.Period; headers.Remaining = rule.Limit.ToString(); } return(headers); }
public virtual RateLimitHeaders GetRateLimitHeaders(RateLimitCounter?counter, RateLimitRule rule, CancellationToken cancellationToken = default) { var headers = new RateLimitHeaders(); double remaining; DateTime reset; if (counter.HasValue) { reset = counter.Value.Timestamp + (rule.PeriodTimespan ?? rule.Period.ToTimeSpan()); remaining = rule.Limit - counter.Value.Count; } else { reset = DateTime.UtcNow + (rule.PeriodTimespan ?? rule.Period.ToTimeSpan()); remaining = rule.Limit; } headers.Reset = reset.ToUniversalTime().ToString("o", DateTimeFormatInfo.InvariantInfo); headers.Limit = rule.Period; headers.Remaining = remaining.ToString(); return(headers); }
public async Task Invoke(HttpContext httpContext) { // check if rate limiting is enabled if (_options == null) { await _next.Invoke(httpContext); return; } // get request details var clientRequest = httpContext.GetClientRequest(_options.ClientIdHeader); // check white list if (_processor.IsWhitelisted(clientRequest)) { await _next.Invoke(httpContext); return; } var rules = _processor.GetMatchingRules(clientRequest); RateLimitResult result = null; foreach (var rule in rules) { // if limit is zero or less, block the request. if (rule.Limit <= 0) { // log blocked request LogBlockedRequest(httpContext, clientRequest, rule); // return quote exceeded await ReturnQuotaExceededResponse(httpContext, rule); return; } // process request result = await _processor.ProcessRequestAsync(clientRequest, rule); // check if limit is exceeded if (!result.Success) { //compute retry after value var retryAfter = Convert.ToInt32((result.Expiry - DateTime.UtcNow).TotalSeconds).ToString(CultureInfo.InvariantCulture); // log blocked request LogBlockedRequest(httpContext, clientRequest, rule); // return quote exceeded await ReturnQuotaExceededResponse(httpContext, rule, retryAfter); return; } } // set X-Rate-Limit headers if (result != null && !_options.DisableRateLimitHeaders) { var rule = rules.Last(); var headers = new RateLimitHeaders { Reset = result.Expiry.ToString("o", DateTimeFormatInfo.InvariantInfo), Limit = rule.Period, Remaining = result.Remaining.ToString() }; httpContext.Response.OnStarting(state => { try { var context = (HttpContext)state; context.Response.Headers["X-Rate-Limit-Limit"] = headers.Limit; context.Response.Headers["X-Rate-Limit-Remaining"] = headers.Remaining; context.Response.Headers["X-Rate-Limit-Reset"] = headers.Reset; } catch { // ignore exception adding headers } return(Task.FromResult(0)); }, httpContext); } await _next.Invoke(httpContext); }