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 context)
        {
            if (_options == null)
            {
                await _next(context);

                return;
            }
            ClientRequestIdentity identity = await ResolveIdentityAsync(context);

            TProcessor processor = _processor;

            if (processor.IsWhitelisted(identity))
            {
                await _next(context);

                return;
            }
            processor = _processor;
            IEnumerable <RateLimitRule> rules = await processor.GetMatchingRulesAsync(identity, context.RequestAborted);

            Dictionary <RateLimitRule, RateLimitCounter> rulesDict = new Dictionary <RateLimitRule, RateLimitCounter>();

            foreach (RateLimitRule rule2 in rules)
            {
                processor = _processor;
                RateLimitCounter rateLimitCounter = await processor.ProcessRequestAsync(identity, rule2, context.RequestAborted);

                if (rule2.Limit > 0.0)
                {
                    if (rateLimitCounter.Timestamp + rule2.PeriodTimespan.Value < DateTime.UtcNow)
                    {
                        continue;
                    }
                    if (rateLimitCounter.Count > rule2.Limit)
                    {
                        string retryAfter = rateLimitCounter.Timestamp.RetryAfterFrom(rule2);
                        LogBlockedRequest(context, identity, rateLimitCounter, rule2);
                        if (_options.RequestBlockedBehaviorAsync != null)
                        {
                            await _options.RequestBlockedBehaviorAsync(context, identity, rateLimitCounter, rule2);
                        }
                        if (!rule2.MonitorMode)
                        {
                            await ReturnQuotaExceededResponse(context, rule2, retryAfter);

                            return;
                        }
                    }
                }
                else
                {
                    LogBlockedRequest(context, identity, rateLimitCounter, rule2);
                    if (_options.RequestBlockedBehaviorAsync != null)
                    {
                        await _options.RequestBlockedBehaviorAsync(context, identity, rateLimitCounter, rule2);
                    }
                    if (!rule2.MonitorMode)
                    {
                        await ReturnQuotaExceededResponse(context, rule2, int.MaxValue.ToString(CultureInfo.InvariantCulture));

                        return;
                    }
                }
                rulesDict.Add(rule2, rateLimitCounter);
            }
            if (rulesDict.Any() && !_options.DisableRateLimitHeaders)
            {
                KeyValuePair <RateLimitRule, RateLimitCounter> rule = rulesDict.OrderByDescending((KeyValuePair <RateLimitRule, RateLimitCounter> x) => x.Key.PeriodTimespan).FirstOrDefault();
                processor = _processor;
                RateLimitHeaders headers = processor.GetRateLimitHeaders(rule.Value, rule.Key, context.RequestAborted);
                headers.Context = context;
                context.Response.OnStarting(new Func <object, Task>(SetRateLimitHeaders), headers);
            }
            await _next(context);
        }