private async Task <bool> HandleCaseWhenPrevAndCurrMinHasValue(ThrottleRequest throttleRequest, string userRateLimitConfigCacheKey, DateTime now, DateTime currMin,
                                                                       int currMinIntegerValue, int prevMinIntergerValue)
        {
            if (currMinIntegerValue >= throttleRequest.AppliedConfig.PerMinLimit)
            {
                return(true);
            }
            var currWindow = now - now.AddMinutes(-1);
            var prevMinPartInCurrWindow           = currMin - (now.AddMinutes(-1));
            var prevMinFractionalPartInCurrWindow = prevMinPartInCurrWindow / currWindow;

            var calculatedvalue = (int)(currMinIntegerValue + prevMinIntergerValue * prevMinFractionalPartInCurrWindow);

            if (calculatedvalue >= throttleRequest.AppliedConfig.PerMinLimit)
            {
                return(true);
            }

            if (_nextThrottler == null)
            {
                await _cacheManager.HashIncrementAsync(userRateLimitConfigCacheKey, currMin.ToString());

                return(false);
            }
            var shouldThrottle = await _nextThrottler.ShouldThrottle(throttleRequest);

            if (!shouldThrottle)
            {
                await _cacheManager.HashIncrementAsync(userRateLimitConfigCacheKey, currMin.ToString());
            }
            return(shouldThrottle);
        }
        private async Task <bool> HandleCaseWhenPrevAndCurrSecHasValue(ThrottleRequest throttleRequest, string userRateLimitConfigCacheKey,
                                                                       DateTime now, DateTime currSec, int currSecValue, int prevSecValue)
        {
            if (currSecValue > throttleRequest.AppliedConfig.PerSecLimit)
            {
                return(true);
            }
            var currWindow = now - (now.AddSeconds(-1));
            var prevSecPartInCurrWindow     = currSec - (now.AddSeconds(-1));
            var prevSecFractionInCurrWindow = prevSecPartInCurrWindow / currWindow;
            var calculatedThrottleLimit     = (int)(currSecValue + (prevSecValue * prevSecFractionInCurrWindow));

            if (calculatedThrottleLimit >= throttleRequest.AppliedConfig.PerSecLimit)
            {
                return(true);
            }

            if (_nextThrottler == null)
            {
                await _redisCacheManager.HashIncrementAsync(userRateLimitConfigCacheKey, currSec.ToString());

                return(false);
            }
            var shouldThrottle = await _nextThrottler.ShouldThrottle(throttleRequest);

            if (!shouldThrottle)
            {
                await _redisCacheManager.HashIncrementAsync(userRateLimitConfigCacheKey, currSec.ToString());
            }
            return(shouldThrottle);
        }
