From c4e3f1b7a083ad4dbeb0e1f2ce9083b17f85f3c6 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 19 Jul 2018 15:57:33 -0700 Subject: [PATCH 1/2] Add Channel class and Client channel override options to public API --- packages/grpc-native-core/binding.gyp | 3 +- packages/grpc-native-core/ext/call.cc | 84 ++---------- packages/grpc-native-core/ext/call.h | 2 + packages/grpc-native-core/ext/channel.cc | 127 +++++++++++++++++- packages/grpc-native-core/ext/channel.h | 1 + packages/grpc-native-core/index.d.ts | 65 ++++++++- packages/grpc-native-core/index.js | 83 ++++++++++++ packages/grpc-native-core/src/client.js | 26 ++-- .../src/client_interceptors.js | 4 +- packages/grpc-native-core/src/constants.js | 15 +++ .../templates/binding.gyp.template | 3 +- packages/grpc-native-core/test/call_test.js | 82 +++++------ .../grpc-native-core/test/channel_test.js | 4 +- .../grpc-native-core/test/end_to_end_test.js | 8 +- 14 files changed, 353 insertions(+), 154 deletions(-) diff --git a/packages/grpc-native-core/binding.gyp b/packages/grpc-native-core/binding.gyp index e0b893aa..b52892ac 100644 --- a/packages/grpc-native-core/binding.gyp +++ b/packages/grpc-native-core/binding.gyp @@ -90,7 +90,8 @@ 'PB_FIELD_32BIT', 'GPR_BACKWARDS_COMPATIBILITY_MODE', 'GRPC_ARES=0', - 'GRPC_UV' + 'GRPC_UV', + 'GRPC_NODE_VERSION="1.14.0-dev"' ], 'conditions': [ ['grpc_gcov=="true"', { diff --git a/packages/grpc-native-core/ext/call.cc b/packages/grpc-native-core/ext/call.cc index f73a70d2..8922bc66 100644 --- a/packages/grpc-native-core/ext/call.cc +++ b/packages/grpc-native-core/ext/call.cc @@ -537,6 +537,8 @@ bool Call::HasInstance(Local val) { return Nan::New(fun_tpl)->HasInstance(val); } +grpc_call *Call::GetWrappedCall() { return this->wrapped_call; } + Local Call::WrapStruct(grpc_call *call) { EscapableHandleScope scope; if (call == NULL) { @@ -575,82 +577,20 @@ NAN_METHOD(Call::New) { */ if (info.IsConstructCall()) { Call *call; - if (info[0]->IsExternal()) { - Local ext = info[0].As(); - // This option is used for wrapping an existing call - grpc_call *call_value = reinterpret_cast(ext->Value()); - call = new Call(call_value); - } else { - if (!Channel::HasInstance(info[0])) { - return Nan::ThrowTypeError("Call's first argument must be a Channel"); - } - if (!info[1]->IsString()) { - return Nan::ThrowTypeError("Call's second argument must be a string"); - } - if (!(info[2]->IsNumber() || info[2]->IsDate())) { - return Nan::ThrowTypeError( - "Call's third argument must be a date or a number"); - } - // These arguments are at the end because they are optional - grpc_call *parent_call = NULL; - if (Call::HasInstance(info[4])) { - Call *parent_obj = - ObjectWrap::Unwrap(Nan::To(info[4]).ToLocalChecked()); - parent_call = parent_obj->wrapped_call; - } else if (!(info[4]->IsUndefined() || info[4]->IsNull())) { - return Nan::ThrowTypeError( - "Call's fifth argument must be another call, if provided"); - } - uint32_t propagate_flags = GRPC_PROPAGATE_DEFAULTS; - if (info[5]->IsUint32()) { - propagate_flags = Nan::To(info[5]).FromJust(); - } else if (!(info[5]->IsUndefined() || info[5]->IsNull())) { - return Nan::ThrowTypeError( - "Call's sixth argument must be propagate flags, if provided"); - } - Local channel_object = Nan::To(info[0]).ToLocalChecked(); - Channel *channel = ObjectWrap::Unwrap(channel_object); - if (channel->GetWrappedChannel() == NULL) { - return Nan::ThrowError("Call cannot be created from a closed channel"); - } - double deadline = Nan::To(info[2]).FromJust(); - grpc_channel *wrapped_channel = channel->GetWrappedChannel(); - grpc_call *wrapped_call; - grpc_slice method = - CreateSliceFromString(Nan::To(info[1]).ToLocalChecked()); - if (info[3]->IsString()) { - grpc_slice *host = new grpc_slice; - *host = - CreateSliceFromString(Nan::To(info[3]).ToLocalChecked()); - wrapped_call = grpc_channel_create_call( - wrapped_channel, parent_call, propagate_flags, GetCompletionQueue(), - method, host, MillisecondsToTimespec(deadline), NULL); - delete host; - } else if (info[3]->IsUndefined() || info[3]->IsNull()) { - wrapped_call = grpc_channel_create_call( - wrapped_channel, parent_call, propagate_flags, GetCompletionQueue(), - method, NULL, MillisecondsToTimespec(deadline), NULL); - } else { - return Nan::ThrowTypeError("Call's fourth argument must be a string"); - } - grpc_slice_unref(method); - call = new Call(wrapped_call); - Nan::Set(info.This(), Nan::New("channel_").ToLocalChecked(), - channel_object); + if (!info[0]->IsExternal()) { + return Nan::ThrowTypeError( + "Call can only be created with Channel.createCall"); } + Local ext = info[0].As(); + // This option is used for wrapping an existing call + grpc_call *call_value = reinterpret_cast(ext->Value()); + call = new Call(call_value); call->Wrap(info.This()); info.GetReturnValue().Set(info.This()); + return; } else { - const int argc = 4; - Local argv[argc] = {info[0], info[1], info[2], info[3]}; - MaybeLocal maybe_instance = - Nan::NewInstance(constructor->GetFunction(), argc, argv); - if (maybe_instance.IsEmpty()) { - // There's probably a pending exception - return; - } else { - info.GetReturnValue().Set(maybe_instance.ToLocalChecked()); - } + return Nan::ThrowTypeError( + "Call can only be created with Channel.createCall"); } } diff --git a/packages/grpc-native-core/ext/call.h b/packages/grpc-native-core/ext/call.h index 5bf83a42..f78822d1 100644 --- a/packages/grpc-native-core/ext/call.h +++ b/packages/grpc-native-core/ext/call.h @@ -52,6 +52,8 @@ class Call : public Nan::ObjectWrap { /* Wrap a grpc_call struct in a javascript object */ static v8::Local WrapStruct(grpc_call *call); + grpc_call *GetWrappedCall(); + void CompleteBatch(bool is_final_op); private: diff --git a/packages/grpc-native-core/ext/channel.cc b/packages/grpc-native-core/ext/channel.cc index db6d2b45..6d41c77f 100644 --- a/packages/grpc-native-core/ext/channel.cc +++ b/packages/grpc-native-core/ext/channel.cc @@ -28,6 +28,7 @@ #include "completion_queue.h" #include "grpc/grpc.h" #include "grpc/grpc_security.h" +#include "slice.h" #include "timeval.h" namespace grpc { @@ -56,11 +57,24 @@ using v8::Value; Callback *Channel::constructor; Persistent Channel::fun_tpl; +static const char grpc_node_user_agent[] = "grpc-node/" GRPC_NODE_VERSION; + +void PopulateUserAgentChannelArg(grpc_arg *arg) { + size_t key_len = sizeof(GRPC_ARG_PRIMARY_USER_AGENT_STRING); + size_t val_len = sizeof(grpc_node_user_agent); + arg->key = reinterpret_cast(calloc(key_len, sizeof(char))); + memcpy(arg->key, GRPC_ARG_PRIMARY_USER_AGENT_STRING, key_len); + arg->type = GRPC_ARG_STRING; + arg->value.string = reinterpret_cast(calloc(val_len, sizeof(char))); + memcpy(arg->value.string, grpc_node_user_agent, val_len); + +} + bool ParseChannelArgs(Local args_val, grpc_channel_args **channel_args_ptr) { if (args_val->IsUndefined() || args_val->IsNull()) { - *channel_args_ptr = NULL; - return true; + // Treat null and undefined the same as an empty object + args_val = Nan::New(); } if (!args_val->IsObject()) { *channel_args_ptr = NULL; @@ -72,9 +86,17 @@ bool ParseChannelArgs(Local args_val, Local args_hash = Nan::To(args_val).ToLocalChecked(); Local keys = Nan::GetOwnPropertyNames(args_hash).ToLocalChecked(); channel_args->num_args = keys->Length(); + /* This is an ugly hack to add in the user agent string argument if it wasn't + * passed by the user */ + bool has_user_agent_arg = Nan::HasOwnProperty( + args_hash, Nan::New(GRPC_ARG_PRIMARY_USER_AGENT_STRING).ToLocalChecked() + ).ToChecked(); + if (!has_user_agent_arg) { + channel_args->num_args += 1; + } channel_args->args = reinterpret_cast( calloc(channel_args->num_args, sizeof(grpc_arg))); - for (unsigned int i = 0; i < channel_args->num_args; i++) { + for (unsigned int i = 0; i < keys->Length(); i++) { Local key = Nan::Get(keys, i).ToLocalChecked(); Utf8String key_str(key); if (*key_str == NULL) { @@ -88,10 +110,31 @@ bool ParseChannelArgs(Local args_val, } else if (value->IsString()) { Utf8String val_str(value); channel_args->args[i].type = GRPC_ARG_STRING; - channel_args->args[i].value.string = - reinterpret_cast(calloc(val_str.length() + 1, sizeof(char))); - memcpy(channel_args->args[i].value.string, *val_str, - val_str.length() + 1); + /* Append the grpc-node user agent string after the application user agent + * string, and put the combination at the beginning of the user agent string + */ + if (strcmp(*key_str, GRPC_ARG_PRIMARY_USER_AGENT_STRING) == 0) { + /* val_str.length() is the string length and does not include the + * trailing 0 byte. sizeof(grpc_node_user_agent) is the array length, + * so it does include the trailing 0 byte. */ + size_t val_str_len = val_str.length(); + size_t user_agent_len = sizeof(grpc_node_user_agent); + /* This is the length of the two parts of the string, plus the space in + * between, plus the 0 at the end, which is included in user_agent_len. + */ + channel_args->args[i].value.string = + reinterpret_cast(calloc(val_str_len + user_agent_len + 1, sizeof(char))); + memcpy(channel_args->args[i].value.string, *val_str, + val_str.length()); + channel_args->args[i].value.string[val_str_len] = ' '; + memcpy(channel_args->args[i].value.string + val_str_len + 1, + grpc_node_user_agent, user_agent_len); + } else { + channel_args->args[i].value.string = + reinterpret_cast(calloc(val_str.length() + 1, sizeof(char))); + memcpy(channel_args->args[i].value.string, *val_str, + val_str.length() + 1); + } } else { // The value does not match either of the accepted types return false; @@ -100,6 +143,11 @@ bool ParseChannelArgs(Local args_val, reinterpret_cast(calloc(key_str.length() + 1, sizeof(char))); memcpy(channel_args->args[i].key, *key_str, key_str.length() + 1); } + /* Add a standard user agent string argument if none was provided */ + if (!has_user_agent_arg) { + size_t index = channel_args->num_args - 1; + PopulateUserAgentChannelArg(&channel_args->args[index]); + } return true; } @@ -141,6 +189,7 @@ void Channel::Init(Local exports) { Nan::SetPrototypeMethod(tpl, "getConnectivityState", GetConnectivityState); Nan::SetPrototypeMethod(tpl, "watchConnectivityState", WatchConnectivityState); + Nan::SetPrototypeMethod(tpl, "createCall", CreateCall); fun_tpl.Reset(tpl); Local ctr = Nan::GetFunction(tpl).ToLocalChecked(); Nan::Set(exports, Nan::New("Channel").ToLocalChecked(), ctr); @@ -268,5 +317,69 @@ NAN_METHOD(Channel::WatchConnectivityState) { CompletionQueueNext(); } +NAN_METHOD(Channel::CreateCall) { + /* Arguments: + * 0: Method + * 1: Deadline + * 2: host + * 3: parent Call + * 4: propagation flags + */ + if (!HasInstance(info.This())) { + return Nan::ThrowTypeError( + "createCall can only be called on Channel objects"); + } + if (!info[0]->IsString()){ + return Nan::ThrowTypeError("createCall's first argument must be a string"); + } + if (!(info[1]->IsNumber() || info[1]->IsDate())) { + return Nan::ThrowTypeError( + "createcall's second argument must be a date or a number"); + } + // These arguments are at the end because they are optional + grpc_call *parent_call = NULL; + if (Call::HasInstance(info[3])) { + Call *parent_obj = + ObjectWrap::Unwrap(Nan::To(info[3]).ToLocalChecked()); + parent_call = parent_obj->GetWrappedCall(); + } else if (!(info[3]->IsUndefined() || info[3]->IsNull())) { + return Nan::ThrowTypeError( + "createCall's fourth argument must be another call, if provided"); + } + uint32_t propagate_flags = GRPC_PROPAGATE_DEFAULTS; + if (info[4]->IsUint32()) { + propagate_flags = Nan::To(info[4]).FromJust(); + } else if (!(info[4]->IsUndefined() || info[4]->IsNull())) { + return Nan::ThrowTypeError( + "createCall's fifth argument must be propagate flags, if provided"); + } + Channel *channel = ObjectWrap::Unwrap(info.This()); + grpc_channel *wrapped_channel = channel->GetWrappedChannel(); + if (wrapped_channel == NULL) { + return Nan::ThrowError("Cannot createCall with a closed Channel"); + } + grpc_slice method = + CreateSliceFromString(Nan::To(info[0]).ToLocalChecked()); + double deadline = Nan::To(info[1]).FromJust(); + grpc_call *wrapped_call = NULL; + if (info[2]->IsString()) { + grpc_slice *host = new grpc_slice; + *host = + CreateSliceFromString(Nan::To(info[3]).ToLocalChecked()); + wrapped_call = grpc_channel_create_call( + wrapped_channel, parent_call, propagate_flags, GetCompletionQueue(), + method, host, MillisecondsToTimespec(deadline), NULL); + delete host; + } else if (info[2]->IsUndefined() || info[2]->IsNull()) { + wrapped_call = grpc_channel_create_call( + wrapped_channel, parent_call, propagate_flags, GetCompletionQueue(), + method, NULL, MillisecondsToTimespec(deadline), NULL); + } else { + return Nan::ThrowTypeError("createCall's third argument must be a string"); + } + grpc_slice_unref(method); + info.GetReturnValue().Set(Call::WrapStruct(wrapped_call)); +} + } // namespace node } // namespace grpc diff --git a/packages/grpc-native-core/ext/channel.h b/packages/grpc-native-core/ext/channel.h index a93dbe9d..7925cef4 100644 --- a/packages/grpc-native-core/ext/channel.h +++ b/packages/grpc-native-core/ext/channel.h @@ -56,6 +56,7 @@ class Channel : public Nan::ObjectWrap { static NAN_METHOD(GetTarget); static NAN_METHOD(GetConnectivityState); static NAN_METHOD(WatchConnectivityState); + static NAN_METHOD(CreateCall); static Nan::Callback *constructor; static Nan::Persistent fun_tpl; diff --git a/packages/grpc-native-core/index.d.ts b/packages/grpc-native-core/index.d.ts index 7d8cfdc8..f026cccd 100644 --- a/packages/grpc-native-core/index.d.ts +++ b/packages/grpc-native-core/index.d.ts @@ -1083,11 +1083,6 @@ declare module "grpc" { waitForReady(deadline: Deadline, callback: (error: Error | null) => void): void; } - /** - * A gRPC channel. - */ - export type Channel = any; - /** * Options that can be set on a call. */ @@ -1465,4 +1460,62 @@ declare module "grpc" { */ recvMessageWithContext(context: object): void; } -} + export enum connectivityState { + IDLE = 0, + CONNECTING = 1, + READY = 2, + TRANSIENT_FAILURE = 3, + SHUTDOWN = 4 + } + + export class Channel { + /** + * This constructor API is almost identical to the Client constructor, + * except that some of the options for the Client constructor are not valid + * here. + * @param target The address of the server to connect to + * @param credentials Channel credentials to use when connecting + * @param options A map of channel options that will be passed to the core + */ + constructor(target: string, credentials: ChannelCredentials, options: {[key:string]: string|number}); + /** + * Close the channel. This has the same functionality as the existing grpc.Client.prototype.close + */ + close(): void; + /** + * Return the target that this channel connects to + */ + getTarget(): string; + /** + * Get the channel's current connectivity state. + * @param tryToConnect If true, the channel will start connecting if it is + * idle. Otherwise, idle channels will only start connecting when a + * call starts. + */ + getConnectivityState(tryToConnect: boolean): connectivityState; + /** + * Watch for connectivity state changes. + * @param currentState The state to watch for transitions from. This should + * always be populated by calling getConnectivityState immediately + * before. + * @param deadline A deadline for waiting for a state change + * @param callback Called with no error when a state change, or with an + * error if the deadline passes without a state change. + */ + watchConnectivityState(currentState: connectivityState, deadline: Date|number, callback: (error?: Error) => void); + /** + * Create a call object. Call is an opaque type that is used by the Client + * and Server classes. This function is called by the gRPC library when + * starting a request. Implementers should return an instance of Call that + * is returned from calling createCall on an instance of the provided + * Channel class. + * @param method The full method string to request. + * @param deadline The call deadline + * @param host A host string override for making the request + * @param parentCall A server call to propagate some information from + * @param propagateFlags A bitwise combination of elements of grpc.propagate + * that indicates what information to propagate from parentCall. + */ + createCall(method: string, deadline: Date|number, host: string|null, parentCall: Call|null, propagateFlags: number|null): Call; + } +} \ No newline at end of file diff --git a/packages/grpc-native-core/index.js b/packages/grpc-native-core/index.js index c0d81055..06038535 100644 --- a/packages/grpc-native-core/index.js +++ b/packages/grpc-native-core/index.js @@ -231,6 +231,8 @@ exports.logVerbosity = constants.logVerbosity; exports.methodTypes = constants.methodTypes; +exports.connectivityState = constants.connectivityState; + exports.credentials = require('./src/credentials.js'); /** @@ -289,3 +291,84 @@ exports.closeClient = function closeClient(client_obj) { }; exports.Client = client.Client; + +/** + * @typedef {Object.} grpc~ChannelOptions + */ + +/** + * This constructor API is almost identical to the Client constructor, + * except that some of the options for the Client constructor are not valid + * here. + * @constructor Channel + * @memberof grpc + * @param {string} target The address of the server to connect to + * @param {grpc.ChannelCredentials} credentials Channel credentials to use when connecting + * @param {grpc~ChannelOptions} options A map of channel options that will be passed to the core + */ +exports.Channel = grpc.Channel; + +/** + * Close the channel. This has the same functionality as the existing grpc.Client#close + * @name grpc.Channel#close + * @kind function + */ + +/** + * Return the target that this channel connects to + * @name grpc.Channel#getTarget + * @kind function + * @return {string} The target + */ + +/** + * Get the channel's current connectivity state. + * @name grpc.Channel#getConnectivityState + * @kind function + * @param {boolean} tryToConnect If true, the channel will start connecting if it is + * idle. Otherwise, idle channels will only start connecting when a + * call starts. + * @return {grpc.connectivityState} The current connectivity state + */ + +/** + * @callback grpc.Channel~watchConnectivityStateCallback + * @param {Error?} error + */ + +/** + * Watch for connectivity state changes. + * @name grpc.Channel#watchConnectivityState + * @kind function + * @param {grpc.ConnectivityState} currentState The state to watch for + * transitions from. This should always be populated by calling + * getConnectivityState immediately before. + * @param {grpc~Deadline} deadline A deadline for waiting for a state change + * @param {grpc.Channel~watchConnectivityStateCallback} callback Called with no + * error when the state changes, or with an error if the deadline passes + * without a state change + */ + +/** + * @name grpc~Call + * @kind class + */ + +/** + * Create a call object. Call is an opaque type used by the {@link grpc.Client} + * and {@link grpc.Server} classes. This function is called by the gRPC library + * when starting a request. Implementers should return an instance of Call that + * is returned from calling createCall on an instance of the provided Channel + * class. + * @name grpc.Channel#createCall + * @kind function + * @param {string} method The full method string to request + * @param {grpc~Deadline} deadline The call deadline + * @param {string|null} host A host string override for making the request + * @param {grpc~Call|null} parentCall A server call to propagate some + * information from + * @param {number|null} propagateFlags A bitwise combination of elements of + * {@link grpc.propagate} that indicates what information to propagate + * from parentCall + * @return {grpc~Call} + */ \ No newline at end of file diff --git a/packages/grpc-native-core/src/client.js b/packages/grpc-native-core/src/client.js index 83d7b750..5c293b20 100644 --- a/packages/grpc-native-core/src/client.js +++ b/packages/grpc-native-core/src/client.js @@ -369,15 +369,6 @@ function Client(address, credentials, options) { if (!options) { options = {}; } - /* Append the grpc-node user agent string after the application user agent - * string, and put the combination at the beginning of the user agent string - */ - if (options['grpc.primary_user_agent']) { - options['grpc.primary_user_agent'] += ' '; - } else { - options['grpc.primary_user_agent'] = ''; - } - options['grpc.primary_user_agent'] += 'grpc-node/' + version; // Resolve interceptor options and assign interceptors to each method if (_.isArray(options.interceptor_providers) && _.isArray(options.interceptors)) { @@ -392,12 +383,23 @@ function Client(address, credentials, options) { .resolveInterceptorProviders(self.$interceptor_providers, method_definition) .concat(self.$interceptors); }); - // Exclude interceptor options which have already been consumed + let channelOverride = options.channelOverride; + let channelFactoryOverride = options.channelFactoryOverride; + // Exclude channel options which have already been consumed var channel_options = _.omit(options, - ['interceptors', 'interceptor_providers']); + ['interceptors', 'interceptor_providers', + 'channelOverride', 'channelFactoryOverride']); /* Private fields use $ as a prefix instead of _ because it is an invalid * prefix of a method name */ - this.$channel = new grpc.Channel(address, credentials, channel_options); + if (channelOverride) { + this.$channel = options.channelOverride; + } else { + if (channelFactoryOverride) { + this.$channel = channelFactoryOverride(address, credentials, channel_options); + } else { + this.$channel = new grpc.Channel(address, credentials, channel_options); + } + } } exports.Client = Client; diff --git a/packages/grpc-native-core/src/client_interceptors.js b/packages/grpc-native-core/src/client_interceptors.js index ddd14721..f240f9d3 100644 --- a/packages/grpc-native-core/src/client_interceptors.js +++ b/packages/grpc-native-core/src/client_interceptors.js @@ -648,8 +648,8 @@ function getCall(channel, path, options) { if (deadline === undefined) { deadline = Infinity; } - var call = new grpc.Call(channel, path, deadline, host, - parent, propagate_flags); + var call = channel.createCall(path, deadline, host, + parent, propagate_flags); if (credentials) { call.setCredentials(credentials); } diff --git a/packages/grpc-native-core/src/constants.js b/packages/grpc-native-core/src/constants.js index e9df9a31..0d245fe1 100644 --- a/packages/grpc-native-core/src/constants.js +++ b/packages/grpc-native-core/src/constants.js @@ -249,3 +249,18 @@ exports.methodTypes = { SERVER_STREAMING: 2, BIDI_STREAMING: 3 }; + +/** + * Connectivity state values + * @memberof grpc + * @alias grpc.connectivityState + * @readonly + * @enum {number} + */ +exports.connectivityState = { + IDLE: 0, + CONNECTING: 1, + READY: 2, + TRANSIENT_FAILURE: 3, + SHUTDOWN: 4 +}; diff --git a/packages/grpc-native-core/templates/binding.gyp.template b/packages/grpc-native-core/templates/binding.gyp.template index 2146f166..f9abaa7c 100644 --- a/packages/grpc-native-core/templates/binding.gyp.template +++ b/packages/grpc-native-core/templates/binding.gyp.template @@ -82,7 +82,8 @@ 'PB_FIELD_32BIT', 'GPR_BACKWARDS_COMPATIBILITY_MODE', 'GRPC_ARES=0', - 'GRPC_UV' + 'GRPC_UV', + 'GRPC_NODE_VERSION="${settings.get('node_version', settings.version)}"' ], 'conditions': [ ['grpc_gcov=="true"', { diff --git a/packages/grpc-native-core/test/call_test.js b/packages/grpc-native-core/test/call_test.js index b5246c4f..f2d1a6ca 100644 --- a/packages/grpc-native-core/test/call_test.js +++ b/packages/grpc-native-core/test/call_test.js @@ -49,60 +49,48 @@ describe('call', function() { after(function() { server.forceShutdown(); }); - describe('constructor', function() { - it('should reject anything less than 3 arguments', function() { + describe('factory', function() { + it('should reject anything less than 2 arguments', function() { assert.throws(function() { - new grpc.Call(); + channel.createCall(); }, TypeError); assert.throws(function() { - new grpc.Call(channel); - }, TypeError); - assert.throws(function() { - new grpc.Call(channel, 'method'); + channel.createCall('method'); }, TypeError); }); - it('should succeed with a Channel, a string, and a date or number', + it('should succeed with a string and a date or number', function() { assert.doesNotThrow(function() { - new grpc.Call(channel, 'method', new Date()); + channel.createCall('method', new Date()); }); assert.doesNotThrow(function() { - new grpc.Call(channel, 'method', 0); + channel.createCall('method', 0); }); }); - it('should accept an optional fourth string parameter', function() { + it('should accept an optional third string parameter', function() { assert.doesNotThrow(function() { - new grpc.Call(channel, 'method', new Date(), 'host_override'); + channel.createCall('method', new Date(), 'host_override'); }); }); it('should fail with a closed channel', function() { var local_channel = new grpc.Channel('hostname', insecureCreds); local_channel.close(); assert.throws(function() { - new grpc.Call(channel, 'method'); + local_channel.createCall('method', 0); }); }); it('should fail with other types', function() { assert.throws(function() { - new grpc.Call({}, 'method', 0); + channel.createCall(null, 0); }, TypeError); assert.throws(function() { - new grpc.Call(channel, null, 0); + channel.createCall('method', 'now'); }, TypeError); - assert.throws(function() { - new grpc.Call(channel, 'method', 'now'); - }, TypeError); - }); - it('should succeed without the new keyword', function() { - assert.doesNotThrow(function() { - var call = grpc.Call(channel, 'method', new Date()); - assert(call instanceof grpc.Call); - }); }); }); describe('deadline', function() { it('should time out immediately with negative deadline', function(done) { - var call = new grpc.Call(channel, 'method', -Infinity); + var call = channel.createCall('method', -Infinity); var batch = {}; batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; call.startBatch(batch, function(err, response) { @@ -114,7 +102,7 @@ describe('call', function() { }); describe('startBatch', function() { it('should fail without an object and a function', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); assert.throws(function() { call.startBatch(); }); @@ -126,7 +114,7 @@ describe('call', function() { }); }); it('should succeed with an empty object', function(done) { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); assert.doesNotThrow(function() { call.startBatch({}, function(err) { assert.ifError(err); @@ -137,7 +125,7 @@ describe('call', function() { }); describe('startBatch with metadata', function() { it('should succeed with a map of strings to string arrays', function(done) { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); assert.doesNotThrow(function() { var batch = {}; batch[grpc.opType.SEND_INITIAL_METADATA] = {'key1': ['value1'], @@ -150,7 +138,7 @@ describe('call', function() { }); }); it('should succeed with a map of strings to buffer arrays', function(done) { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); assert.doesNotThrow(function() { var batch = {}; batch[grpc.opType.SEND_INITIAL_METADATA] = { @@ -165,7 +153,7 @@ describe('call', function() { }); }); it('should fail with other parameter types', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); assert.throws(function() { var batch = {}; batch[grpc.opType.SEND_INITIAL_METADATA] = undefined; @@ -190,7 +178,7 @@ describe('call', function() { }); describe('startBatch with message', function() { it('should fail with null argument', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); assert.throws(function() { var batch = {}; batch[grpc.opType.SEND_MESSAGE] = null; @@ -198,7 +186,7 @@ describe('call', function() { }, TypeError); }); it('should fail with numeric argument', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); assert.throws(function() { var batch = {}; batch[grpc.opType.SEND_MESSAGE] = 5; @@ -206,7 +194,7 @@ describe('call', function() { }, TypeError); }); it('should fail with string argument', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); assert.throws(function() { var batch = {}; batch[grpc.opType.SEND_MESSAGE] = 'value'; @@ -216,7 +204,7 @@ describe('call', function() { }); describe('startBatch with status', function() { it('should fail without a code', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); assert.throws(function() { var batch = {}; batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { @@ -227,7 +215,7 @@ describe('call', function() { }, TypeError); }); it('should fail without details', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); assert.throws(function() { var batch = {}; batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { @@ -238,7 +226,7 @@ describe('call', function() { }, TypeError); }); it('should fail without metadata', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); assert.throws(function() { var batch = {}; batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { @@ -249,7 +237,7 @@ describe('call', function() { }, TypeError); }); it('should fail with incorrectly typed code argument', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); assert.throws(function() { var batch = {}; batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { @@ -261,7 +249,7 @@ describe('call', function() { }, TypeError); }); it('should fail with incorrectly typed details argument', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); assert.throws(function() { var batch = {}; batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { @@ -273,7 +261,7 @@ describe('call', function() { }, TypeError); }); it('should fail with incorrectly typed metadata argument', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); assert.throws(function() { var batch = {}; batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { @@ -287,7 +275,7 @@ describe('call', function() { }); describe('cancel', function() { it('should succeed', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); assert.doesNotThrow(function() { call.cancel(); }); @@ -296,30 +284,30 @@ describe('call', function() { describe('cancelWithStatus', function() { it('should reject anything other than an integer and a string', function() { assert.doesNotThrow(function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); call.cancelWithStatus(1, 'details'); }); assert.throws(function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); call.cancelWithStatus(); }); assert.throws(function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); call.cancelWithStatus(''); }); assert.throws(function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); call.cancelWithStatus(5, {}); }); }); it('should reject the OK status code', function() { assert.throws(function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); call.cancelWithStatus(0, 'details'); }); }); it('should result in the call ending with a status', function(done) { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); var batch = {}; batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; call.startBatch(batch, function(err, response) { @@ -332,7 +320,7 @@ describe('call', function() { }); describe('getPeer', function() { it('should return a string', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); + var call = channel.createCall('method', getDeadline(1)); assert.strictEqual(typeof call.getPeer(), 'string'); }); }); diff --git a/packages/grpc-native-core/test/channel_test.js b/packages/grpc-native-core/test/channel_test.js index 373c5ac3..8c95d84a 100644 --- a/packages/grpc-native-core/test/channel_test.js +++ b/packages/grpc-native-core/test/channel_test.js @@ -19,7 +19,7 @@ 'use strict'; var assert = require('assert'); -var grpc = require('../src/grpc_extension'); +var grpc = require('..'); /** * This is used for testing functions with multiple asynchronous calls that @@ -41,7 +41,7 @@ function multiDone(done, count) { } }; } -var insecureCreds = grpc.ChannelCredentials.createInsecure(); +var insecureCreds = grpc.credentials.createInsecure(); describe('channel', function() { describe('constructor', function() { diff --git a/packages/grpc-native-core/test/end_to_end_test.js b/packages/grpc-native-core/test/end_to_end_test.js index c11dfa93..7db373d6 100644 --- a/packages/grpc-native-core/test/end_to_end_test.js +++ b/packages/grpc-native-core/test/end_to_end_test.js @@ -61,7 +61,7 @@ describe('end-to-end', function() { it('should start and end a request without error', function(complete) { var done = multiDone(complete, 2); var status_text = 'xyz'; - var call = new grpc.Call(channel, + var call = channel.createCall( 'dummy_method', Infinity); var client_batch = {}; @@ -111,7 +111,7 @@ describe('end-to-end', function() { it('should successfully send and receive metadata', function(complete) { var done = multiDone(complete, 2); var status_text = 'xyz'; - var call = new grpc.Call(channel, + var call = channel.createCall( 'dummy_method', Infinity); var client_batch = {}; @@ -167,7 +167,7 @@ describe('end-to-end', function() { var reply_text = 'server_response'; var done = multiDone(complete, 2); var status_text = 'success'; - var call = new grpc.Call(channel, + var call = channel.createCall( 'dummy_method', Infinity); var client_batch = {}; @@ -222,7 +222,7 @@ describe('end-to-end', function() { var done = multiDone(complete, 2); var requests = ['req1', 'req2']; var status_text = 'xyz'; - var call = new grpc.Call(channel, + var call = channel.createCall( 'dummy_method', Infinity); var client_batch = {}; From 36f326281b1298d73c4372f4fb50987377f4624b Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Mon, 23 Jul 2018 13:49:58 -0700 Subject: [PATCH 2/2] Fix a function call --- packages/grpc-native-core/ext/channel.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grpc-native-core/ext/channel.cc b/packages/grpc-native-core/ext/channel.cc index 6d41c77f..bd7a6392 100644 --- a/packages/grpc-native-core/ext/channel.cc +++ b/packages/grpc-native-core/ext/channel.cc @@ -90,7 +90,7 @@ bool ParseChannelArgs(Local args_val, * passed by the user */ bool has_user_agent_arg = Nan::HasOwnProperty( args_hash, Nan::New(GRPC_ARG_PRIMARY_USER_AGENT_STRING).ToLocalChecked() - ).ToChecked(); + ).FromJust(); if (!has_user_agent_arg) { channel_args->num_args += 1; }