Пример #1
0
        private async Task <bool> RevertIfRequired(RateLimitingResult rateLimitingResult, HttpActionContext context,
                                                   RateLimitingRequest request, RateLimitPolicy policy, Decision decision)
        {
            if (decision == Decision.REVERTSUCCESSCOST && rateLimitingResult.State == ResultState.Success)
            {
                await _rateLimiter.LimitRequestAsync(request,
                                                     () => RateLimitingFilter.GetCustomAttributes(context),
                                                     context.Request.Headers.Host,
                                                     getPolicyFuncAsync : _ => Task.FromResult(policy),
                                                     onPostLimitFuncAsync : async(rateLimitingRequest, postPolicy, rateLimitingRevertResult) =>
                {
                    if (rateLimitingRevertResult.State == ResultState.Success)
                    {
                        context.Request.Properties[$"RateLimitingResult_{_filterId}"] = rateLimitingRevertResult;
                    }

                    if (OnPostLimitRevert != null)
                    {
                        await OnPostLimitRevert.Invoke(request, postPolicy, rateLimitingRevertResult, context).ConfigureAwait(false);
                    }
                },
                                                     revert : true).ConfigureAwait(false);

                return(await Task.FromResult(true));
            }

            return(await Task.FromResult(false));
        }
Пример #2
0
        private static void TooManyRequests(AuthorizationFilterContext context,
                                            RateLimitingResult result, string violatedPolicyName = "")
        {
            var throttledResponseParameters =
                RateLimiter.GetThrottledResponseParameters(result, violatedPolicyName);

            context.HttpContext.Response.StatusCode = ThrottledResponseParameters.StatusCode;

            foreach (var header in throttledResponseParameters.RateLimitHeaders.Keys)
            {
                context.HttpContext.Response.Headers.Add(header,
                                                         throttledResponseParameters.RateLimitHeaders[header]);
            }

            context.Result = new ContentResult()
            {
                Content = throttledResponseParameters.Message
            };
        }
Пример #3
0
        private static async Task TooManyRequests(HttpActionContext context,
                                                  RateLimitingResult result, string violatedPolicyName = "")
        {
            var response = context.Response ?? context.Request.CreateResponse();

            var throttledResponseParameters =
                RateLimiter.GetThrottledResponseParameters(result, violatedPolicyName);

            response.StatusCode = (HttpStatusCode)ThrottledResponseParameters.StatusCode;

            foreach (var header in throttledResponseParameters.RateLimitHeaders.Keys)
            {
                response.Headers.TryAddWithoutValidation(header,
                                                         throttledResponseParameters.RateLimitHeaders[header]);
            }

            response.ReasonPhrase = throttledResponseParameters.Message;
            context.Response      = response;

            await Task.FromResult <object>(null);
        }
Пример #4
0
        private static void AddUpdateRateLimitingSuccessHeaders(HttpContext context, RateLimitingResult result)
        {
            var successheaders = new Dictionary <string, string>()
            {
                { RateLimitHeaders.TokensRemaining, result.TokensRemaining.ToString() },
                { RateLimitHeaders.Limit, result.CacheKey.AllowedConsumptionRate.ToString() }
            };

            foreach (var successheader in successheaders.Keys)
            {
                if (context.Response.Headers.ContainsKey(successheader))
                {
                    context.Response.Headers[successheader] = new StringValues(
                        context.Response.Headers[successheader].ToArray()
                        .Append(successheaders[successheader]).ToArray());
                }
                else
                {
                    context.Response.Headers.Add(successheader, new StringValues(
                                                     new string[] { successheaders[successheader] }));
                }
            }
        }
