private bool ApplyThrottling(ActionExecutingContext filterContext, out EnableThrottlingAttribute attr) { var applyThrottling = false; attr = null; if (filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(EnableThrottlingAttribute), true)) { attr = (EnableThrottlingAttribute)filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof(EnableThrottlingAttribute), true).First(); applyThrottling = true; } //disabled on the class if (filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(DisableThrottlingAttribute), true)) { applyThrottling = false; } if (filterContext.ActionDescriptor.IsDefined(typeof(EnableThrottlingAttribute), true)) { attr = (EnableThrottlingAttribute)filterContext.ActionDescriptor.GetCustomAttributes(typeof(EnableThrottlingAttribute), true).First(); applyThrottling = true; } //explicit disabled if (filterContext.ActionDescriptor.IsDefined(typeof(DisableThrottlingAttribute), true)) { applyThrottling = false; } return(applyThrottling); }
public override void OnActionExecuting(ActionExecutingContext filterContext) { EnableThrottlingAttribute attrPolicy = null; var applyThrottling = ApplyThrottling(filterContext, out attrPolicy); if (Policy != null && applyThrottling) { var identity = SetIdentity(filterContext.HttpContext.Request); if (!IsWhitelisted(identity)) { TimeSpan timeSpan = TimeSpan.FromSeconds(1); var rates = Policy.Rates.AsEnumerable(); if (Policy.StackBlockedRequests) { //all requests including the rejected ones will stack in this order: day, hour, min, sec //if a client hits the hour limit then the minutes and seconds counters will expire and will eventually get erased from cache rates = Policy.Rates.Reverse(); } //apply policy //the IP rules are applied last and will overwrite any client rule you might defined foreach (var rate in rates) { var rateLimitPeriod = rate.Key; var rateLimit = rate.Value; switch (rateLimitPeriod) { case RateLimitPeriod.Second: timeSpan = TimeSpan.FromSeconds(1); break; case RateLimitPeriod.Minute: timeSpan = TimeSpan.FromMinutes(1); break; case RateLimitPeriod.Hour: timeSpan = TimeSpan.FromHours(1); break; case RateLimitPeriod.Day: timeSpan = TimeSpan.FromDays(1); break; case RateLimitPeriod.Week: timeSpan = TimeSpan.FromDays(7); break; } //increment counter string requestId; var throttleCounter = ProcessRequest(identity, timeSpan, rateLimitPeriod, out requestId); if (throttleCounter.Timestamp + timeSpan < DateTime.UtcNow) { continue; } //apply EnableThrottlingAttribute policy var attrLimit = attrPolicy.GetLimit(rateLimitPeriod); if (attrLimit > 0) { rateLimit = attrLimit; } //apply endpoint rate limits if (Policy.EndpointRules != null) { var rules = Policy.EndpointRules.Where(x => identity.Endpoint.IndexOf(x.Key, 0, StringComparison.InvariantCultureIgnoreCase) != -1).ToList(); if (rules.Any()) { //get the lower limit from all applying rules var customRate = (from r in rules let rateValue = r.Value.GetLimit(rateLimitPeriod) select rateValue).Min(); if (customRate > 0) { rateLimit = customRate; } } } //apply custom rate limit for clients that will override endpoint limits if (Policy.ClientRules != null && Policy.ClientRules.Keys.Contains(identity.ClientKey)) { var limit = Policy.ClientRules[identity.ClientKey].GetLimit(rateLimitPeriod); if (limit > 0) { rateLimit = limit; } } //apply custom rate limit for user agent if (Policy.UserAgentRules != null && !string.IsNullOrEmpty(identity.UserAgent)) { var rules = Policy.UserAgentRules.Where(x => identity.UserAgent.IndexOf(x.Key, 0, StringComparison.InvariantCultureIgnoreCase) != -1).ToList(); if (rules.Any()) { //get the lower limit from all applying rules var customRate = (from r in rules let rateValue = r.Value.GetLimit(rateLimitPeriod) select rateValue).Min(); rateLimit = customRate; } } //enforce ip rate limit as is most specific string ipRule = null; if (Policy.IpRules != null && IpAddressParser.ContainsIp(Policy.IpRules.Keys.ToList(), identity.ClientIp, out ipRule)) { var limit = Policy.IpRules[ipRule].GetLimit(rateLimitPeriod); if (limit > 0) { rateLimit = limit; } } //check if limit is reached if (rateLimit > 0 && throttleCounter.TotalRequests > rateLimit) { //log blocked request if (Logger != null) { Logger.Log(ComputeLogEntry(requestId, identity, throttleCounter, rateLimitPeriod.ToString(), rateLimit, filterContext.HttpContext.Request)); } //break execution and return 409 var message = string.IsNullOrEmpty(QuotaExceededMessage) ? "HTTP request quota exceeded! maximum admitted {0} per {1}" : QuotaExceededMessage; //add status code and retry after x seconds to response filterContext.HttpContext.Response.StatusCode = (int)QuotaExceededResponseCode; filterContext.HttpContext.Response.Headers.Set("Retry-After", RetryAfterFrom(throttleCounter.Timestamp, rateLimitPeriod)); filterContext.Result = QuotaExceededResult( filterContext.RequestContext, string.Format(message, rateLimit, rateLimitPeriod), QuotaExceededResponseCode, requestId); return; } } } } base.OnActionExecuting(filterContext); }
private bool ApplyThrottling(ActionExecutingContext filterContext, out EnableThrottlingAttribute attr) { var applyThrottling = false; attr = null; if (filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(EnableThrottlingAttribute), true)) { attr = (EnableThrottlingAttribute)filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof(EnableThrottlingAttribute), true).First(); applyThrottling = true; } if (filterContext.ActionDescriptor.IsDefined(typeof(EnableThrottlingAttribute), true)) { attr = (EnableThrottlingAttribute)filterContext.ActionDescriptor.GetCustomAttributes(typeof(EnableThrottlingAttribute), true).First(); applyThrottling = true; } //explicit disabled if (filterContext.ActionDescriptor.IsDefined(typeof(DisableThrottingAttribute), true)) { applyThrottling = false; } return applyThrottling; }