diff --git a/src/Symfony/Component/RateLimiter/Policy/SlidingWindowLimiter.php b/src/Symfony/Component/RateLimiter/Policy/SlidingWindowLimiter.php index bf62b89ffc7f9..75715cce62eaf 100644 --- a/src/Symfony/Component/RateLimiter/Policy/SlidingWindowLimiter.php +++ b/src/Symfony/Component/RateLimiter/Policy/SlidingWindowLimiter.php @@ -65,21 +65,35 @@ public function reserve(int $tokens = 1, float $maxTime = null): Reservation $now = microtime(true); $hitCount = $window->getHitCount(); $availableTokens = $this->getAvailableTokens($hitCount); - if ($availableTokens >= $tokens) { + if (0 !== $tokens && $availableTokens > $tokens) { $window->add($tokens); $reservation = new Reservation($now, new RateLimit($this->getAvailableTokens($window->getHitCount()), \DateTimeImmutable::createFromFormat('U', floor($now)), true, $this->limit)); } else { + if ($availableTokens === $tokens) { + $window->add($tokens); + } + $waitDuration = $window->calculateTimeForTokens($this->limit, max(1, $tokens)); - if (null !== $maxTime && $waitDuration > $maxTime) { + if ($availableTokens !== $tokens && 0 !== $tokens && null !== $maxTime && $waitDuration > $maxTime) { // process needs to wait longer than set interval throw new MaxWaitDurationExceededException(sprintf('The rate limiter wait time ("%d" seconds) is longer than the provided maximum time ("%d" seconds).', $waitDuration, $maxTime), new RateLimit($this->getAvailableTokens($window->getHitCount()), \DateTimeImmutable::createFromFormat('U', floor($now + $waitDuration)), false, $this->limit)); } - $window->add($tokens); + if ($availableTokens !== $tokens) { + $window->add($tokens); + } + + if ($availableTokens === $tokens || 0 === $tokens) { + $accepted = true; + $timeToAct = $now; + } else { + $accepted = false; + $timeToAct = $now + $waitDuration; + } - $reservation = new Reservation($now + $waitDuration, new RateLimit($this->getAvailableTokens($window->getHitCount()), \DateTimeImmutable::createFromFormat('U', floor($now + $waitDuration)), false, $this->limit)); + $reservation = new Reservation($timeToAct, new RateLimit($this->getAvailableTokens($window->getHitCount()), \DateTimeImmutable::createFromFormat('U', floor($now + $waitDuration)), $accepted, $this->limit)); } if (0 < $tokens) { diff --git a/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowLimiterTest.php b/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowLimiterTest.php index 21deb69c3932b..f6e15a10255b0 100644 --- a/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowLimiterTest.php +++ b/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowLimiterTest.php @@ -54,6 +54,44 @@ public function testConsume() $this->assertSame(10, $rateLimit->getLimit()); } + public function testConsumeLastToken() + { + $limiter = $this->createLimiter(); + $limiter->reset(); + $limiter->consume(9); + + $rateLimit = $limiter->consume(1); + $this->assertSame(0, $rateLimit->getRemainingTokens()); + $this->assertTrue($rateLimit->isAccepted()); + $this->assertEquals( + \DateTimeImmutable::createFromFormat('U', (string) floor(microtime(true) + 12 / 10)), + $rateLimit->getRetryAfter() + ); + } + + public function testConsumeZeroTokens() + { + $limiter = $this->createLimiter(); + $limiter->reset(); + + $rateLimit = $limiter->consume(0); + $this->assertTrue($rateLimit->isAccepted()); + $this->assertEquals( + \DateTimeImmutable::createFromFormat('U', (string) floor(microtime(true))), + $rateLimit->getRetryAfter() + ); + + $limiter->reset(); + $limiter->consume(10); + + $rateLimit = $limiter->consume(0); + $this->assertTrue($rateLimit->isAccepted()); + $this->assertEquals( + \DateTimeImmutable::createFromFormat('U', (string) floor(microtime(true) + 12 / 10)), + $rateLimit->getRetryAfter() + ); + } + public function testWaitIntervalOnConsumeOverLimit() { $limiter = $this->createLimiter(); @@ -76,6 +114,9 @@ public function testReserve() // 2 over the limit, causing the WaitDuration to become 2/10th of the 12s interval $this->assertEqualsWithDelta(12 / 5, $limiter->reserve(4)->getWaitDuration(), 1); + + $limiter->reset(); + $this->assertEquals(0, $limiter->reserve(10)->getWaitDuration()); } private function createLimiter(): SlidingWindowLimiter pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy