diff --git a/src/node/ext/call.cc b/src/node/ext/call.cc index e2a0bafe508310305e7040ecdc23af09ba8ab93b..fccb30f547e01544e6e5e0272461d322dc7e7e9c 100644 --- a/src/node/ext/call.cc +++ b/src/node/ext/call.cc @@ -729,13 +729,17 @@ NAN_METHOD(Call::GetPeer) { NAN_METHOD(Call::SetCredentials) { Nan::HandleScope scope; + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError( + "setCredentials can only be called on Call objects"); + } if (!Credentials::HasInstance(info[0])) { return Nan::ThrowTypeError( "setCredentials' first argument must be a credential"); } Call *call = ObjectWrap::Unwrap<Call>(info.This()); Credentials *creds_object = ObjectWrap::Unwrap<Credentials>( - Nan::To<Object>(info[1]).ToLocalChecked()); + Nan::To<Object>(info[0]).ToLocalChecked()); grpc_credentials *creds = creds_object->GetWrappedCredentials(); grpc_call_error error = GRPC_CALL_ERROR; if (creds) { diff --git a/src/node/ext/credentials.cc b/src/node/ext/credentials.cc index eb8c3ea84d22bef021ffffd1ed2b44f9b0c34108..ec5cc5cb0f87b954a235f4c5b5b222682cd0c015 100644 --- a/src/node/ext/credentials.cc +++ b/src/node/ext/credentials.cc @@ -288,12 +288,16 @@ NAN_METHOD(PluginCallback) { return Nan::ThrowTypeError( "The callback's third argument must be an object"); } + shared_ptr<Resources> resources(new Resources); grpc_status_code code = static_cast<grpc_status_code>( Nan::To<uint32_t>(info[0]).FromJust()); - char *details = *Nan::Utf8String(info[1]); + //Utf8String details_str(info[1]); + //char *details = static_cast<char*>(calloc(details_str.length(), sizeof(char))); + //memcpy(details, *details_str, details_str.length()); + char *details = *Utf8String(info[1]); grpc_metadata_array array; if (!CreateMetadataArray(Nan::To<Object>(info[2]).ToLocalChecked(), - &array, shared_ptr<Resources>(new Resources))){ + &array, resources)){ return Nan::ThrowError("Failed to parse metadata"); } grpc_credentials_plugin_metadata_cb cb = @@ -305,7 +309,6 @@ NAN_METHOD(PluginCallback) { Nan::Get(info.Callee(), Nan::New("user_data").ToLocalChecked() ).ToLocalChecked().As<External>()->Value(); - gpr_log(GPR_DEBUG, "Calling plugin metadata callback"); cb(user_data, array.metadata, array.count, code, details); } @@ -329,13 +332,13 @@ NAUV_WORK_CB(SendPluginCallback) { callback->Call(argc, argv); delete data; uv_unref((uv_handle_t *)async); - delete async; + uv_close((uv_handle_t *)async, (uv_close_cb)free); } void plugin_get_metadata(void *state, const char *service_url, grpc_credentials_plugin_metadata_cb cb, void *user_data) { - uv_async_t *async = new uv_async_t; + uv_async_t *async = static_cast<uv_async_t*>(malloc(sizeof(uv_async_t))); uv_async_init(uv_default_loop(), async, SendPluginCallback); @@ -345,6 +348,8 @@ void plugin_get_metadata(void *state, const char *service_url, data->cb = cb; data->user_data = user_data; async->data = data; + /* libuv says that it will coalesce calls to uv_async_send. If there is ever a + * problem with a callback not getting called, that is probably the reason */ uv_async_send(async); } diff --git a/src/node/interop/interop_client.js b/src/node/interop/interop_client.js index 0fae0faa87b6c2faa7b1f736dac6a2f4fef018e3..84cd7aff6e3e1c9143b32cb33d5191e868511ec8 100644 --- a/src/node/interop/interop_client.js +++ b/src/node/interop/interop_client.js @@ -345,7 +345,9 @@ function perRpcAuthTest(expected_user, scope, per_rpc, client, done) { fill_username: true, fill_oauth_scope: true }; - credential = credential.createScoped(scope); + if (credential.createScopedRequired() && scope) { + credential = credential.createScoped(scope); + } var creds = grpc.credentials.createFromGoogleCredential(credential); client.unaryCall(arg, function(err, resp) { assert.ifError(err); diff --git a/src/node/test/channel_test.js b/src/node/test/channel_test.js index d81df2a36d88d4c81ddd9c8d133c42702e79bd26..2e436222d1e26ad6fc8b3e6305cf5941b10cdca8 100644 --- a/src/node/test/channel_test.js +++ b/src/node/test/channel_test.js @@ -149,12 +149,13 @@ describe('channel', function() { afterEach(function() { channel.close(); }); - it('should time out if called alone', function(done) { + it.only('should time out if called alone', function(done) { var old_state = channel.getConnectivityState(); var deadline = new Date(); deadline.setSeconds(deadline.getSeconds() + 1); channel.watchConnectivityState(old_state, deadline, function(err, value) { assert(err); + console.log('Callback from watchConnectivityState'); done(); }); }); diff --git a/src/node/test/credentials_test.js b/src/node/test/credentials_test.js index b18c267ada64a647ed92c695be8249687435d8ee..23f01f866c58ea64e6e821887a0f3f2a8e9bb034 100644 --- a/src/node/test/credentials_test.js +++ b/src/node/test/credentials_test.js @@ -39,7 +39,28 @@ var path = require('path'); var grpc = require('..'); -describe('client credentials', function() { +/** + * 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(); + } + }; +} + +describe.only('client credentials', function() { var Client; var server; var port; @@ -94,7 +115,15 @@ describe('client credentials', function() { after(function() { server.forceShutdown(); }); - it.only('Should update metadata with SSL creds', function(done) { + it('Should accept SSL creds for a client', function(done) { + var client = new Client('localhost:' + port, client_ssl_creds, + client_options); + client.unary({}, function(err, data) { + assert.ifError(err); + done(); + }); + }); + it('Should update metadata with SSL creds', function(done) { var metadataUpdater = function(service_url, callback) { var metadata = new grpc.Metadata(); metadata.set('plugin_key', 'plugin_value'); @@ -103,16 +132,92 @@ describe('client credentials', function() { var creds = grpc.credentials.createFromMetadataGenerator(metadataUpdater); var combined_creds = grpc.credentials.combineCredentials(client_ssl_creds, creds); - //combined_creds = grpc.credentials.createInsecure(); var client = new Client('localhost:' + port, combined_creds, client_options); var call = client.unary({}, function(err, data) { assert.ifError(err); - console.log('Received response'); }); call.on('metadata', function(metadata) { assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); done(); }); }); + it('Should update metadata for two simultaneous calls', function(done) { + done = multiDone(done, 2); + var metadataUpdater = function(service_url, callback) { + var metadata = new grpc.Metadata(); + metadata.set('plugin_key', 'plugin_value'); + callback(null, metadata); + }; + var creds = grpc.credentials.createFromMetadataGenerator(metadataUpdater); + var combined_creds = grpc.credentials.combineCredentials(client_ssl_creds, + creds); + var client = new Client('localhost:' + port, combined_creds, + client_options); + var call = client.unary({}, function(err, data) { + assert.ifError(err); + }); + call.on('metadata', function(metadata) { + assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); + done(); + }); + var call2 = client.unary({}, function(err, data) { + assert.ifError(err); + }); + call2.on('metadata', function(metadata) { + assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); + done(); + }); + }); + describe('Per-rpc creds', function() { + var client; + var updater_creds; + before(function() { + client = new Client('localhost:' + port, client_ssl_creds, + client_options); + var metadataUpdater = function(service_url, callback) { + var metadata = new grpc.Metadata(); + metadata.set('plugin_key', 'plugin_value'); + callback(null, metadata); + }; + updater_creds = grpc.credentials.createFromMetadataGenerator( + metadataUpdater); + }); + it('Should update metadata on a unary call', function(done) { + var call = client.unary({}, function(err, data) { + assert.ifError(err); + }, null, {credentials: updater_creds}); + call.on('metadata', function(metadata) { + assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); + done(); + }); + }); + it('should update metadata on a client streaming call', function(done) { + var call = client.clientStream(function(err, data) { + assert.ifError(err); + }, null, {credentials: updater_creds}); + call.on('metadata', function(metadata) { + assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); + done(); + }); + call.end(); + }); + it('should update metadata on a server streaming call', function(done) { + var call = client.serverStream({}, null, {credentials: updater_creds}); + call.on('data', function() {}); + call.on('metadata', function(metadata) { + assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); + done(); + }); + }); + it('should update metadata on a bidi streaming call', function(done) { + var call = client.bidiStream(null, {credentials: updater_creds}); + call.on('data', function() {}); + call.on('metadata', function(metadata) { + assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']); + done(); + }); + call.end(); + }); + }); });