<?php declare(strict_types=1); /** * This file is part of Hyperf. * * @link https://www.hyperf.io * @document https://hyperf.wiki * @contact group@hyperf.io * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ namespace Hyperf\JsonRpc; use Hyperf\Context\Context; use Hyperf\Context\ResponseContext; use Hyperf\Contract\PackerInterface; use Hyperf\HttpMessage\Stream\SwooleStream; use Hyperf\Rpc\Contract\DataFormatterInterface; use Hyperf\Rpc\ErrorResponse; use Hyperf\Rpc\Response; use Psr\Http\Message\ServerRequestInterface; use Swow\Psr7\Message\ResponsePlusInterface; use Throwable; class ResponseBuilder { public const SERVER_ERROR = -32000; public const INVALID_REQUEST = -32600; public const METHOD_NOT_FOUND = -32601; public const INVALID_PARAMS = -32602; public const INTERNAL_ERROR = -32603; public const PARSE_ERROR = -32700; public function __construct(protected DataFormatterInterface $dataFormatter, protected PackerInterface $packer) { } public function buildErrorResponse(ServerRequestInterface $request, int $code, ?Throwable $error = null): ResponsePlusInterface { $body = new SwooleStream($this->formatErrorResponse($request, $code, $error)); return $this->response()->addHeader('content-type', 'application/json')->setBody($body); } public function buildResponse(ServerRequestInterface $request, $response): ResponsePlusInterface { $body = new SwooleStream($this->formatResponse($response, $request)); return $this->response() ->addHeader('content-type', 'application/json') ->setBody($body); } public function persistToContext(ResponsePlusInterface $response): ResponsePlusInterface { return ResponseContext::set($response); } protected function formatResponse($response, ServerRequestInterface $request): string { $response = $this->dataFormatter->formatResponse( new Response($request->getAttribute('request_id'), $response) ); return $this->packer->pack($response); } protected function formatErrorResponse(ServerRequestInterface $request, int $code, ?Throwable $error = null): string { [$code, $message] = $this->error($code, $error?->getMessage()); $response = $this->dataFormatter->formatErrorResponse( new ErrorResponse($request->getAttribute('request_id'), $code, $message, $error) ); return $this->packer->pack($response); } protected function error(int $code, ?string $message = null): array { $mapping = [ self::PARSE_ERROR => 'Parse error.', self::INVALID_REQUEST => 'Invalid request.', self::METHOD_NOT_FOUND => 'Method not found.', self::INVALID_PARAMS => 'Invalid params.', self::INTERNAL_ERROR => 'Internal error.', ]; if (isset($mapping[$code])) { return [$code, $mapping[$code]]; } return [$code, $message ?? '']; } /** * Get response instance from context. */ protected function response(): ResponsePlusInterface { return ResponseContext::get(); } }