public RateLimitCounter GetOrCreate(string clientId)
        {
            try
            {
                var strResults = _memoryCache.GetString(clientId);
                if (strResults == null)
                {
                    var rateLimitCounter = new RateLimitCounter();
                    _memoryCache.SetString(clientId, JsonConvert.SerializeObject(rateLimitCounter), _distributedCacheEntryOptions);
                    return(rateLimitCounter);
                }
                else
                {
                    var ratelimit = JsonConvert.DeserializeObject <RateLimitCounter>(strResults);
                    ratelimit.Increment();

                    return(ratelimit);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"Exception when Set ClientId: {clientId}. Exception: {ex}");
                return(null);
            }
        }
        public bool Set(string id, RateLimitCounter counter, TimeSpan expirationTime)
        {
            var key    = GetKeyPrefix(id);
            var expiry = (int)expirationTime.TotalSeconds;

            return(_client.Set(key, counter, expiry));
        }
Ejemplo n.º 3
0
        private RateLimitCounter GetCounter(string counterKey, RateLimitRule rule)
        {
            var counter = new RateLimitCounter
            {
                Timestamp = DateTime.UtcNow,
                Count     = 1
            };
            var entry = _rateLimitCounterHandler.Get(counterKey);

            if (entry.HasValue)
            {
                // entry has not expired
                if (entry.Value.Timestamp + rule.PeriodTimespan.Value >= DateTime.UtcNow)
                {
                    // increment request count
                    var totalRequests = entry.Value.Count + 1;

                    // deep copy
                    counter = new RateLimitCounter
                    {
                        Timestamp = entry.Value.Timestamp,
                        Count     = totalRequests
                    };
                }
            }
            return(counter);
        }
Ejemplo n.º 4
0
        public Task <RateLimitResult> AddRequestAsync(string id, RateLimitRule rule)
        {
            var counter = Get(id);

            if (counter == null || counter.IsExpired)
            {
                // serial create of new rate limit counter
                lock (_lock)
                {
                    counter = Get(id);
                    if (counter == null || counter.IsExpired)
                    {
                        counter = new RateLimitCounter(rule.UseSlidingExpiration, rule.PeriodTimeSpan);
                        Set(id, counter, rule.PeriodTimeSpan, rule.UseSlidingExpiration);

                        return(Task.FromResult(new RateLimitResult
                        {
                            Success = true,
                            Remaining = rule.Limit - 1,
                            Expiry = DateTime.UtcNow.Add(rule.PeriodTimeSpan)
                        }));
                    }
                }
            }

            return(Task.FromResult(counter.AddRequest(rule.Limit)));
        }
        /// <summary>
        /// 处理请求数
        /// </summary>
        /// <param name="requestIdentity"></param>
        /// <param name="rule"></param>
        /// <returns></returns>
        private RateLimitCounter ProcessRequest(RequestIdentity requestIdentity, RateLimitRule rule)
        {
            var counter = new RateLimitCounter
            {
                Timestamp     = DateTime.Now,
                TotalRequests = 1
            };

            var counterId = ComputeCounterKey(requestIdentity, rule);

            lock (_processLocker)
            {
                var entry = _counterStore.Get(counterId);
                if (entry != null)
                {
                    // 没有过期,请求数+1
                    if (entry.Timestamp + rule.PeriodTimespan.Value >= DateTime.Now)
                    {
                        var totalRequests = entry.TotalRequests + 1;
                        counter = new RateLimitCounter
                        {
                            Timestamp     = entry.Timestamp,
                            TotalRequests = totalRequests
                        };
                    }
                }
                _counterStore.Set(counterId, counter, rule.PeriodTimespan.Value);
            }
            return(counter);
        }
 public virtual void LogBlockedRequest(
     HttpContext httpContext,
     ClientRequestIdentity identity,
     RateLimitCounter counter,
     RateLimitRule rule)
 {
     this._logger.LogInformation(string.Format("Request {0}:{1} from IP {2} has been blocked, quota {3}/{4} exceeded by {5}. Blocked by rule {6}, TraceIdentifier {7}.", (object)identity.HttpVerb, (object)identity.Path, (object)identity.ClientIp, (object)rule.Limit, (object)rule.Period, (object)counter.TotalRequests, (object)rule.Endpoint, (object)httpContext.TraceIdentifier));
 }
        public void Set(string id, RateLimitCounter counter, TimeSpan expirationTime)
        {
            var options = new MemoryCacheEntryOptions
            {
                Priority = CacheItemPriority.NeverRemove
            };

            options.SetAbsoluteExpiration(expirationTime);

            _cache.Set(id, counter, options);
        }
