Пример #1
0
        public async Task Should_return_too_many_requests_when_request_limit_reached()
        {
            var configuration = new LeakyBucketConfiguration
            {
                RefreshRate         = TimeSpan.FromSeconds(30),
                MaxNumberOfRequests = 4
            };
            var failedRequests = 0;

            using (var server = CreateTestServer(configuration))
            {
                for (var i = 0; i < 10; i++)
                {
                    var response = await server.CreateRequest("/")
                                   .AddHeader("User-Agent", "test")
                                   .GetAsync();

                    if ((int)response.StatusCode == ExtendedHttpStatusCodes.TooManyRequests)
                    {
                        failedRequests++;
                    }
                }
            }

            Assert.AreEqual(7, failedRequests);
        }
Пример #2
0
        public void GivenAConfiguration_WhenHeadersAreEnabled_ShouldReturnTheCorrectHeaders(long maxReq, long leakRateSec, long leakAmount, long reqCost)
        {
            var identity = new LeakyBucketClientIdentity
            {
                UniqueIdentifier = "test"
            };

            var config = new LeakyBucketConfiguration
            {
                MaxRequests   = maxReq,
                LeakRate      = TimeSpan.FromSeconds(leakRateSec),
                LeakAmount    = leakAmount,
                RequestCost   = reqCost,
                EnableHeaders = true
            };

            var rateLimiter = new LeakyBucketRateLimitStrategy();
            var headers     = rateLimiter.GetResponseHeaders(identity, config);

            Assert.Equal(headers["X-RateLimit-Remaining"], maxReq.ToString());
            Assert.Equal(headers["X-RateLimit-LeakRate"], TimeSpan.FromSeconds(leakRateSec).ToString());
            Assert.Equal(headers["X-RateLimit-LeakAmount"], leakAmount.ToString());
            Assert.Equal(headers["X-RateLimit-BucketSize"], maxReq.ToString());
            Assert.Equal(headers["X-RateLimit-Cost"], reqCost.ToString());

            rateLimiter.ReduceAllowanceBy(identity, 1);

            var headersAfterReduction = rateLimiter.GetResponseHeaders(identity, config);

            Assert.Equal(headersAfterReduction["X-RateLimit-Remaining"], (maxReq - 1).ToString());
            Assert.Equal(headersAfterReduction["X-RateLimit-LeakRate"], TimeSpan.FromSeconds(leakRateSec).ToString());
            Assert.Equal(headersAfterReduction["X-RateLimit-LeakAmount"], leakAmount.ToString());
            Assert.Equal(headersAfterReduction["X-RateLimit-BucketSize"], maxReq.ToString());
            Assert.Equal(headersAfterReduction["X-RateLimit-Cost"], reqCost.ToString());
        }
Пример #3
0
 private static TestServer CreateTestServer(LeakyBucketConfiguration configuration)
 {
     return(TestServer.Create(app =>
     {
         app.Use(typeof(LeakyBucketMiddleware), configuration);
         app.Run(async context => await context.Response.WriteAsync("Test"));
     }));
 }
Пример #4
0
        public void GivenAConfiguration_WhenHeadersAreEnabled_ShouldReturnTheCorrectResponse(bool enableHeaders)
        {
            var config = new LeakyBucketConfiguration
            {
                MaxRequests   = 2,
                LeakRate      = TimeSpan.FromSeconds(1),
                LeakAmount    = 1,
                EnableHeaders = enableHeaders
            };

            var rateLimiter = new LeakyBucketRateLimitStrategy();

            Assert.Equal(enableHeaders, rateLimiter.ShouldAddHeaders(config));
        }
Пример #5
0
        public void GivenThereAreRequestsLeft_ThenTheCorrectFunctionShouldBeCalled()
        {
            var identityProvider = new PredictableClientIdentityProvider <LeakyBucketClientIdentity>(
                new LeakyBucketClientIdentity
            {
                UniqueIdentifier = "test"
            });

            var config = new LeakyBucketConfiguration
            {
                MaxRequests = 5,
                LeakRate    = TimeSpan.FromSeconds(5)
            };

            var rateLimiter = new LeakyBucketRateLimitStrategy();

            var successfulRequests = 0;
            var failedRequests     = 0;


            void RunRateLimiter()
            {
                var identity = identityProvider.GetCurrentIdentity();

                if (rateLimiter.HasRemainingAllowance(identity, config))
                {
                    rateLimiter.ReduceAllowanceBy(identity, config);
                    successfulRequests += 1;
                }
                else
                {
                    failedRequests += 1;
                }
            }

            RunRateLimiter();
            Assert.Equal(1, successfulRequests);

            for (var i = 0; i < 5; i++)
            {
                RunRateLimiter();
            }

            Thread.Sleep(TimeSpan.FromSeconds(6));
            Assert.Equal(5, successfulRequests);
            Assert.Equal(1, failedRequests);
        }