Пример #5
0
        public async Task <RateLimitingResult> LimitRequestAsync(string requestId, string method, string host,
                                                                 string routeTemplate,
                                                                 IList <AllowedConsumptionRate> allowedCallRates,
                                                                 int costPerCall = 1)
        {
            return(await _circuitBreakerPolicy.ExecuteAsync(async() =>
            {
                if (!_redisConnection.IsConnected)
                {
                    throw new Exception("Redis is not connected at the moment");
                }

                var redisDb = _redisConnection.GetDatabase();
                var redisTransaction = redisDb.CreateTransaction();
                var utcNowTicks = _clock?.GetCurrentUtcTimeInTicks() ?? DateTime.UtcNow.Ticks;
                IList <Func <long> > numberOfRequestsMadePerAllowedCallRateAsync = new List <Func <long> >();

                IList <RateLimitCacheKey> cacheKeys = new List <RateLimitCacheKey>();

                foreach (var allowedCallRate in allowedCallRates)
                {
                    if (allowedCallRate.Unit == RateLimitUnit.PerCustomPeriod)
                    {
                        var dateTimeNowUtc = new DateTime(utcNowTicks, DateTimeKind.Utc);
                        GetDateRange(allowedCallRate, dateTimeNowUtc, out DateTime fromUtc, out DateTime toUtc);
                        if (!(dateTimeNowUtc >= fromUtc && dateTimeNowUtc <= toUtc))
                        {
                            return new RateLimitingResult(ResultState.NotApplicable);
                        }
                    }

                    numberOfRequestsMadePerAllowedCallRateAsync.Add(
                        GetNumberOfRequestsAsync(requestId, method, host, routeTemplate, allowedCallRate, cacheKeys,
                                                 redisTransaction, utcNowTicks, costPerCall));
                }

                await ExecuteTransactionAsync(redisTransaction).ConfigureAwait(false);

                var violatedCacheKeys = new SortedList <long, RateLimitCacheKey>();

                var minCallsRemaining = int.MaxValue;
                var minCallsCacheKey = default(RateLimitCacheKey);
                for (var i = 0; i < allowedCallRates.Count; i++)
                {
                    var cacheKey = cacheKeys[i];

                    var callsRemaining = (cacheKey.AllowedConsumptionRate.MaxBurst != 0 ? cacheKey.AllowedConsumptionRate.MaxBurst : cacheKey.Limit) -
                                         numberOfRequestsMadePerAllowedCallRateAsync[i]();

                    if (minCallsRemaining > callsRemaining)
                    {
                        minCallsRemaining = callsRemaining > 0 ? (int)callsRemaining : 0;
                        minCallsCacheKey = cacheKey;
                    }

                    if (callsRemaining < 0)
                    {
                        violatedCacheKeys.Add((long)allowedCallRates[i].Unit, cacheKey);
                    }
                }

                if (!violatedCacheKeys.Any() || costPerCall <= 0)
                {
                    return new RateLimitingResult(ResultState.Success, 0, minCallsCacheKey, minCallsRemaining);
                }

                var postViolationTransaction = redisDb.CreateTransaction();

                if (!_countThrottledRequests)
                {
                    UndoUnsuccessfulRequestCount(postViolationTransaction, cacheKeys, utcNowTicks, costPerCall);
                }

                var violatedCacheKey = violatedCacheKeys.Last().Value;

                var getOldestRequestTimestampInTicksFunc =
                    GetOldestRequestTimestampInTicksFunc(postViolationTransaction,
                                                         violatedCacheKey, utcNowTicks);

                var throttleState = ResultState.Throttled;

                try
                {
                    await ExecuteTransactionAsync(postViolationTransaction).ConfigureAwait(false);
                }
                catch
                {
                    if (!_countThrottledRequests)
                    {
                        throttleState = ResultState.ThrottledButCompensationFailed;
                    }
                }

                var rateLimitingResult = new RateLimitingResult(throttleState,
                                                                GetWaitingIntervalInTicks(getOldestRequestTimestampInTicksFunc,
                                                                                          violatedCacheKey, utcNowTicks), violatedCacheKey, 0);

                _onThrottled?.Invoke(rateLimitingResult);

                return rateLimitingResult;
            }, new RateLimitingResult(ResultState.LimitApplicationFailed)).ConfigureAwait(false));
        }
Пример #6
0
        private static void AddUpdateRateLimitingSuccessHeaders(HttpActionExecutedContext context, RateLimitingResult result)
        {
            if (result.State == ResultState.LimitApplicationFailed || result.State == ResultState.NotApplicable)
            {
                return;
            }

            var successheaders = new Dictionary <string, string>()
            {
                { RateLimitHeaders.TokensRemaining, result.TokensRemaining.ToString() },
                { RateLimitHeaders.Limit, result.CacheKey.AllowedConsumptionRate?.ToString() }
            };

            if (context.Response == null)
            {
                return;
            }

            foreach (var successheader in successheaders.Keys)
            {
                if (context.Response.Headers.Contains(successheader))
                {
                    var successHeaderValues = context.Response.Headers.GetValues(successheader)?.ToList() ?? new List <string>();
                    successHeaderValues.Add(successheaders[successheader]);
                    context.Response.Headers.Remove(successheader);
                    context.Response.Headers.Add(successheader, successHeaderValues);
                }
                else
                {
                    context.Response.Headers.Add(successheader, new string[] { successheaders[successheader] });
                }
            }
        }