From 572a74cb7c2adfb24b956f641feb074456d0f573 Mon Sep 17 00:00:00 2001 From: Arman <407448+armanist@users.noreply.github.com> Date: Tue, 5 May 2026 19:57:15 +0400 Subject: [PATCH 1/3] [#502] Add post-response request-context cleanup --- src/App/Traits/WebAppTrait.php | 11 +++++++++++ tests/Unit/App/Adapters/WebAppAdapterTest.php | 15 +++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/App/Traits/WebAppTrait.php b/src/App/Traits/WebAppTrait.php index a25cab53..354072d4 100644 --- a/src/App/Traits/WebAppTrait.php +++ b/src/App/Traits/WebAppTrait.php @@ -143,5 +143,16 @@ private function sendResponse(Response $response): void { $this->handleCors($response); $response->send(); + $this->cleanupRequestContext(); + } + + /** + * Clears request-scoped state after the response has been sent. + */ + private function cleanupRequestContext(): void + { + request()->setMatchedRoute(null); + request()->flush(); + response()->flush(); } } diff --git a/tests/Unit/App/Adapters/WebAppAdapterTest.php b/tests/Unit/App/Adapters/WebAppAdapterTest.php index 95b8e7fd..2678210f 100644 --- a/tests/Unit/App/Adapters/WebAppAdapterTest.php +++ b/tests/Unit/App/Adapters/WebAppAdapterTest.php @@ -29,6 +29,11 @@ public function testWebAppAdapterStartSuccessfully(): void ob_end_clean(); $this->assertEquals(0, $result); + $this->assertNull(request()->getMatchedRoute()); + $this->assertNull(request()->getUri()); + $this->assertSame([], response()->all()); + $this->assertSame([], response()->allHeaders()); + $this->assertSame(200, response()->getStatusCode()); } public function testWebAppAdapterStartFails(): void @@ -40,6 +45,11 @@ public function testWebAppAdapterStartFails(): void ob_end_clean(); $this->assertSame(0, $result); + $this->assertNull(request()->getMatchedRoute()); + $this->assertNull(request()->getUri()); + $this->assertSame([], response()->all()); + $this->assertSame([], response()->allHeaders()); + $this->assertSame(200, response()->getStatusCode()); } public function testWebAppAdapterHandlesPageNotFoundGracefully(): void @@ -51,5 +61,10 @@ public function testWebAppAdapterHandlesPageNotFoundGracefully(): void ob_end_clean(); $this->assertSame(0, $result); + $this->assertNull(request()->getMatchedRoute()); + $this->assertNull(request()->getUri()); + $this->assertSame([], response()->all()); + $this->assertSame([], response()->allHeaders()); + $this->assertSame(200, response()->getStatusCode()); } } From e73707ae9dcb660cd22bf482fb0225efa208aba1 Mon Sep 17 00:00:00 2001 From: Arman <407448+armanist@users.noreply.github.com> Date: Tue, 5 May 2026 19:58:35 +0400 Subject: [PATCH 2/3] [#502] Return from auth middleware template responses --- src/Module/Templates/DemoApi/src/Middlewares/Auth.php.tpl | 2 +- src/Module/Templates/DemoWeb/src/Middlewares/Auth.php.tpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Module/Templates/DemoApi/src/Middlewares/Auth.php.tpl b/src/Module/Templates/DemoApi/src/Middlewares/Auth.php.tpl index 4f5805db..6d8bc9ab 100644 --- a/src/Module/Templates/DemoApi/src/Middlewares/Auth.php.tpl +++ b/src/Module/Templates/DemoApi/src/Middlewares/Auth.php.tpl @@ -28,7 +28,7 @@ class Auth extends BaseMiddleware public function apply(Request $request, Closure $next): Response { if (!auth()->check()) { - $this->respondWithError($request, + return $this->respondWithError($request, t('validation.unauthorizedRequest'), StatusCode::UNAUTHORIZED ); diff --git a/src/Module/Templates/DemoWeb/src/Middlewares/Auth.php.tpl b/src/Module/Templates/DemoWeb/src/Middlewares/Auth.php.tpl index ae961385..70e0bdba 100644 --- a/src/Module/Templates/DemoWeb/src/Middlewares/Auth.php.tpl +++ b/src/Module/Templates/DemoWeb/src/Middlewares/Auth.php.tpl @@ -28,7 +28,7 @@ class Auth extends Middleware public function apply(Request $request, Closure $next): Response { if (!auth()->check()) { - redirect(base_url(true) . '/' . current_lang() . '/signin'); + return redirect(base_url(true) . '/' . current_lang() . '/signin'); } return $next($request); From b2ef61999622e2a0463fd16e956893fb156515da Mon Sep 17 00:00:00 2001 From: Arman <407448+armanist@users.noreply.github.com> Date: Tue, 5 May 2026 20:20:33 +0400 Subject: [PATCH 3/3] [#502] Ensure request cleanup runs after response send failures --- src/App/Traits/WebAppTrait.php | 9 +++-- tests/Unit/App/Adapters/WebAppAdapterTest.php | 35 +++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/App/Traits/WebAppTrait.php b/src/App/Traits/WebAppTrait.php index 354072d4..ebfdd9ec 100644 --- a/src/App/Traits/WebAppTrait.php +++ b/src/App/Traits/WebAppTrait.php @@ -141,9 +141,12 @@ private function handleCors(Response $response): void */ private function sendResponse(Response $response): void { - $this->handleCors($response); - $response->send(); - $this->cleanupRequestContext(); + try { + $this->handleCors($response); + $response->send(); + } finally { + $this->cleanupRequestContext(); + } } /** diff --git a/tests/Unit/App/Adapters/WebAppAdapterTest.php b/tests/Unit/App/Adapters/WebAppAdapterTest.php index 2678210f..955cb55f 100644 --- a/tests/Unit/App/Adapters/WebAppAdapterTest.php +++ b/tests/Unit/App/Adapters/WebAppAdapterTest.php @@ -2,8 +2,11 @@ namespace Quantum\Tests\Unit\App\Adapters; +use Quantum\Http\Exceptions\HttpException; use Quantum\App\Adapters\WebAppAdapter; use Quantum\Tests\Unit\AppTestCase; +use Quantum\Router\Route; +use Throwable; class WebAppAdapterTest extends AppTestCase { @@ -67,4 +70,36 @@ public function testWebAppAdapterHandlesPageNotFoundGracefully(): void $this->assertSame([], response()->allHeaders()); $this->assertSame(200, response()->getStatusCode()); } + + public function testWebAppAdapterCleansUpOnException(): void + { + request()->create('GET', '/test/am/tests'); + request()->setMatchedRoute(null); + request()->setMatchedRoute(new \Quantum\Router\MatchedRoute( + new Route(['GET'], '/test/am/tests', 'TestController', 'tests'), + [] + )); + response()->setHeader('X-Test', '1'); + response()->json(['foo' => 'bar']); + + $throwingResponse = new class () extends \Quantum\Http\Response { + public function send(): void + { + throw new HttpException('boom'); + } + }; + + try { + $this->invokePrivateMethod($this->webAppAdapter, 'sendResponse', [$throwingResponse]); + $this->fail('Expected response sending to fail.'); + } catch (Throwable $exception) { + $this->assertInstanceOf(HttpException::class, $exception); + } + + $this->assertNull(request()->getMatchedRoute()); + $this->assertNull(request()->getUri()); + $this->assertSame([], response()->all()); + $this->assertSame([], response()->allHeaders()); + $this->assertSame(200, response()->getStatusCode()); + } }