Пример #6
0
        public void GivenSomeRequestsHaveBeenMade_ThenItShouldReturnTheCorrectResult(int requests, int maxRequests, bool expectedResult)
        {
            var identity = new LeakyBucketClientIdentity
            {
                UniqueIdentifier = "test"
            };

            var config = new LeakyBucketConfiguration
            {
                MaxRequests = maxRequests,
                LeakRate    = TimeSpan.FromMinutes(5)
            };

            var rateLimiter = new LeakyBucketRateLimitStrategy();

            rateLimiter.ReduceAllowanceBy(identity, requests);

            Assert.Equal(expectedResult, rateLimiter.HasRemainingAllowance(identity, config));
        }
Пример #7
0
        public void GivenNoRequestsHaveBeenMade_ThenThereShouldBeTheExpectedAmountLeft()
        {
            const int expectedMaxRequests = 10;

            var identity = new LeakyBucketClientIdentity
            {
                UniqueIdentifier = "test"
            };

            var config = new LeakyBucketConfiguration
            {
                MaxRequests = expectedMaxRequests,
                LeakRate    = TimeSpan.FromMinutes(5)
            };

            var rateLimiter = new LeakyBucketRateLimitStrategy();

            Assert.Equal(expectedMaxRequests, rateLimiter.GetRemainingAllowance(identity, config));
        }
Пример #8
0
        public async Task Should_not_return_too_many_requests_if_number_of_requests_is_below_configured_value()
        {
            var store         = A.Fake <IRequestStore>();
            var configuration = new LeakyBucketConfiguration
            {
                RefreshRate         = TimeSpan.FromSeconds(1),
                MaxNumberOfRequests = 10,
                RequestStore        = store
            };

            A.CallTo(() => store.NumberOfRequestsFor(A <IClientIdentifier> .Ignored)).Returns(9);

            using (var server = CreateTestServer(configuration))
            {
                var response = await server.CreateRequest("/").GetAsync();

                Assert.AreNotEqual(ExtendedHttpStatusCodes.TooManyRequests, response.StatusCode);
            }
        }
Пример #9
0
        public void GivenAConfiguration_AfterALongInterval_TheCorrectNumberOfRequestsShouldBeAvailable(int maxRequests, int leakAmount, int requestCount, int waitSeconds, int expected)
        {
            var mockIdentity = new LeakyBucketClientIdentity
            {
                UniqueIdentifier = "test"
            };
            var identityProvider = new PredictableClientIdentityProvider <LeakyBucketClientIdentity>(
                mockIdentity);

            var config = new LeakyBucketConfiguration
            {
                MaxRequests = maxRequests,
                LeakRate    = TimeSpan.FromSeconds(1),
                LeakAmount  = leakAmount
            };

            var rateLimiter = new LeakyBucketRateLimitStrategy();

            void RunRateLimiter()
            {
                var identity = identityProvider.GetCurrentIdentity();

                if (rateLimiter.HasRemainingAllowance(identity, config))
                {
                    rateLimiter.ReduceAllowanceBy(identity, config);
                }
            }

            for (var i = 0; i < requestCount; i++)
            {
                RunRateLimiter();
            }

            if (waitSeconds > 0)
            {
                Thread.Sleep(TimeSpan.FromSeconds(waitSeconds));
            }

            RunRateLimiter();

            Assert.Equal(expected, rateLimiter.GetRemainingAllowance(mockIdentity, config));
        }
Пример #10
0
        public async Task UseLeakyBucket_should_attach_middleware()
        {
            var store         = A.Fake <IRequestStore>();
            var configuration = new LeakyBucketConfiguration
            {
                RequestStore = store
            };

            using (var server = TestServer.Create(app =>
            {
                app.UseLeakyBucket(configuration);
                app.Run(async ctx => await ctx.Response.WriteAsync("Test"));
            }))
            {
                await server.CreateRequest("/").GetAsync();
            }

            A.CallTo(() => store.NumberOfRequestsFor(A <IClientIdentifier> .Ignored))
            .MustHaveHappened(Repeated.Exactly.Once);
        }
Пример #11
0
        public async Task Should_check_the_number_of_requests_using_the_request_store()
        {
            var store         = A.Fake <IRequestStore>();
            var identifier    = A.Fake <IClientIdentifier>();
            var configuration = new LeakyBucketConfiguration
            {
                RefreshRate          = TimeSpan.FromSeconds(1),
                MaxNumberOfRequests  = 10,
                RequestStore         = store,
                ClientIdentifierFunc = _ => identifier
            };

            using (var server = CreateTestServer(configuration))
            {
                await server.CreateRequest("/").GetAsync();
            }

            A.CallTo(() => store.NumberOfRequestsFor(identifier))
            .MustHaveHappened(Repeated.Exactly.Once);
        }
