[], 'channel' => null, 'interceptors' => [], 'clientCertSource' => null]; list($addr, $port) = self::normalizeServiceAddress($apiEndpoint); $host = "{$addr}:{$port}"; $stubOpts = $config['stubOpts']; // Set the required 'credentials' key in stubOpts if it is not already set. Use // array_key_exists because null is a valid value. if (!\array_key_exists('credentials', $stubOpts)) { if (isset($config['clientCertSource'])) { list($cert, $key) = self::loadClientCertSource($config['clientCertSource']); $stubOpts['credentials'] = ChannelCredentials::createSsl(null, $key, $cert); } else { $stubOpts['credentials'] = ChannelCredentials::createSsl(); } } $channel = $config['channel']; if (!\is_null($channel) && !$channel instanceof Channel) { throw new ValidationException("Channel argument to GrpcTransport must be of type \\Grpc\\Channel, " . "instead got: " . \print_r($channel, \true)); } try { return new GrpcTransport($host, $stubOpts, $channel, $config['interceptors']); } catch (Exception $ex) { throw new ValidationException("Failed to build GrpcTransport: " . $ex->getMessage(), $ex->getCode(), $ex); } } /** * {@inheritdoc} */ public function startBidiStreamingCall(Call $call, array $options) { $this->verifyUniverseDomain($options); return new BidiStream($this->_bidiRequest('/' . $call->getMethod(), [$call->getDecodeType(), 'decode'], isset($options['headers']) ? $options['headers'] : [], $this->getCallOptions($options)), $call->getDescriptor()); } /** * {@inheritdoc} */ public function startClientStreamingCall(Call $call, array $options) { $this->verifyUniverseDomain($options); return new ClientStream($this->_clientStreamRequest('/' . $call->getMethod(), [$call->getDecodeType(), 'decode'], isset($options['headers']) ? $options['headers'] : [], $this->getCallOptions($options)), $call->getDescriptor()); } /** * {@inheritdoc} */ public function startServerStreamingCall(Call $call, array $options) { $this->verifyUniverseDomain($options); $message = $call->getMessage(); if (!$message) { throw new \InvalidArgumentException('A message is required for ServerStreaming calls.'); } // This simultaenously creates and starts a \Grpc\ServerStreamingCall. $stream = $this->_serverStreamRequest('/' . $call->getMethod(), $message, [$call->getDecodeType(), 'decode'], isset($options['headers']) ? $options['headers'] : [], $this->getCallOptions($options)); return new ServerStream(new ServerStreamingCallWrapper($stream), $call->getDescriptor()); } /** * {@inheritdoc} */ public function startUnaryCall(Call $call, array $options) { $this->verifyUniverseDomain($options); $unaryCall = $this->_simpleRequest('/' . $call->getMethod(), $call->getMessage(), [$call->getDecodeType(), 'decode'], isset($options['headers']) ? $options['headers'] : [], $this->getCallOptions($options)); /** @var Promise $promise */ $promise = new Promise(function () use($unaryCall, $options, &$promise) { list($response, $status) = $unaryCall->wait(); if ($status->code == Code::OK) { if (isset($options['metadataCallback'])) { $metadataCallback = $options['metadataCallback']; $metadataCallback($unaryCall->getMetadata()); } $promise->resolve($response); } else { throw ApiException::createFromStdClass($status); } }, [$unaryCall, 'cancel']); return $promise; } private function verifyUniverseDomain(array $options) { if (isset($options['credentialsWrapper'])) { $options['credentialsWrapper']->checkUniverseDomain(); } } private function getCallOptions(array $options) { $callOptions = $options['transportOptions']['grpcOptions'] ?? []; if (isset($options['credentialsWrapper'])) { $audience = $options['audience'] ?? null; $credentialsWrapper = $options['credentialsWrapper']; $callOptions['call_credentials_callback'] = $credentialsWrapper->getAuthorizationHeaderCallback($audience); } if (isset($options['timeoutMillis'])) { $callOptions['timeout'] = $options['timeoutMillis'] * 1000; } return $callOptions; } private static function loadClientCertSource(callable $clientCertSource) { return \call_user_func($clientCertSource); } }