From 1eb113c61ed87dbc12d9e76649e206190ccd3c4a Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 27 Aug 2015 09:26:33 -0700 Subject: [PATCH] Add metadata echo functionality to interop server, and corresponding interop test --- src/node/interop/interop_client.js | 73 ++++++++++++++++++++++++++++ src/node/interop/interop_server.js | 48 ++++++++++++++++-- src/node/test/interop_sanity_test.js | 4 ++ 3 files changed, 120 insertions(+), 5 deletions(-) diff --git a/src/node/interop/interop_client.js b/src/node/interop/interop_client.js index 8fb8d669206..14ae045275e 100644 --- a/src/node/interop/interop_client.js +++ b/src/node/interop/interop_client.js @@ -49,6 +49,9 @@ var AUTH_USER = ('155450119199-3psnrh1sdr3d8cpj1v46naggf81mhdnk' + var COMPUTE_ENGINE_USER = ('155450119199-r5aaqa2vqoa9g5mv2m6s3m1l293rlmel' + '@developer.gserviceaccount.com'); +var ECHO_INITIAL_KEY = "x-grpc-test-echo-initial"; +var ECHO_TRAILING_KEY = "x-grpc-test-echo-trailing-bin"; + /** * Create a buffer filled with size zeroes * @param {number} size The length of the buffer @@ -60,6 +63,27 @@ function zeroBuffer(size) { return zeros; } +/** + * This is used for testing functions with multiple asynchronous calls that + * can happen in different orders. This should be passed the number of async + * function invocations that can occur last, and each of those should call this + * function's return value + * @param {function()} done The function that should be called when a test is + * complete. + * @param {number} count The number of calls to the resulting function if the + * test passes. + * @return {function()} The function that should be called at the end of each + * sequence of asynchronous functions. + */ +function multiDone(done, count) { + return function() { + count -= 1; + if (count <= 0) { + done(); + } + }; +} + /** * Run the empty_unary test * @param {Client} client The client to test against @@ -271,6 +295,54 @@ function timeoutOnSleepingServer(client, done) { }); } +function customMetadata(client, done) { + done = multiDone(done, 5); + var metadata = new grpc.Metadata(); + metadata.set(ECHO_INITIAL_KEY, 'test_initial_metadata_value'); + metadata.set(ECHO_TRAILING_KEY, new Buffer('ababab', 'hex')); + var arg = { + response_type: 'COMPRESSABLE', + response_size: 314159, + payload: { + body: zeroBuffer(271828) + } + }; + var streaming_arg = { + payload: { + body: zeroBuffer(271828) + } + }; + var unary = client.unaryCall(arg, function(err, resp) { + assert.ifError(err); + done(); + }, metadata); + unary.on('metadata', function(metadata) { + assert.deepEqual(metadata.get(ECHO_INITIAL_KEY), + ['test_initial_metadata_value']); + done(); + }); + unary.on('status', function(status) { + var echo_trailer = status.metadata.get(ECHO_TRAILING_KEY); + assert(echo_trailer.length > 0); + assert.strictEqual(echo_trailer.toString('hex'), 'ababab'); + done(); + }); + var stream = client.fullDuplexCall(metadata); + stream.on('metadata', function(metadata) { + assert.deepEqual(metadata.get(ECHO_INITIAL_KEY), + ['test_initial_metadata_value']); + done(); + }); + stream.on('status', function(status) { + var echo_trailer = status.metadata.get(ECHO_TRAILING_KEY); + assert(echo_trailer.length > 0); + assert.strictEqual(echo_trailer.toString('hex'), 'ababab'); + done(); + }); + stream.write(streaming_arg); + stream.end(); +} + /** * Run one of the authentication tests. * @param {string} expected_user The expected username in the response @@ -358,6 +430,7 @@ var test_cases = { cancel_after_begin: cancelAfterBegin, cancel_after_first_response: cancelAfterFirstResponse, timeout_on_sleeping_server: timeoutOnSleepingServer, + custom_metadata: customMetadata, compute_engine_creds: _.partial(authTest, COMPUTE_ENGINE_USER, null), service_account_creds: _.partial(authTest, AUTH_USER, AUTH_SCOPE), jwt_token_creds: _.partial(authTest, AUTH_USER, null), diff --git a/src/node/interop/interop_server.js b/src/node/interop/interop_server.js index 99155e99584..c23f2376808 100644 --- a/src/node/interop/interop_server.js +++ b/src/node/interop/interop_server.js @@ -39,6 +39,9 @@ var _ = require('lodash'); var grpc = require('..'); var testProto = grpc.load(__dirname + '/test.proto').grpc.testing; +var ECHO_INITIAL_KEY = "x-grpc-test-echo-initial"; +var ECHO_TRAILING_KEY = "x-grpc-test-echo-trailing-bin"; + /** * Create a buffer filled with size zeroes * @param {number} size The length of the buffer @@ -50,6 +53,34 @@ function zeroBuffer(size) { return zeros; } +/** + * Echos a header metadata item as specified in the interop spec. + * @param {Call} call The call to echo metadata on + */ +function echoHeader(call) { + var echo_initial = call.metadata.get(ECHO_INITIAL_KEY); + if (echo_initial.length > 0) { + var response_metadata = new grpc.Metadata(); + response_metadata.set(ECHO_INITIAL_KEY, echo_initial[0]); + call.sendMetadata(response_metadata); + } +} + +/** + * Gets the trailer metadata that should be echoed when the call is done, + * as specified in the interop spec. + * @param {Call} call The call to get metadata from + * @return {grpc.Metadata} The metadata to send as a trailer + */ +function getEchoTrailer(call) { + var echo_trailer = call.metadata.get(ECHO_TRAILING_KEY); + var response_trailer = new grpc.Metadata(); + if (echo_trailer.length > 0) { + response_trailer.set(ECHO_TRAILING_KEY, echo_trailer[0]); + } + return response_trailer; +} + /** * Respond to an empty parameter with an empty response. * NOTE: this currently does not work due to issue #137 @@ -58,7 +89,8 @@ function zeroBuffer(size) { * or error */ function handleEmpty(call, callback) { - callback(null, {}); + echoHeader(call); + callback(null, {}, getEchoTrailer(call)); } /** @@ -68,6 +100,7 @@ function handleEmpty(call, callback) { * error */ function handleUnary(call, callback) { + echoHeader(call); var req = call.request; var zeros = zeroBuffer(req.response_size); var payload_type = req.response_type; @@ -75,7 +108,8 @@ function handleUnary(call, callback) { payload_type = ['COMPRESSABLE', 'UNCOMPRESSABLE'][Math.random() < 0.5 ? 0 : 1]; } - callback(null, {payload: {type: payload_type, body: zeros}}); + callback(null, {payload: {type: payload_type, body: zeros}}, + getEchoTrailer(call)); } /** @@ -85,12 +119,14 @@ function handleUnary(call, callback) { * error */ function handleStreamingInput(call, callback) { + echoHeader(call); var aggregate_size = 0; call.on('data', function(value) { aggregate_size += value.payload.body.length; }); call.on('end', function() { - callback(null, {aggregated_payload_size: aggregate_size}); + callback(null, {aggregated_payload_size: aggregate_size}, + getEchoTrailer(call)); }); } @@ -99,6 +135,7 @@ function handleStreamingInput(call, callback) { * @param {Call} call Call to handle */ function handleStreamingOutput(call) { + echoHeader(call); var req = call.request; var payload_type = req.response_type; if (payload_type === 'RANDOM') { @@ -113,7 +150,7 @@ function handleStreamingOutput(call) { } }); }); - call.end(); + call.end(getEchoTrailer(call)); } /** @@ -122,6 +159,7 @@ function handleStreamingOutput(call) { * @param {Call} call Call to handle */ function handleFullDuplex(call) { + echoHeader(call); call.on('data', function(value) { var payload_type = value.response_type; if (payload_type === 'RANDOM') { @@ -138,7 +176,7 @@ function handleFullDuplex(call) { }); }); call.on('end', function() { - call.end(); + call.end(getEchoTrailer(call)); }); } diff --git a/src/node/test/interop_sanity_test.js b/src/node/test/interop_sanity_test.js index 2ca07c1d50d..d7b6f3316c8 100644 --- a/src/node/test/interop_sanity_test.js +++ b/src/node/test/interop_sanity_test.js @@ -90,4 +90,8 @@ describe('Interop tests', function() { interop_client.runTest(port, name_override, 'timeout_on_sleeping_server', true, true, done); }); + it.only('should pass custom_metadata', function(done) { + interop_client.runTest(port, name_override, 'custom_metadata', + true, true, done); + }); });