2020-02-06 02:35:13 +00:00
|
|
|
<?php
|
2020-10-14 22:19:31 +00:00
|
|
|
|
2021-07-19 05:53:45 +00:00
|
|
|
declare(strict_types=1);
|
|
|
|
|
2020-02-06 02:35:13 +00:00
|
|
|
namespace App;
|
|
|
|
|
|
|
|
use App\Http\ServerRequest;
|
2021-06-28 15:03:21 +00:00
|
|
|
use Psr\Cache\CacheItemPoolInterface;
|
|
|
|
use Symfony\Component\Cache\Adapter\ProxyAdapter;
|
|
|
|
use Symfony\Component\RateLimiter\RateLimiterFactory;
|
|
|
|
use Symfony\Component\RateLimiter\Storage\CacheStorage;
|
2020-02-06 02:35:13 +00:00
|
|
|
|
|
|
|
class RateLimit
|
|
|
|
{
|
2021-06-28 15:03:21 +00:00
|
|
|
protected CacheItemPoolInterface $psr6Cache;
|
|
|
|
|
2021-04-23 05:24:12 +00:00
|
|
|
public function __construct(
|
2021-06-28 15:03:21 +00:00
|
|
|
protected LockFactory $lockFactory,
|
|
|
|
protected Environment $environment,
|
|
|
|
CacheItemPoolInterface $cacheItemPool
|
2021-04-23 05:24:12 +00:00
|
|
|
) {
|
2021-06-28 15:03:21 +00:00
|
|
|
$this->psr6Cache = new ProxyAdapter($cacheItemPool, 'ratelimit.');
|
2020-02-06 02:35:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param ServerRequest $request
|
2021-01-23 17:03:16 +00:00
|
|
|
* @param string $groupName
|
2020-02-06 02:35:13 +00:00
|
|
|
* @param int $interval
|
2021-06-28 15:03:21 +00:00
|
|
|
* @param int $limit
|
2020-02-06 02:35:13 +00:00
|
|
|
*
|
|
|
|
* @throws Exception\RateLimitExceededException
|
|
|
|
*/
|
2021-01-23 17:03:16 +00:00
|
|
|
public function checkRequestRateLimit(
|
2020-02-06 02:35:13 +00:00
|
|
|
ServerRequest $request,
|
2021-01-23 17:03:16 +00:00
|
|
|
string $groupName,
|
2021-06-28 15:03:21 +00:00
|
|
|
int $interval = 5,
|
|
|
|
int $limit = 2
|
2021-01-23 17:03:16 +00:00
|
|
|
): void {
|
2020-12-04 08:41:55 +00:00
|
|
|
if ($this->environment->isTesting() || $this->environment->isCli()) {
|
2021-01-23 17:03:16 +00:00
|
|
|
return;
|
2020-02-06 02:35:13 +00:00
|
|
|
}
|
|
|
|
|
2021-06-28 15:03:21 +00:00
|
|
|
$ipKey = str_replace([':', '.'], '_', $request->getIp());
|
|
|
|
$this->checkRateLimit($groupName, $ipKey, $interval, $limit);
|
2021-01-23 17:03:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string $groupName
|
2021-06-28 15:03:21 +00:00
|
|
|
* @param string $key
|
2021-01-23 17:03:16 +00:00
|
|
|
* @param int $interval
|
2021-06-28 15:03:21 +00:00
|
|
|
* @param int $limit
|
2021-01-23 17:03:16 +00:00
|
|
|
*
|
|
|
|
* @throws Exception\RateLimitExceededException
|
|
|
|
*/
|
|
|
|
public function checkRateLimit(
|
|
|
|
string $groupName,
|
2021-06-28 15:03:21 +00:00
|
|
|
string $key,
|
|
|
|
int $interval = 5,
|
|
|
|
int $limit = 2
|
2021-01-23 17:03:16 +00:00
|
|
|
): void {
|
2021-06-28 15:03:21 +00:00
|
|
|
$cacheStore = new CacheStorage($this->psr6Cache);
|
2021-01-23 17:03:16 +00:00
|
|
|
|
2021-06-28 15:03:21 +00:00
|
|
|
$config = [
|
|
|
|
'id' => 'ratelimit.' . $groupName,
|
|
|
|
'policy' => 'sliding_window',
|
|
|
|
'interval' => $interval . ' seconds',
|
|
|
|
'limit' => $limit,
|
|
|
|
];
|
2020-02-06 02:35:13 +00:00
|
|
|
|
2021-06-28 15:03:21 +00:00
|
|
|
$rateLimiterFactory = new RateLimiterFactory($config, $cacheStore, $this->lockFactory);
|
|
|
|
$rateLimiter = $rateLimiterFactory->create($key);
|
2020-02-06 02:35:13 +00:00
|
|
|
|
2021-06-28 15:03:21 +00:00
|
|
|
if (false === $rateLimiter->consume(1)->isAccepted()) {
|
|
|
|
throw new Exception\RateLimitExceededException();
|
2020-02-06 02:35:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|