/// <summary> /// Initializes a new instance of the <see cref="ThrottlingSentinelAttribute"/> class. /// </summary> /// <param name="rateLimit">The allowed rate from within a given <paramref name="window"/>.</param> /// <param name="window">The duration of the window.</param> /// <param name="windowUnit">One of the enumeration values that specifies the time unit of <paramref name="window"/>.</param> public ThrottlingSentinelAttribute(int rateLimit, double window, TimeUnit windowUnit) { var options = new ThrottlingSentinelOptions(); RateLimit = rateLimit; Window = window; WindowUnit = windowUnit; UseRetryAfterHeader = options.UseRetryAfterHeader; RetryAfterHeader = options.RetryAfterHeader; TooManyRequestsMessage = options.TooManyRequestsMessage; RateLimitHeaderName = options.RateLimitHeaderName; RateLimitRemainingHeaderName = options.RateLimitRemainingHeaderName; RateLimitResetHeaderName = options.RateLimitResetHeaderName; }
public static async Task InvokeThrottlerSentinelAsync(HttpContext context, IThrottlingCache tc, ThrottlingSentinelOptions options, Action <HttpResponseMessage, HttpResponse> transformer) { var utcNow = DateTime.UtcNow; var throttlingContext = options.ContextResolver?.Invoke(context); if (!throttlingContext.IsNullOrWhiteSpace()) { try { await ThrottleLocker.WaitAsync(); if (!tc.TryGetValue(throttlingContext, out var tr)) { tr = new ThrottleRequest(options.Quota); tc.AddIfNotContainsKey(throttlingContext, tr); } else { tr.Refresh(); tr.Total++; } var window = new TimeRange(utcNow, tr.Expires); var delta = window.Duration; var reset = utcNow.Add(delta); context.Response.Headers.AddOrUpdate(options.RateLimitHeaderName, tr.Quota.RateLimit.ToString(CultureInfo.InvariantCulture)); context.Response.Headers.AddOrUpdate(options.RateLimitRemainingHeaderName, Math.Max(tr.Quota.RateLimit - tr.Total, 0).ToString(CultureInfo.InvariantCulture)); context.Response.Headers.AddOrUpdate(options.RateLimitResetHeaderName, reset.ToEpochTime().ToString(CultureInfo.InvariantCulture)); if (tr.Total > tr.Quota.RateLimit && tr.Expires > utcNow) { var message = options.ResponseBroker?.Invoke(delta, reset); if (message != null) { message.ToHttpResponse(context.Response, transformer); throw new ThrottlingException((int)message.StatusCode, await message.Content.ReadAsStringAsync(), tr.Quota.RateLimit, delta, reset); } } tc[throttlingContext] = tr; } finally { ThrottleLocker.Release(); } } }