diff --git a/Dockerfile b/Dockerfile
index 6eaa763448..84442c7198 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -14,6 +14,10 @@ RUN apt-get update \
&& docker-php-ext-install -j$(nproc) opcache \
&& docker-php-ext-configure pdo_mysql \
&& docker-php-ext-install -j$(nproc) pdo_mysql \
+ && pecl install brotli \
+ && docker-php-ext-enable brotli \
+ && pecl install zstd \
+ && docker-php-ext-enable zstd \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
diff --git a/composer.json b/composer.json
index 553cedaf86..cd669a3e00 100644
--- a/composer.json
+++ b/composer.json
@@ -28,6 +28,7 @@
"filp/whoops": "^2.14",
"gabordemooij/redbean": "^5.7",
"geoip2/geoip2": "^3.0.0",
+ "hostbybelle/compressionbuffer": "^1.0",
"io-developer/php-whois": "^4.1",
"lcharette/webpack-encore-twig": "^1.2.0",
"league/commonmark": "^2.3",
diff --git a/composer.lock b/composer.lock
index 8ac614fcbc..8266cf382a 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "08b744c6a4c8e1a0f880570457a581cb",
+ "content-hash": "9b6fd56ed203486b5a99e3ed8887b12d",
"packages": [
{
"name": "antcms/antloader",
@@ -53,6 +53,61 @@
},
"time": "2024-03-12T22:17:58+00:00"
},
+ {
+ "name": "asispts/http-accept",
+ "version": "v1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/asispts/http-accept.git",
+ "reference": "7beca5a724ef329bb5a0094b5aef7bfeab1193ae"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/asispts/http-accept/zipball/7beca5a724ef329bb5a0094b5aef7bfeab1193ae",
+ "reference": "7beca5a724ef329bb5a0094b5aef7bfeab1193ae",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2|^8.0"
+ },
+ "require-dev": {
+ "asispts/ptscs": "^1.0",
+ "phpstan/phpstan": "^1.4",
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpunit/phpunit": "^8.5|^9.5|^10.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "HttpAccept\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Asis Pattisahusiwa",
+ "email": "asispts@gmail.com"
+ }
+ ],
+ "description": "PHP Parser to deal with HTTP Accept, Accept-Language, Accept-Encoding, and Content-Type headers",
+ "homepage": "https://github.com/asispts/http-accept",
+ "keywords": [
+ "accept",
+ "accept-encoding",
+ "accept-language",
+ "content-type",
+ "http-header-parser",
+ "http-headers"
+ ],
+ "support": {
+ "issues": "https://github.com/asispts/http-accept/issues",
+ "source": "https://github.com/asispts/http-accept/tree/v1.0.0"
+ },
+ "time": "2023-04-28T02:59:23+00:00"
+ },
{
"name": "brick/math",
"version": "0.11.0",
@@ -899,6 +954,56 @@
],
"time": "2023-12-03T20:05:35+00:00"
},
+ {
+ "name": "hostbybelle/compressionbuffer",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/HostByBelle/CompressionBuffer.git",
+ "reference": "a5ed8b63c9657a235f5551db1b2b7de37801f253"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/HostByBelle/CompressionBuffer/zipball/a5ed8b63c9657a235f5551db1b2b7de37801f253",
+ "reference": "a5ed8b63c9657a235f5551db1b2b7de37801f253",
+ "shasum": ""
+ },
+ "require": {
+ "asispts/http-accept": "^1.0",
+ "php": ">=8.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.53",
+ "phpstan/phpstan": "^1.10"
+ },
+ "suggest": {
+ "ext-brotli": "For brotli output compression",
+ "ext-zlib": "For gzip & deflate output compression",
+ "ext-zstd": "For zstd output compression"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "HostByBelle\\": "src/HostByBelle"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Belle Aerni",
+ "email": "belleaerni@gmail.com"
+ }
+ ],
+ "description": "CompressionBuffer provides easy access to zstd, brotli, and gzip output buffering with PHP on any webserver.",
+ "support": {
+ "issues": "https://github.com/HostByBelle/CompressionBuffer/issues",
+ "source": "https://github.com/HostByBelle/CompressionBuffer/tree/1.0.0"
+ },
+ "time": "2024-04-08T22:57:40+00:00"
+ },
{
"name": "io-developer/php-whois",
"version": "4.1.10",
diff --git a/cspell.json b/cspell.json
index bbdfe1d309..b0c063ed21 100644
--- a/cspell.json
+++ b/cspell.json
@@ -393,7 +393,8 @@
"intval",
"thisfiledoesnotexist",
"exchangerate",
- "currencydata"
+ "currencydata",
+ "brotli"
],
"ignorePaths": [
"tests-legacy/**",
diff --git a/phpstan.neon b/phpstan.neon
index 5233682888..f20f5da050 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -26,8 +26,6 @@ parameters:
- '#^Function __trans not found\.$#'
- '#^Function __pluralTrans not found\.$#'
- '#^Inner named functions are not supported by PHPStan\. Consider refactoring to an anonymous function, class method, or a top\-level\-defined function\. See issue \#165 \(https\://github\.com/phpstan/phpstan/issues/165\) for more details\.$#'
- - message: '#^Result of function header \(void\) is used\.$#'
- path: src/modules/Custompages/Controller/Client.php
- message: '#^Variable \$ext_id on left side of \?\?\= is never defined\.$#'
path: src/modules/Extension/Service.php
- '#^Access to an undefined property RedBeanPHP\\SimpleModel\:\:\$updated_at\.$#'
diff --git a/src/config-sample.php b/src/config-sample.php
index 7082b536de..1dd2f679ac 100644
--- a/src/config-sample.php
+++ b/src/config-sample.php
@@ -48,27 +48,45 @@
'report_errors' => false,
],
+ 'system' => [
+ /*
+ * Full URL where FOSSBilling is installed with trailing slash.
+ */
+ 'url' => 'http://localhost/',
+
+ /*
+ * The URL prefix to access the BB admin area. Ex: '/admin' for https://example.com/admin.
+ */
+ 'admin_area_prefix' => '/admin',
+
+ /*
+ * Configure the update branch for the automatic updater.
+ * Currently acceptable options are "release" or "preview".
+ */
+ 'update_branch' => 'release',
+
+ /*
+ * FOSSBilling will automatically execute cron when you login to the admin panel if it hasn't been executed in awhile. You can disable this fallback here.
+ */
+ 'disable_auto_cron' => false,
+
+ /*
+ * Set location to store sensitive data.
+ */
+ 'path_data' => __DIR__ . '/data',
+
+ /*
+ * FOSSBilling will automatically perform zstd, brotli, gzip, or deflate output compression depending on installed extensions and the client connecting.
+ * Disable this here if you want to not use output compression or control it outside of FOSSBilling itself (such as your webserver)
+ */
+ 'do_output_compression' => true,
+ ],
+
'info' => [
'salt' => bin2hex(random_bytes(16)),
'instance_id' => 'XXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXX',
],
- /*
- * Full URL where FOSSBilling is installed with trailing slash.
- */
- 'url' => 'http://localhost/',
-
- /*
- * The URL prefix to access the BB admin area. Ex: '/admin' for https://example.com/admin.
- */
- 'admin_area_prefix' => '/admin',
-
- /*
- * Configure the update branch for the automatic updater.
- * Currently acceptable options are "release" or "preview".
- */
- 'update_branch' => 'release',
-
'maintenance_mode' => [
/*
* Enable or disable the system maintenance mode.
@@ -90,11 +108,6 @@
'allowed_ips' => [],
],
- /*
- * FOSSBilling will automatically execute cron when you login to the admin panel if it hasn't been executed in awhile. You can disable this fallback here.
- */
- 'disable_auto_cron' => false,
-
/*
* These configuration options allow you to configure the default localisation.
*/
@@ -112,11 +125,6 @@
'datetime_pattern' => '',
],
- /*
- * Set location to store sensitive data.
- */
- 'path_data' => __DIR__ . '/data',
-
'db' => [
/*
* Database type. Don't change this if in doubt.
@@ -168,7 +176,7 @@
// How many requests allowed per time span
'rate_limit' => 1000,
- /**
+ /*
* Note about rate-limiting login attempts:
* When the limit is reached, a default delay of 2 seconds is added to the request.
* This makes brute-forcing a password useless while not outright blocking legitimate traffic.
@@ -184,13 +192,13 @@
'rate_limit_login' => 20,
/*
- * This enables the usage of a token to protect the system from CSRF attacks.
- * Disabling this is highly discouraged and opens your instance to a known vulnerability.
- * This option is only here for backwards compatibility.
- */
+ * This enables the usage of a token to protect the system from CSRF attacks.
+ * Disabling this is highly discouraged and opens your instance to a known vulnerability.
+ * This option is only here for backwards compatibility.
+ */
'CSRFPrevention' => true,
- /**
+ /*
* Any IP address within this list will not be put through the rate-limiter system.
* This is useful if you have an application with a static IP address that needs to make frequent API requests to FOSSBilling.
*/
diff --git a/src/di.php b/src/di.php
index e8a0343d0b..d6d9c43ec4 100644
--- a/src/di.php
+++ b/src/di.php
@@ -406,6 +406,7 @@
$di['set_return_uri'];
header(sprintf('Location: %s', $di['url']->adminLink('staff/login')));
+ ob_end_flush();
exit;
}
@@ -439,6 +440,7 @@
// Redirect to login page if browser request
$login_url = $di['url']->link('login');
header("Location: $login_url");
+ ob_end_flush();
exit;
}
}
@@ -477,6 +479,7 @@
// Redirect to login page if browser request
$login_url = $di['url']->adminLink('staff/login');
header("Location: $login_url");
+ ob_end_flush();
exit;
}
}
@@ -528,6 +531,7 @@
// If they aren't attempting to access their profile, redirect them to it.
$login_url = $di['url']->link('client/profile');
header("Location: $login_url");
+ ob_end_flush();
exit;
}
}
@@ -834,6 +838,7 @@
$csv->output($outputName);
// Prevent further output from being added to the end of the CSV
+ ob_end_flush();
exit;
});
diff --git a/src/index.php b/src/index.php
index 8fdb5fb828..9430995c78 100644
--- a/src/index.php
+++ b/src/index.php
@@ -1,4 +1,5 @@
applyCorePatches();
$di['tools']->emptyFolder(PATH_CACHE);
- exit('Any missing config migrations or database patches have been applied and the cache has been cleared');
+ echo 'Any missing config migrations or database patches have been applied and the cache has been cleared';
} catch (Exception $e) {
- exit('An error occurred while attempting to apply patches:
' . $e->getMessage());
+ echo 'An error occurred while attempting to apply patches:
' . $e->getMessage();
}
+ exit;
}
$debugBar['time']->startMeasure('session_start', 'Starting / restoring the session');
@@ -90,6 +92,9 @@
exit;
}
-// If no HTTP error passed, run the app.
-echo $app->run();
+// Start output buffering & run the app
+ob_start(CompressionBuffer::handler(...));
+header('X-Accel-Buffering: no');
+$app->run();
+ob_end_flush();
exit;
diff --git a/src/library/Box/App.php b/src/library/Box/App.php
index 664d9bf23e..93d95e59c5 100644
--- a/src/library/Box/App.php
+++ b/src/library/Box/App.php
@@ -112,7 +112,7 @@ public function delete(string $url, string $methodName, ?array $conditions = [],
$this->event('delete', $url, $methodName, $conditions, $class);
}
- public function run(): string
+ public function run(): void
{
$this->debugBar['time']->startMeasure('registerModule', 'Registering module routes');
$this->registerModule();
@@ -126,7 +126,7 @@ public function run(): string
$this->checkPermission();
$this->debugBar['time']->stopMeasure('checkperm');
- return $this->processRequest();
+ echo $this->processRequest();
}
/**
@@ -136,6 +136,7 @@ public function redirect($path): never
{
$location = $this->di['url']->link($path);
header("Location: $location");
+ ob_end_flush();
exit;
}
diff --git a/src/library/Box/AppAdmin.php b/src/library/Box/AppAdmin.php
index f7176b4465..9b44d9a22b 100644
--- a/src/library/Box/AppAdmin.php
+++ b/src/library/Box/AppAdmin.php
@@ -34,6 +34,7 @@ protected function checkPermission(): void
http_response_code(403);
$e = new FOSSBilling\InformationException('You do not have permission to access the :mod: module', [':mod:' => $this->mod], 403);
echo $this->render('error', ['exception' => $e]);
+ ob_end_flush();
exit;
}
}
@@ -49,6 +50,7 @@ public function redirect($path): never
{
$location = $this->di['url']->adminLink($path);
header("Location: $location");
+ ob_end_flush();
exit;
}
diff --git a/src/library/FOSSBilling/CentralAlerts.php b/src/library/FOSSBilling/CentralAlerts.php
index 45919bdd5a..61651eb444 100644
--- a/src/library/FOSSBilling/CentralAlerts.php
+++ b/src/library/FOSSBilling/CentralAlerts.php
@@ -77,7 +77,7 @@ public function filterAlerts(array $type = [], string $version = Version::VERSIO
}
if ($version) {
- if (Config::getProperty('update_branch', 'release') === 'preview') {
+ if (Config::getProperty('system.update_branch', 'release') === 'preview') {
$alerts = array_filter($alerts, fn ($alert) => $alert['include_preview_branch']);
} else {
$alerts = array_filter($alerts, function ($alert) use ($version) {
diff --git a/src/library/FOSSBilling/ErrorPage.php b/src/library/FOSSBilling/ErrorPage.php
index 45057ebb85..35814cf27f 100644
--- a/src/library/FOSSBilling/ErrorPage.php
+++ b/src/library/FOSSBilling/ErrorPage.php
@@ -324,6 +324,7 @@ function toggle() {