Skip to content

Commit

Permalink
Merge pull request grpc#3099 from murgatroid99/node_interop_echo_meta…
Browse files Browse the repository at this point in the history
…data

Node interop custom_metadata
  • Loading branch information
jtattermusch committed Oct 7, 2015
2 parents cb5b2b8 + f20d7db commit 57ee3db
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 8 deletions.
5 changes: 3 additions & 2 deletions src/node/ext/call.cc
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,9 @@ Local<Value> ParseMetadata(const grpc_metadata_array *metadata_array) {
}
if (EndsWith(elem->key, "-bin")) {
Nan::Set(array, index_map[elem->key],
Nan::CopyBuffer(elem->value,
elem->value_length).ToLocalChecked());
MakeFastBuffer(
Nan::CopyBuffer(elem->value,
elem->value_length).ToLocalChecked()));
} else {
Nan::Set(array, index_map[elem->key],
Nan::New(elem->value).ToLocalChecked());
Expand Down
73 changes: 73 additions & 0 deletions src/node/interop/interop_client.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ var AUTH_USER = ('155450119199-vefjjaekcc6cmsd5914v6lqufunmh9ue' +
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
Expand All @@ -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
Expand Down Expand Up @@ -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[0].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[0].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
Expand Down Expand Up @@ -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),
Expand Down
48 changes: 43 additions & 5 deletions src/node/interop/interop_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -58,7 +89,8 @@ function zeroBuffer(size) {
* or error
*/
function handleEmpty(call, callback) {
callback(null, {});
echoHeader(call);
callback(null, {}, getEchoTrailer(call));
}

/**
Expand All @@ -68,14 +100,16 @@ 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;
if (payload_type === 'RANDOM') {
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));
}

/**
Expand All @@ -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));
});
}

Expand All @@ -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') {
Expand All @@ -113,7 +150,7 @@ function handleStreamingOutput(call) {
}
});
});
call.end();
call.end(getEchoTrailer(call));
}

/**
Expand All @@ -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') {
Expand All @@ -138,7 +176,7 @@ function handleFullDuplex(call) {
});
});
call.on('end', function() {
call.end();
call.end(getEchoTrailer(call));
});
}

Expand Down
5 changes: 4 additions & 1 deletion src/node/src/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ function normalizeKey(key) {
function validate(key, value) {
if (_.endsWith(key, '-bin')) {
if (!(value instanceof Buffer)) {
console.log(value.constructor.toString());
throw new Error('keys that end with \'-bin\' must have Buffer values');
}
} else {
Expand Down Expand Up @@ -173,7 +174,9 @@ Metadata.prototype._getCoreRepresentation = function() {
Metadata._fromCoreRepresentation = function(metadata) {
var newMetadata = new Metadata();
if (metadata) {
newMetadata._internal_repr = _.cloneDeep(metadata);
_.forOwn(metadata, function(value, key) {
newMetadata._internal_repr[key] = _.clone(value);
});
}
return newMetadata;
};
Expand Down
4 changes: 4 additions & 0 deletions src/node/test/interop_sanity_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,8 @@ describe('Interop tests', function() {
interop_client.runTest(port, name_override, 'timeout_on_sleeping_server',
true, true, done);
});
it('should pass custom_metadata', function(done) {
interop_client.runTest(port, name_override, 'custom_metadata',
true, true, done);
});
});

0 comments on commit 57ee3db

Please sign in to comment.