예제 #1
0
        public ThrottlingStateProvider(ThrottlingConfiguration configuration, IThrottlingStateActualizer actualizer)
        {
            this.configuration = configuration;
            this.actualizer    = actualizer;

            state = new ThrottlingState();
        }
        public void Validate(ThrottlingConfiguration configuration)
        {
            _log.Debug($"Validating {nameof(ThrottlingConfiguration)}");

            if (configuration.ConcurrentRequestsLimit < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(configuration.ConcurrentRequestsLimit), $"{nameof(configuration.ConcurrentRequestsLimit)} has to be greater than 0");
            }

            if (configuration.QueueLimit < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(configuration.QueueLimit), $"{nameof(configuration.QueueLimit)} has to be greater or equal to 0");
            }

            if (configuration.QueueTimeout.TotalSeconds < 5)
            {
                throw new ArgumentOutOfRangeException(nameof(configuration.QueueTimeout), $"{nameof(configuration.QueueTimeout)} has to be greater or equal to 5");
            }

            if (configuration.MaximumServerConnections < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(configuration.MaximumServerConnections), $"{nameof(configuration.MaximumServerConnections)} has to be greater than 0");
            }

            if (configuration.MaximumServerConnections < configuration.ConcurrentRequestsLimit + configuration.QueueLimit)
            {
                throw new ArgumentOutOfRangeException(nameof(configuration.MaximumServerConnections), $"{nameof(configuration.MaximumServerConnections)} has to be greater or equal to the sum of {nameof(configuration.ConcurrentRequestsLimit)} and {nameof(configuration.QueueLimit)}");
            }

            _log.Info($"{nameof(ThrottlingConfiguration)} valid");
        }
예제 #3
0
        private static void addResetHeader(IHttpResponse response, ThrottlingConfiguration configuration, RequestCount count)
        {
            string numberOfSecondsLeftInPeriod = count != null?
                                                 count.Remaining(DateTimeOffset.UtcNow).ToString(CultureInfo.InvariantCulture) :
                                                     configuration.FormattedSeconds;

            response.AddHeader("X-Rate-Limit-Reset", numberOfSecondsLeftInPeriod);
        }
예제 #4
0
        private static void addRemainingHeader(IHttpResponse response, ThrottlingConfiguration configuration, RequestCount count)
        {
            string numberOfRequestLeftInPeriod = count != null?
                                                 count.Remaining(configuration.NumberOfRequests).ToString(CultureInfo.InvariantCulture) :
                                                     configuration.FormattedRequests;

            response.AddHeader("X-Rate-Limit-Remaining", numberOfRequestLeftInPeriod);
        }
예제 #5
0
        public ThrottlingMiddlewareTests()
        {
            _throttlingConfiguration = new ThrottlingConfiguration
            {
                ConcurrentRequestLimit = 5,
                Enabled = true,
            };

            _cts = new CancellationTokenSource();
            _httpContext.RequestAborted = _cts.Token;
            _httpContext.User           = new ClaimsPrincipal(new ClaimsIdentity("authenticationType", "nametype", "roletype"));

            _throttlingConfiguration.ExcludedEndpoints.Add(new ExcludedEndpoint {
                Method = "get", Path = "/health/check"
            });

            _middleware = new Lazy <ThrottlingMiddleware>(
                () => new ThrottlingMiddleware(
                    async x =>
            {
                x.Response.StatusCode = 200;
                try
                {
                    if (!int.TryParse(Regex.Match(x.Request.Path, "/duration/(\\d+)").Groups[1].Value, out var duration))
                    {
                        duration = 5000;
                    }

                    var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token, x.RequestAborted);
                    await Task.Delay(duration, linkedTokenSource.Token);
                }
                catch (TaskCanceledException) when(_cts.Token.IsCancellationRequested)
                {
                }
                catch (TaskCanceledException) when(x.RequestAborted.IsCancellationRequested)
                {
                    x.Response.StatusCode = StatusCodes.Status408RequestTimeout;
                }
            },
                    Options.Create(_throttlingConfiguration),
                    Options.Create(new SecurityConfiguration {
                Enabled = _securityEnabled
            }),
                    NullLogger <ThrottlingMiddleware> .Instance));

            IActionResultExecutor <ObjectResult> executor = Substitute.For <IActionResultExecutor <ObjectResult> >();

            executor.ExecuteAsync(Arg.Any <ActionContext>(), Arg.Any <ObjectResult>()).ReturnsForAnyArgs(Task.CompletedTask);
            _collection.AddSingleton <IActionResultExecutor <ObjectResult> >(executor);
            _provider = _collection.BuildServiceProvider();
            _httpContext.RequestServices = _provider;
        }