Ejemplo n.º 8
0
        private void Set(string id, RateLimitCounter counter, TimeSpan expirationPeriod, bool slidingExpiration)
        {
            var options = new MemoryCacheEntryOptions();

            if (slidingExpiration)
            {
                options.SetSlidingExpiration(expirationPeriod);
            }
            else
            {
                options.SetAbsoluteExpiration(expirationPeriod);
            }
            _memoryCache.Set(id, counter, options);
        }
Ejemplo n.º 9
0
        private async Task SetAsync(string id, RateLimitCounter counter, TimeSpan expirationPeriod, bool slidingExpiration)
        {
            var options = new DistributedCacheEntryOptions();

            if (slidingExpiration)
            {
                options.SetSlidingExpiration(expirationPeriod);
            }
            else
            {
                options.SetAbsoluteExpiration(expirationPeriod);
            }
            await _memoryCache.SetStringAsync(id, JsonConvert.SerializeObject(counter), options);
        }
        public async Task Invoke(HttpContext httpContext)
        {
            MyIPRateMiddleware rateLimitMiddleware = this;

            if (rateLimitMiddleware._options == null)
            {
                await rateLimitMiddleware._next(httpContext);
            }
            else
            {
                ClientRequestIdentity identity = rateLimitMiddleware.SetIdentity(httpContext);
                if (rateLimitMiddleware._processor.IsWhitelisted(identity))
                {
                    await rateLimitMiddleware._next(httpContext);
                }
                else
                {
                    List <RateLimitRule> rules = rateLimitMiddleware._processor.GetMatchingRules(identity);
                    foreach (RateLimitRule rule in rules)
                    {
                        if (rule.Limit > 0L)
                        {
                            RateLimitCounter counter = rateLimitMiddleware._processor.ProcessRequest(identity, rule);
                            if (!(counter.Timestamp + rule.PeriodTimespan.Value < DateTime.UtcNow) && counter.TotalRequests > rule.Limit)
                            {
                                string retryAfter = rateLimitMiddleware._processor.RetryAfterFrom(counter.Timestamp, rule);
                                rateLimitMiddleware.LogBlockedRequest(httpContext, identity, counter, rule);
                                await rateLimitMiddleware.ReturnQuotaExceededResponse(httpContext, rule, retryAfter);

                                return;
                            }
                        }
                    }
                    if (rules.Any <RateLimitRule>() && !rateLimitMiddleware._options.DisableRateLimitHeaders)
                    {
                        RateLimitRule    rule             = rules.OrderByDescending <RateLimitRule, TimeSpan>((Func <RateLimitRule, TimeSpan>)(x => x.PeriodTimespan.Value)).First <RateLimitRule>();
                        RateLimitHeaders rateLimitHeaders = rateLimitMiddleware._processor.GetRateLimitHeaders(identity, rule);
                        rateLimitHeaders.Context = httpContext;
                        httpContext.Response.OnStarting(new Func <object, Task>(rateLimitMiddleware.SetRateLimitHeaders), (object)rateLimitHeaders);
                    }
                    await rateLimitMiddleware._next(httpContext);
                }
            }
        }
Ejemplo n.º 11
0
        public override void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity,
                                               RateLimitCounter counter, RateLimitRule rule)
        {
            base.LogBlockedRequest(httpContext, identity, counter, rule);
            var key = $"blockedIp_{identity.ClientIp}";

            _memoryCache.TryGetValue(key, out int blockedCount);

            blockedCount++;
            if (blockedCount > 10)
            {
                _blockIpService.BlockIpAsync(identity.ClientIp, false);
                _logger.LogInformation($"Blocked {identity.ClientIp}");
            }
            else
            {
                _memoryCache.Set(key, blockedCount,
                                 new MemoryCacheEntryOptions().SetSlidingExpiration(new System.TimeSpan(0, 5, 0)));
            }
        }
Ejemplo n.º 12
0
        public override void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity,
            RateLimitCounter counter, RateLimitRule rule)
        {
            base.LogBlockedRequest(httpContext, identity, counter, rule);
            var key = $"blockedIp_{identity.ClientIp}";

            int blockedCount;
            _memoryCache.TryGetValue(key, out blockedCount);

            blockedCount++;
            if(blockedCount > 10)
            {
                _blockIpService.BlockIpAsync(identity.ClientIp, false);
                _logger.LogDebug("Blocked " + identity.ClientIp);
            }
            else
            {
                _memoryCache.Set(key, blockedCount,
                    new MemoryCacheEntryOptions().SetSlidingExpiration(new System.TimeSpan(0, 5, 0)));
            }
        }
