1
0
Fork 0
mirror of synced 2024-09-20 03:17:30 +12:00

Merge pull request #8561 from appwrite/feat-benchmark-headers

Feat: Headers validator benchmark
This commit is contained in:
Christy Jacob 2024-08-22 17:54:06 +04:00 committed by GitHub
commit 7b8c1c2892
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 1435 additions and 29 deletions

View file

@ -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

File diff suppressed because it is too large Load diff

6
phpbench.json Normal file
View 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"
}

View file

@ -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;
}
/**

View 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);
}
}
}

View file

@ -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));
}
}