예제 #6
0
        public static void Throttle(this Testing.Commons.ServiceStack.v3.HostTesterBase tester, ushort numberOfRequests, TimeSpan period)
        {
            var configuration = new ThrottlingConfiguration
            {
                NumberOfRequests = numberOfRequests,
                Period           = period
            };

            var manager = Substitute.For <IResourceManager>();

            manager.Get(ThrottlingConfiguration.Key, Arg.Any <ThrottlingConfiguration>())
            .Returns(configuration);
            tester.Replacing(manager);
        }
예제 #7
0
        public async Task setup_scenario()
        {
            var throttleManager = new StubThrottleManager {
                Sync = new StubThrottleSync(ConcurrentRequests)
            };

            _expectedResponse = new HttpResponseMessage(HttpStatusCode.Accepted);
            var baseHttpClient = new StubHttpClient(_expectedResponse);
            var configuration  = new ThrottlingConfiguration {
                ThrottlePolicy = "default"
            };
            var httpClient = baseHttpClient.AddThrottling(configuration, throttleManager);

            _actualResponse = await httpClient.GetAsync("/ping");
        }
예제 #8
0
        public void setup_scenario()
        {
            var throttleManager = new StubThrottleManager {
                Sync = new StubThrottleSync(ExpectedMaxConcurrentRequests)
            };

            _baseHttpClient = new CountingHttpClient();
            var configuration = new ThrottlingConfiguration {
                ThrottlePolicy = "default"
            };
            var httpClient = _baseHttpClient.AddThrottling(configuration, throttleManager);

            var tasks = Enumerable.Range(1, ExpectedTotalRequests).Select(c => httpClient.GetAsync("/ping")).ToArray();

            Task.WhenAll(tasks).Wait(5000);
        }
예제 #9
0
        public void TestSetup()
        {
            essentials = new ThrottlingEssentials {
                RefreshPeriod = TimeSpan.Zero
            };

            configuration = new ThrottlingConfigurationBuilder()
                            .SetEssentials(() => essentials)
                            .Build();

            actualizer = Substitute.For <IThrottlingStateActualizer>();
            provider   = new ThrottlingStateProvider(configuration, actualizer);

            state = provider.ObtainState();

            actualizer.ClearReceivedCalls();
        }
예제 #10
0
        public void Rate(IHttpRequest request, IHttpResponse response)
        {
            ApiKey apiKey = ApiKey.ExtractFrom(request);

            if (!apiKey.IsMissing)
            {
                var resources = request.TryResolve <IResourceManager>();
                ThrottlingConfiguration configuration = resources.Get(ThrottlingConfiguration.Key, ThrottlingConfiguration.Empty());

                var          repository = request.TryResolve <IRequestCountRepository>();
                RequestCount count      = repository.Get(apiKey);

                addLimitHeader(response, configuration);
                addRemainingHeader(response, configuration, count);
                addResetHeader(response, configuration, count);
            }
        }
        public ThrottlingMiddlewareTests()
        {
            _cts = new CancellationTokenSource();
            _httpContext.RequestAborted = _cts.Token;
            var throttlingConfiguration = new ThrottlingConfiguration
            {
                ConcurrentRequestLimit = 5,
            };
            throttlingConfiguration.ExcludedEndpoints.Add("get:/health/check");

            _middleware = new ThrottlingMiddleware(
                async x =>
                {
                    x.Response.StatusCode = 200;
                    await Task.Delay(5000, _cts.Token);
                },
                Options.Create(throttlingConfiguration),
                NullLogger<ThrottlingMiddleware>.Instance);
        }
