public async Task <IEnumerable <RateLimitRule> > GetMatchingRulesAsync(ClientRequestIdentity identity) { var policy = await _policyStore.GetAsync($"{_options.ClientPolicyPrefix}_{identity.ClientId}"); return(GetMatchingRules(identity, policy?.Rules)); }
protected abstract void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule);
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}. Blocked by rule {rule.Endpoint}, TraceIdentifier {httpContext.TraceIdentifier}."); }
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" var pathLimits = rules.Where(r => $"*:{identity.Path}".IsWildcardMatch(r.Endpoint)); limits.AddRange(pathLimits); // search for rules with endpoints like "matching_verb:/matching_path" var verbLimits = rules.Where(r => $"{identity.HttpVerb}:{identity.Path}".IsWildcardMatch(r.Endpoint)); 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}".IsWildcardMatch(r.Endpoint)); 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}".IsWildcardMatch(r.Endpoint)); 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) { // 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); }