diff --git a/composer.json b/composer.json index 50c59e9..c5ed610 100644 --- a/composer.json +++ b/composer.json @@ -12,9 +12,10 @@ "require": { "php": ">=7.4", "ext-json": "*", - "symfony/polyfill-php80": "^1.22", + "google/common-protos": "^3.1", "google/protobuf": "^3.7", - "spiral/roadrunner": "^2.0" + "spiral/roadrunner": "^2.0", + "symfony/polyfill-php80": "^1.22" }, "require-dev": { "jetbrains/phpstorm-attributes": "^1.0", diff --git a/src/Server.php b/src/Server.php index 5694619..fa64f90 100644 --- a/src/Server.php +++ b/src/Server.php @@ -12,6 +12,7 @@ namespace Spiral\RoadRunner\GRPC; use Google\Protobuf\Any; +use Google\Rpc\Status; use Spiral\RoadRunner\GRPC\Exception\GRPCException; use Spiral\RoadRunner\GRPC\Exception\GRPCExceptionInterface; use Spiral\RoadRunner\GRPC\Exception\NotFoundException; @@ -148,7 +149,7 @@ public function serve(Worker $worker = null, callable $finalize = null): void $this->workerSend($worker, $answerBody, $answerHeaders); } catch (GRPCExceptionInterface $e) { - $this->workerError($worker, $this->packError($e)); + $this->workerGrpcError($worker, $e); } catch (\Throwable $e) { $this->workerError($worker, $this->isDebugMode() ? (string)$e : $e->getMessage()); } finally { @@ -171,37 +172,32 @@ public function serve(Worker $worker = null, callable $finalize = null): void */ protected function invoke(string $service, string $method, ContextInterface $context, string $body): string { - if (! isset($this->services[$service])) { + if (!isset($this->services[$service])) { throw NotFoundException::create("Service `{$service}` not found.", StatusCode::NOT_FOUND); } return $this->services[$service]->invoke($method, $context, $body); } - /** - * Packs exception message and code into one string. - * - * Internal agreement: - * - * Details will be sent as serialized google.protobuf.Any messages after - * code and exception message separated with |:| delimiter. - * - * @param GRPCExceptionInterface $e - * @return string - */ - private function packError(GRPCExceptionInterface $e): string + private function workerGrpcError(Worker $worker, GRPCExceptionInterface $e): void { - $data = [$e->getCode(), $e->getMessage()]; - - foreach ($e->getDetails() as $detail) { - $anyMessage = new Any(); - - $anyMessage->pack($detail); - - $data[] = $anyMessage->serializeToString(); - } - - return \implode('|:|', $data); + $status = new Status([ + 'code' => $e->getCode(), + 'message' => $e->getMessage(), + 'details' => \array_map( + static function ($detail) { + $message = new Any(); + $message->pack($detail); + + return $message; + }, + $e->getDetails() + ), + ]); + + $this->workerSend($worker, '', Json::encode([ + 'error' => \base64_encode($status->serializeToString()), + ])); } /** diff --git a/tests/ServerTest.php b/tests/ServerTest.php index 9c15472..7cd0519 100644 --- a/tests/ServerTest.php +++ b/tests/ServerTest.php @@ -17,7 +17,7 @@ use Service\TestInterface; use Spiral\Goridge\Frame; use Spiral\Goridge\RelayInterface; -use Spiral\GRPC\Server; +use Spiral\RoadRunner\GRPC\Server; use Spiral\RoadRunner\GRPC\Tests\Stub\TestService; use Spiral\RoadRunner\Worker; @@ -67,7 +67,9 @@ public function testNotFound(): void ); $relay->shouldReceive('send')->once()->withArgs(function (Frame $frame) { - return $frame->payload === '5|:|Service `service.Test2` not found.'; + $error = base64_decode(json_decode($frame->payload, true)['error']); + + return str_contains($error, 'Service `service.Test2` not found.'); }); $this->server->serve( @@ -87,7 +89,9 @@ public function testNotFound2(): void ); $relay->shouldReceive('send')->once()->withArgs(function (Frame $frame) { - return $frame->payload === '5|:|Method `Echo2` not found in service `service.Test`.'; + $error = base64_decode(json_decode($frame->payload, true)['error']); + + return str_contains($error, 'Method `Echo2` not found in service `service.Test`.'); }); $this->server->serve(