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); }
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()); }
private static TestServer CreateTestServer(LeakyBucketConfiguration configuration) { return(TestServer.Create(app => { app.Use(typeof(LeakyBucketMiddleware), configuration); app.Run(async context => await context.Response.WriteAsync("Test")); })); }
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)); }
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); }
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)); }
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)); }
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); } }
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)); }
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); }
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); }
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)); }
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()); }
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()); }
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)); }
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); }