$defaultHeaders */ public function __construct( private readonly ClientInterface $http, private readonly RequestFactoryInterface $requestFactory, private readonly StreamFactoryInterface $streamFactory, private readonly string $baseUrl, private readonly array $defaultHeaders, private readonly ?LoggerInterface $logger = null, ) { } /** * @template T of Message * @param class-string $responseClass * @param array $extraHeaders * @return T * @throws \Openstatus\Sdk\Exception\OpenstatusException */ public function call( string $serviceFqn, string $method, Message $request, string $responseClass, array $extraHeaders = [], ): Message { $url = rtrim($this->baseUrl, '/') . '/rpc/' . $serviceFqn . '/' . $method; $body = $this->streamFactory->createStream($request->serializeToJsonString()); $req = $this->requestFactory->createRequest('POST', $url)->withBody($body); foreach (array_merge($this->defaultHeaders, $extraHeaders) as $name => $value) { $req = $req->withHeader($name, $value); } $started = microtime(true); try { $res = $this->http->sendRequest($req); } catch (ClientExceptionInterface $e) { throw new ServiceUnavailableException( message: 'HTTP transport error: ' . $e->getMessage(), connectCode: 'unavailable', httpStatus: 0, details: [], rawBody: '', previous: $e, ); } $this->logger?->debug('openstatus.rpc', [ 'service' => $serviceFqn, 'method' => $method, 'status' => $res->getStatusCode(), 'ms' => (int) round((microtime(true) - $started) * 1000), ]); if ($res->getStatusCode() >= 400) { ConnectErrorMapper::rethrow($res); } $response = new $responseClass(); $response->mergeFromJsonString((string) $res->getBody()); return $response; } }