예제 #12
0
        private void Init(bool securityEnabled)
        {
            _cts = new CancellationTokenSource();
            _httpContext.RequestAborted = _cts.Token;
            _httpContext.User           = new ClaimsPrincipal(new ClaimsIdentity("authenticationType", "nametype", "roletype"));

            var throttlingConfiguration = new ThrottlingConfiguration
            {
                ConcurrentRequestLimit = 5,
            };

            throttlingConfiguration.ExcludedEndpoints.Add(new ExcludedEndpoint {
                Method = "get", Path = "/health/check"
            });

            _middleware = new ThrottlingMiddleware(
                async x =>
            {
                x.Response.StatusCode = 200;
                try
                {
                    await Task.Delay(5000, _cts.Token);
                }
                catch (TaskCanceledException) when(_cts.Token.IsCancellationRequested)
                {
                }
            },
                Options.Create(throttlingConfiguration),
                Options.Create(new Microsoft.Health.Fhir.Core.Configs.SecurityConfiguration {
                Enabled = securityEnabled
            }),
                NullLogger <ThrottlingMiddleware> .Instance);

            _executor = Substitute.For <IActionResultExecutor <ObjectResult> >();
            _executor.ExecuteAsync(Arg.Any <ActionContext>(), Arg.Any <ObjectResult>()).ReturnsForAnyArgs(Task.CompletedTask);
            _collection.AddSingleton <IActionResultExecutor <ObjectResult> >(_executor);
            _provider = _collection.BuildServiceProvider();
            _httpContext.RequestServices = _provider;
        }
예제 #13
0
        public static IRequestCountRepository SetupThrottling(this Testing.Commons.ServiceStack.v3.HostTesterBase tester, ushort numberOfRequests, TimeSpan period, RequestCount count = null)
        {
            var configuration = new ThrottlingConfiguration
            {
                NumberOfRequests = numberOfRequests,
                Period           = period
            };

            var manager = Substitute.For <IResourceManager>();

            manager.Get(ThrottlingConfiguration.Key, Arg.Any <ThrottlingConfiguration>())
            .Returns(configuration);
            tester.Replacing(manager);

            var repository = Substitute.For <IRequestCountRepository>();

            if (count != null)
            {
                repository.Get(Arg.Any <ApiKey>()).Returns(count);
            }
            tester.Replacing(repository);
            return(repository);
        }
예제 #14
0
        public void Throttle(IHttpRequest request, IHttpResponse response)
        {
            var resources = request.TryResolve <IResourceManager>();
            ThrottlingConfiguration configuration = resources.Get(ThrottlingConfiguration.Key, ThrottlingConfiguration.Empty());

            var key = ApiKey.ExtractFrom(request);

            if (!key.IsMissing && configuration.ThrottlingEnabled)
            {
                var          repository = request.TryResolve <IRequestCountRepository>();
                RequestCount count      = repository.Ensure(key, () => new RequestCount(configuration.Period));

                if (count.IsLessThan(configuration.NumberOfRequests))
                {
                    repository.Update(key, count.Increase());
                }
                else
                {
                    response.AddHeader("Retry-After", configuration.FormattedSeconds);
                    throw new HttpError(429, configuration.ErrorMessage());
                }
            }
        }
예제 #15
0
 public ThrottlingMiddleware(RequestDelegate next, ThrottlingConfiguration throttlingConfiguration)
 {
     _next          = next ?? throw new ArgumentNullException(nameof(next));
     _capacityGuard = new ThrottlingCapacityGuard(throttlingConfiguration);
 }
예제 #16
0
        private static void addLimitHeader(IHttpResponse response, ThrottlingConfiguration configuration)
        {
            string numberOfRequestsAllowedInPeriod = configuration.FormattedRequests;

            response.AddHeader("X-Rate-Limit-Limit", numberOfRequestsAllowedInPeriod);
        }
예제 #17
0
 public ThrottlingStateProvider(ThrottlingConfiguration configuration)
     : this(configuration, new ThrottlingStateActualizer(configuration))
 {
 }
 public ThrottlingCapacityGuard(ThrottlingConfiguration throttlingConfiguration)
 {
     this._throttlingConfiguration = throttlingConfiguration;
 }
예제 #19
0
 public ThrottlingProvider([NotNull] ThrottlingConfiguration configuration)
     : this(new ThrottlingStateProvider(configuration ?? throw new ArgumentNullException(nameof(configuration))), configuration.ErrorCallback)
예제 #20
0
 public ThrottlingMiddleware(RequestDelegate next, IOptions <ThrottlingConfiguration> configuration, ILogger <ThrottlingMiddleware> logger)
 {
     _next          = EnsureArg.IsNotNull(next, nameof(next));
     _logger        = EnsureArg.IsNotNull(logger, nameof(logger));
     _configuration = EnsureArg.IsNotNull(configuration?.Value, nameof(configuration));
 }
예제 #21
0
 public ThrottlingStateActualizer(ThrottlingConfiguration configuration)
 => this.configuration = configuration;