/// <summary> /// Initializes a new instance of the <see cref="ThrottlingMiddleware"/> class. /// Persists the policy object in cache using <see cref="IPolicyRepository"/> implementation. /// The policy object can be updated by <see cref="ThrottleManager"/> at runtime. /// </summary> /// <param name="policy"> /// The policy. /// </param> /// <param name="policyRepository"> /// The policy repository. /// </param> /// <param name="repository"> /// The repository. /// </param> /// <param name="logger"> /// The logger. /// </param> /// <param name="ipAddressParser"> /// The IpAddressParser /// </param> public ThrottlingMiddleware(OwinMiddleware next, ThrottlePolicy policy, IPolicyRepository policyRepository, IThrottleRepository repository, IThrottleLogger logger, IIpAddressParser ipAddressParser) : base(next) { core = new ThrottlingCore(); core.Repository = repository; Repository = repository; Logger = logger; if (ipAddressParser != null) { core.IpAddressParser = ipAddressParser; } QuotaExceededResponseCode = (HttpStatusCode)429; this.policy = policy; this.policyRepository = policyRepository; if (policyRepository != null) { policyRepository.Save(ThrottleManager.GetPolicyKey(), policy); } }
public static ThrottlePolicy FromStore(IThrottlePolicyProvider provider) { var settings = provider.ReadSettings(); var whitelists = provider.AllWhitelists(); var rules = provider.AllRules(); var policy = new ThrottlePolicy( perSecond: settings.LimitPerSecond, perMinute: settings.LimitPerMinute, perHour: settings.LimitPerHour, perDay: settings.LimitPerDay, perWeek: settings.LimitPerWeek); policy.IpThrottling = settings.IpThrottling; policy.ClientThrottling = settings.ClientThrottling; policy.EndpointThrottling = settings.EndpointThrottling; policy.StackBlockedRequests = settings.StackBlockedRequests; policy.IpRules = new Dictionary <string, RateLimits>(); policy.ClientRules = new Dictionary <string, RateLimits>(); policy.EndpointRules = new Dictionary <string, RateLimits>(); policy.EndpointWhitelist = new List <string>(); policy.IpWhitelist = new List <string>(); policy.ClientWhitelist = new List <string>(); foreach (var item in rules) { var rateLimit = new RateLimits { PerSecond = item.LimitPerSecond, PerMinute = item.LimitPerMinute, PerHour = item.LimitPerHour, PerDay = item.LimitPerDay, PerWeek = item.LimitPerWeek }; switch (item.PolicyType) { case ThrottlePolicyType.IpThrottling: policy.IpRules.Add(item.Entry, rateLimit); break; case ThrottlePolicyType.ClientThrottling: policy.ClientRules.Add(item.Entry, rateLimit); break; case ThrottlePolicyType.EndpointThrottling: policy.EndpointRules.Add(item.Entry, rateLimit); break; } } if (whitelists != null) { policy.IpWhitelist.AddRange(whitelists.Where(x => x.PolicyType == ThrottlePolicyType.IpThrottling).Select(x => x.Entry)); policy.ClientWhitelist.AddRange(whitelists.Where(x => x.PolicyType == ThrottlePolicyType.ClientThrottling).Select(x => x.Entry)); policy.EndpointWhitelist.AddRange(whitelists.Where(x => x.PolicyType == ThrottlePolicyType.EndpointThrottling).Select(x => x.Entry)); } return(policy); }
/// <summary> /// Initializes a new instance of the <see cref="ThrottlingHandler"/> class. /// Persists the policy object in cache using <see cref="IPolicyRepository"/> implementation. /// The policy object can be updated by <see cref="ThrottleManager"/> at runtime. /// </summary> /// <param name="policy"> /// The policy. /// </param> /// <param name="policyRepository"> /// The policy repository. /// </param> /// <param name="repository"> /// The repository. /// </param> /// <param name="logger"> /// The logger. /// </param> /// <param name="ipAddressParser"> /// The IpAddressParser /// </param> public ThrottlingHandler(ThrottlePolicy policy, IPolicyRepository policyRepository, IThrottleRepository repository, IThrottleLogger logger, IIpAddressParser ipAddressParser = null) { core = new ThrottlingCore { Repository = repository }; Repository = repository; Logger = logger; if (ipAddressParser != null) { core.IpAddressParser = ipAddressParser; } QuotaExceededResponseCode = (HttpStatusCode)429; this.Policy = policy; this.PolicyRepository = policyRepository; policyRepository?.Save(ThrottleManager.GetPolicyKey(), policy); }
public void Save(string id, ThrottlePolicy policy) { if (memCache[id] != null) { memCache[id] = policy; } else { memCache.Add( id, policy, new CacheItemPolicy()); } }
/// <summary> /// Initializes a new instance of the <see cref="ThrottlingFilter"/> class. /// Persists the policy object in cache using <see cref="IPolicyRepository"/> implementation. /// The policy object can be updated by <see cref="ThrottleManager"/> at runtime. /// </summary> /// <param name="policy"> /// The policy. /// </param> /// <param name="policyRepository"> /// The policy repository. /// </param> /// <param name="repository"> /// The repository. /// </param> /// <param name="logger"> /// The logger. /// </param> public ThrottlingFilter(ThrottlePolicy policy, IPolicyRepository policyRepository, IThrottleRepository repository, IThrottleLogger logger) { core = new ThrottlingCore(); core.Repository = repository; Repository = repository; Logger = logger; QuotaExceededResponseCode = (HttpStatusCode)429; this.policy = policy; this.policyRepository = policyRepository; if (policyRepository != null) { policyRepository.Save(ThrottleManager.GetPolicyKey(), policy); } }
public void Save(string id, ThrottlePolicy policy) { if (HttpContext.Current.Cache[id] != null) { HttpContext.Current.Cache[id] = policy; } else { HttpContext.Current.Cache.Add( id, policy, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.High, null); } }
public HttpModuleThrottlingHandler(ThrottlePolicy policy, IPolicyRepository policyRepository, IThrottleRepository repository, IIpAddressParser ipAddressParser = null) { core = new ThrottlingCore(); core.Repository = repository; Repository = repository; if (ipAddressParser != null) { core.IpAddressParser = ipAddressParser; } QuotaExceededResponseCode = (HttpStatusCode)429; this.policy = policy; this.policyRepository = policyRepository; if (policyRepository != null) { policyRepository.Save(ThrottleManager.GetPolicyKey(), policy); } }
public override async Task Invoke(IOwinContext context) { var response = context.Response; var request = context.Request; // get policy from repo if (policyRepository != null) { policy = policyRepository.FirstOrDefault(ThrottleManager.GetPolicyKey()); } if (policy == null || (!policy.IpThrottling && !policy.ClientThrottling && !policy.EndpointThrottling)) { await Next.Invoke(context); return; } core.Repository = Repository; core.Policy = policy; var identity = SetIdentity(request); if (core.IsWhitelisted(identity)) { await Next.Invoke(context); return; } TimeSpan timeSpan = TimeSpan.FromSeconds(1); // get default rates var defRates = core.RatesWithDefaults(Policy.Rates.ToList()); if (Policy.StackBlockedRequests) { // all requests including the rejected ones will stack in this order: week, 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 defRates.Reverse(); } // apply policy foreach (var rate in defRates) { var rateLimitPeriod = rate.Key; var rateLimit = rate.Value; timeSpan = core.GetTimeSpanFromPeriod(rateLimitPeriod); // apply global rules core.ApplyRules(identity, timeSpan, rateLimitPeriod, ref rateLimit); if (rateLimit > 0) { // increment counter string requestId; var throttleCounter = core.ProcessRequest(identity, timeSpan, rateLimitPeriod, out requestId); // check if key expired if (throttleCounter.Timestamp + timeSpan < DateTime.UtcNow) { continue; } // check if limit is reached if (throttleCounter.TotalRequests > rateLimit) { // log blocked request if (Logger != null) { Logger.Log(core.ComputeLogEntry(requestId, identity, throttleCounter, rateLimitPeriod.ToString(), rateLimit, null)); } var message = !string.IsNullOrEmpty(this.QuotaExceededMessage) ? this.QuotaExceededMessage : "API calls quota exceeded! maximum admitted {0} per {1}."; // break execution response.OnSendingHeaders(state => { var resp = (OwinResponse)state; resp.Headers.Add("Retry-After", new string[] { core.RetryAfterFrom(throttleCounter.Timestamp, rateLimitPeriod) }); resp.StatusCode = (int)QuotaExceededResponseCode; resp.ReasonPhrase = string.Format(message, rateLimit, rateLimitPeriod); }, response); return; } } } // no throttling required await Next.Invoke(context); }
public static ThrottlePolicy FromStore(IThrottlePolicyProvider provider) { var settings = provider.ReadSettings(); var whitelists = provider.AllWhitelists(); var rules = provider.AllRules(); var policy = new ThrottlePolicy(perSecond: settings.LimitPerSecond, perMinute: settings.LimitPerMinute, perHour: settings.LimitPerHour, perDay: settings.LimitPerDay, perWeek: settings.LimitPerWeek); policy.IpThrottling = settings.IpThrottling; policy.ClientThrottling = settings.ClientThrottling; policy.EndpointThrottling = settings.EndpointThrottling; policy.StackBlockedRequests = settings.StackBlockedRequests; policy.IpRules = new Dictionary<string, RateLimits>(); policy.ClientRules = new Dictionary<string, RateLimits>(); policy.EndpointRules = new Dictionary<string, RateLimits>(); foreach (var item in rules) { var rateLimit = new RateLimits { PerSecond = item.LimitPerSecond, PerMinute = item.LimitPerMinute, PerHour = item.LimitPerHour, PerDay = item.LimitPerDay, PerWeek = item.LimitPerWeek }; switch (item.PolicyType) { case ThrottlePolicyType.IpThrottling: policy.IpRules.Add(item.Entry, rateLimit); break; case ThrottlePolicyType.ClientThrottling: policy.ClientRules.Add(item.Entry, rateLimit); break; case ThrottlePolicyType.EndpointThrottling: policy.EndpointRules.Add(item.Entry, rateLimit); break; } } return policy; }
private ThrottleCounter ProcessRequest(ThrottlePolicy throttlePolicy, RequestIndentity throttleEntry, TimeSpan timeSpan, RateLimitPeriod period, out string id) { var throttleCounter = new ThrottleCounter(); throttleCounter.Timestamp = DateTime.UtcNow; throttleCounter.TotalRequests = 1; //computed request unique id from IP, client key, url and period id = "throttle"; if (throttlePolicy.IpThrottling) { if (throttlePolicy.IpWhitelist != null && throttlePolicy.IpWhitelist.Contains(throttleEntry.ClientIp)) { return throttleCounter; } id += "_" + throttleEntry.ClientIp; } if (throttlePolicy.ClientThrottling) { if (throttlePolicy.ClientWhitelist != null && throttlePolicy.ClientWhitelist.Contains(throttleEntry.ClientKey)) { return throttleCounter; } id += "_" + throttleEntry.ClientKey; } if (throttlePolicy.EndpointThrottling) { if (throttlePolicy.EndpointWhitelist != null && Policy.EndpointWhitelist.Any(x => throttleEntry.Endpoint.Contains(x))) { return throttleCounter; } id += "_" + throttleEntry.Endpoint; } id += "_" + period; //get the hash value of the computed id var hashId = ComputeHash(id); //serial reads and writes lock (_processLocker) { var entry = Repository.FirstOrDefault(hashId); if (entry.HasValue) { //entry has not expired if (entry.Value.Timestamp + timeSpan >= DateTime.UtcNow) { //increment request count var totalRequests = entry.Value.TotalRequests + 1; //deep copy throttleCounter = new ThrottleCounter { Timestamp = entry.Value.Timestamp, TotalRequests = totalRequests }; } } //stores: id (string) - timestamp (datetime) - total (long) Repository.Save(hashId, throttleCounter, timeSpan); } return throttleCounter; }
public override void OnActionExecuting(HttpActionContext actionContext) { EnableThrottlingAttribute attrPolicy = null; var applyThrottling = ApplyThrottling(actionContext, out attrPolicy); // get policy from repo if (policyRepository != null) { policy = policyRepository.FirstOrDefault(ThrottleManager.GetPolicyKey()); } if (Policy != null && applyThrottling) { core.Repository = Repository; core.Policy = Policy; var identity = SetIndentity(actionContext.Request); if (!core.IsWhitelisted(identity)) { TimeSpan timeSpan = TimeSpan.FromSeconds(1); // get default rates var defRates = core.RatesWithDefaults(Policy.Rates.ToList()); if (Policy.StackBlockedRequests) { // all requests including the rejected ones will stack in this order: week, 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 defRates.Reverse(); } // apply policy foreach (var rate in defRates) { var rateLimitPeriod = rate.Key; var rateLimit = rate.Value; timeSpan = core.GetTimeSpanFromPeriod(rateLimitPeriod); // apply EnableThrottlingAttribute policy var attrLimit = attrPolicy.GetLimit(rateLimitPeriod); if (attrLimit > 0) { rateLimit = attrLimit; } // apply global rules core.ApplyRules(identity, timeSpan, rateLimitPeriod, ref rateLimit); if (rateLimit > 0) { // increment counter string requestId; var throttleCounter = core.ProcessRequest(identity, timeSpan, rateLimitPeriod, out requestId); // check if key expired if (throttleCounter.Timestamp + timeSpan < DateTime.UtcNow) { continue; } // check if limit is reached if (throttleCounter.TotalRequests > rateLimit) { // log blocked request if (Logger != null) { Logger.Log(core.ComputeLogEntry(requestId, identity, throttleCounter, rateLimitPeriod.ToString(), rateLimit, actionContext.Request)); } var message = !string.IsNullOrEmpty(this.QuotaExceededMessage) ? this.QuotaExceededMessage : "API calls quota exceeded! maximum admitted {0} per {1}."; var content = this.QuotaExceededContent != null ? this.QuotaExceededContent(rateLimit, rateLimitPeriod) : string.Format(message, rateLimit, rateLimitPeriod); // add status code and retry after x seconds to response actionContext.Response = QuotaExceededResponse( actionContext.Request, string.Format(message, rateLimit, rateLimitPeriod), QuotaExceededResponseCode, core.RetryAfterFrom(throttleCounter.Timestamp, rateLimitPeriod)); } } } } } base.OnActionExecuting(actionContext); }
protected override Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // get policy from repo if (policyRepository != null) { policy = policyRepository.FirstOrDefault(ThrottleManager.GetPolicyKey()); } if (policy == null || (!policy.IpThrottling && !policy.ClientThrottling && !policy.EndpointThrottling)) { return(base.SendAsync(request, cancellationToken)); } core.Repository = Repository; core.Policy = policy; var identity = SetIndentity(request); if (core.IsWhitelisted(identity)) { return(base.SendAsync(request, cancellationToken)); } TimeSpan timeSpan = TimeSpan.FromSeconds(1); // get default rates var defRates = core.RatesWithDefaults(Policy.Rates.ToList()); if (Policy.StackBlockedRequests) { // all requests including the rejected ones will stack in this order: week, 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 defRates.Reverse(); } // apply policy foreach (var rate in defRates) { var rateLimitPeriod = rate.Key; var rateLimit = rate.Value; timeSpan = core.GetTimeSpanFromPeriod(rateLimitPeriod); // apply global rules core.ApplyRules(identity, timeSpan, rateLimitPeriod, ref rateLimit); if (rateLimit > 0) { // increment counter string requestId; var throttleCounter = core.ProcessRequest(identity, timeSpan, rateLimitPeriod, out requestId); // check if key expired if (throttleCounter.Timestamp + timeSpan < DateTime.UtcNow) { continue; } // check if limit is reached if (throttleCounter.TotalRequests > rateLimit * policy.WarningLevel) { // log blocked request if (Logger != null) { Logger.Log(core.ComputeLogEntry(requestId, identity, throttleCounter, rateLimitPeriod.ToString(), rateLimit, policy.WarningLevel, request)); } if (throttleCounter.TotalRequests > rateLimit) { var message = !string.IsNullOrEmpty(this.QuotaExceededMessage) ? this.QuotaExceededMessage : "API calls quota exceeded! maximum admitted {0} per {1}."; var content = this.QuotaExceededContent != null ? this.QuotaExceededContent(rateLimit, rateLimitPeriod) : string.Format(message, rateLimit, rateLimitPeriod); // break execution return(QuotaExceededResponse( request, content, QuotaExceededResponseCode, core.RetryAfterFrom(throttleCounter.Timestamp, rateLimitPeriod))); } } } } // no throttling required return(base.SendAsync(request, cancellationToken)); }
/// <summary> /// Updates the policy object cached value /// </summary> /// <param name="policy"> /// The policy. /// </param> /// <param name="cacheRepository"> /// The policy repository. /// </param> public static void UpdatePolicy(ThrottlePolicy policy, IPolicyRepository cacheRepository) { cacheRepository.Save(GetPolicyKey(), policy); }
/// <summary> /// Reads the policy object from store and updates the cache /// </summary> /// <param name="storeProvider"> /// The store provider. /// </param> /// <param name="cacheRepository"> /// The cache repository. /// </param> public static void UpdatePolicy(IThrottlePolicyProvider storeProvider, IPolicyRepository cacheRepository) { var policy = ThrottlePolicy.FromStore(storeProvider); cacheRepository.Save(GetPolicyKey(), policy); }
public static ThrottlePolicy FromStore(IThrottlePolicyProvider provider) { var settings = provider.ReadSettings(); var whitelists = provider.AllWhitelists(); var rules = provider.AllRules(); var policy = new ThrottlePolicy( perSecond: settings.LimitPerSecond, perMinute: settings.LimitPerMinute, perHour: settings.LimitPerHour, perDay: settings.LimitPerDay, perWeek: settings.LimitPerWeek); policy.IpThrottling = settings.IpThrottling; policy.ClientThrottling = settings.ClientThrottling; policy.EndpointThrottling = settings.EndpointThrottling; policy.StackBlockedRequests = settings.StackBlockedRequests; policy.IpRules = new Dictionary<string, RateLimits>(); policy.ClientRules = new Dictionary<string, RateLimits>(); policy.EndpointRules = new Dictionary<string, RateLimits>(); policy.EndpointWhitelist = new List<string>(); policy.IpWhitelist = new List<string>(); policy.ClientWhitelist = new List<string>(); //Set default EndpointRules httpmethod "get" var defaultEndpoint = "{0}/{1}"; foreach (var item in rules) { var rateLimit = new RateLimits { PerSecond = item.LimitPerSecond, PerMinute = item.LimitPerMinute, PerHour = item.LimitPerHour, PerDay = item.LimitPerDay, PerWeek = item.LimitPerWeek }; switch (item.PolicyType) { case ThrottlePolicyType.IpThrottling: policy.IpRules.Add(item.Entry, rateLimit); break; case ThrottlePolicyType.ClientThrottling: policy.ClientRules.Add(item.Entry, rateLimit); break; case ThrottlePolicyType.EndpointThrottling: policy.EndpointRules.Add(string.Format(defaultEndpoint, item.HttpMethod, item.Entry), rateLimit); break; } } if (whitelists != null) { policy.IpWhitelist.AddRange(whitelists.Where(x => x.PolicyType == ThrottlePolicyType.IpThrottling).Select(x => x.Entry)); policy.ClientWhitelist.AddRange(whitelists.Where(x => x.PolicyType == ThrottlePolicyType.ClientThrottling).Select(x => x.Entry)); policy.EndpointWhitelist.AddRange(whitelists.Where(x => x.PolicyType == ThrottlePolicyType.EndpointThrottling).Select(x => x.Entry)); } return policy; }
public override void OnActionExecuting(HttpActionContext actionContext) { EnableThrottlingAttribute attrPolicy = null; var applyThrottling = ApplyThrottling(actionContext, out attrPolicy); // get policy from repo if(policyRepository != null) { policy = policyRepository.FirstOrDefault(ThrottleManager.GetPolicyKey()); } if (Policy != null && applyThrottling) { core.Repository = Repository; core.Policy = Policy; var identity = SetIndentity(actionContext.Request); if (!core.IsWhitelisted(identity)) { TimeSpan timeSpan = TimeSpan.FromSeconds(1); // get default rates var defRates = core.RatesWithDefaults(Policy.Rates.ToList()); if (Policy.StackBlockedRequests) { // all requests including the rejected ones will stack in this order: week, 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 defRates.Reverse(); } // apply policy foreach (var rate in defRates) { var rateLimitPeriod = rate.Key; var rateLimit = rate.Value; timeSpan = core.GetTimeSpanFromPeriod(rateLimitPeriod); // apply EnableThrottlingAttribute policy var attrLimit = attrPolicy.GetLimit(rateLimitPeriod); if (attrLimit > 0) { rateLimit = attrLimit; } // apply global rules core.ApplyRules(identity, timeSpan, rateLimitPeriod, ref rateLimit); if (rateLimit > 0) { // increment counter string requestId; var throttleCounter = core.ProcessRequest(identity, timeSpan, rateLimitPeriod, out requestId); // check if key expired if (throttleCounter.Timestamp + timeSpan < DateTime.UtcNow) { continue; } // check if limit is reached if (throttleCounter.TotalRequests > rateLimit) { // log blocked request if (Logger != null) { Logger.Log(core.ComputeLogEntry(requestId, identity, throttleCounter, rateLimitPeriod.ToString(), rateLimit, actionContext.Request)); } var message = !string.IsNullOrEmpty(this.QuotaExceededMessage) ? this.QuotaExceededMessage : "API calls quota exceeded! maximum admitted {0} per {1}."; // add status code and retry after x seconds to response actionContext.Response = QuotaExceededResponse( actionContext.Request, string.Format(message, rateLimit, rateLimitPeriod), QuotaExceededResponseCode, core.RetryAfterFrom(throttleCounter.Timestamp, rateLimitPeriod)); } } } } } base.OnActionExecuting(actionContext); }
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // get policy from repo if (policyRepository != null) { policy = policyRepository.FirstOrDefault(ThrottleManager.GetPolicyKey()); } if (policy == null || (!policy.IpThrottling && !policy.ClientThrottling && !policy.EndpointThrottling)) { return base.SendAsync(request, cancellationToken); } core.Repository = Repository; core.Policy = policy; var identity = SetIndentity(request); if (core.IsWhitelisted(identity)) { return base.SendAsync(request, cancellationToken); } TimeSpan timeSpan = TimeSpan.FromSeconds(1); // get default rates var defRates = core.RatesWithDefaults(Policy.Rates.ToList()); if (Policy.StackBlockedRequests) { // all requests including the rejected ones will stack in this order: week, 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 defRates.Reverse(); } // apply policy foreach (var rate in defRates) { var rateLimitPeriod = rate.Key; var rateLimit = rate.Value; timeSpan = core.GetTimeSpanFromPeriod(rateLimitPeriod); // apply global rules core.ApplyRules(identity, timeSpan, rateLimitPeriod, ref rateLimit); if (rateLimit > 0) { // increment counter string requestId; var throttleCounter = core.ProcessRequest(identity, timeSpan, rateLimitPeriod, out requestId); // check if key expired if (throttleCounter.Timestamp + timeSpan < DateTime.UtcNow) { continue; } // check if limit is reached if (throttleCounter.TotalRequests > rateLimit) { // log blocked request if (Logger != null) { Logger.Log(core.ComputeLogEntry(requestId, identity, throttleCounter, rateLimitPeriod.ToString(), rateLimit, request)); } var message = !string.IsNullOrEmpty(this.QuotaExceededMessage) ? this.QuotaExceededMessage : "API calls quota exceeded! maximum admitted {0} per {1}."; // break execution return QuotaExceededResponse( request, string.Format(message, rateLimit, rateLimitPeriod), QuotaExceededResponseCode, core.RetryAfterFrom(throttleCounter.Timestamp, rateLimitPeriod)); } } } // no throttling required return base.SendAsync(request, cancellationToken); }
public CustomThrottlingMiddleware(OwinMiddleware next, ThrottlePolicy policy, IPolicyRepository policyRepository, IThrottleRepository repository, IThrottleLogger logger) : base(next, policy, policyRepository, repository, logger) { }
public void OnEvent(HttpContextBase context) { HttpRequestBase request = context.Request; HttpResponseBase response = context.Response; // get policy from repo if (policyRepository != null) { policy = policyRepository.FirstOrDefault(ThrottleManager.GetPolicyKey()); } if (policy == null || (!policy.IpThrottling && !policy.ClientThrottling && !policy.EndpointThrottling)) { response.StatusCode = (int)HttpStatusCode.OK; return; } core.Repository = Repository; core.Policy = policy; var identity = SetIdentity(request); if (core.IsWhitelisted(identity)) { response.StatusCode = (int)HttpStatusCode.OK; return; } if (policy.Endpoints != null && policy.Endpoints.Count > 0) { if (!core.IsEndpointMonitored(identity)) { response.StatusCode = (int)HttpStatusCode.OK; return; } } TimeSpan timeSpan = TimeSpan.FromSeconds(1); // get default rates var defRates = core.RatesWithDefaults(Policy.Rates.ToList()); if (Policy.StackBlockedRequests) { // all requests including the rejected ones will stack in this order: week, 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 defRates.Reverse(); } // apply policy foreach (var rate in defRates) { var rateLimitPeriod = rate.Key; var rateLimit = rate.Value; timeSpan = core.GetTimeSpanFromPeriod(rateLimitPeriod); // apply global rules core.ApplyRules(identity, timeSpan, rateLimitPeriod, ref rateLimit); if (rateLimit > 0) { // increment counter var requestId = ComputeThrottleKey(identity, rateLimitPeriod); var throttleCounter = core.ProcessRequest(timeSpan, requestId); // check if key expired if (throttleCounter.Timestamp + timeSpan < DateTime.UtcNow) { continue; } // check if limit is reached if (throttleCounter.TotalRequests > rateLimit) { // log blocked request if (Logger != null) { Logger.Log(core.ComputeLogEntry(requestId, identity, throttleCounter, rateLimitPeriod.ToString(), rateLimit, request)); } var message = !string.IsNullOrEmpty(this.QuotaExceededMessage) ? this.QuotaExceededMessage : "API calls quota exceeded! maximum admitted {0} per {1}."; var content = this.QuotaExceededContent != null ? this.QuotaExceededContent(rateLimit, rateLimitPeriod) : string.Format(message, rateLimit, rateLimitPeriod); // break execution response.StatusCode = (int)QuotaExceededResponseCode; response.AddHeader("Retry-After", core.RetryAfterFrom(throttleCounter.Timestamp, rateLimitPeriod)); response.Write(content); return; } } } // no throttling required response.StatusCode = (int)HttpStatusCode.OK; return; }
public override async Task Invoke(IOwinContext context) { var response = context.Response; var request = context.Request; // get policy from repo if (policyRepository != null) { policy = policyRepository.FirstOrDefault(ThrottleManager.GetPolicyKey()); } if (policy == null || (!policy.IpThrottling && !policy.ClientThrottling && !policy.EndpointThrottling)) { await Next.Invoke(context); return; } core.Repository = Repository; core.Policy = policy; var identity = SetIndentity(request); if (core.IsWhitelisted(identity)) { await Next.Invoke(context); return; } TimeSpan timeSpan = TimeSpan.FromSeconds(1); // get default rates var defRates = core.RatesWithDefaults(Policy.Rates.ToList()); if (Policy.StackBlockedRequests) { // all requests including the rejected ones will stack in this order: week, 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 defRates.Reverse(); } // apply policy foreach (var rate in defRates) { var rateLimitPeriod = rate.Key; var rateLimit = rate.Value; timeSpan = core.GetTimeSpanFromPeriod(rateLimitPeriod); // apply global rules core.ApplyRules(identity, timeSpan, rateLimitPeriod, ref rateLimit); if (rateLimit > 0) { // increment counter string requestId; var throttleCounter = core.ProcessRequest(identity, timeSpan, rateLimitPeriod, out requestId); // check if key expired if (throttleCounter.Timestamp + timeSpan < DateTime.UtcNow) { continue; } // check if limit is reached if (throttleCounter.TotalRequests > rateLimit) { // log blocked request if (Logger != null) { Logger.Log(core.ComputeLogEntry(requestId, identity, throttleCounter, rateLimitPeriod.ToString(), rateLimit, null)); } var message = !string.IsNullOrEmpty(this.QuotaExceededMessage) ? this.QuotaExceededMessage : "API calls quota exceeded! maximum admitted {0} per {1}."; // break execution response.OnSendingHeaders(state => { var resp = (OwinResponse)state; resp.Headers.Add("Retry-After", new string[] { core.RetryAfterFrom(throttleCounter.Timestamp, rateLimitPeriod) }); resp.StatusCode = (int)QuotaExceededResponseCode; resp.ReasonPhrase = string.Format(message, rateLimit, rateLimitPeriod); }, response); return; } } } // no throttling required await Next.Invoke(context); }