From 9644b0c27acfd35dd32898169bf904aeece9c676 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:44:02 +0100 Subject: [PATCH 01/11] feat: compression --- composer.json | 3 +- composer.lock | 284 ++++++++------ src/App.php | 29 ++ src/Http/Response.php | 837 ++++++++++++++++++++++++++++++++++++++++++ src/Response.php | 98 +++-- 5 files changed, 1110 insertions(+), 141 deletions(-) create mode 100644 src/Http/Response.php diff --git a/composer.json b/composer.json index fe84d0d0..d856b786 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,8 @@ "bench": "vendor/bin/phpbench run --report=benchmark" }, "require": { - "php": ">=8.0" + "php": ">=8.0", + "utopia-php/compression": "0.1.*" }, "require-dev": { "phpunit/phpunit": "^9.5.25", diff --git a/composer.lock b/composer.lock index ecf6f2c8..af10d95d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,21 +4,68 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d41ea47cadd897ad4053410229874733", - "packages": [], + "content-hash": "29435f73c84b4ebe3f18aa8bcd7e008e", + "packages": [ + { + "name": "utopia-php/compression", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/compression.git", + "reference": "8c6d9bcb5b0972faa27e5bf70923c20403aaf25c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/compression/zipball/8c6d9bcb5b0972faa27e5bf70923c20403aaf25c", + "reference": "8c6d9bcb5b0972faa27e5bf70923c20403aaf25c", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Compression\\": "src/Compression" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple Compression library to handle file compression", + "keywords": [ + "compression", + "framework", + "php", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/compression/issues", + "source": "https://github.com/utopia-php/compression/tree/0.1.0" + }, + "time": "2024-10-23T10:17:46+00:00" + } + ], "packages-dev": [ { "name": "doctrine/annotations", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" + "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", - "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/901c2ee5d26eb64ff43c47976e114bf00843acf7", + "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7", "shasum": "" }, "require": { @@ -30,10 +77,10 @@ "require-dev": { "doctrine/cache": "^2.0", "doctrine/coding-standard": "^10", - "phpstan/phpstan": "^1.8.0", + "phpstan/phpstan": "^1.10.28", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "symfony/cache": "^5.4 || ^6", - "vimeo/psalm": "^4.10" + "symfony/cache": "^5.4 || ^6.4 || ^7", + "vimeo/psalm": "^4.30 || ^5.14" }, "suggest": { "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" @@ -79,9 +126,9 @@ ], "support": { "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/2.0.1" + "source": "https://github.com/doctrine/annotations/tree/2.0.2" }, - "time": "2023-02-02T22:02:53+00:00" + "time": "2024-09-05T10:17:24+00:00" }, { "name": "doctrine/instantiator", @@ -232,16 +279,16 @@ }, { "name": "laravel/pint", - "version": "v1.17.2", + "version": "v1.18.1", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "e8a88130a25e3f9d4d5785e6a1afca98268ab110" + "reference": "35c00c05ec43e6b46d295efc0f4386ceb30d50d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/e8a88130a25e3f9d4d5785e6a1afca98268ab110", - "reference": "e8a88130a25e3f9d4d5785e6a1afca98268ab110", + "url": "https://api.github.com/repos/laravel/pint/zipball/35c00c05ec43e6b46d295efc0f4386ceb30d50d9", + "reference": "35c00c05ec43e6b46d295efc0f4386ceb30d50d9", "shasum": "" }, "require": { @@ -252,13 +299,13 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.61.1", - "illuminate/view": "^10.48.18", + "friendsofphp/php-cs-fixer": "^3.64.0", + "illuminate/view": "^10.48.20", "larastan/larastan": "^2.9.8", "laravel-zero/framework": "^10.4.0", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^2.35.0" + "pestphp/pest": "^2.35.1" }, "bin": [ "builds/pint" @@ -294,7 +341,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-08-06T15:11:54+00:00" + "time": "2024-09-24T17:22:50+00:00" }, { "name": "myclabs/deep-copy", @@ -358,16 +405,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.1.0", + "version": "v5.3.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", - "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", "shasum": "" }, "require": { @@ -410,9 +457,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" }, - "time": "2024-07-01T20:03:41+00:00" + "time": "2024-10-08T18:51:32+00:00" }, { "name": "phar-io/manifest", @@ -632,6 +679,7 @@ "issues": "https://github.com/phpbench/dom/issues", "source": "https://github.com/phpbench/dom/tree/0.3.3" }, + "abandoned": true, "time": "2023-03-06T23:46:57+00:00" }, { @@ -735,16 +783,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.11.10", + "version": "1.12.7", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "640410b32995914bde3eed26fa89552f9c2c082f" + "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/640410b32995914bde3eed26fa89552f9c2c082f", - "reference": "640410b32995914bde3eed26fa89552f9c2c082f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", + "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", "shasum": "" }, "require": { @@ -789,39 +837,39 @@ "type": "github" } ], - "time": "2024-08-08T09:02:50+00:00" + "time": "2024-10-18T11:12:07+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.31", + "version": "9.2.32", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.18 || ^5.0", + "nikic/php-parser": "^4.19.1 || ^5.1.0", "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-text-template": "^2.0.4", + "sebastian/code-unit-reverse-lookup": "^2.0.3", + "sebastian/complexity": "^2.0.3", + "sebastian/environment": "^5.1.5", + "sebastian/lines-of-code": "^1.0.4", + "sebastian/version": "^3.0.2", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.6" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -830,7 +878,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "9.2.x-dev" } }, "autoload": { @@ -859,7 +907,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" }, "funding": [ { @@ -867,7 +915,7 @@ "type": "github" } ], - "time": "2024-03-02T06:37:42+00:00" + "time": "2024-08-22T04:23:01+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1112,16 +1160,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.20", + "version": "9.6.21", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "49d7820565836236411f5dc002d16dd689cde42f" + "reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/49d7820565836236411f5dc002d16dd689cde42f", - "reference": "49d7820565836236411f5dc002d16dd689cde42f", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa", + "reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa", "shasum": "" }, "require": { @@ -1136,7 +1184,7 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.31", + "phpunit/php-code-coverage": "^9.2.32", "phpunit/php-file-iterator": "^3.0.6", "phpunit/php-invoker": "^3.1.1", "phpunit/php-text-template": "^2.0.4", @@ -1195,7 +1243,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.20" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.21" }, "funding": [ { @@ -1211,7 +1259,7 @@ "type": "tidelift" } ], - "time": "2024-07-10T11:45:39+00:00" + "time": "2024-09-19T10:50:18+00:00" }, { "name": "psr/cache", @@ -1317,16 +1365,16 @@ }, { "name": "psr/log", - "version": "3.0.0", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { @@ -1361,9 +1409,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2021-07-14T16:46:02+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { "name": "sebastian/cli-parser", @@ -2394,16 +2442,16 @@ }, { "name": "symfony/console", - "version": "v7.1.3", + "version": "v7.1.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9" + "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9", - "reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9", + "url": "https://api.github.com/repos/symfony/console/zipball/0fa539d12b3ccf068a722bbbffa07ca7079af9ee", + "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee", "shasum": "" }, "require": { @@ -2467,7 +2515,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.3" + "source": "https://github.com/symfony/console/tree/v7.1.5" }, "funding": [ { @@ -2483,7 +2531,7 @@ "type": "tidelift" } ], - "time": "2024-07-26T12:41:01+00:00" + "time": "2024-09-20T08:28:38+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2554,16 +2602,16 @@ }, { "name": "symfony/filesystem", - "version": "v7.1.2", + "version": "v7.1.5", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "92a91985250c251de9b947a14bb2c9390b1a562c" + "reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/92a91985250c251de9b947a14bb2c9390b1a562c", - "reference": "92a91985250c251de9b947a14bb2c9390b1a562c", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/61fe0566189bf32e8cfee78335d8776f64a66f5a", + "reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a", "shasum": "" }, "require": { @@ -2600,7 +2648,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.1.2" + "source": "https://github.com/symfony/filesystem/tree/v7.1.5" }, "funding": [ { @@ -2616,20 +2664,20 @@ "type": "tidelift" } ], - "time": "2024-06-28T10:03:55+00:00" + "time": "2024-09-17T09:16:35+00:00" }, { "name": "symfony/finder", - "version": "v7.1.3", + "version": "v7.1.4", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "717c6329886f32dc65e27461f80f2a465412fdca" + "reference": "d95bbf319f7d052082fb7af147e0f835a695e823" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/717c6329886f32dc65e27461f80f2a465412fdca", - "reference": "717c6329886f32dc65e27461f80f2a465412fdca", + "url": "https://api.github.com/repos/symfony/finder/zipball/d95bbf319f7d052082fb7af147e0f835a695e823", + "reference": "d95bbf319f7d052082fb7af147e0f835a695e823", "shasum": "" }, "require": { @@ -2664,7 +2712,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.1.3" + "source": "https://github.com/symfony/finder/tree/v7.1.4" }, "funding": [ { @@ -2680,7 +2728,7 @@ "type": "tidelift" } ], - "time": "2024-07-24T07:08:44+00:00" + "time": "2024-08-13T14:28:19+00:00" }, { "name": "symfony/options-resolver", @@ -2751,20 +2799,20 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -2810,7 +2858,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { @@ -2826,24 +2874,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -2888,7 +2936,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" }, "funding": [ { @@ -2904,24 +2952,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -2969,7 +3017,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" }, "funding": [ { @@ -2985,24 +3033,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -3049,7 +3097,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -3065,20 +3113,20 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/process", - "version": "v7.1.3", + "version": "v7.1.5", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca" + "reference": "5c03ee6369281177f07f7c68252a280beccba847" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/7f2f542c668ad6c313dc4a5e9c3321f733197eca", - "reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca", + "url": "https://api.github.com/repos/symfony/process/zipball/5c03ee6369281177f07f7c68252a280beccba847", + "reference": "5c03ee6369281177f07f7c68252a280beccba847", "shasum": "" }, "require": { @@ -3110,7 +3158,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.1.3" + "source": "https://github.com/symfony/process/tree/v7.1.5" }, "funding": [ { @@ -3126,7 +3174,7 @@ "type": "tidelift" } ], - "time": "2024-07-26T12:44:47+00:00" + "time": "2024-09-19T21:48:23+00:00" }, { "name": "symfony/service-contracts", @@ -3213,16 +3261,16 @@ }, { "name": "symfony/string", - "version": "v7.1.3", + "version": "v7.1.5", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "ea272a882be7f20cad58d5d78c215001617b7f07" + "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ea272a882be7f20cad58d5d78c215001617b7f07", - "reference": "ea272a882be7f20cad58d5d78c215001617b7f07", + "url": "https://api.github.com/repos/symfony/string/zipball/d66f9c343fa894ec2037cc928381df90a7ad4306", + "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306", "shasum": "" }, "require": { @@ -3280,7 +3328,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.3" + "source": "https://github.com/symfony/string/tree/v7.1.5" }, "funding": [ { @@ -3296,7 +3344,7 @@ "type": "tidelift" } ], - "time": "2024-07-22T10:25:37+00:00" + "time": "2024-09-20T08:28:38+00:00" }, { "name": "theseer/tokenizer", diff --git a/src/App.php b/src/App.php index 1458838c..0dd463f7 100755 --- a/src/App.php +++ b/src/App.php @@ -4,6 +4,8 @@ class App { + public const COMPRESSION_MIN_SIZE_DEFAULT = 1024; + /** * Request method constants */ @@ -102,6 +104,12 @@ class App */ protected static ?Route $wildcardRoute = null; + /** + * Compression + */ + protected bool $compression = false; + protected int $compressionMinSize = App::COMPRESSION_MIN_SIZE_DEFAULT; + /** * App * @@ -112,6 +120,22 @@ public function __construct(string $timezone) \date_default_timezone_set($timezone); } + /** + * Set Compression + */ + public function setCompression(bool $compression) + { + $this->compression = $compression; + } + + /** + * Set minimum compression size + */ + public function setCompressionMinSize(int $compressionMinSize) + { + $this->compressionMinSize = $compressionMinSize; + } + /** * GET * @@ -642,6 +666,11 @@ protected function getArguments(Hook $hook, array $values, array $requestParams) */ public function run(Request $request, Response $response): static { + if ($this->compression) { + $response->setAcceptEncoding($request->getHeader('accept-encoding') ?? ''); + $response->setCompressionMinSize($this->compressionMinSize); + } + $this->resources['request'] = $request; $this->resources['response'] = $response; diff --git a/src/Http/Response.php b/src/Http/Response.php new file mode 100644 index 00000000..0f2cab39 --- /dev/null +++ b/src/Http/Response.php @@ -0,0 +1,837 @@ + 'Continue', + self::STATUS_CODE_SWITCHING_PROTOCOLS => 'Switching Protocols', + self::STATUS_CODE_OK => 'OK', + self::STATUS_CODE_CREATED => 'Created', + self::STATUS_CODE_ACCEPTED => 'Accepted', + self::STATUS_CODE_NON_AUTHORITATIVE_INFORMATION => 'Non-Authoritative Information', + self::STATUS_CODE_NOCONTENT => 'No Content', + self::STATUS_CODE_RESETCONTENT => 'Reset Content', + self::STATUS_CODE_PARTIALCONTENT => 'Partial Content', + self::STATUS_CODE_MULTIPLE_CHOICES => 'Multiple Choices', + self::STATUS_CODE_MOVED_PERMANENTLY => 'Moved Permanently', + self::STATUS_CODE_FOUND => 'Found', + self::STATUS_CODE_SEE_OTHER => 'See Other', + self::STATUS_CODE_NOT_MODIFIED => 'Not Modified', + self::STATUS_CODE_USE_PROXY => 'Use Proxy', + self::STATUS_CODE_UNUSED => '(Unused)', + self::STATUS_CODE_TEMPORARY_REDIRECT => 'Temporary Redirect', + self::STATUS_CODE_BAD_REQUEST => 'Bad Request', + self::STATUS_CODE_UNAUTHORIZED => 'Unauthorized', + self::STATUS_CODE_PAYMENT_REQUIRED => 'Payment Required', + self::STATUS_CODE_FORBIDDEN => 'Forbidden', + self::STATUS_CODE_NOT_FOUND => 'Not Found', + self::STATUS_CODE_METHOD_NOT_ALLOWED => 'Method Not Allowed', + self::STATUS_CODE_NOT_ACCEPTABLE => 'Not Acceptable', + self::STATUS_CODE_PROXY_AUTHENTICATION_REQUIRED => 'Proxy Authentication Required', + self::STATUS_CODE_REQUEST_TIMEOUT => 'Request Timeout', + self::STATUS_CODE_CONFLICT => 'Conflict', + self::STATUS_CODE_GONE => 'Gone', + self::STATUS_CODE_LENGTH_REQUIRED => 'Length Required', + self::STATUS_CODE_PRECONDITION_FAILED => 'Precondition Failed', + self::STATUS_CODE_REQUEST_ENTITY_TOO_LARGE => 'Request Entity Too Large', + self::STATUS_CODE_REQUEST_URI_TOO_LONG => 'Request-URI Too Long', + self::STATUS_CODE_UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type', + self::STATUS_CODE_REQUESTED_RANGE_NOT_SATISFIABLE => 'Requested Range Not Satisfiable', + self::STATUS_CODE_EXPECTATION_FAILED => 'Expectation Failed', + self::STATUS_CODE_TOO_EARLY => 'Too Early', + self::STATUS_CODE_TOO_MANY_REQUESTS => 'Too Many Requests', + self::STATUS_CODE_UNAVAILABLE_FOR_LEGAL_REASONS => 'Unavailable For Legal Reasons', + self::STATUS_CODE_INTERNAL_SERVER_ERROR => 'Internal Server Error', + self::STATUS_CODE_NOT_IMPLEMENTED => 'Not Implemented', + self::STATUS_CODE_BAD_GATEWAY => 'Bad Gateway', + self::STATUS_CODE_SERVICE_UNAVAILABLE => 'Service Unavailable', + self::STATUS_CODE_GATEWAY_TIMEOUT => 'Gateway Timeout', + self::STATUS_CODE_HTTP_VERSION_NOT_SUPPORTED => 'HTTP Version Not Supported', + ]; + + /** + * Mime Types with compression support + * + * @var array + */ + protected $compressed = [ + 'text/plain' => true, + 'text/css' => true, + 'text/javascript' => true, + 'application/javascript' => true, + 'text/html' => true, + 'text/html; charset=UTF-8' => true, + 'application/json' => true, + 'application/json; charset=UTF-8' => true, + 'image/svg+xml' => true, + 'application/xml+rss' => true, + ]; + + public const COOKIE_SAMESITE_NONE = 'None'; + + public const COOKIE_SAMESITE_STRICT = 'Strict'; + + public const COOKIE_SAMESITE_LAX = 'Lax'; + + public const CHUNK_SIZE = 2000000; //2mb + + /** + * @var int + */ + protected int $statusCode = self::STATUS_CODE_OK; + + /** + * @var string + */ + protected string $contentType = ''; + + /** + * @var bool + */ + protected bool $disablePayload = false; + + /** + * @var bool + */ + protected bool $sent = false; + + /** + * @var array + */ + protected array $headers = []; + + /** + * @var array + */ + protected array $cookies = []; + + /** + * @var float + */ + protected float $startTime = 0; + + /** + * @var int + */ + protected int $size = 0; + + /** + * @var string + */ + protected string $acceptEncoding = ''; + + /** + * @var int + */ + protected int $compressionMinSize = Http::COMPRESSION_MIN_SIZE_DEFAULT; + + /** + * Response constructor. + * + * @param float $time response start time + */ + public function __construct(float $time = 0) + { + $this->startTime = (!empty($time)) ? $time : \microtime(true); + } + + /** + * Set content type + * + * Set HTTP content type header. + * + * @param string $type + * @param string $charset + */ + public function setContentType(string $type, string $charset = ''): static + { + $this->contentType = $type.((!empty($charset) ? '; charset='.$charset : '')); + + return $this; + } + + /** + * Set accept encoding + * + * Set HTTP accept encoding header. + * + * @param string $acceptEncoding + */ + public function setAcceptEncoding(string $acceptEncoding): static + { + $this->acceptEncoding = $acceptEncoding; + return $this; + } + + /** + * Set min compression size + * + * Set minimum size for compression to be applied in bytes. + * + * @param int $compressionMinSize + */ + public function setCompressionMinSize(int $compressionMinSize): static + { + $this->compressionMinSize = $compressionMinSize; + return $this; + } + + /** + * Get content type + * + * Get HTTP content type header. + * + * @return string + */ + public function getContentType(): string + { + return $this->contentType; + } + + /** + * Get if response was already sent + * + * @return bool + */ + public function isSent(): bool + { + return $this->sent; + } + + /** + * Set status code + * + * Set HTTP response status code between available options. if status code is unknown an exception will be thrown + * + * @param int $code + * + * @throws Exception + */ + public function setStatusCode(int $code = 200): static + { + if (!\array_key_exists($code, $this->statusCodes)) { + throw new Exception('Unknown HTTP status code'); + } + + $this->statusCode = $code; + + return $this; + } + + /** + * Get status code + * + * Get HTTP response status code + * + * @return int + **/ + public function getStatusCode(): int + { + return $this->statusCode; + } + + /** + * Get Response Size + * + * Return output response size in bytes + * + * @return int + */ + public function getSize(): int + { + return $this->size; + } + + /** + * Don't allow payload on response output + */ + public function disablePayload(): static + { + $this->disablePayload = true; + + return $this; + } + + /** + * Allow payload on response output + */ + public function enablePayload(): static + { + $this->disablePayload = false; + + return $this; + } + + /** + * Add header + * + * Add an HTTP response header + * + * @param string $key + * @param string $value + */ + public function addHeader(string $key, string $value): static + { + $this->headers[$key] = $value; + + return $this; + } + + /** + * Remove header + * + * Remove HTTP response header + * + * @param string $key + */ + public function removeHeader(string $key): static + { + if (isset($this->headers[$key])) { + unset($this->headers[$key]); + } + + return $this; + } + + /** + * Get Headers + * + * Return array of all response headers + * + * @return array + */ + public function getHeaders(): array + { + return $this->headers; + } + + /** + * Add cookie + * + * Add an HTTP cookie to response header + * + * @param string $name + * @param string $value + * @param int $expire + * @param string $path + * @param string $domain + * @param bool $secure + * @param bool $httponly + * @param string $sameSite + */ + public function addCookie(string $name, string $value = null, int $expire = null, string $path = null, string $domain = null, bool $secure = null, bool $httponly = null, string $sameSite = null): static + { + $name = strtolower($name); + + $this->cookies[] = [ + 'name' => $name, + 'value' => $value, + 'expire' => $expire, + 'path' => $path, + 'domain' => $domain, + 'secure' => $secure, + 'httponly' => $httponly, + 'samesite' => $sameSite, + ]; + + return $this; + } + + /** + * Remove cookie + * + * Remove HTTP response cookie + * + * @param string $name + */ + public function removeCookie(string $name): static + { + $this->cookies = array_filter($this->cookies, function ($cookie) use ($name) { + return $cookie['name'] !== $name; + }); + + return $this; + } + + /** + * Get Cookies + * + * Return array of all response cookies + * + * @return array + */ + public function getCookies(): array + { + return $this->cookies; + } + + /** + * Output response + * + * Generate HTTP response output including the response header (+cookies) and body and prints them. + * + * @param string $body + * @return void + */ + public function send(string $body = ''): void + { + if ($this->sent) { + return; + } + + $this->sent = true; + + $serverHeader = $this->headers['Server'] ?? 'Utopia/Http'; + $this->addHeader('Server', $serverHeader); + $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime)); + + $this->appendCookies()->appendHeaders(); + + // Send response + if ($this->disablePayload) { + $this->end(); + return; + } + + // Compress body + if ( + !empty($this->acceptEncoding) && + isset($this->compressed[$this->contentType]) && + strlen($body) > $this->compressionMinSize + ) { + $algorithm = Compression::fromAcceptEncoding($this->acceptEncoding, [ + Compression::BROTLI, + Compression::GZIP, + Compression::DEFLATE, + ]); + + if ($algorithm) { + $body = $algorithm->compress($body); + $this->addHeader('Content-Encoding', $algorithm->getContentEncoding()); + $this->addHeader('Vary', 'Accept-Encoding'); + } + } + + $headerSize = strlen(implode("\n", $this->headers)); + $bodyLength = strlen($body); + $this->size += $headerSize + $bodyLength; + + if ($bodyLength <= self::CHUNK_SIZE) { + $this->end($body); + } else { + $chunks = str_split($body, self::CHUNK_SIZE); + foreach ($chunks as $chunk) { + $this->write($chunk); + } + $this->end(); + } + + $this->disablePayload(); + } + + /** + * Write + * + * Send output + * + * @param string $content + * @return bool False if write cannot complete, such as request ended by client + */ + abstract public function write(string $content): bool; + + /** + * End + * + * Send optional content and end + * + * @param string $content + * @return void + */ + abstract public function end(string $content = ''): void; + + /** + * Output response + * + * Generate HTTP response output including the response header (+cookies) and body and prints them. + * + * @param string $body + * @param bool $end + * + * @return void + */ + public function chunk(string $body = '', bool $end = false): void + { + if ($this->sent) { + return; + } + + if ($end) { + $this->sent = true; + } + + $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime)); + + $this + ->appendCookies() + ->appendHeaders(); + + if (!$this->disablePayload) { + $this->write($body); + if ($end) { + $this->disablePayload(); + $this->end(); + } + } else { + $this->end(); + } + } + + /** + * Append headers + * + * Iterating over response headers to generate them using native PHP header function. + * This method is also responsible for generating the response and content type headers. + */ + protected function appendHeaders(): static + { + // Send status code header + $this->sendStatus($this->statusCode, $this->statusCodes[$this->statusCode] ?? 'Unknown HTTP status code'); + + // Send content type header + if (!empty($this->contentType)) { + $this->addHeader('Content-Type', $this->contentType); + } + + // Set application headers + foreach ($this->headers as $key => $value) { + $this->sendHeader($key, $value); + } + + return $this; + } + + /** + * Send Status Code + * + * @param int $statusCode + * @param string $reason + * @return void + */ + abstract protected function sendStatus(int $statusCode, string $reason): void; + + /** + * Send Header + * + * Output Header + * + * @param string $key + * @param string $value + * @return void + */ + abstract public function sendHeader(string $key, string $value): void; + + /** + * Send Cookie + * + * Output Cookie + * + * @param string $name + * @param string $value + * @param array $options + * @return void + */ + abstract protected function sendCookie(string $name, string $value, array $options): void; + + /** + * Append cookies + * + * Iterating over response cookies to generate them using native PHP cookie function. + */ + protected function appendCookies(): static + { + foreach ($this->cookies as $cookie) { + $this->sendCookie($cookie['name'], $cookie['value'], [ + 'expire' => $cookie['expire'], + 'path' => $cookie['path'], + 'domain' => $cookie['domain'], + 'secure' => $cookie['secure'], + 'httponly' => $cookie['httponly'], + 'samesite' => $cookie['samesite'], + ]); + } + + return $this; + } + + /** + * Redirect + * + * This helper is for sending a 30* HTTP response. + * After setting relevant HTTP headers for redirect response this helper stop application native flow what means the shutdown method will not be executed + * + * NOTICE: it seems webkit based browsers have problems redirecting link with 300 status codes. + * + * @see https://code.google.com/p/chromium/issues/detail?id=75540 + * @see https://bugs.webkit.org/show_bug.cgi?id=47425 + * + * @param string $url complete absolute URI for redirection as required by the internet standard RFC 2616 (HTTP 1.1) + * @param int $statusCode valid HTTP status code + * @return void + * + * @throws Exception + * + * @see http://tools.ietf.org/html/rfc2616 + */ + public function redirect(string $url, int $statusCode = 301): void + { + if (300 == $statusCode) { + \trigger_error('It seems webkit based browsers have problems redirecting link with 300 status codes!', E_USER_NOTICE); + } + + $this + ->addHeader('Location', $url) + ->setStatusCode($statusCode) + ->send(''); + } + + /** + * HTML + * + * This helper is for sending an HTML HTTP response and sets relevant content type header ('text/html'). + * + * @see http://en.wikipedia.org/wiki/JSON + * + * @param string $data + * @return void + */ + public function html(string $data): void + { + $this + ->setContentType(self::CONTENT_TYPE_HTML, self::CHARSET_UTF8) + ->send($data); + } + + /** + * Text + * + * This helper is for sending plain text HTTP response and sets relevant content type header ('text/plain'). + * + * @see http://en.wikipedia.org/wiki/JSON + * + * @param string $data + * @return void + */ + public function text(string $data): void + { + $this + ->setContentType(self::CONTENT_TYPE_TEXT, self::CHARSET_UTF8) + ->send($data); + } + + /** + * JSON + * + * This helper is for sending JSON HTTP response. + * It sets relevant content type header ('application/json') and convert a PHP array ($data) to valid JSON using native json_encode + * + * @see http://en.wikipedia.org/wiki/JSON + * + * @param mixed $data + * @return void + */ + public function json($data): void + { + if (!is_array($data) && !$data instanceof \stdClass) { + throw new \Exception('Invalid JSON input var'); + } + + $this + ->setContentType(Response::CONTENT_TYPE_JSON, self::CHARSET_UTF8) + ->send(\json_encode($data, JSON_UNESCAPED_UNICODE)); + } + + /** + * JSON with padding + * + * This helper is for sending JSONP HTTP response. + * It sets relevant content type header ('text/javascript') and convert a PHP array ($data) to valid JSON using native json_encode + * + * @see http://en.wikipedia.org/wiki/JSONP + * + * @param string $callback + * @param array $data + * @return void + */ + public function jsonp(string $callback, array $data): void + { + $this + ->setContentType(self::CONTENT_TYPE_JAVASCRIPT, self::CHARSET_UTF8) + ->send('parent.'.$callback.'('.\json_encode($data).');'); + } + + /** + * Iframe + * + * This helper is for sending iframe HTTP response. + * It sets relevant content type header ('text/html') and convert a PHP array ($data) to valid JSON using native json_encode + * + * @param string $callback + * @param array $data + * @return void + */ + public function iframe(string $callback, array $data): void + { + $this + ->setContentType(self::CONTENT_TYPE_HTML, self::CHARSET_UTF8) + ->send(''); + } + + /** + * No Content + * + * This helper is for sending no content HTTP response. + * + * The server has successfully fulfilled the request + * and that there is no additional content to send in the response payload body. + * + * @return void + */ + public function noContent(): void + { + $this + ->setStatusCode(self::STATUS_CODE_NOCONTENT) + ->send(''); + } +} diff --git a/src/Response.php b/src/Response.php index 8d19f5a8..b39da39c 100755 --- a/src/Response.php +++ b/src/Response.php @@ -2,6 +2,8 @@ namespace Utopia; +use Utopia\Compression\Compression; + class Response { /** @@ -245,6 +247,16 @@ class Response */ protected int $size = 0; + /** + * @var string + */ + protected string $acceptEncoding = ''; + + /** + * @var int + */ + protected int $compressionMinSize = App::COMPRESSION_MIN_SIZE_DEFAULT; + /** * Response constructor. * @@ -270,6 +282,32 @@ public function setContentType(string $type, string $charset = ''): static return $this; } + /** + * Set accept encoding + * + * Set HTTP accept encoding header. + * + * @param string $acceptEncoding + */ + public function setAcceptEncoding(string $acceptEncoding): static + { + $this->acceptEncoding = $acceptEncoding; + return $this; + } + + /** + * Set min compression size + * + * Set minimum size for compression to be applied in bytes. + * + * @param int $compressionMinSize + */ + public function setCompressionMinSize(int $compressionMinSize): static + { + $this->compressionMinSize = $compressionMinSize; + return $this; + } + /** * Get content type * @@ -473,36 +511,52 @@ public function send(string $body = ''): void return; } - $this->sent = true; - - $this->addHeader('X-Debug-Speed', (string) (\microtime(true) - $this->startTime)); - - $this - ->appendCookies() - ->appendHeaders(); - - if (!$this->disablePayload) { - $length = strlen($body); + $serverHeader = $this->headers['Server'] ?? 'Utopia/Http'; + $this->addHeader('Server', $serverHeader); + $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime)); - $this->size = $this->size + strlen(implode("\n", $this->headers)) + $length; + $this->appendCookies()->appendHeaders(); + + // Send response + if ($this->disablePayload) { + $this->end(); + return; + } - if (array_key_exists( - $this->contentType, - $this->compressed - ) && ($length <= self::CHUNK_SIZE)) { // Dont compress with GZIP / Brotli if header is not listed and size is bigger than 2mb - $this->end($body); - } else { - for ($i = 0; $i < ceil($length / self::CHUNK_SIZE); $i++) { - $this->write(substr($body, ($i * self::CHUNK_SIZE), min(self::CHUNK_SIZE, $length - ($i * self::CHUNK_SIZE)))); - } + // Compress body + if ( + !empty($this->acceptEncoding) && + isset($this->compressed[$this->contentType]) && + strlen($body) > $this->compressionMinSize + ) { + $algorithm = Compression::fromAcceptEncoding($this->acceptEncoding, [ + Compression::BROTLI, + Compression::GZIP, + Compression::DEFLATE, + ]); - $this->end(); + if ($algorithm) { + $body = $algorithm->compress($body); + $this->addHeader('Content-Encoding', $algorithm->getContentEncoding()); + $this->addHeader('Vary', 'Accept-Encoding'); } + } + + $headerSize = strlen(implode("\n", $this->headers)); + $bodyLength = strlen($body); + $this->size += $headerSize + $bodyLength; - $this->disablePayload(); + if ($bodyLength <= self::CHUNK_SIZE) { + $this->end($body); } else { + $chunks = str_split($body, self::CHUNK_SIZE); + foreach ($chunks as $chunk) { + $this->write($chunk); + } $this->end(); } + + $this->disablePayload(); } /** From eaebc955de32de6458aabcd37246e0638df26191 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:49:11 +0100 Subject: [PATCH 02/11] test: enable compression --- tests/e2e/server.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/e2e/server.php b/tests/e2e/server.php index cef864ee..caa84ebe 100644 --- a/tests/e2e/server.php +++ b/tests/e2e/server.php @@ -50,4 +50,5 @@ $response = new Response(); $app = new App('UTC'); +$app->setCompression(true); $app->run($request, $response); From f7dfee210fc08dc0349adfe0facd9f6fb60728ce Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:54:53 +0100 Subject: [PATCH 03/11] fix: lint --- src/App.php | 2 +- src/Response.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/App.php b/src/App.php index 0dd463f7..00bd44a2 100755 --- a/src/App.php +++ b/src/App.php @@ -667,7 +667,7 @@ protected function getArguments(Hook $hook, array $values, array $requestParams) public function run(Request $request, Response $response): static { if ($this->compression) { - $response->setAcceptEncoding($request->getHeader('accept-encoding') ?? ''); + $response->setAcceptEncoding($request->getHeader('accept-encoding', '')); $response->setCompressionMinSize($this->compressionMinSize); } diff --git a/src/Response.php b/src/Response.php index b39da39c..76bf02b4 100755 --- a/src/Response.php +++ b/src/Response.php @@ -295,7 +295,7 @@ public function setAcceptEncoding(string $acceptEncoding): static return $this; } - /** + /** * Set min compression size * * Set minimum size for compression to be applied in bytes. @@ -516,7 +516,7 @@ public function send(string $body = ''): void $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime)); $this->appendCookies()->appendHeaders(); - + // Send response if ($this->disablePayload) { $this->end(); From f4f83a364bce9457155fdb9ce4b525138b42b312 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 29 Oct 2024 12:53:12 +0000 Subject: [PATCH 04/11] chore: fmt --- composer.lock | 76 ++++++++++++++++++------------------- src/Validator/ArrayList.php | 2 +- tests/ResponseTest.php | 2 +- tests/ViewTest.php | 2 +- 4 files changed, 41 insertions(+), 41 deletions(-) diff --git a/composer.lock b/composer.lock index af10d95d..4677e800 100644 --- a/composer.lock +++ b/composer.lock @@ -2442,16 +2442,16 @@ }, { "name": "symfony/console", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee" + "reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0fa539d12b3ccf068a722bbbffa07ca7079af9ee", - "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee", + "url": "https://api.github.com/repos/symfony/console/zipball/bb5192af6edc797cbab5c8e8ecfea2fe5f421e57", + "reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57", "shasum": "" }, "require": { @@ -2515,7 +2515,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.5" + "source": "https://github.com/symfony/console/tree/v7.1.6" }, "funding": [ { @@ -2531,7 +2531,7 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:28:38+00:00" + "time": "2024-10-09T08:46:59+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2602,16 +2602,16 @@ }, { "name": "symfony/filesystem", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a" + "reference": "c835867b3c62bb05c7fe3d637c871c7ae52024d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/61fe0566189bf32e8cfee78335d8776f64a66f5a", - "reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/c835867b3c62bb05c7fe3d637c871c7ae52024d4", + "reference": "c835867b3c62bb05c7fe3d637c871c7ae52024d4", "shasum": "" }, "require": { @@ -2648,7 +2648,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.1.5" + "source": "https://github.com/symfony/filesystem/tree/v7.1.6" }, "funding": [ { @@ -2664,20 +2664,20 @@ "type": "tidelift" } ], - "time": "2024-09-17T09:16:35+00:00" + "time": "2024-10-25T15:11:02+00:00" }, { "name": "symfony/finder", - "version": "v7.1.4", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "d95bbf319f7d052082fb7af147e0f835a695e823" + "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/d95bbf319f7d052082fb7af147e0f835a695e823", - "reference": "d95bbf319f7d052082fb7af147e0f835a695e823", + "url": "https://api.github.com/repos/symfony/finder/zipball/2cb89664897be33f78c65d3d2845954c8d7a43b8", + "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8", "shasum": "" }, "require": { @@ -2712,7 +2712,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.1.4" + "source": "https://github.com/symfony/finder/tree/v7.1.6" }, "funding": [ { @@ -2728,20 +2728,20 @@ "type": "tidelift" } ], - "time": "2024-08-13T14:28:19+00:00" + "time": "2024-10-01T08:31:23+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.1.1", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55" + "reference": "85e95eeede2d41cd146146e98c9c81d9214cae85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/47aa818121ed3950acd2b58d1d37d08a94f9bf55", - "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/85e95eeede2d41cd146146e98c9c81d9214cae85", + "reference": "85e95eeede2d41cd146146e98c9c81d9214cae85", "shasum": "" }, "require": { @@ -2779,7 +2779,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.1.1" + "source": "https://github.com/symfony/options-resolver/tree/v7.1.6" }, "funding": [ { @@ -2795,7 +2795,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3117,16 +3117,16 @@ }, { "name": "symfony/process", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "5c03ee6369281177f07f7c68252a280beccba847" + "reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/5c03ee6369281177f07f7c68252a280beccba847", - "reference": "5c03ee6369281177f07f7c68252a280beccba847", + "url": "https://api.github.com/repos/symfony/process/zipball/6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e", + "reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e", "shasum": "" }, "require": { @@ -3158,7 +3158,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.1.5" + "source": "https://github.com/symfony/process/tree/v7.1.6" }, "funding": [ { @@ -3174,7 +3174,7 @@ "type": "tidelift" } ], - "time": "2024-09-19T21:48:23+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/service-contracts", @@ -3261,16 +3261,16 @@ }, { "name": "symfony/string", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306" + "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/d66f9c343fa894ec2037cc928381df90a7ad4306", - "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306", + "url": "https://api.github.com/repos/symfony/string/zipball/61b72d66bf96c360a727ae6232df5ac83c71f626", + "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626", "shasum": "" }, "require": { @@ -3328,7 +3328,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.5" + "source": "https://github.com/symfony/string/tree/v7.1.6" }, "funding": [ { @@ -3344,7 +3344,7 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:28:38+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "theseer/tokenizer", @@ -3448,12 +3448,12 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { "php": ">=8.0" }, - "platform-dev": [], + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/src/Validator/ArrayList.php b/src/Validator/ArrayList.php index 2468aed0..f7c38fc6 100644 --- a/src/Validator/ArrayList.php +++ b/src/Validator/ArrayList.php @@ -46,7 +46,7 @@ public function getDescription(): string { $msg = 'Value must a valid array'; - if($this->length > 0) { + if ($this->length > 0) { $msg .= ' no longer than ' . $this->length . ' items'; } diff --git a/tests/ResponseTest.php b/tests/ResponseTest.php index 30b20477..b894556b 100755 --- a/tests/ResponseTest.php +++ b/tests/ResponseTest.php @@ -35,7 +35,7 @@ public function testCanSetStatus() try { $this->response->setStatusCode(0); // Unknown status code - } catch(\Exception $e) { + } catch (\Exception $e) { $this->assertInstanceOf('\Exception', $e); return; diff --git a/tests/ViewTest.php b/tests/ViewTest.php index e21131af..98e427f3 100755 --- a/tests/ViewTest.php +++ b/tests/ViewTest.php @@ -67,7 +67,7 @@ public function testCanRenderHtml() $this->view->setRendered(false); $this->view->setPath('just-a-broken-string.phtml'); $this->view->render(); - } catch(\Exception $e) { + } catch (\Exception $e) { return; } From 10036b0293e1ded7d0ab2154def2e06744bae80b Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 29 Oct 2024 12:56:10 +0000 Subject: [PATCH 05/11] chore: remove extra file --- src/Http/Response.php | 837 ------------------------------------------ 1 file changed, 837 deletions(-) delete mode 100644 src/Http/Response.php diff --git a/src/Http/Response.php b/src/Http/Response.php deleted file mode 100644 index 0f2cab39..00000000 --- a/src/Http/Response.php +++ /dev/null @@ -1,837 +0,0 @@ - 'Continue', - self::STATUS_CODE_SWITCHING_PROTOCOLS => 'Switching Protocols', - self::STATUS_CODE_OK => 'OK', - self::STATUS_CODE_CREATED => 'Created', - self::STATUS_CODE_ACCEPTED => 'Accepted', - self::STATUS_CODE_NON_AUTHORITATIVE_INFORMATION => 'Non-Authoritative Information', - self::STATUS_CODE_NOCONTENT => 'No Content', - self::STATUS_CODE_RESETCONTENT => 'Reset Content', - self::STATUS_CODE_PARTIALCONTENT => 'Partial Content', - self::STATUS_CODE_MULTIPLE_CHOICES => 'Multiple Choices', - self::STATUS_CODE_MOVED_PERMANENTLY => 'Moved Permanently', - self::STATUS_CODE_FOUND => 'Found', - self::STATUS_CODE_SEE_OTHER => 'See Other', - self::STATUS_CODE_NOT_MODIFIED => 'Not Modified', - self::STATUS_CODE_USE_PROXY => 'Use Proxy', - self::STATUS_CODE_UNUSED => '(Unused)', - self::STATUS_CODE_TEMPORARY_REDIRECT => 'Temporary Redirect', - self::STATUS_CODE_BAD_REQUEST => 'Bad Request', - self::STATUS_CODE_UNAUTHORIZED => 'Unauthorized', - self::STATUS_CODE_PAYMENT_REQUIRED => 'Payment Required', - self::STATUS_CODE_FORBIDDEN => 'Forbidden', - self::STATUS_CODE_NOT_FOUND => 'Not Found', - self::STATUS_CODE_METHOD_NOT_ALLOWED => 'Method Not Allowed', - self::STATUS_CODE_NOT_ACCEPTABLE => 'Not Acceptable', - self::STATUS_CODE_PROXY_AUTHENTICATION_REQUIRED => 'Proxy Authentication Required', - self::STATUS_CODE_REQUEST_TIMEOUT => 'Request Timeout', - self::STATUS_CODE_CONFLICT => 'Conflict', - self::STATUS_CODE_GONE => 'Gone', - self::STATUS_CODE_LENGTH_REQUIRED => 'Length Required', - self::STATUS_CODE_PRECONDITION_FAILED => 'Precondition Failed', - self::STATUS_CODE_REQUEST_ENTITY_TOO_LARGE => 'Request Entity Too Large', - self::STATUS_CODE_REQUEST_URI_TOO_LONG => 'Request-URI Too Long', - self::STATUS_CODE_UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type', - self::STATUS_CODE_REQUESTED_RANGE_NOT_SATISFIABLE => 'Requested Range Not Satisfiable', - self::STATUS_CODE_EXPECTATION_FAILED => 'Expectation Failed', - self::STATUS_CODE_TOO_EARLY => 'Too Early', - self::STATUS_CODE_TOO_MANY_REQUESTS => 'Too Many Requests', - self::STATUS_CODE_UNAVAILABLE_FOR_LEGAL_REASONS => 'Unavailable For Legal Reasons', - self::STATUS_CODE_INTERNAL_SERVER_ERROR => 'Internal Server Error', - self::STATUS_CODE_NOT_IMPLEMENTED => 'Not Implemented', - self::STATUS_CODE_BAD_GATEWAY => 'Bad Gateway', - self::STATUS_CODE_SERVICE_UNAVAILABLE => 'Service Unavailable', - self::STATUS_CODE_GATEWAY_TIMEOUT => 'Gateway Timeout', - self::STATUS_CODE_HTTP_VERSION_NOT_SUPPORTED => 'HTTP Version Not Supported', - ]; - - /** - * Mime Types with compression support - * - * @var array - */ - protected $compressed = [ - 'text/plain' => true, - 'text/css' => true, - 'text/javascript' => true, - 'application/javascript' => true, - 'text/html' => true, - 'text/html; charset=UTF-8' => true, - 'application/json' => true, - 'application/json; charset=UTF-8' => true, - 'image/svg+xml' => true, - 'application/xml+rss' => true, - ]; - - public const COOKIE_SAMESITE_NONE = 'None'; - - public const COOKIE_SAMESITE_STRICT = 'Strict'; - - public const COOKIE_SAMESITE_LAX = 'Lax'; - - public const CHUNK_SIZE = 2000000; //2mb - - /** - * @var int - */ - protected int $statusCode = self::STATUS_CODE_OK; - - /** - * @var string - */ - protected string $contentType = ''; - - /** - * @var bool - */ - protected bool $disablePayload = false; - - /** - * @var bool - */ - protected bool $sent = false; - - /** - * @var array - */ - protected array $headers = []; - - /** - * @var array - */ - protected array $cookies = []; - - /** - * @var float - */ - protected float $startTime = 0; - - /** - * @var int - */ - protected int $size = 0; - - /** - * @var string - */ - protected string $acceptEncoding = ''; - - /** - * @var int - */ - protected int $compressionMinSize = Http::COMPRESSION_MIN_SIZE_DEFAULT; - - /** - * Response constructor. - * - * @param float $time response start time - */ - public function __construct(float $time = 0) - { - $this->startTime = (!empty($time)) ? $time : \microtime(true); - } - - /** - * Set content type - * - * Set HTTP content type header. - * - * @param string $type - * @param string $charset - */ - public function setContentType(string $type, string $charset = ''): static - { - $this->contentType = $type.((!empty($charset) ? '; charset='.$charset : '')); - - return $this; - } - - /** - * Set accept encoding - * - * Set HTTP accept encoding header. - * - * @param string $acceptEncoding - */ - public function setAcceptEncoding(string $acceptEncoding): static - { - $this->acceptEncoding = $acceptEncoding; - return $this; - } - - /** - * Set min compression size - * - * Set minimum size for compression to be applied in bytes. - * - * @param int $compressionMinSize - */ - public function setCompressionMinSize(int $compressionMinSize): static - { - $this->compressionMinSize = $compressionMinSize; - return $this; - } - - /** - * Get content type - * - * Get HTTP content type header. - * - * @return string - */ - public function getContentType(): string - { - return $this->contentType; - } - - /** - * Get if response was already sent - * - * @return bool - */ - public function isSent(): bool - { - return $this->sent; - } - - /** - * Set status code - * - * Set HTTP response status code between available options. if status code is unknown an exception will be thrown - * - * @param int $code - * - * @throws Exception - */ - public function setStatusCode(int $code = 200): static - { - if (!\array_key_exists($code, $this->statusCodes)) { - throw new Exception('Unknown HTTP status code'); - } - - $this->statusCode = $code; - - return $this; - } - - /** - * Get status code - * - * Get HTTP response status code - * - * @return int - **/ - public function getStatusCode(): int - { - return $this->statusCode; - } - - /** - * Get Response Size - * - * Return output response size in bytes - * - * @return int - */ - public function getSize(): int - { - return $this->size; - } - - /** - * Don't allow payload on response output - */ - public function disablePayload(): static - { - $this->disablePayload = true; - - return $this; - } - - /** - * Allow payload on response output - */ - public function enablePayload(): static - { - $this->disablePayload = false; - - return $this; - } - - /** - * Add header - * - * Add an HTTP response header - * - * @param string $key - * @param string $value - */ - public function addHeader(string $key, string $value): static - { - $this->headers[$key] = $value; - - return $this; - } - - /** - * Remove header - * - * Remove HTTP response header - * - * @param string $key - */ - public function removeHeader(string $key): static - { - if (isset($this->headers[$key])) { - unset($this->headers[$key]); - } - - return $this; - } - - /** - * Get Headers - * - * Return array of all response headers - * - * @return array - */ - public function getHeaders(): array - { - return $this->headers; - } - - /** - * Add cookie - * - * Add an HTTP cookie to response header - * - * @param string $name - * @param string $value - * @param int $expire - * @param string $path - * @param string $domain - * @param bool $secure - * @param bool $httponly - * @param string $sameSite - */ - public function addCookie(string $name, string $value = null, int $expire = null, string $path = null, string $domain = null, bool $secure = null, bool $httponly = null, string $sameSite = null): static - { - $name = strtolower($name); - - $this->cookies[] = [ - 'name' => $name, - 'value' => $value, - 'expire' => $expire, - 'path' => $path, - 'domain' => $domain, - 'secure' => $secure, - 'httponly' => $httponly, - 'samesite' => $sameSite, - ]; - - return $this; - } - - /** - * Remove cookie - * - * Remove HTTP response cookie - * - * @param string $name - */ - public function removeCookie(string $name): static - { - $this->cookies = array_filter($this->cookies, function ($cookie) use ($name) { - return $cookie['name'] !== $name; - }); - - return $this; - } - - /** - * Get Cookies - * - * Return array of all response cookies - * - * @return array - */ - public function getCookies(): array - { - return $this->cookies; - } - - /** - * Output response - * - * Generate HTTP response output including the response header (+cookies) and body and prints them. - * - * @param string $body - * @return void - */ - public function send(string $body = ''): void - { - if ($this->sent) { - return; - } - - $this->sent = true; - - $serverHeader = $this->headers['Server'] ?? 'Utopia/Http'; - $this->addHeader('Server', $serverHeader); - $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime)); - - $this->appendCookies()->appendHeaders(); - - // Send response - if ($this->disablePayload) { - $this->end(); - return; - } - - // Compress body - if ( - !empty($this->acceptEncoding) && - isset($this->compressed[$this->contentType]) && - strlen($body) > $this->compressionMinSize - ) { - $algorithm = Compression::fromAcceptEncoding($this->acceptEncoding, [ - Compression::BROTLI, - Compression::GZIP, - Compression::DEFLATE, - ]); - - if ($algorithm) { - $body = $algorithm->compress($body); - $this->addHeader('Content-Encoding', $algorithm->getContentEncoding()); - $this->addHeader('Vary', 'Accept-Encoding'); - } - } - - $headerSize = strlen(implode("\n", $this->headers)); - $bodyLength = strlen($body); - $this->size += $headerSize + $bodyLength; - - if ($bodyLength <= self::CHUNK_SIZE) { - $this->end($body); - } else { - $chunks = str_split($body, self::CHUNK_SIZE); - foreach ($chunks as $chunk) { - $this->write($chunk); - } - $this->end(); - } - - $this->disablePayload(); - } - - /** - * Write - * - * Send output - * - * @param string $content - * @return bool False if write cannot complete, such as request ended by client - */ - abstract public function write(string $content): bool; - - /** - * End - * - * Send optional content and end - * - * @param string $content - * @return void - */ - abstract public function end(string $content = ''): void; - - /** - * Output response - * - * Generate HTTP response output including the response header (+cookies) and body and prints them. - * - * @param string $body - * @param bool $end - * - * @return void - */ - public function chunk(string $body = '', bool $end = false): void - { - if ($this->sent) { - return; - } - - if ($end) { - $this->sent = true; - } - - $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime)); - - $this - ->appendCookies() - ->appendHeaders(); - - if (!$this->disablePayload) { - $this->write($body); - if ($end) { - $this->disablePayload(); - $this->end(); - } - } else { - $this->end(); - } - } - - /** - * Append headers - * - * Iterating over response headers to generate them using native PHP header function. - * This method is also responsible for generating the response and content type headers. - */ - protected function appendHeaders(): static - { - // Send status code header - $this->sendStatus($this->statusCode, $this->statusCodes[$this->statusCode] ?? 'Unknown HTTP status code'); - - // Send content type header - if (!empty($this->contentType)) { - $this->addHeader('Content-Type', $this->contentType); - } - - // Set application headers - foreach ($this->headers as $key => $value) { - $this->sendHeader($key, $value); - } - - return $this; - } - - /** - * Send Status Code - * - * @param int $statusCode - * @param string $reason - * @return void - */ - abstract protected function sendStatus(int $statusCode, string $reason): void; - - /** - * Send Header - * - * Output Header - * - * @param string $key - * @param string $value - * @return void - */ - abstract public function sendHeader(string $key, string $value): void; - - /** - * Send Cookie - * - * Output Cookie - * - * @param string $name - * @param string $value - * @param array $options - * @return void - */ - abstract protected function sendCookie(string $name, string $value, array $options): void; - - /** - * Append cookies - * - * Iterating over response cookies to generate them using native PHP cookie function. - */ - protected function appendCookies(): static - { - foreach ($this->cookies as $cookie) { - $this->sendCookie($cookie['name'], $cookie['value'], [ - 'expire' => $cookie['expire'], - 'path' => $cookie['path'], - 'domain' => $cookie['domain'], - 'secure' => $cookie['secure'], - 'httponly' => $cookie['httponly'], - 'samesite' => $cookie['samesite'], - ]); - } - - return $this; - } - - /** - * Redirect - * - * This helper is for sending a 30* HTTP response. - * After setting relevant HTTP headers for redirect response this helper stop application native flow what means the shutdown method will not be executed - * - * NOTICE: it seems webkit based browsers have problems redirecting link with 300 status codes. - * - * @see https://code.google.com/p/chromium/issues/detail?id=75540 - * @see https://bugs.webkit.org/show_bug.cgi?id=47425 - * - * @param string $url complete absolute URI for redirection as required by the internet standard RFC 2616 (HTTP 1.1) - * @param int $statusCode valid HTTP status code - * @return void - * - * @throws Exception - * - * @see http://tools.ietf.org/html/rfc2616 - */ - public function redirect(string $url, int $statusCode = 301): void - { - if (300 == $statusCode) { - \trigger_error('It seems webkit based browsers have problems redirecting link with 300 status codes!', E_USER_NOTICE); - } - - $this - ->addHeader('Location', $url) - ->setStatusCode($statusCode) - ->send(''); - } - - /** - * HTML - * - * This helper is for sending an HTML HTTP response and sets relevant content type header ('text/html'). - * - * @see http://en.wikipedia.org/wiki/JSON - * - * @param string $data - * @return void - */ - public function html(string $data): void - { - $this - ->setContentType(self::CONTENT_TYPE_HTML, self::CHARSET_UTF8) - ->send($data); - } - - /** - * Text - * - * This helper is for sending plain text HTTP response and sets relevant content type header ('text/plain'). - * - * @see http://en.wikipedia.org/wiki/JSON - * - * @param string $data - * @return void - */ - public function text(string $data): void - { - $this - ->setContentType(self::CONTENT_TYPE_TEXT, self::CHARSET_UTF8) - ->send($data); - } - - /** - * JSON - * - * This helper is for sending JSON HTTP response. - * It sets relevant content type header ('application/json') and convert a PHP array ($data) to valid JSON using native json_encode - * - * @see http://en.wikipedia.org/wiki/JSON - * - * @param mixed $data - * @return void - */ - public function json($data): void - { - if (!is_array($data) && !$data instanceof \stdClass) { - throw new \Exception('Invalid JSON input var'); - } - - $this - ->setContentType(Response::CONTENT_TYPE_JSON, self::CHARSET_UTF8) - ->send(\json_encode($data, JSON_UNESCAPED_UNICODE)); - } - - /** - * JSON with padding - * - * This helper is for sending JSONP HTTP response. - * It sets relevant content type header ('text/javascript') and convert a PHP array ($data) to valid JSON using native json_encode - * - * @see http://en.wikipedia.org/wiki/JSONP - * - * @param string $callback - * @param array $data - * @return void - */ - public function jsonp(string $callback, array $data): void - { - $this - ->setContentType(self::CONTENT_TYPE_JAVASCRIPT, self::CHARSET_UTF8) - ->send('parent.'.$callback.'('.\json_encode($data).');'); - } - - /** - * Iframe - * - * This helper is for sending iframe HTTP response. - * It sets relevant content type header ('text/html') and convert a PHP array ($data) to valid JSON using native json_encode - * - * @param string $callback - * @param array $data - * @return void - */ - public function iframe(string $callback, array $data): void - { - $this - ->setContentType(self::CONTENT_TYPE_HTML, self::CHARSET_UTF8) - ->send(''); - } - - /** - * No Content - * - * This helper is for sending no content HTTP response. - * - * The server has successfully fulfilled the request - * and that there is no additional content to send in the response payload body. - * - * @return void - */ - public function noContent(): void - { - $this - ->setStatusCode(self::STATUS_CODE_NOCONTENT) - ->send(''); - } -} From 9112676ca82cb45e8cf5cea6db5dbbadddd9d8f1 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 31 Oct 2024 11:39:41 +0000 Subject: [PATCH 06/11] feat: supported compression algorithms --- src/App.php | 10 ++++++++++ src/Response.php | 22 +++++++++++++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/App.php b/src/App.php index 00bd44a2..1b8c5788 100755 --- a/src/App.php +++ b/src/App.php @@ -109,6 +109,7 @@ class App */ protected bool $compression = false; protected int $compressionMinSize = App::COMPRESSION_MIN_SIZE_DEFAULT; + protected mixed $compressionSupported; /** * App @@ -136,6 +137,14 @@ public function setCompressionMinSize(int $compressionMinSize) $this->compressionMinSize = $compressionMinSize; } + /** + * Set supported compression algorithms + */ + public function setCompressionSupported(mixed $compressionSupported) + { + $this->compressionSupported = $compressionSupported; + } + /** * GET * @@ -669,6 +678,7 @@ public function run(Request $request, Response $response): static if ($this->compression) { $response->setAcceptEncoding($request->getHeader('accept-encoding', '')); $response->setCompressionMinSize($this->compressionMinSize); + $response->setCompressionSupported($this->compressionSupported); } $this->resources['request'] = $request; diff --git a/src/Response.php b/src/Response.php index 76bf02b4..625db69c 100755 --- a/src/Response.php +++ b/src/Response.php @@ -257,6 +257,11 @@ class Response */ protected int $compressionMinSize = App::COMPRESSION_MIN_SIZE_DEFAULT; + /** + * @var mixed + */ + protected mixed $compressionSupported; + /** * Response constructor. * @@ -308,6 +313,17 @@ public function setCompressionMinSize(int $compressionMinSize): static return $this; } + /** + * Set supported compression algorithms + * + * @param mixed $compressionSupported + */ + public function setCompressionSupported(mixed $compressionSupported): static + { + $this->compressionSupported = $compressionSupported; + return $this; + } + /** * Get content type * @@ -529,11 +545,7 @@ public function send(string $body = ''): void isset($this->compressed[$this->contentType]) && strlen($body) > $this->compressionMinSize ) { - $algorithm = Compression::fromAcceptEncoding($this->acceptEncoding, [ - Compression::BROTLI, - Compression::GZIP, - Compression::DEFLATE, - ]); + $algorithm = Compression::fromAcceptEncoding($this->acceptEncoding, $this->compressionSupported); if ($algorithm) { $body = $algorithm->compress($body); From a501a56f89097f2fd48c13eb2c632aafb1285cff Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:05:05 +0000 Subject: [PATCH 07/11] fix: define compression supported --- src/App.php | 2 +- src/Response.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/App.php b/src/App.php index 1b8c5788..e61d12f4 100755 --- a/src/App.php +++ b/src/App.php @@ -109,7 +109,7 @@ class App */ protected bool $compression = false; protected int $compressionMinSize = App::COMPRESSION_MIN_SIZE_DEFAULT; - protected mixed $compressionSupported; + protected mixed $compressionSupported = []; /** * App diff --git a/src/Response.php b/src/Response.php index 625db69c..3cbd13c9 100755 --- a/src/Response.php +++ b/src/Response.php @@ -260,7 +260,7 @@ class Response /** * @var mixed */ - protected mixed $compressionSupported; + protected mixed $compressionSupported = []; /** * Response constructor. From 72239fb95a65fc65e3a8e435f1645e234b00ea04 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Thu, 7 Nov 2024 18:00:49 +0100 Subject: [PATCH 08/11] debug: compression --- src/Response.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Response.php b/src/Response.php index 3cbd13c9..4e1c7f5b 100755 --- a/src/Response.php +++ b/src/Response.php @@ -2,6 +2,7 @@ namespace Utopia; +use Utopia\CLI\Console; use Utopia\Compression\Compression; class Response @@ -315,7 +316,7 @@ public function setCompressionMinSize(int $compressionMinSize): static /** * Set supported compression algorithms - * + * * @param mixed $compressionSupported */ public function setCompressionSupported(mixed $compressionSupported): static @@ -540,6 +541,10 @@ public function send(string $body = ''): void } // Compress body + Console::log('Accept-Encoding: '.$this->acceptEncoding . ' - ' . $this->compressionMinSize); + Console::log('Content-Type: '.$this->contentType . ' - ' . $this->compressed[$this->contentType]); + Console::log('Content-Length: '.strlen($body) . ' - ' . $this->compressionMinSize); + if ( !empty($this->acceptEncoding) && isset($this->compressed[$this->contentType]) && @@ -547,8 +552,15 @@ public function send(string $body = ''): void ) { $algorithm = Compression::fromAcceptEncoding($this->acceptEncoding, $this->compressionSupported); + Console::log('Compression Algorithm: '.($algorithm ? $algorithm->getName() : 'none')); + if ($algorithm) { + Console::log('Body before compression: '.strlen($body)); + $body = $algorithm->compress($body); + + Console::log('Body after compression: '.strlen($body)); + $this->addHeader('Content-Encoding', $algorithm->getContentEncoding()); $this->addHeader('Vary', 'Accept-Encoding'); } From 238c9c2040621f0d5286825d2c452310a1c21e2d Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 8 Nov 2024 13:10:14 +0100 Subject: [PATCH 09/11] fix: send headers after --- src/Response.php | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/Response.php b/src/Response.php index 4e1c7f5b..828eea45 100755 --- a/src/Response.php +++ b/src/Response.php @@ -532,19 +532,9 @@ public function send(string $body = ''): void $this->addHeader('Server', $serverHeader); $this->addHeader('X-Debug-Speed', (string) (microtime(true) - $this->startTime)); - $this->appendCookies()->appendHeaders(); - - // Send response - if ($this->disablePayload) { - $this->end(); - return; - } + $this->appendCookies(); // Compress body - Console::log('Accept-Encoding: '.$this->acceptEncoding . ' - ' . $this->compressionMinSize); - Console::log('Content-Type: '.$this->contentType . ' - ' . $this->compressed[$this->contentType]); - Console::log('Content-Length: '.strlen($body) . ' - ' . $this->compressionMinSize); - if ( !empty($this->acceptEncoding) && isset($this->compressed[$this->contentType]) && @@ -552,20 +542,21 @@ public function send(string $body = ''): void ) { $algorithm = Compression::fromAcceptEncoding($this->acceptEncoding, $this->compressionSupported); - Console::log('Compression Algorithm: '.($algorithm ? $algorithm->getName() : 'none')); - if ($algorithm) { - Console::log('Body before compression: '.strlen($body)); - $body = $algorithm->compress($body); - - Console::log('Body after compression: '.strlen($body)); - $this->addHeader('Content-Encoding', $algorithm->getContentEncoding()); $this->addHeader('Vary', 'Accept-Encoding'); } } + $this->appendHeaders(); + + // Send response + if ($this->disablePayload) { + $this->end(); + return; + } + $headerSize = strlen(implode("\n", $this->headers)); $bodyLength = strlen($body); $this->size += $headerSize + $bodyLength; From c91b5769ee0ccddec011c8ad02090470d2426ef2 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 8 Nov 2024 13:18:17 +0100 Subject: [PATCH 10/11] chore: fmt --- src/Response.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Response.php b/src/Response.php index 828eea45..5210ed34 100755 --- a/src/Response.php +++ b/src/Response.php @@ -2,7 +2,6 @@ namespace Utopia; -use Utopia\CLI\Console; use Utopia\Compression\Compression; class Response From 58851dc88c4d8ac32757f30f1508cdf55a26cb4a Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 8 Nov 2024 13:23:25 +0100 Subject: [PATCH 11/11] chore: composer update --- composer.lock | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/composer.lock b/composer.lock index 4677e800..efc9715a 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "utopia-php/compression", - "version": "0.1.0", + "version": "0.1.1", "source": { "type": "git", "url": "https://github.com/utopia-php/compression.git", - "reference": "8c6d9bcb5b0972faa27e5bf70923c20403aaf25c" + "reference": "2ac5709e39823dbccb9fa66099ccebe809078c2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/compression/zipball/8c6d9bcb5b0972faa27e5bf70923c20403aaf25c", - "reference": "8c6d9bcb5b0972faa27e5bf70923c20403aaf25c", + "url": "https://api.github.com/repos/utopia-php/compression/zipball/2ac5709e39823dbccb9fa66099ccebe809078c2e", + "reference": "2ac5709e39823dbccb9fa66099ccebe809078c2e", "shasum": "" }, "require": { @@ -48,9 +48,9 @@ ], "support": { "issues": "https://github.com/utopia-php/compression/issues", - "source": "https://github.com/utopia-php/compression/tree/0.1.0" + "source": "https://github.com/utopia-php/compression/tree/0.1.1" }, - "time": "2024-10-23T10:17:46+00:00" + "time": "2024-11-08T12:22:15+00:00" } ], "packages-dev": [ @@ -783,16 +783,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.7", + "version": "1.12.8", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0" + "reference": "f6a60a4d66142b8156c9da923f1972657bc4748c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", - "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f6a60a4d66142b8156c9da923f1972657bc4748c", + "reference": "f6a60a4d66142b8156c9da923f1972657bc4748c", "shasum": "" }, "require": { @@ -837,7 +837,7 @@ "type": "github" } ], - "time": "2024-10-18T11:12:07+00:00" + "time": "2024-11-06T19:06:49+00:00" }, { "name": "phpunit/php-code-coverage", @@ -2442,16 +2442,16 @@ }, { "name": "symfony/console", - "version": "v7.1.6", + "version": "v7.1.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57" + "reference": "3284aafcac338b6e86fd955ee4d794cbe434151a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/bb5192af6edc797cbab5c8e8ecfea2fe5f421e57", - "reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57", + "url": "https://api.github.com/repos/symfony/console/zipball/3284aafcac338b6e86fd955ee4d794cbe434151a", + "reference": "3284aafcac338b6e86fd955ee4d794cbe434151a", "shasum": "" }, "require": { @@ -2515,7 +2515,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.6" + "source": "https://github.com/symfony/console/tree/v7.1.7" }, "funding": [ { @@ -2531,7 +2531,7 @@ "type": "tidelift" } ], - "time": "2024-10-09T08:46:59+00:00" + "time": "2024-11-05T15:34:55+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3117,16 +3117,16 @@ }, { "name": "symfony/process", - "version": "v7.1.6", + "version": "v7.1.7", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e" + "reference": "9b8a40b7289767aa7117e957573c2a535efe6585" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e", - "reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e", + "url": "https://api.github.com/repos/symfony/process/zipball/9b8a40b7289767aa7117e957573c2a535efe6585", + "reference": "9b8a40b7289767aa7117e957573c2a535efe6585", "shasum": "" }, "require": { @@ -3158,7 +3158,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.1.6" + "source": "https://github.com/symfony/process/tree/v7.1.7" }, "funding": [ { @@ -3174,7 +3174,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-11-06T09:25:12+00:00" }, { "name": "symfony/service-contracts",