diff --git a/src/App/Traits/WebAppTrait.php b/src/App/Traits/WebAppTrait.php index a25cab53..ebfdd9ec 100644 --- a/src/App/Traits/WebAppTrait.php +++ b/src/App/Traits/WebAppTrait.php @@ -141,7 +141,21 @@ private function handleCors(Response $response): void */ private function sendResponse(Response $response): void { - $this->handleCors($response); - $response->send(); + try { + $this->handleCors($response); + $response->send(); + } finally { + $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/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); diff --git a/tests/Unit/App/Adapters/WebAppAdapterTest.php b/tests/Unit/App/Adapters/WebAppAdapterTest.php index 95b8e7fd..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 { @@ -29,6 +32,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 +48,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 +64,42 @@ 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()); + } + + 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()); } }