diff --git a/src/node/ext/call.cc b/src/node/ext/call.cc index fddc1e214f5b72950c40199c754c4e3307fa7e92..560869e6fa420389f1453e22b1c509350a8f6190 100644 --- a/src/node/ext/call.cc +++ b/src/node/ext/call.cc @@ -461,6 +461,9 @@ void Call::Init(Handle<Object> exports) { NanNew<FunctionTemplate>(StartBatch)->GetFunction()); NanSetPrototypeTemplate(tpl, "cancel", NanNew<FunctionTemplate>(Cancel)->GetFunction()); + NanSetPrototypeTemplate( + tpl, "cancelWithStatus", + NanNew<FunctionTemplate>(CancelWithStatus)->GetFunction()); NanSetPrototypeTemplate(tpl, "getPeer", NanNew<FunctionTemplate>(GetPeer)->GetFunction()); NanAssignPersistent(fun_tpl, tpl); @@ -643,6 +646,26 @@ NAN_METHOD(Call::Cancel) { NanReturnUndefined(); } +NAN_METHOD(Call::CancelWithStatus) { + NanScope(); + if (!HasInstance(args.This())) { + return NanThrowTypeError("cancel can only be called on Call objects"); + } + if (!args[0]->IsUint32()) { + return NanThrowTypeError( + "cancelWithStatus's first argument must be a status code"); + } + if (!args[1]->IsString()) { + return NanThrowTypeError( + "cancelWithStatus's second argument must be a string"); + } + Call *call = ObjectWrap::Unwrap<Call>(args.This()); + grpc_status_code code = static_cast<grpc_status_code>(args[0]->Uint32Value()); + NanUtf8String details(args[0]); + grpc_call_cancel_with_status(call->wrapped_call, code, *details, NULL); + NanReturnUndefined(); +} + NAN_METHOD(Call::GetPeer) { NanScope(); if (!HasInstance(args.This())) { diff --git a/src/node/ext/call.h b/src/node/ext/call.h index ef6e5fcd21083353c7d241ca9fee8e8fc8ed0990..89f81dcf4dc3660aadcd4e6bff5836fd6ca49005 100644 --- a/src/node/ext/call.h +++ b/src/node/ext/call.h @@ -133,6 +133,7 @@ class Call : public ::node::ObjectWrap { static NAN_METHOD(New); static NAN_METHOD(StartBatch); static NAN_METHOD(Cancel); + static NAN_METHOD(CancelWithStatus); static NAN_METHOD(GetPeer); static NanCallback *constructor; // Used for typechecking instances of this javascript class diff --git a/src/node/src/client.js b/src/node/src/client.js index e1bed3512e9b12b5dfb5d8917fe66c59bdfdccb4..6a4949091013de486b346764be183f39924dc2c2 100644 --- a/src/node/src/client.js +++ b/src/node/src/client.js @@ -142,7 +142,14 @@ function _read(size) { return; } var data = event.read; - if (self.push(self.deserialize(data)) && data !== null) { + var deserialized; + try { + deserialized = self.deserialize(data); + } catch (e) { + self.call.cancelWithStatus(grpc.status.INTERNAL, + 'Failed to parse server response'); + } + if (self.push(deserialized) && data !== null) { var read_batch = {}; read_batch[grpc.opType.RECV_MESSAGE] = true; self.call.startBatch(read_batch, readCallback); @@ -296,23 +303,38 @@ function makeUnaryRequestFunction(method, serialize, deserialize) { call.startBatch(client_batch, function(err, response) { response.status.metadata = Metadata._fromCoreRepresentation( response.status.metadata); - emitter.emit('status', response.status); - if (response.status.code !== grpc.status.OK) { - var error = new Error(response.status.details); - error.code = response.status.code; - error.metadata = response.status.metadata; - callback(error); - return; - } else { + var status = response.status; + var error; + var deserialized; + if (status.code === grpc.status.OK) { if (err) { // Got a batch error, but OK status. Something went wrong callback(err); return; + } else { + try { + deserialized = deserialize(response.read); + } catch (e) { + /* Change status to indicate bad server response. This will result + * in passing an error to the callback */ + status = { + code: grpc.status.INTERNAL, + details: 'Failed to parse server response' + }; + } } } + if (status.code !== grpc.status.OK) { + error = new Error(response.status.details); + error.code = status.code; + error.metadata = status.metadata; + callback(error); + } else { + callback(null, deserialized); + } + emitter.emit('status', status); emitter.emit('metadata', Metadata._fromCoreRepresentation( response.metadata)); - callback(null, deserialize(response.read)); }); }); return emitter; @@ -374,21 +396,36 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) { call.startBatch(client_batch, function(err, response) { response.status.metadata = Metadata._fromCoreRepresentation( response.status.metadata); - stream.emit('status', response.status); - if (response.status.code !== grpc.status.OK) { - var error = new Error(response.status.details); - error.code = response.status.code; - error.metadata = response.status.metadata; - callback(error); - return; - } else { + var status = response.status; + var error; + var deserialized; + if (status.code === grpc.status.OK) { if (err) { // Got a batch error, but OK status. Something went wrong callback(err); return; + } else { + try { + deserialized = deserialize(response.read); + } catch (e) { + /* Change status to indicate bad server response. This will result + * in passing an error to the callback */ + status = { + code: grpc.status.INTERNAL, + details: 'Failed to parse server response' + }; + } } } - callback(null, deserialize(response.read)); + if (status.code !== grpc.status.OK) { + error = new Error(response.status.details); + error.code = status.code; + error.metadata = status.metadata; + callback(error); + } else { + callback(null, deserialized); + } + stream.emit('status', status); }); }); return stream;