protected virtual List <RateLimitRule> GetMatchingRules(ClientRequestIdentity identity, List <RateLimitRule> rules = null) { var limits = new List <RateLimitRule>(); if (rules?.Any() == true) { if (_options.EnableEndpointRateLimiting) { // search for rules with endpoints like "*" and "*:/matching_path" string path = _options.EnableRegexRuleMatching ? $".+:{identity.Path}" : $"*:{identity.Path}"; var pathLimits = rules.Where(r => path.IsUrlMatch(r.Endpoint, _options.EnableRegexRuleMatching)); limits.AddRange(pathLimits); // search for rules with endpoints like "matching_verb:/matching_path" var verbLimits = rules.Where(r => $"{identity.HttpVerb}:{identity.Path}".IsUrlMatch(r.Endpoint, _options.EnableRegexRuleMatching)); limits.AddRange(verbLimits); } else { // ignore endpoint rules and search for global rules only var genericLimits = rules.Where(r => r.Endpoint == "*"); 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(r => $"*:{identity.Path}".IsUrlMatch(r.Endpoint, _options.EnableRegexRuleMatching)); matchingGeneralLimits.AddRange(pathLimits); // search for rules with endpoints like "matching_verb:/matching_path" in general rules var verbLimits = _options.GeneralRules.Where(r => $"{identity.HttpVerb}:{identity.Path}".IsUrlMatch(r.Endpoint, _options.EnableRegexRuleMatching)); matchingGeneralLimits.AddRange(verbLimits); } else { // ignore endpoint rules and search for global rules in general rules var genericLimits = _options.GeneralRules.Where(r => r.Endpoint == "*"); 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).ThenBy(x => x.Endpoint)) .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) { if (!item.PeriodTimespan.HasValue) { // parse period text into time spans item.PeriodTimespan = item.Period.ToTimeSpan(); } } limits = limits.OrderBy(l => l.PeriodTimespan).ToList(); if (_options.StackBlockedRequests) { limits.Reverse(); } return(limits); }
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 virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule) { _logger.LogInformation($"Request {identity.HttpVerb}:{identity.Path} from User {identity.User} has been blocked, quota {rule.Limit}/{rule.Period} exceeded by {counter.TotalRequests}. Blocked by rule {rule.Endpoint}, TraceIdentifier {httpContext.TraceIdentifier}."); }
public RateLimitHeaders GetRateLimitHeaders(ClientRequestIdentity requestIdentity, RateLimitRule rule) { return(_core.GetRateLimitHeaders(requestIdentity, rule)); }
public IEnumerable <RateLimitRule> GetMatchingRules(ClientRequestIdentity identity) { var limits = new List <RateLimitRule>(); var policy = _policyStore.Get($"{_options.ClientPolicyPrefix}_{identity.ClientId}"); if (policy != null) { if (_options.EnableEndpointRateLimiting) { // search for rules with endpoints like "*" and "*:/matching_path" var pathLimits = policy.Rules.Where(l => $"*:{identity.Path}".ContainsIgnoreCase(l.Endpoint)) .AsEnumerable(); limits.AddRange(pathLimits); // search for rules with endpoints like "matching_verb:/matching_path" var verbLimits = policy.Rules .Where(l => $"{identity.HttpVerb}:{identity.Path}".ContainsIgnoreCase(l.Endpoint)) .AsEnumerable(); limits.AddRange(verbLimits); } else { //ignore endpoint rules and search for global rules only var genericLimits = policy.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}".ContainsIgnoreCase(l.Endpoint)).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}".ContainsIgnoreCase(l.Endpoint)) .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 async Task <RateLimitCounter> ProcessRequestAsync(ClientRequestIdentity requestIdentity, RateLimitRule rule, CancellationToken cancellationToken = default) { return(await _processingStrategy.ProcessRequestAsync(requestIdentity, rule, _counterKeyBuilder, _options, cancellationToken)); }
public RateLimitCounter ProcessRequest(ClientRequestIdentity requestIdentity, RateLimitRule rule) { return(_core.ProcessRequest(requestIdentity, rule)); }
protected override void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule) { _logger.LogInformation("Request {HttpVerb}:{Path} from ClientId {ClientId} has been blocked, quota {Limit}/{Period} exceeded by {Count}. Blocked by rule {Endpoint}, TraceIdentifier {TraceIdentifier}.", identity.HttpVerb, identity.Path, identity.ClientId, rule.Limit, rule.Period, counter.Count, rule.Endpoint, httpContext.TraceIdentifier); }
public async Task <IEnumerable <RateLimitRule> > GetMatchingRulesAsync(ClientRequestIdentity identity, CancellationToken cancellationToken = default) { var policy = await _policyStore.GetAsync($"{_options.ClientPolicyPrefix}_{identity.ClientId}", cancellationToken); return(GetMatchingRules(identity, policy?.Rules)); }
public abstract Task <RateLimitCounter> ProcessRequestAsync(ClientRequestIdentity requestIdentity, RateLimitRule rule, ICounterKeyBuilder counterKeyBuilder, RateLimitOptions rateLimitOptions, CancellationToken cancellationToken = default);
protected abstract void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule);
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)); var rules = new List <RateLimitRule>(); foreach (var item in matchPolicies) { rules.AddRange(item.Rules); } if (_options.EnableEndpointRateLimiting) { var query_result = EndPortMatcher.Checked(rules, identity); limits.AddRange(query_result); /* * // search for rules with endpoints like "*" and "*:/matching_path" * var pathLimits = rules.Where(l => $"*:{identity.Path}".ToLowerInvariant().Contains(l.Endpoint.ToLowerInvariant())); * 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())); * limits.AddRange(verbLimits); */ } else { //ignore endpoint rules and search for global rules only var genericLimits = rules.Where(l => l.Endpoint == "*"); 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); }
protected override void LogBlockedRequest(HttpContext httpContext, AspNetCoreRateLimit.ClientRequestIdentity identity, AspNetCoreRateLimit.RateLimitCounter counter, AspNetCoreRateLimit.RateLimitRule rule) { base.LogBlockedRequest(httpContext, identity, counter, rule); }
public virtual Task <RateLimitCounter> ProcessRequestAsync(ClientRequestIdentity requestIdentity, RateLimitRule rule, CancellationToken cancellationToken = default) { var counterId = BuildCounterKey(requestIdentity, rule); return(_counterStore.IncrementAsync(counterId, rule.PeriodTimespan.Value, _config.RateIncrementer)); }