public List <RateLimitRule> GetMatchingRules(ClientRequestIdentity identity) { var limits = new List <RateLimitRule>(); var policies = _policyStore.Get($"{_options.IpPolicyPrefix}"); if (policies != null && policies.IpRules != null && policies.IpRules.Any()) { // search for rules with IP intervals containing client IP var matchPolicies = policies.IpRules.Where(r => _ipParser.ContainsIp(r.Ip, identity.ClientIp)).AsEnumerable(); var rules = new List <RateLimitRule>(); foreach (var item in matchPolicies) { rules.AddRange(item.Rules); } foreach (var rule in rules) { var regex = new Regex(rule.UrlRegex, RegexOptions.IgnoreCase); var match = regex.Match(identity.HttpVerb + ":" + identity.Path); if (match.Success) { limits.Add(rule); } } } // get the most restrictive limit for each period limits = limits.GroupBy(l => l.Period).Select(l => l.OrderBy(x => x.Limit)).Select(l => l.First()).ToList(); // search for matching general rules if (_options.GeneralRules != null) { var matchingGeneralLimits = new List <RateLimitRule>(); foreach (var generalRule in _options.GeneralRules) { var regex = new Regex(generalRule.UrlRegex, RegexOptions.IgnoreCase); var match = regex.Match(identity.HttpVerb + ":" + identity.Path); if (match.Success) { matchingGeneralLimits.Add(generalRule); } } // get the most restrictive general limit for each period var generalLimits = matchingGeneralLimits.GroupBy(l => l.Period).Select(l => l.OrderBy(x => x.Limit)).Select(l => l.First()).ToList(); foreach (var generalLimit in generalLimits) { // add general rule if no specific rule is declared for the specified period if (!limits.Exists(l => l.Period == generalLimit.Period)) { limits.Add(generalLimit); } } } foreach (var item in limits) { //parse period text into time spans item.PeriodTimespan = _core.ConvertToTimeSpan(item.Period); } limits = limits.OrderBy(l => l.PeriodTimespan).ToList(); if (_options.StackBlockedRequests) { limits.Reverse(); } return(limits); }
public List <RateLimitRule> GetMatchingRules(ClientRequestIdentity identity) { var limits = new List <RateLimitRule>(); var policies = _policyStore.Get($"{_options.IpPolicyPrefix}"); if (policies != null && policies.IpRules != null && policies.IpRules.Any()) { // search for rules with IP intervals containing client IP var matchPolicies = policies.IpRules.Where(r => _ipParser.ContainsIp(r.Ip, identity.ClientIp)).AsEnumerable(); var rules = new List <RateLimitRule>(); foreach (var item in matchPolicies) { rules.AddRange(item.Rules); } if (_options.EnableEndpointRateLimiting) { // search for rules with endpoints like "*" and "*:/matching_path" var pathLimits = rules.Where(l => $"*:{identity.Path}".ToLowerInvariant().Contains(l.Endpoint.ToLowerInvariant())).AsEnumerable(); limits.AddRange(pathLimits); // search for rules with endpoints like "matching_verb:/matching_path" var verbLimits = rules.Where(l => $"{identity.HttpVerb}:{identity.Path}".ToLowerInvariant().Contains(l.Endpoint.ToLowerInvariant())).AsEnumerable(); limits.AddRange(verbLimits); } else { //ignore endpoint rules and search for global rules only var genericLimits = rules.Where(l => l.Endpoint == "*").AsEnumerable(); limits.AddRange(genericLimits); } } // get the most restrictive limit for each period limits = limits.GroupBy(l => l.Period).Select(l => l.OrderBy(x => x.Limit)).Select(l => l.First()).ToList(); // search for matching general rules if (_options.GeneralRules != null) { var matchingGeneralLimits = new List <RateLimitRule>(); if (_options.EnableEndpointRateLimiting) { // search for rules with endpoints like "*" and "*:/matching_path" in general rules var pathLimits = _options.GeneralRules.Where(l => $"*:{identity.Path}".ToLowerInvariant().Contains(l.Endpoint.ToLowerInvariant())).AsEnumerable(); matchingGeneralLimits.AddRange(pathLimits); // search for rules with endpoints like "matching_verb:/matching_path" in general rules var verbLimits = _options.GeneralRules.Where(l => $"{identity.HttpVerb}:{identity.Path}".ToLowerInvariant().IsMatch(l.Endpoint.ToLowerInvariant())).AsEnumerable(); matchingGeneralLimits.AddRange(verbLimits); } else { //ignore endpoint rules and search for global rules in general rules var genericLimits = _options.GeneralRules.Where(l => l.Endpoint == "*").AsEnumerable(); matchingGeneralLimits.AddRange(genericLimits); } // get the most restrictive general limit for each period var generalLimits = matchingGeneralLimits.GroupBy(l => l.Period).Select(l => l.OrderBy(x => x.Limit)).Select(l => l.First()).ToList(); foreach (var generalLimit in generalLimits) { // add general rule if no specific rule is declared for the specified period if (!limits.Exists(l => l.Period == generalLimit.Period)) { limits.Add(generalLimit); } } } foreach (var item in limits) { //parse period text into time spans item.PeriodTimespan = _core.ConvertToTimeSpan(item.Period); } limits = limits.OrderBy(l => l.PeriodTimespan).ToList(); if (_options.StackBlockedRequests) { limits.Reverse(); } return(limits); }
public bool RateExceeded(HttpContext context, IPRateLimitingSetting setting, out int retryAfter) { retryAfter = 0; var path = context.Request.Path; var method = context.Request.Method.ToLower(); var clientIP = _ipParser.GetClientIp(context); //System.Diagnostics.Debugger.Break(); if (_ipParser.ContainsIp(setting.IPWhitelist, clientIP.ToString())) { return(false); } if (_ipParser.ContainsIp(setting.IPBlockedlist, clientIP.ToString())) { return(true); } var generalRuleExists = setting.GeneralRules.FirstOrDefault(q => q.Verbs.ToLower().Contains(method) && q.Path == path); if (generalRuleExists == null) { return(false); } if (!IpRateLimiterDictionary.TryGetValue(clientIP.ToString(), out IPRateCounter existedRate)) { var iPRateCounter = new IPRateCounter() { Count = 1, CreatedAt = DateTime.Now }; IpRateLimiterDictionary.AddOrUpdate(clientIP.ToString(), iPRateCounter, (key, oldValue) => iPRateCounter); return(false); } var now = DateTime.Now.AddSeconds(-generalRuleExists.PeriodTime); var createdAt = existedRate.CreatedAt; if (createdAt > now) { retryAfter = (int)(now - createdAt).TotalSeconds; if (existedRate.Count >= generalRuleExists.Limit) { return(true); } else { existedRate.Count++; return(false); } } else { existedRate.Count = 1; existedRate.CreatedAt = DateTime.Now; IpRateLimiterDictionary.AddOrUpdate(clientIP.ToString(), existedRate, (key, oldValue) => existedRate); return(false); } }