Пример #12
0
        public void GivenWeAreOutOfRequests_WhenTheIntervalPasses_ThenWeShouldHaveRequestsAgain()
        {
            var identity = new LeakyBucketClientIdentity
            {
                UniqueIdentifier = "test"
            };

            var config = new LeakyBucketConfiguration
            {
                MaxRequests = 5,
                LeakRate    = TimeSpan.FromSeconds(5),
                LeakAmount  = 5
            };

            var rateLimiter = new LeakyBucketRateLimitStrategy();

            rateLimiter.ReduceAllowanceBy(identity, 5);
            Assert.False(rateLimiter.HasRemainingAllowance(identity, config));

            Thread.Sleep(TimeSpan.FromSeconds(6));
            Assert.True(rateLimiter.HasRemainingAllowance(identity, config));
        }
Пример #13
0
        public void GivenWeAreOutOfRequests_WhenTheIntervalPasses_ThenWeShouldHaveRequestsAgain()
        {
            var identityProvider = new PredictableClientIdentityProvider <LeakyBucketClientIdentity>(
                new LeakyBucketClientIdentity
            {
                UniqueIdentifier = "test"
            });

            var config = new LeakyBucketConfiguration
            {
                MaxRequests      = 5,
                LeakRate         = TimeSpan.FromSeconds(5),
                IdentityProvider = identityProvider
            };

            var rateLimiter = new LeakyBucketRateLimitStrategy(config);

            rateLimiter.ReduceAllowanceBy(5);
            Assert.Equal(false, rateLimiter.HasRemainingAllowance());

            Thread.Sleep(TimeSpan.FromSeconds(6));
            Assert.Equal(true, rateLimiter.HasRemainingAllowance());
        }
Пример #14
0
        public void GivenSomeRequestsHaveBeenMade_ThenThereShouldBeTheExpectedAmountLeft(int requests)
        {
            const int expectedMaxRequests = 10;

            var identityProvider = new PredictableClientIdentityProvider <LeakyBucketClientIdentity>(
                new LeakyBucketClientIdentity
            {
                UniqueIdentifier = "test"
            });

            var config = new LeakyBucketConfiguration
            {
                MaxRequests      = expectedMaxRequests,
                LeakRate         = TimeSpan.FromMinutes(5),
                IdentityProvider = identityProvider
            };

            var rateLimiter = new LeakyBucketRateLimitStrategy(config);

            rateLimiter.ReduceAllowanceBy(requests);

            Assert.Equal(expectedMaxRequests - requests, rateLimiter.GetRemainingAllowance());
        }
Пример #15
0
        public void GivenWeMakeRequestsAtASteadyRate_WhenThatRateIsJustAboveTheAllowed_ThenWeShouldApproachTheLimit()
        {
            var identity = new LeakyBucketClientIdentity
            {
                UniqueIdentifier = "test"
            };

            var config = new LeakyBucketConfiguration
            {
                MaxRequests = 40,
                LeakRate    = TimeSpan.FromSeconds(1),
                LeakAmount  = 2
            };

            var rateLimiter = new LeakyBucketRateLimitStrategy();

            rateLimiter.ReduceAllowanceBy(identity, 3);
            Assert.True(rateLimiter.HasRemainingAllowance(identity, config));
            Assert.Equal(37, rateLimiter.GetRemainingAllowance(identity, config));
            Thread.Sleep(TimeSpan.FromSeconds(1));

            rateLimiter.ReduceAllowanceBy(identity, 3);
            Assert.True(rateLimiter.HasRemainingAllowance(identity, config));
            Assert.Equal(36, rateLimiter.GetRemainingAllowance(identity, config));
            Thread.Sleep(TimeSpan.FromSeconds(1));

            rateLimiter.ReduceAllowanceBy(identity, 3);
            Assert.True(rateLimiter.HasRemainingAllowance(identity, config));
            Assert.Equal(35, rateLimiter.GetRemainingAllowance(identity, config));
            Thread.Sleep(TimeSpan.FromSeconds(1));

            rateLimiter.ReduceAllowanceBy(identity, 3);
            Assert.True(rateLimiter.HasRemainingAllowance(identity, config));
            Assert.Equal(34, rateLimiter.GetRemainingAllowance(identity, config));
            Thread.Sleep(TimeSpan.FromSeconds(1));
        }
Пример #16
0
        public async Task Should_use_the_default_client_identifier_if_not_specified_in_config()
        {
            var store         = A.Fake <IRequestStore>();
            var configuration = new LeakyBucketConfiguration
            {
                RefreshRate         = TimeSpan.FromSeconds(1),
                MaxNumberOfRequests = 10,
                RequestStore        = store
            };

            using (var server = CreateTestServer(configuration))
            {
                await server.CreateRequest("/")
                .AddHeader("User-Agent", "test")
                .GetAsync();
            }

            A.CallTo(() => store.NumberOfRequestsFor(
                         A <IClientIdentifier> .That.IsInstanceOf(typeof(DefaultClientIdentifier))))
            .MustHaveHappened(Repeated.Exactly.Once);
            A.CallTo(() => store.NumberOfRequestsFor(
                         A <IClientIdentifier> .That.Matches(x => ((DefaultClientIdentifier)x).UserAgentAddress == "test")))
            .MustHaveHappened(Repeated.Exactly.Once);
        }