From c4362f89b486e6bfb9d3dd24870b58421b6b4be9 Mon Sep 17 00:00:00 2001 From: hongweipeng Date: Mon, 15 Apr 2024 17:12:12 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=BA=E7=B1=BB=E5=9E=8B=E5=A3=B0=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 1 - src/Throttle.php | 57 ++++++++++++----------------- src/throttle/CounterFixed.php | 8 +++- src/throttle/CounterSlider.php | 12 ++++-- src/throttle/LeakyBucket.php | 12 ++++-- src/throttle/ThrottleAbstract.php | 12 +++--- src/throttle/TokenBucket.php | 20 ++++++---- tests/Base.php | 27 ++++++++------ tests/CustomCacheTest.php | 18 +++++---- tests/GCApp.php | 30 ++++++++------- tests/ResidentMemoryTest.php | 6 ++- tests/RouteThrottleTest.php | 12 +++--- tests/ThrottleDefaultConfigTest.php | 8 +--- tests/ThrottleDriverTest.php | 3 +- tests/VisitRateTest.php | 7 +++- 15 files changed, 125 insertions(+), 108 deletions(-) diff --git a/composer.json b/composer.json index 47ddcb5..eae1b04 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,6 @@ } } }, - "minimum-stability": "dev", "require-dev": { "phpunit/phpunit": "^9.5", "guzzlehttp/guzzle": "^7.7", diff --git a/src/Throttle.php b/src/Throttle.php index 396cef5..557ccaa 100644 --- a/src/Throttle.php +++ b/src/Throttle.php @@ -13,6 +13,7 @@ use think\middleware\throttle\ThrottleAbstract; use think\Request; use think\Response; +use TypeError; use function sprintf; /** @@ -26,7 +27,7 @@ class Throttle * 默认配置参数 * @var array */ - public static $default_config = [ + public static array $default_config = [ 'prefix' => 'throttle_', // 缓存键前缀,防止键与其他应用冲突 'key' => true, // 节流规则 true为自动规则 'visit_method' => ['GET', 'HEAD'], // 要被限制的请求类型 @@ -38,7 +39,7 @@ class Throttle 'driver_name' => CounterFixed::class, // 限流算法驱动 ]; - public static $duration = [ + public static array $duration = [ 's' => 1, 'm' => 60, 'h' => 3600, @@ -49,20 +50,19 @@ class Throttle * 缓存对象 * @var CacheInterface */ - protected $cache; + protected CacheInterface $cache; /** * 配置参数 * @var array */ - protected $config = []; + protected array $config = []; - protected $key = null; // 解析后的标识 - protected $wait_seconds = 0; // 下次合法请求还有多少秒 - protected $now = 0; // 当前时间戳 - protected $max_requests = 0; // 规定时间内允许的最大请求次数 - protected $expire = 0; // 规定时间 - protected $remaining = 0; // 规定时间内还能请求的次数 + protected int $wait_seconds = 0; // 下次合法请求还有多少秒 + protected int $now = 0; // 当前时间戳 + protected int $max_requests = 0; // 规定时间内允许的最大请求次数 + protected int $expire = 0; // 规定时间 + protected int $remaining = 0; // 规定时间内还能请求的次数 /** * Throttle constructor. @@ -93,17 +93,17 @@ protected function allowRequest(Request $request): bool } [$max_requests, $duration] = $this->parseRate($this->config['visit_rate']); - $micronow = microtime(true); + $micro_now = microtime(true); // float $driver = Container::getInstance()->invokeClass($this->config['driver_name']); if (!($driver instanceof ThrottleAbstract)) { - throw new \TypeError('The throttle driver must extends ' . ThrottleAbstract::class); + throw new TypeError('The throttle driver must extends ' . ThrottleAbstract::class); } - $allow = $driver->allowRequest($key, $micronow, $max_requests, $duration, $this->cache); + $allow = $driver->allowRequest($key, $micro_now, $max_requests, $duration, $this->cache); if ($allow) { // 允许访问 - $this->now = (int) $micronow; + $this->now = (int) $micro_now; $this->expire = $duration; $this->max_requests = $max_requests; $this->remaining = $max_requests - $driver->getCurRequests(); @@ -135,7 +135,11 @@ public function handle(Request $request, Closure $next, array $params=[]): Respo $response = $next($request); if (200 <= $response->getCode() && 300 > $response->getCode() && $this->config['visit_enable_show_rate_limit']) { // 将速率限制 headers 添加到响应中 - $response->header($this->getRateLimitHeaders()); + $response->header([ + 'X-Rate-Limit-Limit' => $this->max_requests, + 'X-Rate-Limit-Remaining' => max($this->remaining, 0), + 'X-Rate-Limit-Reset' => $this->now + $this->expire, + ]); } return $response; } @@ -149,7 +153,7 @@ protected function getCacheKey(Request $request): ?string { $key = $this->config['key']; - if ($key instanceof \Closure) { + if ($key instanceof Closure) { $key = Container::getInstance()->invokeFunction($key, [$this, $request]); } @@ -160,7 +164,7 @@ protected function getCacheKey(Request $request): ?string if ($key === true) { $key = $request->ip(); - } elseif (is_string($key) && false !== strpos($key, '__')) { + } elseif (is_string($key) && str_contains($key, '__')) { $key = str_replace(['__CONTROLLER__', '__ACTION__', '__IP__'], [$request->controller(), $request->action(), $request->ip()], $key); } @@ -172,7 +176,7 @@ protected function getCacheKey(Request $request): ?string * @param string $rate * @return int[] */ - protected function parseRate($rate): array + protected function parseRate(string $rate): array { [$num, $period] = explode("/", $rate); $max_requests = (int) $num; @@ -213,19 +217,6 @@ public function setDriverClass(string $class_name): self return $this; } - /** - * 获取速率限制头 - * @return array - */ - public function getRateLimitHeaders(): array - { - return [ - 'X-Rate-Limit-Limit' => $this->max_requests, - 'X-Rate-Limit-Remaining' => $this->remaining < 0 ? 0 : $this->remaining, - 'X-Rate-Limit-Reset' => $this->now + $this->expire, - ]; - } - /** * 构建 Response Exception * @param int $wait_seconds @@ -234,10 +225,10 @@ public function getRateLimitHeaders(): array */ public function buildLimitException(int $wait_seconds, Request $request): HttpResponseException { $visitFail = $this->config['visit_fail_response'] ?? null; - if ($visitFail instanceof \Closure) { + if ($visitFail instanceof Closure) { $response = Container::getInstance()->invokeFunction($visitFail, [$this, $request, $wait_seconds]); if (!$response instanceof Response) { - throw new \TypeError(sprintf('The closure must return %s instance', Response::class)); + throw new TypeError(sprintf('The closure must return %s instance', Response::class)); } } else { $content = str_replace('__WAIT__', (string) $wait_seconds, $this->config['visit_fail_text']); diff --git a/src/throttle/CounterFixed.php b/src/throttle/CounterFixed.php index 30c0b6d..39f627d 100644 --- a/src/throttle/CounterFixed.php +++ b/src/throttle/CounterFixed.php @@ -4,6 +4,7 @@ namespace think\middleware\throttle; use Psr\SimpleCache\CacheInterface; +use Psr\SimpleCache\InvalidArgumentException; /** * 计数器固定窗口算法 @@ -13,10 +14,13 @@ class CounterFixed extends ThrottleAbstract { - public function allowRequest(string $key, float $micronow, int $max_requests, int $duration, CacheInterface $cache): bool + /** + * @throws InvalidArgumentException + */ + public function allowRequest(string $key, float $micro_now, int $max_requests, int $duration, CacheInterface $cache): bool { $cur_requests = (int) $cache->get($key, 0); - $now = (int) $micronow; + $now = (int) $micro_now; $wait_reset_seconds = $duration - $now % $duration; // 距离下次重置还有n秒时间 $this->wait_seconds = $wait_reset_seconds % $duration + 1; $this->cur_requests = $cur_requests; diff --git a/src/throttle/CounterSlider.php b/src/throttle/CounterSlider.php index 012a750..369d420 100644 --- a/src/throttle/CounterSlider.php +++ b/src/throttle/CounterSlider.php @@ -4,18 +4,22 @@ namespace think\middleware\throttle; use Psr\SimpleCache\CacheInterface; +use Psr\SimpleCache\InvalidArgumentException; /** * 计数器滑动窗口算法 - * Class CouterSlider + * Class CounterSlider * @package think\middleware\throttle */ class CounterSlider extends ThrottleAbstract { - public function allowRequest(string $key, float $micronow, int $max_requests, int $duration, CacheInterface $cache): bool + /** + * @throws InvalidArgumentException + */ + public function allowRequest(string $key, float $micro_now, int $max_requests, int $duration, CacheInterface $cache): bool { $history = $cache->get($key, []); - $now = (int) $micronow; + $now = (int) $micro_now; // 移除过期的请求的记录 $history = array_values(array_filter($history, function ($val) use ($now, $duration) { return $val >= $now - $duration; @@ -31,7 +35,7 @@ public function allowRequest(string $key, float $micronow, int $max_requests, in if ($history) { $wait_seconds = $duration - ($now - $history[0]) + 1; - $this->wait_seconds = $wait_seconds > 0 ? $wait_seconds : 0; + $this->wait_seconds = max($wait_seconds, 0); } return false; diff --git a/src/throttle/LeakyBucket.php b/src/throttle/LeakyBucket.php index 4cf199b..938687d 100644 --- a/src/throttle/LeakyBucket.php +++ b/src/throttle/LeakyBucket.php @@ -4,6 +4,7 @@ namespace think\middleware\throttle; use Psr\SimpleCache\CacheInterface; +use Psr\SimpleCache\InvalidArgumentException; /** * 漏桶算法 @@ -13,19 +14,22 @@ class LeakyBucket extends ThrottleAbstract { - public function allowRequest(string $key, float $micronow, int $max_requests, int $duration, CacheInterface $cache): bool + /** + * @throws InvalidArgumentException + */ + public function allowRequest(string $key, float $micro_now, int $max_requests, int $duration, CacheInterface $cache): bool { if ($max_requests <= 0) return false; $last_time = (float) $cache->get($key, 0); // 最近一次请求 $rate = (float) $duration / $max_requests; // 平均 n 秒一个请求 - if ($micronow - $last_time < $rate) { + if ($micro_now - $last_time < $rate) { $this->cur_requests = 1; - $this->wait_seconds = ceil($rate - ($micronow - $last_time)); + $this->wait_seconds = (int) ceil($rate - ($micro_now - $last_time)); return false; } - $cache->set($key, $micronow, $duration); + $cache->set($key, $micro_now, $duration); return true; } } \ No newline at end of file diff --git a/src/throttle/ThrottleAbstract.php b/src/throttle/ThrottleAbstract.php index bd560cd..d6d0c11 100644 --- a/src/throttle/ThrottleAbstract.php +++ b/src/throttle/ThrottleAbstract.php @@ -9,20 +9,20 @@ abstract class ThrottleAbstract { /** @var int */ - protected $cur_requests = 0; // 当前已有的请求数 + protected int $cur_requests = 0; // 当前已有的请求数 /** @var int */ - protected $wait_seconds = 0; // 距离下次合法请求还有多少秒 + protected int $wait_seconds = 0; // 距离下次合法请求还有多少秒 /** * 是否允许访问 * @param string $key 缓存键 - * @param float $micronow 当前时间戳,可含毫秒 + * @param float $micro_now 当前时间戳,可含毫秒 * @param int $max_requests 允许最大请求数 * @param int $duration 限流时长 * @param CacheInterface $cache 缓存对象 * @return bool */ - abstract public function allowRequest(string $key, float $micronow, int $max_requests, int $duration, CacheInterface $cache): bool; + abstract public function allowRequest(string $key, float $micro_now, int $max_requests, int $duration, CacheInterface $cache): bool; /** * 计算距离下次合法请求还有多少秒 @@ -30,7 +30,7 @@ abstract public function allowRequest(string $key, float $micronow, int $max_req */ public function getWaitSeconds(): int { - return (int) $this->wait_seconds; + return $this->wait_seconds; } /** @@ -39,7 +39,7 @@ public function getWaitSeconds(): int */ public function getCurRequests(): int { - return (int) $this->cur_requests; + return $this->cur_requests; } } \ No newline at end of file diff --git a/src/throttle/TokenBucket.php b/src/throttle/TokenBucket.php index a035e7d..ffc39b9 100644 --- a/src/throttle/TokenBucket.php +++ b/src/throttle/TokenBucket.php @@ -4,6 +4,7 @@ namespace think\middleware\throttle; use Psr\SimpleCache\CacheInterface; +use Psr\SimpleCache\InvalidArgumentException; /** * 令牌桶算法 @@ -12,32 +13,35 @@ */ class TokenBucket extends ThrottleAbstract { - public function allowRequest(string $key, float $micronow, int $max_requests, int $duration, CacheInterface $cache): bool + /** + * @throws InvalidArgumentException + */ + public function allowRequest(string $key, float $micro_now, int $max_requests, int $duration, CacheInterface $cache): bool { if ($max_requests <= 0 || $duration <= 0) return false; $assist_key = $key . 'store_num'; // 辅助缓存 $rate = (float) $max_requests / $duration; // 平均一秒生成 n 个 token - $last_time = $cache->get($key, null); - $store_num = $cache->get($assist_key, null); + $last_time = $cache->get($key, 0); + $store_num = $cache->get($assist_key, 0); - if ($last_time === null || $store_num === null) { // 首次访问 - $cache->set($key, $micronow, $duration); + if ($last_time === 0 || $store_num === 0) { // 首次访问 + $cache->set($key, $micro_now, $duration); $cache->set($assist_key, $max_requests - 1, $duration); return true; } - $create_num = floor(($micronow - $last_time) * $rate); // 推算生成的 token 数 + $create_num = floor(($micro_now - $last_time) * $rate); // 推算生成的 token 数 $token_left = (int) min($max_requests, $store_num + $create_num); //当前剩余 tokens 数量 if ($token_left < 1) { $tmp = (int) ceil($duration / $max_requests); - $this->wait_seconds = $tmp - intval(($micronow - $last_time)) % $tmp; + $this->wait_seconds = $tmp - intval(($micro_now - $last_time)) % $tmp; return false; } $this->cur_requests = $max_requests - $token_left; - $cache->set($key, $micronow, $duration); + $cache->set($key, $micro_now, $duration); $cache->set($assist_key, $token_left - 1, $duration); return true; } diff --git a/tests/Base.php b/tests/Base.php index 180695f..c12a8f4 100644 --- a/tests/Base.php +++ b/tests/Base.php @@ -3,22 +3,23 @@ namespace tests; use PHPUnit\Framework\TestCase; +use think\Request; +use think\Response; abstract class Base extends TestCase { static string $ROOT_PATH = __DIR__ . "/../vendor/topthink/think"; static string $RUNTIME_PATH = __DIR__ . "/../runtime/"; - protected \think\App $app; protected array $throttle_config = []; protected string $middleware_file = __DIR__ . "/config/global-middleware.php"; protected string $middleware_type = 'global'; /** * thinkphp 一般运行在 php-fpm 模式下,每次处理请求都要重新加载配置文件 - * @param \think\Request $request - * @return \think\Response + * @param Request $request + * @return Response */ - function get_response(\think\Request $request): \think\Response { + function get_response(Request $request): Response { // 创建 \think\App 对象,设置配置 $app = new GCApp(static::$ROOT_PATH); $app->setRuntimePath(static::$RUNTIME_PATH); @@ -39,15 +40,17 @@ function get_response(\think\Request $request): \think\Response { * @param string $host * @param array $data * @param array $headers - * @return \think\Request + * @return Request */ - function create_request($uri = '/', $method = 'GET', $host = '127.0.0.1', $data = [], $headers = []): \think\Request + function create_request(string $uri, string $method = 'GET', string $host = '127.0.0.1', array $data = [], array $headers = []): Request { - $request = new \think\Request(); + $request = new Request(); $request->setMethod($method); $request->setHost($host); $request->setDomain($host); - $request->setUrl(sprintf('http://%s/%s', $host, $uri)); + $request->setUrl(sprintf('https://%s/%s', $host, $uri)); + $request->withPost($data); + $request->withHeader($headers); // uri 中提取 path info $path = strpos($uri, '?') ? strstr($uri, '?', true) : $uri; @@ -57,7 +60,7 @@ function create_request($uri = '/', $method = 'GET', $host = '127.0.0.1', $data return $request; } - function visit_with_http_code(\think\Request $request, int $http_code = 200): bool + function visit_with_http_code(Request $request, int $http_code = 200): bool { $response = $this->get_response($request); return $response->getCode() == $http_code; @@ -101,7 +104,8 @@ function get_default_throttle_config(): array { * @param string $file 文件的路径 eg: $this->app->getBasePath() . 'middleware.php' * @param string $type 类型:global 全局;route 路由;controller 控制器 */ - function set_middleware(string $file, string $type = 'global') { + function set_middleware(string $file, string $type = 'global'): void + { $this->middleware_file = $file; $this->middleware_type = $type; } @@ -110,7 +114,8 @@ function set_middleware(string $file, string $type = 'global') { * 设置 throttle 配置 * @param array $config */ - function set_throttle_config(array $config) { + function set_throttle_config(array $config): void + { $this->throttle_config = $config; } diff --git a/tests/CustomCacheTest.php b/tests/CustomCacheTest.php index 981f3a9..bf792c4 100644 --- a/tests/CustomCacheTest.php +++ b/tests/CustomCacheTest.php @@ -5,18 +5,20 @@ */ namespace tests; +use DateInterval; use Psr\SimpleCache\CacheInterface; use think\middleware\Throttle; +use think\Request; class CustomCache implements CacheInterface { - protected $data = []; + protected array $data = []; public function get(string $key, mixed $default = null): mixed { - return isset($this->data[$key]) ? $this->data[$key] : $default; + return $this->data[$key] ?? $default; } - public function set(string $key, mixed $value, null|int|\DateInterval $ttl = null): bool + public function set(string $key, mixed $value, null|int|DateInterval $ttl = null): bool { $this->data[$key] = $value; return true; @@ -25,7 +27,7 @@ public function set(string $key, mixed $value, null|int|\DateInterval $ttl = nul public function delete(string $key): bool { return true; } public function clear(): bool { return true; } public function getMultiple(iterable $keys, mixed $default = null): iterable { return [];} - public function setMultiple(iterable $values, null|int|\DateInterval $ttl = null): bool { return true; } + public function setMultiple(iterable $values, null|int|DateInterval $ttl = null): bool { return true; } public function deleteMultiple(iterable $keys): bool { return true; } public function has(string $key): bool { return true; } } @@ -36,11 +38,11 @@ public function get($key, $default = null): mixed return $default; } - public function set(string $key, mixed $value, null|int|\DateInterval $ttl = null): bool { return true; } + public function set(string $key, mixed $value, null|int|DateInterval $ttl = null): bool { return true; } public function delete(string $key): bool { return true; } public function clear(): bool { return true; } public function getMultiple(iterable $keys, mixed $default = null): iterable { return [];} - public function setMultiple(iterable $values, null|int|\DateInterval $ttl = null): bool { return true; } + public function setMultiple(iterable $values, null|int|DateInterval $ttl = null): bool { return true; } public function deleteMultiple(iterable $keys): bool { return true; } public function has(string $key): bool { return true; } } @@ -65,7 +67,7 @@ function test_custom_cache() $cache = new CustomCache(); $config = $this->get_default_throttle_config(); $config['visit_rate'] = '10/m'; - $config['key'] = function(Throttle $throttle, \think\Request $request) use ($cache) { + $config['key'] = function(Throttle $throttle, Request $request) use (&$cache) { $throttle->setCache($cache); return true; }; @@ -80,7 +82,7 @@ function test_dummy_cache() $cache = new DummyCache(); $config = $this->get_default_throttle_config(); $config['visit_rate'] = '10/m'; - $config['key'] = function(Throttle $throttle, \think\Request $request) use ($cache) { + $config['key'] = function(Throttle $throttle, Request $request) use (&$cache) { $throttle->setCache($cache); return true; }; diff --git a/tests/GCApp.php b/tests/GCApp.php index 6ef9475..856dd09 100644 --- a/tests/GCApp.php +++ b/tests/GCApp.php @@ -8,6 +8,7 @@ namespace tests; +use Exception; use think\App; use think\initializer\BootService; use think\initializer\Error; @@ -21,7 +22,7 @@ class GCError extends Error { * 从 parent::init() 中移除 register_shutdown_function * @param App $app */ - public function init(App $app) + public function init(App $app): void { $this->app = $app; error_reporting(E_ALL); @@ -32,11 +33,13 @@ public function init(App $app) } class GCValidate extends Validate { - public static function cleanMaker() { static::$maker = []; } + public static function cleanMaker(): void + { static::$maker = []; } } class GCModel extends Model { - public static function cleanMaker() { static::$maker = []; } + public static function cleanMaker(): void + { static::$maker = []; } } /** @@ -53,23 +56,24 @@ class GCApp extends App { /** * 添加清理函数 - * @throws \Exception */ - public function refClear() + public function refClear(): void { $this->route->clear(); // 清理路由规则 - // 清理绑定在 App 的实例 - $names = []; - foreach ($this->getIterator() as $name=>$_v) { - $names[] = $name; - } - foreach ($names as $name) { - $this->delete($name); - } // 清理异常 handler restore_error_handler(); restore_exception_handler(); GCValidate::cleanMaker(); GCModel::cleanMaker(); + // 清理绑定在 App 的实例 + $names = []; + try { + foreach ($this->getIterator() as $name => $_v) { + $names[] = $name; + } + } catch (Exception) {} + foreach ($names as $name) { + $this->delete($name); + } } } \ No newline at end of file diff --git a/tests/ResidentMemoryTest.php b/tests/ResidentMemoryTest.php index 7e424f9..d3d5c3c 100644 --- a/tests/ResidentMemoryTest.php +++ b/tests/ResidentMemoryTest.php @@ -4,6 +4,8 @@ namespace tests; +use think\Request; + /** * 常驻内存型的单元测试,当 TP 运行在常驻内存型的时候。 * 一个 App 实例,处理多次请求 @@ -25,7 +27,7 @@ public function test_resident_memory() for ($i = 0; $i < 200; $i++) { // 受访问频率限制 - $request = new \think\Request(); + $request = new Request(); $request->setMethod('GET'); $request->setUrl('/'); $response = $app->http->run($request); @@ -34,7 +36,7 @@ public function test_resident_memory() } // 不受访问频率限制 - $request = new \think\Request(); + $request = new Request(); $request->setMethod('POST'); $request->setUrl('/'); diff --git a/tests/RouteThrottleTest.php b/tests/RouteThrottleTest.php index 297440f..7f75009 100644 --- a/tests/RouteThrottleTest.php +++ b/tests/RouteThrottleTest.php @@ -11,17 +11,12 @@ */ class RouteThrottleTest extends Base { - public function __construct(?string $name = null, array $data = [], $dataName = '') + public function test_route_middleware_type() { - parent::__construct($name, $data, $dataName); + $this->middleware_type = 'route'; // 路由中间件 $config = $this->get_default_throttle_config(); $config['key'] = '__CONTROLLER__/__ACTION__/__IP__'; $this->set_throttle_config($config); - } - - public function test_route_middleware_type() - { - $this->middleware_type = 'route'; // 路由中间件 $allowCount1 = 0; $allowCount2 = 0; for ($i = 0; $i < 200; $i++) { @@ -41,6 +36,9 @@ public function test_route_middleware_type() public function test_global_middleware_type() { $this->middleware_type = 'global'; // 全局中间件 + $config = $this->get_default_throttle_config(); + $config['key'] = '__CONTROLLER__/__ACTION__/__IP__'; + $this->set_throttle_config($config); $allowCount1 = 0; $allowCount2 = 0; // 默认 100/m ,所以两个路由都是 50 次成功 diff --git a/tests/ThrottleDefaultConfigTest.php b/tests/ThrottleDefaultConfigTest.php index d5cf3a3..ebdca8d 100644 --- a/tests/ThrottleDefaultConfigTest.php +++ b/tests/ThrottleDefaultConfigTest.php @@ -10,14 +10,9 @@ */ class ThrottleDefaultConfigTest extends Base { - function __construct($name = null, array $data = [], $dataName = '') - { - parent::__construct($name, $data, $dataName); - $this->set_throttle_config($this->get_default_throttle_config()); - } - function test_visit_rate() { + $this->set_throttle_config($this->get_default_throttle_config()); // 默认的访问频率为 '100/m' $allowCount = 0; for ($i = 0; $i < 200; $i++) { @@ -31,6 +26,7 @@ function test_visit_rate() function test_unlimited_request_method() { + $this->set_throttle_config($this->get_default_throttle_config()); // 默认只限制了 ['GET', 'HEAD'] ,对 POST 不做限制 $allowCount = 0; for ($i = 0; $i < 200; $i++) { diff --git a/tests/ThrottleDriverTest.php b/tests/ThrottleDriverTest.php index 17afe6e..474d8a9 100644 --- a/tests/ThrottleDriverTest.php +++ b/tests/ThrottleDriverTest.php @@ -13,7 +13,8 @@ */ class ThrottleDriverTest extends Base { - function driver_run(string $derive_name) { + function driver_run(string $derive_name): int + { $config = $this->get_default_throttle_config(); $config['driver_name'] = $derive_name; $config['visit_rate'] = '60/m'; diff --git a/tests/VisitRateTest.php b/tests/VisitRateTest.php index 9f5e23a..d196155 100644 --- a/tests/VisitRateTest.php +++ b/tests/VisitRateTest.php @@ -7,12 +7,14 @@ namespace tests; use think\middleware\Throttle; +use think\middleware\throttle\CounterSlider; +use think\Request; class VisitRateTest extends Base { function is_visit_allow(string $uri): bool { - $request = new \think\Request(); + $request = new Request(); $request->setUrl($uri); $response = $this->get_response($request); return $response->getCode() == 200; @@ -23,7 +25,8 @@ function is_visit_allow(string $uri): bool */ function test_custom_visit_rate() { $config = $this->get_default_throttle_config(); - $config['key'] = function(Throttle $throttle, \think\Request $request) { + $config['key'] = function(Throttle $throttle, Request $request) { + $throttle->setDriverClass(CounterSlider::class); $path = $request->baseUrl(); if ($path === '/path1') { $throttle->setRate('10/m');