mirror of https://github.com/grpc/grpc-node.git
Merge pull request #446 from murgatroid99/native_channel_API
Add Channel class and Client channel override options to public API
This commit is contained in:
commit
e66462933a
|
|
@ -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"', {
|
||||
|
|
|
|||
|
|
@ -537,6 +537,8 @@ bool Call::HasInstance(Local<Value> val) {
|
|||
return Nan::New(fun_tpl)->HasInstance(val);
|
||||
}
|
||||
|
||||
grpc_call *Call::GetWrappedCall() { return this->wrapped_call; }
|
||||
|
||||
Local<Value> 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<External> ext = info[0].As<External>();
|
||||
// This option is used for wrapping an existing call
|
||||
grpc_call *call_value = reinterpret_cast<grpc_call *>(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<Call>(Nan::To<Object>(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<uint32_t>(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<Object> channel_object = Nan::To<Object>(info[0]).ToLocalChecked();
|
||||
Channel *channel = ObjectWrap::Unwrap<Channel>(channel_object);
|
||||
if (channel->GetWrappedChannel() == NULL) {
|
||||
return Nan::ThrowError("Call cannot be created from a closed channel");
|
||||
}
|
||||
double deadline = Nan::To<double>(info[2]).FromJust();
|
||||
grpc_channel *wrapped_channel = channel->GetWrappedChannel();
|
||||
grpc_call *wrapped_call;
|
||||
grpc_slice method =
|
||||
CreateSliceFromString(Nan::To<String>(info[1]).ToLocalChecked());
|
||||
if (info[3]->IsString()) {
|
||||
grpc_slice *host = new grpc_slice;
|
||||
*host =
|
||||
CreateSliceFromString(Nan::To<String>(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<External> ext = info[0].As<External>();
|
||||
// This option is used for wrapping an existing call
|
||||
grpc_call *call_value = reinterpret_cast<grpc_call *>(ext->Value());
|
||||
call = new Call(call_value);
|
||||
call->Wrap(info.This());
|
||||
info.GetReturnValue().Set(info.This());
|
||||
return;
|
||||
} else {
|
||||
const int argc = 4;
|
||||
Local<Value> argv[argc] = {info[0], info[1], info[2], info[3]};
|
||||
MaybeLocal<Object> 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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ class Call : public Nan::ObjectWrap {
|
|||
/* Wrap a grpc_call struct in a javascript object */
|
||||
static v8::Local<v8::Value> WrapStruct(grpc_call *call);
|
||||
|
||||
grpc_call *GetWrappedCall();
|
||||
|
||||
void CompleteBatch(bool is_final_op);
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -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<FunctionTemplate> 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<char *>(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<char *>(calloc(val_len, sizeof(char)));
|
||||
memcpy(arg->value.string, grpc_node_user_agent, val_len);
|
||||
|
||||
}
|
||||
|
||||
bool ParseChannelArgs(Local<Value> 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<Object>();
|
||||
}
|
||||
if (!args_val->IsObject()) {
|
||||
*channel_args_ptr = NULL;
|
||||
|
|
@ -72,9 +86,17 @@ bool ParseChannelArgs(Local<Value> args_val,
|
|||
Local<Object> args_hash = Nan::To<Object>(args_val).ToLocalChecked();
|
||||
Local<Array> 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()
|
||||
).FromJust();
|
||||
if (!has_user_agent_arg) {
|
||||
channel_args->num_args += 1;
|
||||
}
|
||||
channel_args->args = reinterpret_cast<grpc_arg *>(
|
||||
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<Value> key = Nan::Get(keys, i).ToLocalChecked();
|
||||
Utf8String key_str(key);
|
||||
if (*key_str == NULL) {
|
||||
|
|
@ -88,10 +110,31 @@ bool ParseChannelArgs(Local<Value> 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<char *>(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<char *>(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<char *>(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<Value> args_val,
|
|||
reinterpret_cast<char *>(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<Object> exports) {
|
|||
Nan::SetPrototypeMethod(tpl, "getConnectivityState", GetConnectivityState);
|
||||
Nan::SetPrototypeMethod(tpl, "watchConnectivityState",
|
||||
WatchConnectivityState);
|
||||
Nan::SetPrototypeMethod(tpl, "createCall", CreateCall);
|
||||
fun_tpl.Reset(tpl);
|
||||
Local<Function> 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<Call>(Nan::To<Object>(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<uint32_t>(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<Channel>(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<String>(info[0]).ToLocalChecked());
|
||||
double deadline = Nan::To<double>(info[1]).FromJust();
|
||||
grpc_call *wrapped_call = NULL;
|
||||
if (info[2]->IsString()) {
|
||||
grpc_slice *host = new grpc_slice;
|
||||
*host =
|
||||
CreateSliceFromString(Nan::To<String>(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
|
||||
|
|
|
|||
|
|
@ -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<v8::FunctionTemplate> fun_tpl;
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
@ -1470,4 +1465,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;
|
||||
}
|
||||
}
|
||||
|
|
@ -236,6 +236,8 @@ exports.logVerbosity = constants.logVerbosity;
|
|||
|
||||
exports.methodTypes = constants.methodTypes;
|
||||
|
||||
exports.connectivityState = constants.connectivityState;
|
||||
|
||||
exports.credentials = require('./src/credentials.js');
|
||||
|
||||
/**
|
||||
|
|
@ -294,3 +296,84 @@ exports.closeClient = function closeClient(client_obj) {
|
|||
};
|
||||
|
||||
exports.Client = client.Client;
|
||||
|
||||
/**
|
||||
* @typedef {Object.<string, string | number>} 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}
|
||||
*/
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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"', {
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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 = {};
|
||||
|
|
|
|||
Loading…
Reference in New Issue