Merge pull request #11304 from murgatroid99/node_call_lifetime_fixes

Add more null checks to call methods
pull/11311/head
Michael Lumish 8 years ago committed by GitHub
commit 3410c8d7dc
  1. 20
      src/node/ext/call.cc
  2. 1
      src/node/ext/call.h
  3. 1
      src/node/test/common_test.js
  4. 25
      src/node/test/surface_test.js

@ -562,10 +562,12 @@ void Call::DestroyCall() {
Call::Call(grpc_call *call) : wrapped_call(call), Call::Call(grpc_call *call) : wrapped_call(call),
pending_batches(0), pending_batches(0),
has_final_op_completed(false) { has_final_op_completed(false) {
peer = grpc_call_get_peer(call);
} }
Call::~Call() { Call::~Call() {
DestroyCall(); DestroyCall();
gpr_free(peer);
} }
void Call::Init(Local<Object> exports) { void Call::Init(Local<Object> exports) {
@ -794,6 +796,11 @@ NAN_METHOD(Call::Cancel) {
return Nan::ThrowTypeError("cancel can only be called on Call objects"); return Nan::ThrowTypeError("cancel can only be called on Call objects");
} }
Call *call = ObjectWrap::Unwrap<Call>(info.This()); Call *call = ObjectWrap::Unwrap<Call>(info.This());
if (call->wrapped_call == NULL) {
/* Cancel is supposed to be idempotent. If the call has already finished,
* cancel should just complete silently */
return;
}
grpc_call_error error = grpc_call_cancel(call->wrapped_call, NULL); grpc_call_error error = grpc_call_cancel(call->wrapped_call, NULL);
if (error != GRPC_CALL_OK) { if (error != GRPC_CALL_OK) {
return Nan::ThrowError(nanErrorWithCode("cancel failed", error)); return Nan::ThrowError(nanErrorWithCode("cancel failed", error));
@ -814,6 +821,11 @@ NAN_METHOD(Call::CancelWithStatus) {
"cancelWithStatus's second argument must be a string"); "cancelWithStatus's second argument must be a string");
} }
Call *call = ObjectWrap::Unwrap<Call>(info.This()); Call *call = ObjectWrap::Unwrap<Call>(info.This());
if (call->wrapped_call == NULL) {
/* Cancel is supposed to be idempotent. If the call has already finished,
* cancel should just complete silently */
return;
}
grpc_status_code code = static_cast<grpc_status_code>( grpc_status_code code = static_cast<grpc_status_code>(
Nan::To<uint32_t>(info[0]).FromJust()); Nan::To<uint32_t>(info[0]).FromJust());
if (code == GRPC_STATUS_OK) { if (code == GRPC_STATUS_OK) {
@ -830,9 +842,7 @@ NAN_METHOD(Call::GetPeer) {
return Nan::ThrowTypeError("getPeer can only be called on Call objects"); return Nan::ThrowTypeError("getPeer can only be called on Call objects");
} }
Call *call = ObjectWrap::Unwrap<Call>(info.This()); Call *call = ObjectWrap::Unwrap<Call>(info.This());
char *peer = grpc_call_get_peer(call->wrapped_call); Local<Value> peer_value = Nan::New(call->peer).ToLocalChecked();
Local<Value> peer_value = Nan::New(peer).ToLocalChecked();
gpr_free(peer);
info.GetReturnValue().Set(peer_value); info.GetReturnValue().Set(peer_value);
} }
@ -847,6 +857,10 @@ NAN_METHOD(Call::SetCredentials) {
"setCredentials' first argument must be a CallCredentials"); "setCredentials' first argument must be a CallCredentials");
} }
Call *call = ObjectWrap::Unwrap<Call>(info.This()); Call *call = ObjectWrap::Unwrap<Call>(info.This());
if (call->wrapped_call == NULL) {
return Nan::ThrowError(
"Cannot set credentials on a call that has already started");
}
CallCredentials *creds_object = ObjectWrap::Unwrap<CallCredentials>( CallCredentials *creds_object = ObjectWrap::Unwrap<CallCredentials>(
Nan::To<Object>(info[0]).ToLocalChecked()); Nan::To<Object>(info[0]).ToLocalChecked());
grpc_call_credentials *creds = creds_object->GetWrappedCredentials(); grpc_call_credentials *creds = creds_object->GetWrappedCredentials();

@ -97,6 +97,7 @@ class Call : public Nan::ObjectWrap {
call, this is GRPC_OP_RECV_STATUS_ON_CLIENT and for a server call, this call, this is GRPC_OP_RECV_STATUS_ON_CLIENT and for a server call, this
is GRPC_OP_SEND_STATUS_FROM_SERVER */ is GRPC_OP_SEND_STATUS_FROM_SERVER */
bool has_final_op_completed; bool has_final_op_completed;
char *peer;
}; };
class Op { class Op {

@ -100,7 +100,6 @@ describe('Proto message long int serialize and deserialize', function() {
var longNumDeserialize = deserializeCls(messages_proto.LongValues, var longNumDeserialize = deserializeCls(messages_proto.LongValues,
num_options); num_options);
var serialized = longSerialize({int_64: pos_value}); var serialized = longSerialize({int_64: pos_value});
console.log(longDeserialize(serialized));
assert.strictEqual(typeof longDeserialize(serialized).int_64, 'string'); assert.strictEqual(typeof longDeserialize(serialized).int_64, 'string');
/* With the longsAsStrings option disabled, long values are represented as /* With the longsAsStrings option disabled, long values are represented as
* objects with 3 keys: low, high, and unsigned */ * objects with 3 keys: low, high, and unsigned */

@ -1110,6 +1110,18 @@ describe('Other conditions', function() {
done(); done();
}); });
}); });
it('after the call has fully completed', function(done) {
var peer;
var call = client.unary({error: false}, function(err, data) {
assert.ifError(err);
setImmediate(function() {
assert.strictEqual(peer, call.getPeer());
done();
});
});
peer = call.getPeer();
assert.strictEqual(typeof peer, 'string');
});
}); });
}); });
describe('Call propagation', function() { describe('Call propagation', function() {
@ -1352,4 +1364,17 @@ describe('Cancelling surface client', function() {
}); });
call.cancel(); call.cancel();
}); });
it('Should be idempotent', function(done) {
var call = client.div({'divisor': 0, 'dividend': 0}, function(err, resp) {
assert.strictEqual(err.code, surface_client.status.CANCELLED);
// Call asynchronously to try cancelling after call is fully completed
setImmediate(function() {
assert.doesNotThrow(function() {
call.cancel();
});
done();
});
});
call.cancel();
});
}); });

Loading…
Cancel
Save