code !== Grpc\STATUS_OK) { echo "Call did not complete successfully. Status object:\n"; var_dump($status); exit(1); } } /** * Run the empty_unary test. * * @param $stub Stub object that has service methods */ function emptyUnary($stub) { list($result, $status) = $stub->EmptyCall(new Grpc\Testing\EmptyMessage())->wait(); hardAssertIfStatusOk($status); hardAssert($result !== null, 'Call completed with a null response'); } /** * Run the large_unary test. * * @param $stub Stub object that has service methods */ function largeUnary($stub) { performLargeUnary($stub); } /** * Shared code between large unary test and auth test. * * @param $stub Stub object that has service methods * @param $fillUsername boolean whether to fill result with username * @param $fillOauthScope boolean whether to fill result with oauth scope */ function performLargeUnary($stub, $fillUsername = false, $fillOauthScope = false, $callback = false) { $request_len = 271828; $response_len = 314159; $request = new Grpc\Testing\SimpleRequest(); $request->setResponseType(Grpc\Testing\PayloadType::COMPRESSABLE); $request->setResponseSize($response_len); $payload = new Grpc\Testing\Payload(); $payload->setType(Grpc\Testing\PayloadType::COMPRESSABLE); $payload->setBody(str_repeat("\0", $request_len)); $request->setPayload($payload); $request->setFillUsername($fillUsername); $request->setFillOauthScope($fillOauthScope); $options = []; if ($callback) { $options['call_credentials_callback'] = $callback; } list($result, $status) = $stub->UnaryCall($request, [], $options)->wait(); hardAssertIfStatusOk($status); hardAssert($result !== null, 'Call returned a null response'); $payload = $result->getPayload(); hardAssert($payload->getType() === Grpc\Testing\PayloadType::COMPRESSABLE, 'Payload had the wrong type'); hardAssert(strlen($payload->getBody()) === $response_len, 'Payload had the wrong length'); hardAssert($payload->getBody() === str_repeat("\0", $response_len), 'Payload had the wrong content'); return $result; } /** * Run the client_compressed_unary test. * * @param $stub Stub object that has service methods */ function clientCompressedUnary($stub) { $request_len = 271828; $response_len = 314159; $falseBoolValue = new Grpc\Testing\BoolValue(['value' => false]); $trueBoolValue = new Grpc\Testing\BoolValue(['value' => true]); // 1. Probing for compression-checks support $payload = new Grpc\Testing\Payload([ 'body' => str_repeat("\0", $request_len), ]); $request = new Grpc\Testing\SimpleRequest([ 'payload' => $payload, 'response_size' => $response_len, 'expect_compressed' => $trueBoolValue, // lie ]); list($result, $status) = $stub->UnaryCall($request, [], [])->wait(); hardAssert( $status->code === GRPC\STATUS_INVALID_ARGUMENT, 'Received unexpected UnaryCall status code: ' . $status->code ); // 2. with/without compressed message foreach ([true, false] as $compression) { $request->setExpectCompressed($compression ? $trueBoolValue : $falseBoolValue); $metadata = $compression ? [ 'grpc-internal-encoding-request' => ['gzip'], ] : []; list($result, $status) = $stub->UnaryCall($request, $metadata, [])->wait(); hardAssertIfStatusOk($status); hardAssert($result !== null, 'Call returned a null response'); $payload = $result->getPayload(); hardAssert( strlen($payload->getBody()) === $response_len, 'Payload had the wrong length' ); hardAssert( $payload->getBody() === str_repeat("\0", $response_len), 'Payload had the wrong content' ); } } /** * Run the service account credentials auth test. * * @param $stub Stub object that has service methods * @param $args array command line args */ function serviceAccountCreds($stub, $args) { if (!array_key_exists('oauth_scope', $args)) { throw new Exception('Missing oauth scope'); } $jsonKey = json_decode( file_get_contents(getenv(CredentialsLoader::ENV_VAR)), true); $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true); hardAssert($result->getUsername() === $jsonKey['client_email'], 'invalid email returned'); hardAssert(strpos($args['oauth_scope'], $result->getOauthScope()) !== false, 'invalid oauth scope returned'); } /** * Run the compute engine credentials auth test. * Has not been run from gcloud as of 2015-05-05. * * @param $stub Stub object that has service methods * @param $args array command line args */ function computeEngineCreds($stub, $args) { if (!array_key_exists('oauth_scope', $args)) { throw new Exception('Missing oauth scope'); } if (!array_key_exists('default_service_account', $args)) { throw new Exception('Missing default_service_account'); } $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true); hardAssert($args['default_service_account'] === $result->getUsername(), 'invalid email returned'); } /** * Run the jwt token credentials auth test. * * @param $stub Stub object that has service methods * @param $args array command line args */ function jwtTokenCreds($stub, $args) { $jsonKey = json_decode( file_get_contents(getenv(CredentialsLoader::ENV_VAR)), true); $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true); hardAssert($result->getUsername() === $jsonKey['client_email'], 'invalid email returned'); } /** * Run the oauth2_auth_token auth test. * * @param $stub Stub object that has service methods * @param $args array command line args */ function oauth2AuthToken($stub, $args) { $jsonKey = json_decode( file_get_contents(getenv(CredentialsLoader::ENV_VAR)), true); $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true); hardAssert($result->getUsername() === $jsonKey['client_email'], 'invalid email returned'); } function updateAuthMetadataCallback($context) { $authUri = $context->service_url; $methodName = $context->method_name; $auth_credentials = ApplicationDefaultCredentials::getCredentials(); $metadata = []; $result = $auth_credentials->updateMetadata([], $authUri); foreach ($result as $key => $value) { $metadata[strtolower($key)] = $value; } return $metadata; } /** * Run the per_rpc_creds auth test. * * @param $stub Stub object that has service methods * @param $args array command line args */ function perRpcCreds($stub, $args) { $jsonKey = json_decode( file_get_contents(getenv(CredentialsLoader::ENV_VAR)), true); $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true, 'updateAuthMetadataCallback'); hardAssert($result->getUsername() === $jsonKey['client_email'], 'invalid email returned'); } /** * Run the client_streaming test. * * @param $stub Stub object that has service methods */ function clientStreaming($stub) { $request_lengths = [27182, 8, 1828, 45904]; $requests = array_map( function ($length) { $request = new Grpc\Testing\StreamingInputCallRequest(); $payload = new Grpc\Testing\Payload(); $payload->setBody(str_repeat("\0", $length)); $request->setPayload($payload); return $request; }, $request_lengths); $call = $stub->StreamingInputCall(); foreach ($requests as $request) { $call->write($request); } list($result, $status) = $call->wait(); hardAssertIfStatusOk($status); hardAssert($result->getAggregatedPayloadSize() === 74922, 'aggregated_payload_size was incorrect'); } /** * Run the client_compressed_streaming test. * * @param $stub Stub object that has service methods */ function clientCompressedStreaming($stub) { $request_len = 27182; $request2_len = 45904; $response_len = 73086; $falseBoolValue = new Grpc\Testing\BoolValue(['value' => false]); $trueBoolValue = new Grpc\Testing\BoolValue(['value' => true]); // 1. Probing for compression-checks support $payload = new Grpc\Testing\Payload([ 'body' => str_repeat("\0", $request_len), ]); $request = new Grpc\Testing\StreamingInputCallRequest([ 'payload' => $payload, 'expect_compressed' => $trueBoolValue, // lie ]); $call = $stub->StreamingInputCall(); $call->write($request); list($result, $status) = $call->wait(); hardAssert( $status->code === GRPC\STATUS_INVALID_ARGUMENT, 'Received unexpected StreamingInputCall status code: ' . $status->code ); // 2. write compressed message $call = $stub->StreamingInputCall([ 'grpc-internal-encoding-request' => ['gzip'], ]); $request->setExpectCompressed($trueBoolValue); $call->write($request); // 3. write uncompressed message $payload2 = new Grpc\Testing\Payload([ 'body' => str_repeat("\0", $request2_len), ]); $request->setPayload($payload2); $request->setExpectCompressed($falseBoolValue); $call->write($request, [ 'flags' => 0x02 // GRPC_WRITE_NO_COMPRESS ]); // 4. verify response list($result, $status) = $call->wait(); hardAssertIfStatusOk($status); hardAssert( $result->getAggregatedPayloadSize() === $response_len, 'aggregated_payload_size was incorrect' ); } /** * Run the server_streaming test. * * @param $stub Stub object that has service methods. */ function serverStreaming($stub) { $sizes = [31415, 9, 2653, 58979]; $request = new Grpc\Testing\StreamingOutputCallRequest(); $request->setResponseType(Grpc\Testing\PayloadType::COMPRESSABLE); foreach ($sizes as $size) { $response_parameters = new Grpc\Testing\ResponseParameters(); $response_parameters->setSize($size); $request->getResponseParameters()[] = $response_parameters; } $call = $stub->StreamingOutputCall($request); $i = 0; foreach ($call->responses() as $value) { hardAssert($i < 4, 'Too many responses'); $payload = $value->getPayload(); hardAssert( $payload->getType() === Grpc\Testing\PayloadType::COMPRESSABLE, 'Payload '.$i.' had the wrong type'); hardAssert(strlen($payload->getBody()) === $sizes[$i], 'Response '.$i.' had the wrong length'); $i += 1; } hardAssertIfStatusOk($call->getStatus()); } /** * Run the ping_pong test. * * @param $stub Stub object that has service methods. */ function pingPong($stub) { $request_lengths = [27182, 8, 1828, 45904]; $response_lengths = [31415, 9, 2653, 58979]; $call = $stub->FullDuplexCall(); for ($i = 0; $i < 4; ++$i) { $request = new Grpc\Testing\StreamingOutputCallRequest(); $request->setResponseType(Grpc\Testing\PayloadType::COMPRESSABLE); $response_parameters = new Grpc\Testing\ResponseParameters(); $response_parameters->setSize($response_lengths[$i]); $request->getResponseParameters()[] = $response_parameters; $payload = new Grpc\Testing\Payload(); $payload->setBody(str_repeat("\0", $request_lengths[$i])); $request->setPayload($payload); $call->write($request); $response = $call->read(); hardAssert($response !== null, 'Server returned too few responses'); $payload = $response->getPayload(); hardAssert( $payload->getType() === Grpc\Testing\PayloadType::COMPRESSABLE, 'Payload '.$i.' had the wrong type'); hardAssert(strlen($payload->getBody()) === $response_lengths[$i], 'Payload '.$i.' had the wrong length'); } $call->writesDone(); hardAssert($call->read() === null, 'Server returned too many responses'); hardAssertIfStatusOk($call->getStatus()); } /** * Run the empty_stream test. * * @param $stub Stub object that has service methods. */ function emptyStream($stub) { $call = $stub->FullDuplexCall(); $call->writesDone(); hardAssert($call->read() === null, 'Server returned too many responses'); hardAssertIfStatusOk($call->getStatus()); } /** * Run the cancel_after_begin test. * * @param $stub Stub object that has service methods. */ function cancelAfterBegin($stub) { $call = $stub->StreamingInputCall(); $call->cancel(); list($result, $status) = $call->wait(); hardAssert($status->code === Grpc\STATUS_CANCELLED, 'Call status was not CANCELLED'); } /** * Run the cancel_after_first_response test. * * @param $stub Stub object that has service methods. */ function cancelAfterFirstResponse($stub) { $call = $stub->FullDuplexCall(); $request = new Grpc\Testing\StreamingOutputCallRequest(); $request->setResponseType(Grpc\Testing\PayloadType::COMPRESSABLE); $response_parameters = new Grpc\Testing\ResponseParameters(); $response_parameters->setSize(31415); $request->getResponseParameters()[] = $response_parameters; $payload = new Grpc\Testing\Payload(); $payload->setBody(str_repeat("\0", 27182)); $request->setPayload($payload); $call->write($request); $response = $call->read(); $call->cancel(); hardAssert($call->getStatus()->code === Grpc\STATUS_CANCELLED, 'Call status was not CANCELLED'); } function timeoutOnSleepingServer($stub) { $call = $stub->FullDuplexCall([], ['timeout' => 1000]); $request = new Grpc\Testing\StreamingOutputCallRequest(); $request->setResponseType(Grpc\Testing\PayloadType::COMPRESSABLE); $response_parameters = new Grpc\Testing\ResponseParameters(); $response_parameters->setSize(8); $request->getResponseParameters()[] = $response_parameters; $payload = new Grpc\Testing\Payload(); $payload->setBody(str_repeat("\0", 9)); $request->setPayload($payload); $call->write($request); $response = $call->read(); hardAssert($call->getStatus()->code === Grpc\STATUS_DEADLINE_EXCEEDED, 'Call status was not DEADLINE_EXCEEDED'); } function customMetadata($stub) { $ECHO_INITIAL_KEY = 'x-grpc-test-echo-initial'; $ECHO_INITIAL_VALUE = 'test_initial_metadata_value'; $ECHO_TRAILING_KEY = 'x-grpc-test-echo-trailing-bin'; $ECHO_TRAILING_VALUE = 'ababab'; $request_len = 271828; $response_len = 314159; $request = new Grpc\Testing\SimpleRequest(); $request->setResponseType(Grpc\Testing\PayloadType::COMPRESSABLE); $request->setResponseSize($response_len); $payload = new Grpc\Testing\Payload(); $payload->setType(Grpc\Testing\PayloadType::COMPRESSABLE); $payload->setBody(str_repeat("\0", $request_len)); $request->setPayload($payload); $metadata = [ $ECHO_INITIAL_KEY => [$ECHO_INITIAL_VALUE], $ECHO_TRAILING_KEY => [$ECHO_TRAILING_VALUE], ]; $call = $stub->UnaryCall($request, $metadata); $initial_metadata = $call->getMetadata(); hardAssert(array_key_exists($ECHO_INITIAL_KEY, $initial_metadata), 'Initial metadata does not contain expected key'); hardAssert( $initial_metadata[$ECHO_INITIAL_KEY][0] === $ECHO_INITIAL_VALUE, 'Incorrect initial metadata value'); list($result, $status) = $call->wait(); hardAssertIfStatusOk($status); $trailing_metadata = $call->getTrailingMetadata(); hardAssert(array_key_exists($ECHO_TRAILING_KEY, $trailing_metadata), 'Trailing metadata does not contain expected key'); hardAssert( $trailing_metadata[$ECHO_TRAILING_KEY][0] === $ECHO_TRAILING_VALUE, 'Incorrect trailing metadata value'); $streaming_call = $stub->FullDuplexCall($metadata); $streaming_request = new Grpc\Testing\StreamingOutputCallRequest(); $streaming_request->setPayload($payload); $response_parameters = new Grpc\Testing\ResponseParameters(); $response_parameters->setSize($response_len); $streaming_request->getResponseParameters()[] = $response_parameters; $streaming_call->write($streaming_request); $streaming_call->writesDone(); $result = $streaming_call->read(); hardAssertIfStatusOk($streaming_call->getStatus()); $streaming_initial_metadata = $streaming_call->getMetadata(); hardAssert(array_key_exists($ECHO_INITIAL_KEY, $streaming_initial_metadata), 'Initial metadata does not contain expected key'); hardAssert( $streaming_initial_metadata[$ECHO_INITIAL_KEY][0] === $ECHO_INITIAL_VALUE, 'Incorrect initial metadata value'); $streaming_trailing_metadata = $streaming_call->getTrailingMetadata(); hardAssert(array_key_exists($ECHO_TRAILING_KEY, $streaming_trailing_metadata), 'Trailing metadata does not contain expected key'); hardAssert($streaming_trailing_metadata[$ECHO_TRAILING_KEY][0] === $ECHO_TRAILING_VALUE, 'Incorrect trailing metadata value'); } function statusCodeAndMessage($stub) { $echo_status = new Grpc\Testing\EchoStatus(); $echo_status->setCode(2); $echo_status->setMessage('test status message'); $request = new Grpc\Testing\SimpleRequest(); $request->setResponseStatus($echo_status); $call = $stub->UnaryCall($request); list($result, $status) = $call->wait(); hardAssert($status->code === 2, 'Received unexpected UnaryCall status code: '. $status->code); hardAssert($status->details === 'test status message', 'Received unexpected UnaryCall status details: '. $status->details); $streaming_call = $stub->FullDuplexCall(); $streaming_request = new Grpc\Testing\StreamingOutputCallRequest(); $streaming_request->setResponseStatus($echo_status); $streaming_call->write($streaming_request); $streaming_call->writesDone(); $result = $streaming_call->read(); $status = $streaming_call->getStatus(); hardAssert($status->code === 2, 'Received unexpected FullDuplexCall status code: '. $status->code); hardAssert($status->details === 'test status message', 'Received unexpected FullDuplexCall status details: '. $status->details); } function specialStatusMessage($stub) { $test_code = Grpc\STATUS_UNKNOWN; $test_msg = "\t\ntest with whitespace\r\nand Unicode BMP ☺ and non-BMP 😈\t\n"; $echo_status = new Grpc\Testing\EchoStatus(); $echo_status->setCode($test_code); $echo_status->setMessage($test_msg); $request = new Grpc\Testing\SimpleRequest(); $request->setResponseStatus($echo_status); $call = $stub->UnaryCall($request); list($result, $status) = $call->wait(); hardAssert( $status->code === $test_code, 'Received unexpected UnaryCall status code: ' . $status->code ); hardAssert( $status->details === $test_msg, 'Received unexpected UnaryCall status details: ' . $status->details ); } # NOTE: the stub input to this function is from UnimplementedService function unimplementedService($stub) { $call = $stub->UnimplementedCall(new Grpc\Testing\EmptyMessage()); list($result, $status) = $call->wait(); hardAssert($status->code === Grpc\STATUS_UNIMPLEMENTED, 'Received unexpected status code'); } # NOTE: the stub input to this function is from TestService function unimplementedMethod($stub) { $call = $stub->UnimplementedCall(new Grpc\Testing\EmptyMessage()); list($result, $status) = $call->wait(); hardAssert($status->code === Grpc\STATUS_UNIMPLEMENTED, 'Received unexpected status code'); } function _makeStub($args) { if (!array_key_exists('server_host', $args)) { throw new Exception('Missing argument: --server_host is required'); } if (!array_key_exists('server_port', $args)) { throw new Exception('Missing argument: --server_port is required'); } if (!array_key_exists('test_case', $args)) { throw new Exception('Missing argument: --test_case is required'); } $server_address = $args['server_host'].':'.$args['server_port']; $test_case = $args['test_case']; $host_override = ''; if (array_key_exists('server_host_override', $args)) { $host_override = $args['server_host_override']; } $use_tls = false; if (array_key_exists('use_tls', $args) && $args['use_tls'] != 'false') { $use_tls = true; } $use_test_ca = false; if (array_key_exists('use_test_ca', $args) && $args['use_test_ca'] != 'false') { $use_test_ca = true; } $opts = []; if ($use_tls) { if ($use_test_ca) { $ssl_credentials = Grpc\ChannelCredentials::createSsl( file_get_contents(dirname(__FILE__).'/../data/ca.pem')); } else { $ssl_credentials = Grpc\ChannelCredentials::createSsl(); } $opts['credentials'] = $ssl_credentials; if (!empty($host_override)) { $opts['grpc.ssl_target_name_override'] = $host_override; } } else { $opts['credentials'] = Grpc\ChannelCredentials::createInsecure(); } if (in_array($test_case, ['service_account_creds', 'compute_engine_creds', 'jwt_token_creds', ])) { if ($test_case === 'jwt_token_creds') { $auth_credentials = ApplicationDefaultCredentials::getCredentials(); } else { $auth_credentials = ApplicationDefaultCredentials::getCredentials( $args['oauth_scope'] ); } $opts['update_metadata'] = $auth_credentials->getUpdateMetadataFunc(); } if ($test_case === 'oauth2_auth_token') { $auth_credentials = ApplicationDefaultCredentials::getCredentials( $args['oauth_scope'] ); $token = $auth_credentials->fetchAuthToken(); $update_metadata = function ($metadata, $authUri = null, ClientInterface $client = null) use ($token) { $metadata_copy = $metadata; $metadata_copy[CredentialsLoader::AUTH_METADATA_KEY] = [sprintf('%s %s', $token['token_type'], $token['access_token'])]; return $metadata_copy; }; $opts['update_metadata'] = $update_metadata; } if ($test_case === 'unimplemented_service') { $stub = new Grpc\Testing\UnimplementedServiceClient($server_address, $opts); } else { $stub = new Grpc\Testing\TestServiceClient($server_address, $opts); } return $stub; } function interop_main($args, $stub = false) { if (!$stub) { $stub = _makeStub($args); } $test_case = $args['test_case']; echo "Running test case $test_case\n"; switch ($test_case) { case 'empty_unary': emptyUnary($stub); break; case 'large_unary': largeUnary($stub); break; case 'client_streaming': clientStreaming($stub); break; case 'server_streaming': serverStreaming($stub); break; case 'ping_pong': pingPong($stub); break; case 'empty_stream': emptyStream($stub); break; case 'cancel_after_begin': cancelAfterBegin($stub); break; case 'cancel_after_first_response': cancelAfterFirstResponse($stub); break; case 'timeout_on_sleeping_server': timeoutOnSleepingServer($stub); break; case 'custom_metadata': customMetadata($stub); break; case 'status_code_and_message': statusCodeAndMessage($stub); break; case 'special_status_message': specialStatusMessage($stub); case 'unimplemented_service': unimplementedService($stub); break; case 'unimplemented_method': unimplementedMethod($stub); break; case 'service_account_creds': serviceAccountCreds($stub, $args); break; case 'compute_engine_creds': computeEngineCreds($stub, $args); break; case 'jwt_token_creds': jwtTokenCreds($stub, $args); break; case 'oauth2_auth_token': oauth2AuthToken($stub, $args); break; case 'per_rpc_creds': perRpcCreds($stub, $args); break; case 'client_compressed_unary': clientCompressedUnary($stub); break; case 'client_compressed_streaming': clientCompressedStreaming($stub); break; default: echo "Unsupported test case $test_case\n"; exit(1); } return $stub; } if (isset($_SERVER['PHP_SELF']) && preg_match('/interop_client/', $_SERVER['PHP_SELF'])) { $args = getopt('', ['server_host:', 'server_port:', 'test_case:', 'use_tls::', 'use_test_ca::', 'server_host_override:', 'oauth_scope:', 'default_service_account:', ]); interop_main($args); }