From 987a11373d9f4892d27b7e3e6e19720bb0f61157 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Fri, 2 Aug 2024 18:14:24 +0100 Subject: [PATCH 1/2] fix: RoadRunner streaming through generators --- src/RoadRunner/RoadRunnerClient.php | 16 ++++++++++++++++ tests/RoadRunnerClientTest.php | 29 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/RoadRunner/RoadRunnerClient.php b/src/RoadRunner/RoadRunnerClient.php index dd76523e8..31c40249f 100644 --- a/src/RoadRunner/RoadRunnerClient.php +++ b/src/RoadRunner/RoadRunnerClient.php @@ -2,6 +2,7 @@ namespace Laravel\Octane\RoadRunner; +use Generator; use Illuminate\Foundation\Application; use Illuminate\Http\Request; use Laravel\Octane\Contracts\Client; @@ -10,6 +11,7 @@ use Laravel\Octane\Octane; use Laravel\Octane\OctaneResponse; use Laravel\Octane\RequestContext; +use ReflectionFunction; use Spiral\RoadRunner\Http\PSR7Worker; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\StreamedResponse; @@ -47,6 +49,20 @@ public function respond(RequestContext $context, OctaneResponse $octaneResponse) ); } + if ( + ($octaneResponse->response instanceof StreamedResponse) && + ($responseCallback = $octaneResponse->response->getCallback()) && + (((new ReflectionFunction($responseCallback))->getReturnType()?->getName()) === Generator::class) + ) { + $this->client->getHttpWorker()->respond( + $octaneResponse->response->getStatusCode(), + $responseCallback(), + $this->toPsr7Response($octaneResponse->response)->getHeaders(), + ); + + return; + } + $this->client->respond($this->toPsr7Response($octaneResponse->response)); } diff --git a/tests/RoadRunnerClientTest.php b/tests/RoadRunnerClientTest.php index 380f4d71c..b484c01c7 100644 --- a/tests/RoadRunnerClientTest.php +++ b/tests/RoadRunnerClientTest.php @@ -3,6 +3,8 @@ namespace Laravel\Octane\Tests; use Exception; +use Generator; +use Hamcrest\Core\IsInstanceOf; use Illuminate\Http\Request; use Illuminate\Http\Response; use Laminas\Diactoros\ServerRequestFactory; @@ -11,6 +13,7 @@ use Laravel\Octane\RoadRunner\RoadRunnerClient; use Mockery; use Psr\Http\Message\ResponseInterface; +use Spiral\RoadRunner\Http\HttpWorker; use Spiral\RoadRunner\Http\PSR7Worker; use Symfony\Component\HttpFoundation\StreamedResponse; @@ -63,6 +66,32 @@ public function test_respond_method_send_streamed_response_to_roadrunner() }, 200))); } + /** @doesNotPerformAssertions @test */ + public function test_respond_method_send_streamed_generator_response_to_roadrunner() + { + $client = new RoadRunnerClient($psr7Client = Mockery::mock(PSR7Worker::class)); + + $psr7Request = (new ServerRequestFactory)->createServerRequest('GET', '/home'); + $psr7Request = $psr7Request->withQueryParams(['name' => 'Taylor']); + + $httpWorker = Mockery::mock(HttpWorker::class); + + $responseCallback = function (): Generator { + yield 'Hello World'; + }; + + $psr7Client->shouldReceive('getHttpWorker')->once()->andReturn($httpWorker); + $httpWorker->shouldReceive('respond')->once()->with( + 200, + IsInstanceOf::anInstanceOf(Generator::class), + Mockery::hasKey('cache-control'), + ); + + $client->respond(new RequestContext([ + 'psr7Request' => $psr7Request, + ]), new OctaneResponse(new StreamedResponse($responseCallback, 200))); + } + /** @doesNotPerformAssertions @test */ public function test_error_method_sends_error_response_to_roadrunner() { From 6e43e5d0e828d9adcf8f27c28664f1ac1c4d277d Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Fri, 2 Aug 2024 18:25:34 +0100 Subject: [PATCH 2/2] Update RoadRunnerClient.php --- src/RoadRunner/RoadRunnerClient.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RoadRunner/RoadRunnerClient.php b/src/RoadRunner/RoadRunnerClient.php index 31c40249f..5d2f01a35 100644 --- a/src/RoadRunner/RoadRunnerClient.php +++ b/src/RoadRunner/RoadRunnerClient.php @@ -52,7 +52,7 @@ public function respond(RequestContext $context, OctaneResponse $octaneResponse) if ( ($octaneResponse->response instanceof StreamedResponse) && ($responseCallback = $octaneResponse->response->getCallback()) && - (((new ReflectionFunction($responseCallback))->getReturnType()?->getName()) === Generator::class) + ((new ReflectionFunction($responseCallback))->getReturnType()?->getName() === Generator::class) ) { $this->client->getHttpWorker()->respond( $octaneResponse->response->getStatusCode(),