async Task <IRequestTrackerResult> PostEvaluateRateLimitRules(IdentityServerRequestRecord requestRecord, bool error)
        {
            if (!error)
            {
                // only increment if successful
                // Increment the counters on the way out.  Allow the request to finish
                var identity = new ClientRequestIdentity()
                {
                    ClientId    = requestRecord.Client.ClientId,
                    EndpointKey = requestRecord.EndpointKey
                };
                var rateLimitClientsRule = _processor.GetRateLimitClientsRule(identity);
                if (rateLimitClientsRule != null)
                {
                    foreach (var rule in rateLimitClientsRule.Settings.RateLimitRules)
                    {
                        if (rule.Limit > 0)
                        {
                            // increment counter
                            var counter = _processor.ProcessRequest(identity, rule);
                        }
                    }

                    //set X-Rate-Limit headers for the longest period
                    if (rateLimitClientsRule.Settings.RateLimitRules.Any() &&
                        !rateLimitClientsRule.Settings.DisableRateLimitHeaders)
                    {
                        var rule = rateLimitClientsRule.Settings.RateLimitRules
                                   .OrderByDescending(x => x.PeriodTimespan.Value).First();
                        var headers = _processor.GetRateLimitHeaders(identity, rule);
                        headers.Context = requestRecord.HttpContext;
                        requestRecord.HttpContext.Response.OnStarting(SetRateLimitHeaders, state: headers);
                    }
                }
            }
            return(null);
        }
        public async Task Invoke(HttpContext httpContext)
        {
            // check if rate limiting is enabled
            if (_options == null)
            {
                await _next.Invoke(httpContext);
                return;
            }

            // compute identity from request
            var identity = await _clientRequestStore.GetClientRequestIdentityAsync(httpContext);
            if (identity == null)
            {
                await _next.Invoke(httpContext);
                return;
            }
            // check white list
            if (_processor.IsWhitelisted(identity))
            {
                await _next.Invoke(httpContext);
                return;
            }

            var rules = _processor.GetMatchingRules(identity);

            foreach (var rule in rules)
            {
                if(rule.Limit > 0)
                {
                    // increment counter
                    var counter = _processor.ProcessRequest(identity, rule);

                    // check if key expired
                    if (counter.Timestamp + rule.PeriodTimespan.Value < DateTime.UtcNow)
                    {
                        continue;
                    }

                    // check if limit is reached
                    if (counter.TotalRequests > rule.Limit)
                    {
                        //compute retry after value
                        var retryAfter = _processor.RetryAfterFrom(counter.Timestamp, rule);

                        // log blocked request
                        LogBlockedRequest(httpContext, identity, counter, rule);
                  
                        // break execution
                        await ReturnQuotaExceededResponse(httpContext, rule, retryAfter);
                        return;
                    }
                }
                // if limit is zero or less, block the request.
                else
                {
                    // process request count
                    var counter = _processor.ProcessRequest(identity, rule);

                    // log blocked request
                    LogBlockedRequest(httpContext, identity, counter, rule);

                    // break execution (Int32 max used to represent infinity)
                    await ReturnQuotaExceededResponse(httpContext, rule, Int32.MaxValue.ToString(System.Globalization.CultureInfo.InvariantCulture));
                    return;
                }
            }

            //set X-Rate-Limit headers for the longest period
            if(rules.Any() && !_options.DisableRateLimitHeaders)
            {
                var rule = rules.OrderByDescending(x => x.PeriodTimespan.Value).First();
                var headers = _processor.GetRateLimitHeaders(identity, rule);
                headers.Context = httpContext;

                httpContext.Response.OnStarting(SetRateLimitHeaders, state: headers);
            }

            await _next.Invoke(httpContext);
        }