public async Task <RateLimitRecord> ProcessRequestAsync(RateLimitRecord rateRecord, RateLimitRule rule, DateTime currentTime, CancellationToken cancellationToken = default)
        {
            if (rateRecord != null)
            {
                // entry has not expired
                if (rateRecord.Timestamp + rule.PeriodTimespan.Value >= currentTime)
                {
                    // increment request count per rule's period
                    rateRecord.Count += 1;
                }
                else
                {
                    var timeSpan = currentTime - rule.PeriodTimespan.Value;
                    IEnumerable <RequestLogRecord> records = await _requestLogRepository.GetByClientIdAsync(rateRecord.ClientId, cancellationToken, (req) => req.Timestamp > timeSpan);

                    rateRecord.Timestamp = records.First().Timestamp.Add(rule.PeriodTimespan.Value);
                    rateRecord.Count     = records.Count() + 1;
                }
            }
            else
            {
                rateRecord = new RateLimitRecord
                {
                    Timestamp = DateTime.UtcNow,
                    Count     = 1
                };
            }

            //// stores: id (string) - timestamp (datetime) - total_requests (long)
            await _counterRepository.UpdateOrAddCounterAsync(rateRecord.ClientId, rateRecord, cancellationToken);


            return(rateRecord);
        }
        public async Task <RateLimitRecord> UpdateOrAddCounterAsync(string id, RateLimitRecord entry, CancellationToken cancellationToken = default)
        {
            return(await Task <RateLimitRecord> .Factory.StartNew(() =>
            {
                if (_records.ContainsKey(id))
                {
                    _records[id] = entry;
                    return entry;
                }
                else
                {
                    _records.Add(id, entry);
                }

                return entry;
            }
                                                                  ));
        }
Пример #3
0
        // IMyScopedService is injected into Invoke
        public async Task InvokeAsync(HttpContext context, IRateLimitService rateLimitService)
        {
            // check if rate limiting is enabled
            if (_options == null)
            {
                await _next.Invoke(context);

                return;
            }

            // compute identity from request
            var identity = await ClientIdentityHelper.ResolveIdentityAsync(context, _options);

            var currentTime = DateTime.UtcNow;

            // check if user is already requested
            var record = await rateLimitService.GetRateLimitRecordAsync(identity);

            if (record == null)
            {
                record = new RateLimitRecord
                {
                    ClientId  = identity.ClientId,
                    Timestamp = currentTime
                }
            }
            ;

            // check if user is blocked
            if (record.isBlocked)
            {
                var blockTime = record.BlockedUntillTime - currentTime;
                if (blockTime.TotalSeconds > 0)
                {
                    await ReturnQuotaExceededResponse(context, record.BlockedUntillTime);

                    return;
                }
                else
                {
                    //reset rateCounter record if time for block is run out
                    await rateLimitService.ResetUserAsync(identity, context.RequestAborted);
                }
            }

            //find all rules eligibe for request
            IEnumerable <RateLimitRule> rules = await rateLimitService.GetMatchingRulesAsync(identity, context.RequestAborted);

            foreach (var rule in rules)
            {
                RateLimitRecord rateCounter = await rateLimitService.ProcessRequestAsync(record, rule, currentTime, context.RequestAborted);

                if (rule.Limit > 0)
                {
                    // check if limit is reached
                    if (rateCounter.Count > rule.Limit)
                    {
                        var blockedUser = await rateLimitService.BlockUserByRateLimitAsync(identity, rule);

                        // break execution
                        await ReturnQuotaExceededResponse(context, blockedUser.BlockedUntillTime);

                        return;
                    }
                }
            }

            // Call the next delegate/middleware in the pipeline
            await _next(context);
        }