Ejemplo n.º 13
0
 public RateLimitCounter GetOrCreate(string clientId)
 {
     try
     {
         return(_memoryCache.GetOrCreate(clientId, entry =>
         {
             //common issue with getOrCreate of IMemoryCache force us to use lock
             lock (_locker)
             {
                 var rateLimitCounter = new RateLimitCounter();
                 entry.SetValue(rateLimitCounter);
                 entry.SetAbsoluteExpiration(_options.Value.ExpirationTime);
                 return rateLimitCounter;
             }
         }));
     }
     catch (Exception ex)
     {
         _logger.LogError($"Exception when Set ClientId: {clientId}. Exception: {ex}");
         return(null);
     }
 }
Ejemplo n.º 14
0
        public virtual async Task <RateLimitCounter> ProcessRequestAsync(ClientRequestIdentity requestIdentity, RateLimitRule rule, CancellationToken cancellationToken = default)
        {
            var counter = new RateLimitCounter
            {
                Timestamp = DateTime.UtcNow,
                Count     = 1
            };

            var counterId = BuildCounterKey(requestIdentity, rule);

            // serial reads and writes on same key
            using (await AsyncLock.WriterLockAsync(counterId).ConfigureAwait(false))
            {
                var cache = _cacheManager.GetCache(nameof(RateLimitCounter));
                var entry = await cache.GetOrDefaultAsync <string, RateLimitCounter?>(counterId);

                if (entry.HasValue)
                {
                    // entry has not expired
                    if (entry.Value.Timestamp + rule.PeriodTimespan.Value >= DateTime.UtcNow)
                    {
                        // increment request count
                        var totalCount = entry.Value.Count + _config.RateIncrementer?.Invoke() ?? 1;

                        // deep copy
                        counter = new RateLimitCounter
                        {
                            Timestamp = entry.Value.Timestamp,
                            Count     = totalCount
                        };
                    }
                }

                // stores: id (string) - timestamp (datetime) - total_requests (long)
                await cache.SetAsync(counterId, counter, rule.PeriodTimespan.Value);
            }

            return(counter);
        }
Ejemplo n.º 15
0
        public override void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity,
                                               RateLimitCounter counter, RateLimitRule rule)
        {
            base.LogBlockedRequest(httpContext, identity, counter, rule);
            var key = $"blockedIp_{identity.ClientIp}";

            _memoryCache.TryGetValue(key, out int blockedCount);

            blockedCount++;
            if (blockedCount > 10)
            {
                _blockIpService.BlockIpAsync(identity.ClientIp, false);
                _logger.LogInformation(Constants.BypassFiltersEventId, null,
                                       "Banned {0}. \nInfo: \n{1}", identity.ClientIp, GetRequestInfo(httpContext));
            }
            else
            {
                _logger.LogInformation(Constants.BypassFiltersEventId, null,
                                       "Request blocked {0}. \nInfo: \n{1}", identity.ClientIp, GetRequestInfo(httpContext));
                _memoryCache.Set(key, blockedCount,
                                 new MemoryCacheEntryOptions().SetSlidingExpiration(new TimeSpan(0, 5, 0)));
            }
        }
Ejemplo n.º 16
0
        public RateLimitCounter ProcessRequest(ClientRequestIdentity requestIdentity, RateLimitRule rule)
        {
            var counter = new RateLimitCounter
            {
                Timestamp     = DateTime.UtcNow,
                TotalRequests = 1
            };

            var counterId = ComputeCounterKey(requestIdentity, rule);

            // serial reads and writes
            lock (_processLocker)
            {
                var entry = _counterStore.Get(counterId);
                if (entry.HasValue)
                {
                    // entry has not expired
                    if (entry.Value.Timestamp + rule.PeriodTimespan.Value >= DateTime.UtcNow)
                    {
                        // increment request count
                        var totalRequests = entry.Value.TotalRequests + 1;

                        // deep copy
                        counter = new RateLimitCounter
                        {
                            Timestamp     = entry.Value.Timestamp,
                            TotalRequests = totalRequests
                        };
                    }
                }

                // stores: id (string) - timestamp (datetime) - total_requests (long)
                _counterStore.Set(counterId, counter, rule.PeriodTimespan.Value);
            }

            return(counter);
        }
