From 96fbfd3d35cfc495ff47df746a1e860e61e4e6e7 Mon Sep 17 00:00:00 2001 From: Roly Gutierrez Date: Mon, 3 Aug 2026 13:22:33 -0400 Subject: [PATCH] =?UTF-8?q?FOUR-32507=20[Octane]=20MEDIUM=20=E2=80=94=20Ac?= =?UTF-8?q?cumulating=20State=20"ServerTimingMiddleware"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Description: Fix Octane state leak in ServerTimingMiddleware by reading min_package_time from config per request instead of a static property. Add tests for package timing threshold and config refresh between requests. Related tickets: https://processmaker.atlassian.net/browse/FOUR-32507 --- .../Middleware/ServerTimingMiddleware.php | 13 ++--- tests/Feature/ServerTimingMiddlewareTest.php | 53 +++++++++++++++++++ 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/ProcessMaker/Http/Middleware/ServerTimingMiddleware.php b/ProcessMaker/Http/Middleware/ServerTimingMiddleware.php index 2a15e84a6d..aab562465f 100644 --- a/ProcessMaker/Http/Middleware/ServerTimingMiddleware.php +++ b/ProcessMaker/Http/Middleware/ServerTimingMiddleware.php @@ -9,18 +9,10 @@ class ServerTimingMiddleware { - // Minimum time in ms to include a package in the Server-Timing header - private static $minPackageTime; - - public function __construct() - { - self::$minPackageTime = config('app.server_timing.min_package_time'); - } - /** * Handle an incoming request. * - * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next + * @param Closure(Request): (Response) $next */ public function handle(Request $request, Closure $next): Response { @@ -56,12 +48,13 @@ public function handle(Request $request, Closure $next): Response } $packageTimes = ProcessMakerServiceProvider::getPackageBootTiming(); + $minPackageTime = config('app.server_timing.min_package_time'); foreach ($packageTimes as $package => $timing) { $time = ($timing['end'] - $timing['start']) * 1000; // Only include packages that took more than MIN_PACKAGE_TIME ms - if ($time > self::$minPackageTime) { + if ($time > $minPackageTime) { $serverTiming[] = "{$package};dur={$time}"; } } diff --git a/tests/Feature/ServerTimingMiddlewareTest.php b/tests/Feature/ServerTimingMiddlewareTest.php index 040401d227..11ce958fd4 100644 --- a/tests/Feature/ServerTimingMiddlewareTest.php +++ b/tests/Feature/ServerTimingMiddlewareTest.php @@ -22,6 +22,11 @@ private function getHeader($response, $header) return $headers[$header]; } + private function getServerTimingHeaderValue($response): string + { + return implode(',', $this->getHeader($response, 'server-timing')); + } + public function testServerTimingHeaderIncludesAllMetrics() { Route::middleware(ServerTimingMiddleware::class)->get('/test', function () { @@ -162,6 +167,54 @@ public function testServerTimingOnLogin() $this->assertStringContainsString('db;dur=', $serverTiming[2]); } + public function testPackageTimingRespectsMinPackageTimeThreshold() + { + config([ + 'app.server_timing.enabled' => true, + 'app.server_timing.min_package_time' => 5, + ]); + + ProcessMakerServiceProvider::setPackageBootStart('foour32507-fast-package', 0.0); + ProcessMakerServiceProvider::setPackageBootedTime('foour32507-fast-package', 0.002); + + ProcessMakerServiceProvider::setPackageBootStart('foour32507-slow-package', 0.0); + ProcessMakerServiceProvider::setPackageBootedTime('foour32507-slow-package', 0.010); + + Route::middleware(ServerTimingMiddleware::class)->get('/package-threshold-test', function () { + return response()->json(['message' => 'Package threshold test']); + }); + + $response = $this->get('/package-threshold-test'); + $response->assertHeader('Server-Timing'); + + $serverTiming = $this->getServerTimingHeaderValue($response); + + $this->assertStringNotContainsString('foour32507-fast-package;dur=', $serverTiming); + $this->assertStringContainsString('foour32507-slow-package;dur=', $serverTiming); + } + + public function testMinPackageTimeReadsConfigPerRequest() + { + config(['app.server_timing.enabled' => true]); + + ProcessMakerServiceProvider::setPackageBootStart('foour32507-octane-package', 0.0); + ProcessMakerServiceProvider::setPackageBootedTime('foour32507-octane-package', 0.008); + + Route::middleware(ServerTimingMiddleware::class)->get('/octane-min-package-test', function () { + return response()->json(['message' => 'Octane min package test']); + }); + + config(['app.server_timing.min_package_time' => 10]); + $responseAboveThreshold = $this->get('/octane-min-package-test'); + $serverTimingAboveThreshold = $this->getServerTimingHeaderValue($responseAboveThreshold); + $this->assertStringNotContainsString('foour32507-octane-package;dur=', $serverTimingAboveThreshold); + + config(['app.server_timing.min_package_time' => 5]); + $responseBelowThreshold = $this->get('/octane-min-package-test'); + $serverTimingBelowThreshold = $this->getServerTimingHeaderValue($responseBelowThreshold); + $this->assertStringContainsString('foour32507-octane-package;dur=', $serverTimingBelowThreshold); + } + public function testServerTimingIfIsDisabled() { config(['app.server_timing.enabled' => false]);