From 07fafb7848ae90221199460e7359e6c5a4256f27 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Thu, 21 Sep 2023 16:04:47 +1000 Subject: [PATCH] Fix array cache driver expiry --- src/Illuminate/Cache/ArrayStore.php | 2 +- .../Integration/Http/ThrottleRequestsTest.php | 59 +++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Cache/ArrayStore.php b/src/Illuminate/Cache/ArrayStore.php index 22b42ba5f10f..b1f066dff939 100644 --- a/src/Illuminate/Cache/ArrayStore.php +++ b/src/Illuminate/Cache/ArrayStore.php @@ -57,7 +57,7 @@ public function get($key) $expiresAt = $item['expiresAt'] ?? 0; - if ($expiresAt !== 0 && $this->currentTime() > $expiresAt) { + if ($expiresAt !== 0 && $this->currentTime() >= $expiresAt) { $this->forget($key); return; diff --git a/tests/Integration/Http/ThrottleRequestsTest.php b/tests/Integration/Http/ThrottleRequestsTest.php index 6ddd2a0ef51e..7f4382ad0462 100644 --- a/tests/Integration/Http/ThrottleRequestsTest.php +++ b/tests/Integration/Http/ThrottleRequestsTest.php @@ -4,6 +4,7 @@ use Illuminate\Cache\RateLimiter; use Illuminate\Cache\RateLimiting\GlobalLimit; +use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Container\Container; use Illuminate\Http\Exceptions\ThrottleRequestsException; use Illuminate\Routing\Middleware\ThrottleRequests; @@ -116,4 +117,62 @@ public function testItCanGenerateDefinitionViaStaticMethod() $signature = (string) ThrottleRequests::with(prefix: 'foo'); $this->assertSame('Illuminate\Routing\Middleware\ThrottleRequests:60,1,foo', $signature); } + + public function testItCanThrottlePerMinute() + { + $rateLimiter = Container::getInstance()->make(RateLimiter::class); + $rateLimiter->for('test', fn () => Limit::perMinute(3)); + Route::get('/', fn () => 'ok')->middleware(ThrottleRequests::using('test')); + + Carbon::setTestNow('2000-01-01 00:00:00.000'); + + // Make 3 requests, each a second apart, that should all be successful. + + for ($i = 0; $i < 3; $i++) { + match ($i) { + 0 => $this->assertSame('2000-01-01 00:00:00.000', now()->toDateTimeString('m')), + 1 => $this->assertSame('2000-01-01 00:00:01.000', now()->toDateTimeString('m')), + 2 => $this->assertSame('2000-01-01 00:00:02.000', now()->toDateTimeString('m')), + }; + + $response = $this->get('/'); + $response->assertOk(); + $response->assertContent('ok'); + $response->assertHeader('X-RateLimit-Limit', 3); + $response->assertHeader('X-RateLimit-Remaining', 3 - ($i + 1)); + + Carbon::setTestNow(now()->addSecond()); + } + + // It is now 3 seconds past and we will make another request that + // should be rate limited. + + $this->assertSame('2000-01-01 00:00:03.000', now()->toDateTimeString('m')); + + $response = $this->get('/'); + $response->assertStatus(429); + $response->assertHeader('Retry-After', 57); + $response->assertHeader('X-RateLimit-Reset', now()->addSeconds(57)->timestamp); + $response->assertHeader('X-RateLimit-Limit', 3); + $response->assertHeader('X-RateLimit-Remaining', 0); + + // We will now make it the very end of the minute, to check boundaries, + // and make another request that should be rate limited and tell us to + // try again in 1 second. + Carbon::setTestNow(now()->endOfMinute()); + $this->assertSame('2000-01-01 00:00:59.999', now()->toDateTimeString('m')); + + $response = $this->get('/'); + $response->assertHeader('Retry-After', 1); + $response->assertHeader('X-RateLimit-Reset', now()->addSeconds(1)->timestamp); + $response->assertHeader('X-RateLimit-Limit', 3); + $response->assertHeader('X-RateLimit-Remaining', 0); + + // We now tick over into the next second. We should now be able to make + // requests again. + Carbon::setTestNow('2000-01-01 00:01:00.000'); + + $response = $this->get('/'); + $response->assertOk(); + } }