Ejemplo n.º 17
0
        public async Task <RateLimitResult> AddRequestAsync(string id, RateLimitRule rule)
        {
            // TODO: REQUIRES REVIEW
            // RateLimitCounter should not be serialised to distributed cache.
            // Directly store underlying data to facilitate atomic distributed updates.
            // REDIS example: http://tech.domain.com.au/2017/11/protect-your-api-resources-with-rate-limiting/

            var counter = await GetAsync(id);

            if (counter == null)
            {
                counter = new RateLimitCounter(rule.UseSlidingExpiration, rule.PeriodTimeSpan);
                await SetAsync(id, counter, rule.PeriodTimeSpan, rule.UseSlidingExpiration);

                return(new RateLimitResult
                {
                    Success = true,
                    Remaining = rule.Limit - 1,
                    Expiry = DateTime.UtcNow.Add(rule.PeriodTimeSpan)
                });
            }

            return(counter.AddRequest(rule.Limit));
        }
Ejemplo n.º 18
0
 public virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule)
 {
     _logger.LogDebug($"Request {identity.HttpVerb}:{identity.Path} from ClientId {identity.ClientId} has been blocked, quota {rule.Limit}/{rule.Period} exceeded by {counter.TotalRequests}. Blocked by rule { DownstreamRoute.ReRoute.UpstreamPathTemplate }, TraceIdentifier {httpContext.TraceIdentifier}.");
 }
Ejemplo n.º 19
0
        private static void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule)
        {
            var log = $"{DateTime.Now:yyyy-MM-dd-HH-mm-ss}\t{identity.ClientIp}\t{identity.HttpVerb}:{identity.Path}\t{identity.ClientId} has been blocked, quota {rule.Limit}/{rule.Period} exceeded by {counter.TotalRequests}. Blocked by rule {rule.Endpoint} .";

            Logger.Log(log);
        }
Ejemplo n.º 20
0
 public virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule, DownstreamRoute downstreamRoute)
 {
     _logger.LogInformation(@$ "Request { identity.HttpVerb}:{ identity.Path} from ClientId { identity.ClientId} has been blocked, quota { rule.Limit}/{ rule.Period} exceeded by { counter.TotalRequests}. Blocked by rule .");
 }
Ejemplo n.º 21
0
 public void Set(string id, RateLimitCounter counter, TimeSpan expirationTime)
 {
     _memoryCache.SetOrReset(id, counter, expirationTime);
 }
Ejemplo n.º 22
0
 protected override void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule)
 {
     _logger.LogInformation($"Request {identity.HttpVerb}:{identity.Path} from IP {identity.ClientIp} has been blocked, quota {rule.Limit}/{rule.Period} exceeded by {counter.Count - rule.Limit}. Blocked by rule {rule.Endpoint}, TraceIdentifier {httpContext.TraceIdentifier}. MonitorMode: {rule.MonitorMode}");
 }
        public override void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule)
        {
            // custom logging for request blocking
            this.logger.LogWarning($" {DateTime.Now.ToString()} : User with id {identity.ClientId} was blocked to access {identity.HttpVerb} : {identity.Path}  due to [ Limit :  {rule.Limit} , Period : {rule.Period} , Endpoint : {rule.Endpoint} ]! ");

            base.LogBlockedRequest(httpContext, identity, counter, rule);
        }
Ejemplo n.º 24
0
        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);
        }
Ejemplo n.º 25
0
 protected abstract void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule);
        /// <summary>
        /// 日志
        /// </summary>
        /// <param name="requestIdentity"></param>
        /// <param name="counter"></param>
        /// <param name="rule"></param>
        public void LogBlockedRequest(RequestIdentity requestIdentity, RateLimitCounter counter, RateLimitRule rule)
        {
            var log = $"{requestIdentity.RequestPath}\r\n已限制来自[{requestIdentity.PolicyType.ToString()}]{requestIdentity.Value}的请求  {requestIdentity.HttpVerb}:{requestIdentity.Path}\r\n匹配规则: {rule.Endpoint},{rule.Limit}/{rule.Period}\r\n计数器: [{ComputeCounterKey(requestIdentity, rule)}] {counter.TotalRequests},{counter.Timestamp.ToString("yyyy-MM-dd HH:mm:ss")}";

            _options.LogHandler.Invoke(log);
        }
Ejemplo n.º 27
0
 public bool Set(string id, RateLimitCounter counter, TimeSpan expirationTime)
 {
     _client.Set(id, counter, expirationTime);
     return(true);
 }