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); }
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);