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, _options.RealIpHeader);

            // 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);
        }
        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 = SetIdentity(httpContext);

            // 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, int.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);
        }
예제 #3
0
        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 = SetIdentity(httpContext);

            _logger.LogInformation($"Identity; User:[{identity.ClientId}]; IP:[{identity.ClientIp}]");

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

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