private async Task <RateLimiterContext> GetRateLimiterContextAsync(HttpContext context) { var ip = context.Connection.RemoteIpAddress?.ToString(); var rateLimiterContext = new RateLimiterContext(); rateLimiterContext.IpNumber = ip; foreach (var rule in Settings.Rules) { var cacheKey = $"{rule.Name}:{ip}"; var contextString = await _cache.GetStringAsync(cacheKey); RateLimiterRuleContext ruleContext; if (contextString != null) { ruleContext = new RateLimiterRuleContext(contextString); ruleContext.ExistsInCache = true; ruleContext.Rule = Settings.Rules.Find(r => r.Name == rule.Name); } else { ruleContext = new RateLimiterRuleContext(rule); } ruleContext.Context = rateLimiterContext; rateLimiterContext.Rules.Add(ruleContext); } return(rateLimiterContext); }
private void ProcessFailedRequest(Exception exception, RateLimiterRuleContext ruleContext) { var rule = ruleContext.Rule; var exceptionName = exception.GetType().Name; if (!rule.RateLimitExceptions.Contains(exceptionName)) { ProcessSuccessfulRequest(ruleContext); return; } ruleContext.SoftRequests++; ruleContext.HardRequests++; // Use HarmlessRequests (i.e. successful requests with low response time) // to reduce the value of SoftRequests when comparing to the limit. // This way, a caller with many clients won't be banned if there are many // users that are failing their requests due to natural reasons. var modulatedRequests = ruleContext.SoftRequests - ruleContext.HarmlessRequests / ruleContext.Rule.SuccessfulToFailedRatio; if (modulatedRequests > rule.SoftRequestLimit || ruleContext.HardRequests > rule.HardRequestLimit) { ruleContext.BannedUntil = DateTime.UtcNow.AddMinutes(rule.BanForMinutes); } }
private void ProcessSuccessfulRequest(RateLimiterRuleContext ruleContext) { if (ruleContext.Context.ResponseTime > ruleContext.Rule.RateLimitResponseMilliseconds) { ruleContext.SoftRequests++; ruleContext.HardRequests++; } else { ruleContext.HarmlessRequests++; } }