Merge pull request #8561 from appwrite/feat-benchmark-headers
Feat: Headers validator benchmark
This commit is contained in:
commit
7b8c1c2892
6 changed files with 1435 additions and 29 deletions
|
@ -13,7 +13,8 @@
|
|||
"scripts": {
|
||||
"test": "vendor/bin/phpunit",
|
||||
"lint": "vendor/bin/pint --test",
|
||||
"format": "vendor/bin/pint"
|
||||
"format": "vendor/bin/pint",
|
||||
"bench": "vendor/bin/phpbench run --report=benchmark"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
@ -86,7 +87,8 @@
|
|||
"phpunit/phpunit": "9.5.20",
|
||||
"swoole/ide-helper": "5.1.2",
|
||||
"textalk/websocket": "1.5.7",
|
||||
"laravel/pint": "^1.14"
|
||||
"laravel/pint": "^1.14",
|
||||
"phpbench/phpbench": "^1.2"
|
||||
},
|
||||
"provide": {
|
||||
"ext-phpiredis": "*"
|
||||
|
|
1318
composer.lock
generated
1318
composer.lock
generated
File diff suppressed because it is too large
Load diff
6
phpbench.json
Normal file
6
phpbench.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"$schema":"vendor/phpbench/phpbench/phpbench.schema.json",
|
||||
"runner.bootstrap": "vendor/autoload.php",
|
||||
"runner.path": "tests",
|
||||
"runner.file_pattern": "*Bench.php"
|
||||
}
|
|
@ -11,11 +11,8 @@ use Utopia\Validator;
|
|||
*/
|
||||
class Headers extends Validator
|
||||
{
|
||||
protected bool $allowEmpty;
|
||||
|
||||
public function __construct(bool $allowEmpty = true)
|
||||
public function __construct(protected bool $allowEmpty = true, protected int $maxKeys = 100, protected int $maxSize = 16384)
|
||||
{
|
||||
$this->allowEmpty = $allowEmpty;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -27,7 +24,7 @@ class Headers extends Validator
|
|||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Invalid header format. Header keys can only contain alphanumeric characters, underscores, and hyphens. Header keys cannot start with "x-appwrite-" prefix.';
|
||||
return 'Invalid headers: Alphanumeric characters or hyphens only, cannot start with "x-appwrite", maximum ' . $this->maxKeys . ' keys, and total size ' . $this->maxSize . '.';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,34 +44,41 @@ class Headers extends Validator
|
|||
return false;
|
||||
}
|
||||
|
||||
if (\is_array($value)) {
|
||||
foreach ($value as $key => $val) {
|
||||
$length = \strlen($key);
|
||||
// Reject non-string keys
|
||||
if (!\is_string($key) || $length === 0) {
|
||||
return false;
|
||||
}
|
||||
if(\count($value) > $this->maxKeys) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check first and last character
|
||||
if (!ctype_alnum($key[0]) || !ctype_alnum($key[$length - 1])) {
|
||||
return false;
|
||||
}
|
||||
$size = 0;
|
||||
foreach ($value as $key => $val) {
|
||||
$length = \strlen($key);
|
||||
// Reject non-string keys
|
||||
if (!\is_string($key) || $length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check middle characters
|
||||
for ($i = 1; $i < $length - 1; $i++) {
|
||||
if (!ctype_alnum($key[$i]) && $key[$i] !== '-') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$size += $length + \strlen($val);
|
||||
if($size >= $this->maxSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for x-appwrite- prefix
|
||||
if (str_starts_with($key, 'x-appwrite-')) {
|
||||
// Check first and last character
|
||||
if (!ctype_alnum($key[0]) || !ctype_alnum($key[$length - 1])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check middle characters
|
||||
for ($i = 1; $i < $length - 1; $i++) {
|
||||
if (!ctype_alnum($key[$i]) && $key[$i] !== '-') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
// Check for x-appwrite- prefix
|
||||
if (str_starts_with($key, 'x-appwrite-')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
60
tests/unit/Functions/Validator/HeadersBench.php
Normal file
60
tests/unit/Functions/Validator/HeadersBench.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit\Functions\Validator;
|
||||
|
||||
use Appwrite\Functions\Validator\Headers;
|
||||
use PhpBench\Attributes\AfterMethods;
|
||||
use PhpBench\Attributes\Assert;
|
||||
use PhpBench\Attributes\BeforeMethods;
|
||||
use PhpBench\Attributes\Iterations;
|
||||
use PhpBench\Attributes\ParamProviders;
|
||||
|
||||
final class HeadersBench
|
||||
{
|
||||
private Headers $validator;
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function prepare(): void
|
||||
{
|
||||
$this->validator = new Headers();
|
||||
}
|
||||
|
||||
public function providers(): iterable
|
||||
{
|
||||
yield 'empty' => [ 'value' => [] ];
|
||||
|
||||
$value = [];
|
||||
for($i = 0; $i < 10; $i++) {
|
||||
$value[bin2hex(random_bytes(8))] = bin2hex(random_bytes(8));
|
||||
}
|
||||
yield 'items_10-size_320' => [ 'value' => $value ];
|
||||
|
||||
$value = [];
|
||||
for($i = 0; $i < 100; $i++) {
|
||||
$value[bin2hex(random_bytes(8))] = bin2hex(random_bytes(8));
|
||||
}
|
||||
yield 'items_100-size_3200' => [ 'value' => $value ];
|
||||
|
||||
$value = [];
|
||||
for($i = 0; $i < 100; $i++) {
|
||||
$value[bin2hex(random_bytes(32))] = bin2hex(random_bytes(32));
|
||||
}
|
||||
yield 'items_100-size_12800' => [ 'value' => $value ];
|
||||
}
|
||||
|
||||
#[BeforeMethods('prepare')]
|
||||
#[AfterMethods('tearDown')]
|
||||
#[ParamProviders('providers')]
|
||||
#[Iterations(50)]
|
||||
#[Assert('mode(variant.time.avg) < 1 ms')]
|
||||
public function benchHeadersValidator(array $data): void
|
||||
{
|
||||
$assertion = $this->validator->isValid($data['value']);
|
||||
if(!$assertion) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -104,5 +104,23 @@ class HeadersTest extends TestCase
|
|||
|
||||
$headers = 'string';
|
||||
$this->assertFalse($this->object->isValid($headers));
|
||||
|
||||
$headers = [ ];
|
||||
$this->assertTrue($this->object->isValid($headers));
|
||||
|
||||
$headers = [];
|
||||
for($i = 0; $i < 100; $i++) {
|
||||
$headers['key-' . $i] = 'value_' . $i;
|
||||
}
|
||||
$this->assertTrue($this->object->isValid($headers));
|
||||
|
||||
$headers['key-oversized'] = bin2hex(random_bytes(100000));
|
||||
$this->assertFalse($this->object->isValid($headers));
|
||||
|
||||
unset($headers['key-oversized']);
|
||||
$this->assertTrue($this->object->isValid($headers));
|
||||
|
||||
$headers['key-101'] = 'value_101';
|
||||
$this->assertFalse($this->object->isValid($headers));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue