diff --git a/src/Symfony/Component/RateLimiter/Policy/SlidingWindowLimiter.php b/src/Symfony/Component/RateLimiter/Policy/SlidingWindowLimiter.php index fc9173de49277..d3bd37f7b02e0 100644 --- a/src/Symfony/Component/RateLimiter/Policy/SlidingWindowLimiter.php +++ b/src/Symfony/Component/RateLimiter/Policy/SlidingWindowLimiter.php @@ -74,7 +74,14 @@ public function reserve(int $tokens = 1, ?float $maxTime = null): Reservation if ($availableTokens >= $tokens) { $window->add($tokens); - $reservation = new Reservation($now, new RateLimit($this->getAvailableTokens($window->getHitCount()), \DateTimeImmutable::createFromFormat('U', floor($now)), true, $this->limit)); + if ($availableTokens === $tokens) { + $resetDuration = $window->calculateTimeForTokens($this->limit, $window->getHitCount()); + $retryAfter = $now + $resetDuration; + } else { + $retryAfter = $now; + } + + $reservation = new Reservation($now, new RateLimit($this->getAvailableTokens($window->getHitCount()), \DateTimeImmutable::createFromFormat('U', floor($retryAfter)), true, $this->limit)); } else { $waitDuration = $window->calculateTimeForTokens($this->limit, $tokens); diff --git a/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowLimiterTest.php b/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowLimiterTest.php index 835c6cc767da6..a5605247a7ef0 100644 --- a/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowLimiterTest.php +++ b/src/Symfony/Component/RateLimiter/Tests/Policy/SlidingWindowLimiterTest.php @@ -70,6 +70,21 @@ public function testWaitIntervalOnConsumeOverLimit() $this->assertTrue($limiter->consume()->isAccepted()); } + 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)), + $rateLimit->getRetryAfter() + ); + } + public function testReserve() { $limiter = $this->createLimiter();