示例#3
0
        public void POST_Register_ThrottleRequest()
        {
            //Arrange
            var viewModel = new UserAccountViewModel
            {
                UserName = "",
                Password = ""
            };

            _controller.ModelState.AddModelError("UserName", @"Username required");

            var request = new Mock <HttpRequestBase>();

            request.SetupGet(x => x.ServerVariables).Returns(new NameValueCollection());
            request.SetupGet(x => x.UserHostAddress).Returns("localhost");
            request.SetupGet(x => x.UserAgent).Returns("test user agent");
            request.SetupGet(x => x.QueryString).Returns(new NameValueCollection());

            var context = new Mock <HttpContextBase>();

            context.SetupGet(x => x.Request).Returns(request.Object);
            context.SetupGet(x => x.Cache).Returns(HttpRuntime.Cache);

            var routeData = new RouteData();

            routeData.Values.Add("culture", "en");
            routeData.Values.Add("controller", "UserAccount");
            routeData.Values.Add("action", "Register");

            var filterContext = new Mock <ActionExecutingContext>();

            filterContext.SetupGet(x => x.HttpContext).Returns(context.Object);
            filterContext.SetupGet(x => x.RouteData).Returns(routeData);
            filterContext.SetupGet(x => x.Controller).Returns(_controller);

            var filter = new ThrottleRequest {
                ErrorResourceKey = "RegistrationThrottleError"
            };

            //Act
            filter.OnActionExecuting(filterContext.Object);
            _controller.Register(viewModel);
            filter.OnActionExecuting(filterContext.Object);
            var result = (ViewResult)_controller.Register(viewModel);

            //Assert
            Assert.That(result.ViewName, Is.Empty);
            Assert.That(_controller.ModelState[string.Empty].Errors[0].ErrorMessage, Is.EqualTo(SharedStrings.RegistrationThrottleError));
        }
        private async Task <bool> HandleCaseWhenCurrMinValueIsNull(ThrottleRequest throttleRequest, string userRateLimitConfigCacheKey, DateTime currMin)
        {
            if (_nextThrottler == null)
            {
                await _cacheManager.HashIncrementAsync(userRateLimitConfigCacheKey, currMin.ToString());

                return(false);
            }
            var shouldThrottle = await _nextThrottler.ShouldThrottle(throttleRequest);

            if (!shouldThrottle)
            {
                await _cacheManager.HashIncrementAsync(userRateLimitConfigCacheKey, currMin.ToString());
            }
            return(shouldThrottle);
        }
        private async Task <bool> HandleCaseWhenCurrSecValueIsNull(ThrottleRequest throttleRequest, string userRateLimitConfigCacheKey, DateTime currSec)
        {
            if (_nextThrottler == null)
            {
                await _redisCacheManager.HashIncrementAsync(userRateLimitConfigCacheKey, currSec.ToString());

                return(false);
            }
            var shouldThrottle = await _nextThrottler.ShouldThrottle(throttleRequest);

            // If the request is not throttled, increase counter for curr sec.
            if (!shouldThrottle)
            {
                await _redisCacheManager.HashIncrementAsync(userRateLimitConfigCacheKey, currSec.ToString());
            }
            return(shouldThrottle);
        }
        public static async Task InvokeThrottlerSentinelAsync(HttpContext context, IThrottlingCache tc, ThrottlingSentinelOptions options, Action <HttpResponseMessage, HttpResponse> transformer)
        {
            var utcNow            = DateTime.UtcNow;
            var throttlingContext = options.ContextResolver?.Invoke(context);

            if (!throttlingContext.IsNullOrWhiteSpace())
            {
                try
                {
                    await ThrottleLocker.WaitAsync();

                    if (!tc.TryGetValue(throttlingContext, out var tr))
                    {
                        tr = new ThrottleRequest(options.Quota);
                        tc.AddIfNotContainsKey(throttlingContext, tr);
                    }
                    else
                    {
                        tr.Refresh();
                        tr.Total++;
                    }

                    var window = new TimeRange(utcNow, tr.Expires);
                    var delta  = window.Duration;
                    var reset  = utcNow.Add(delta);
                    context.Response.Headers.AddOrUpdate(options.RateLimitHeaderName, tr.Quota.RateLimit.ToString(CultureInfo.InvariantCulture));
                    context.Response.Headers.AddOrUpdate(options.RateLimitRemainingHeaderName, Math.Max(tr.Quota.RateLimit - tr.Total, 0).ToString(CultureInfo.InvariantCulture));
                    context.Response.Headers.AddOrUpdate(options.RateLimitResetHeaderName, reset.ToEpochTime().ToString(CultureInfo.InvariantCulture));
                    if (tr.Total > tr.Quota.RateLimit && tr.Expires > utcNow)
                    {
                        var message = options.ResponseBroker?.Invoke(delta, reset);
                        if (message != null)
                        {
                            message.ToHttpResponse(context.Response, transformer);
                            throw new ThrottlingException((int)message.StatusCode, await message.Content.ReadAsStringAsync(), tr.Quota.RateLimit, delta, reset);
                        }
                    }

                    tc[throttlingContext] = tr;
                }
                finally
                {
                    ThrottleLocker.Release();
                }
            }
        }
        public override async Task <bool> ShouldThrottle(ThrottleRequest throttleRequest)
        {
            if (throttleRequest is null)
            {
                throw new System.ArgumentNullException(nameof(throttleRequest));
            }
            var configOption = throttleRequest.AppliedConfig;
            var userId       = throttleRequest.UserId;

            var perMinLimit = configOption.PerMinLimit;
            var perSecLimit = configOption.PerSecLimit;

            Console.WriteLine($"perMinLimit: {perMinLimit}");

            var userRateLimitConfigCacheKey = Utils.GetPerUserPerRateLimitConfigCacheKey(userId, configOption);

            var now        = DateTime.Now;
            var currMin    = now.AddSeconds(-1 * now.Second);
            var prevMin    = currMin.AddMinutes(-1);
            var hashFields = new string[] { prevMin.ToString(), currMin.ToString() };
            var values     = await _cacheManager.HashGet(userRateLimitConfigCacheKey, hashFields);

            if (values == null || values.Length < 2)
            {
                Console.WriteLine("some error while retriving date from redis. Allowing the request to not throttle. But look into this");
                return(false);
            }
            var currMinValue = values[1];

            if (currMinValue.IsNull)
            {
                return(await HandleCaseWhenCurrMinValueIsNull(throttleRequest, userRateLimitConfigCacheKey, currMin));
            }
            currMinValue.TryParse(out int currMinIntegerValue);
            var prevMinValue = values[0];

            if (prevMinValue.IsNull)
            {
                return(await HandleCaseWhenPrevMinValueIsNull(throttleRequest, userRateLimitConfigCacheKey, currMin, currMinIntegerValue));
            }
            prevMinValue.TryParse(out int prevMinIntergerValue);
            return(await HandleCaseWhenPrevAndCurrMinHasValue(throttleRequest, userRateLimitConfigCacheKey, now, currMin,
                                                              currMinIntegerValue, prevMinIntergerValue));
        }
        private async Task <bool> HandleCaseWhenPrevSecValueIsNull(ThrottleRequest throttleRequest, string userRateLimitConfigCacheKey, DateTime currSec, int currSecValue)
        {
            if (currSecValue < throttleRequest.AppliedConfig.PerSecLimit)
            {
                if (_nextThrottler == null)
                {
                    await _redisCacheManager.HashIncrementAsync(userRateLimitConfigCacheKey, currSec.ToString());

                    return(false);
                }
                var shouldThrottle = await _nextThrottler.ShouldThrottle(throttleRequest);

                if (!shouldThrottle)
                {
                    await _redisCacheManager.HashIncrementAsync(userRateLimitConfigCacheKey, currSec.ToString());
                }
                return(shouldThrottle);
            }
            return(true);
        }
        public override async Task <bool> ShouldThrottle(ThrottleRequest throttleRequest)
        {
            if (throttleRequest is null)
            {
                throw new System.ArgumentNullException(nameof(throttleRequest));
            }
            var config  = throttleRequest.AppliedConfig;
            var userId  = throttleRequest.UserId;
            var now     = DateTime.Now;
            var currSec = now.AddMilliseconds(-1 * now.Millisecond);
            var prevSec = currSec.AddSeconds(-1);


            var userRateLimitConfigCacheKey = Utils.GetPerUserPerRateLimitConfigCacheKey(userId, config);
            var hashFields = new string[] { prevSec.ToString(), currSec.ToString() };

            var hashFieldValues = await _redisCacheManager.HashGet(userRateLimitConfigCacheKey, hashFields);

            if (hashFieldValues == null || hashFieldValues.Length < 2)
            {
                Console.WriteLine("some error while fetching values from redis");
                return(false);
            }
            var currSecValue = hashFieldValues[1];

            if (currSecValue.IsNull)
            {
                return(await HandleCaseWhenCurrSecValueIsNull(throttleRequest, userRateLimitConfigCacheKey, currSec));
            }
            currSecValue.TryParse(out int currSecIntVal);

            var prevSecValue = hashFieldValues[0];

            if (prevSecValue.IsNull)
            {
                return(await HandleCaseWhenPrevSecValueIsNull(throttleRequest, userRateLimitConfigCacheKey, currSec, currSecIntVal));
            }
            prevSecValue.TryParse(out int prevSecIntVal);

            return(await HandleCaseWhenPrevAndCurrSecHasValue(throttleRequest, userRateLimitConfigCacheKey, now, currSec, currSecIntVal, prevSecIntVal));
        }
 public abstract Task <bool> ShouldThrottle(ThrottleRequest throttleRequest);