/// <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);
        }
Example #4
0
        /// <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);
            }
        }
Example #5
0
 public void Save(string id, ThrottlePolicy policy)
 {
     if (memCache[id] != null)
     {
         memCache[id] = policy;
     }
     else
     {
         memCache.Add(
             id,
             policy,
             new CacheItemPolicy());
     }
 }
 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);
            }
        }
Example #8
0
        /// <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 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);
            }
        }
Example #12
0
        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;
        }
Example #15
0
        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);
        }
Example #16
0
        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));
        }
Example #17
0
 /// <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);
 }
Example #18
0
        /// <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);
        }
Example #19
0
        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);
        }
Example #25
-3
 /// <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);
 }