LCOV - code coverage report
Current view: top level - node/ext - call.cc (source / functions) Hit Total Coverage
Test: tmp.CaZ6RjdVn2 Lines: 348 359 96.9 %
Date: 2015-12-10 22:15:08 Functions: 59 66 89.4 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  * Copyright 2015, Google Inc.
       4             :  * All rights reserved.
       5             :  *
       6             :  * Redistribution and use in source and binary forms, with or without
       7             :  * modification, are permitted provided that the following conditions are
       8             :  * met:
       9             :  *
      10             :  *     * Redistributions of source code must retain the above copyright
      11             :  * notice, this list of conditions and the following disclaimer.
      12             :  *     * Redistributions in binary form must reproduce the above
      13             :  * copyright notice, this list of conditions and the following disclaimer
      14             :  * in the documentation and/or other materials provided with the
      15             :  * distribution.
      16             :  *     * Neither the name of Google Inc. nor the names of its
      17             :  * contributors may be used to endorse or promote products derived from
      18             :  * this software without specific prior written permission.
      19             :  *
      20             :  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      21             :  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      22             :  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      23             :  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      24             :  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      25             :  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      26             :  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      27             :  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      28             :  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      29             :  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      30             :  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      31             :  *
      32             :  */
      33             : 
      34             : #include <memory>
      35             : #include <vector>
      36             : #include <map>
      37             : 
      38             : #include <node.h>
      39             : 
      40             : #include "grpc/support/log.h"
      41             : #include "grpc/grpc.h"
      42             : #include "grpc/grpc_security.h"
      43             : #include "grpc/support/alloc.h"
      44             : #include "grpc/support/time.h"
      45             : #include "byte_buffer.h"
      46             : #include "call.h"
      47             : #include "channel.h"
      48             : #include "completion_queue_async_worker.h"
      49             : #include "call_credentials.h"
      50             : #include "timeval.h"
      51             : 
      52             : using std::unique_ptr;
      53             : using std::shared_ptr;
      54             : using std::vector;
      55             : 
      56             : namespace grpc {
      57             : namespace node {
      58             : 
      59             : using Nan::Callback;
      60             : using Nan::EscapableHandleScope;
      61             : using Nan::HandleScope;
      62             : using Nan::Maybe;
      63             : using Nan::MaybeLocal;
      64             : using Nan::ObjectWrap;
      65             : using Nan::Persistent;
      66             : using Nan::Utf8String;
      67             : 
      68             : using v8::Array;
      69             : using v8::Boolean;
      70             : using v8::Exception;
      71             : using v8::External;
      72             : using v8::Function;
      73             : using v8::FunctionTemplate;
      74             : using v8::Integer;
      75             : using v8::Local;
      76             : using v8::Number;
      77             : using v8::Object;
      78             : using v8::ObjectTemplate;
      79             : using v8::Uint32;
      80             : using v8::String;
      81             : using v8::Value;
      82             : 
      83             : Callback *Call::constructor;
      84           1 : Persistent<FunctionTemplate> Call::fun_tpl;
      85             : 
      86             : /**
      87             :  * Helper function for throwing errors with a grpc_call_error value.
      88             :  * Modified from the answer by Gus Goose to
      89             :  * http://stackoverflow.com/questions/31794200.
      90             :  */
      91           0 : Local<Value> nanErrorWithCode(const char *msg, grpc_call_error code) {
      92             :   EscapableHandleScope scope;
      93           0 :   Local<Object> err = Nan::Error(msg).As<Object>();
      94           0 :   Nan::Set(err, Nan::New("code").ToLocalChecked(), Nan::New<Uint32>(code));
      95           0 :   return scope.Escape(err);
      96             : }
      97             : 
      98         251 : bool EndsWith(const char *str, const char *substr) {
      99         251 :   return strcmp(str+strlen(str)-strlen(substr), substr) == 0;
     100             : }
     101             : 
     102         261 : bool CreateMetadataArray(Local<Object> metadata, grpc_metadata_array *array,
     103             :                          shared_ptr<Resources> resources) {
     104             :   HandleScope scope;
     105         261 :   grpc_metadata_array_init(array);
     106         261 :   Local<Array> keys = Nan::GetOwnPropertyNames(metadata).ToLocalChecked();
     107         343 :   for (unsigned int i = 0; i < keys->Length(); i++) {
     108             :     Local<String> current_key = Nan::To<String>(
     109         164 :         Nan::Get(keys, i).ToLocalChecked()).ToLocalChecked();
     110          82 :     Local<Value> value_array = Nan::Get(metadata, current_key).ToLocalChecked();
     111          82 :     if (!value_array->IsArray()) {
     112             :       return false;
     113             :     }
     114          82 :     array->capacity += Local<Array>::Cast(value_array)->Length();
     115             :   }
     116             :   array->metadata = reinterpret_cast<grpc_metadata*>(
     117         261 :       gpr_malloc(array->capacity * sizeof(grpc_metadata)));
     118         343 :   for (unsigned int i = 0; i < keys->Length(); i++) {
     119         164 :     Local<String> current_key(keys->Get(i)->ToString());
     120         164 :     Utf8String *utf8_key = new Utf8String(current_key);
     121         164 :     resources->strings.push_back(unique_ptr<Utf8String>(utf8_key));
     122             :     Local<Array> values = Local<Array>::Cast(
     123         164 :         Nan::Get(metadata, current_key).ToLocalChecked());
     124         166 :     for (unsigned int j = 0; j < values->Length(); j++) {
     125          84 :       Local<Value> value = Nan::Get(values, j).ToLocalChecked();
     126          84 :       grpc_metadata *current = &array->metadata[array->count];
     127          84 :       current->key = **utf8_key;
     128             :       // Only allow binary headers for "-bin" keys
     129          84 :       if (EndsWith(current->key, "-bin")) {
     130           6 :         if (::node::Buffer::HasInstance(value)) {
     131           6 :           current->value = ::node::Buffer::Data(value);
     132           6 :           current->value_length = ::node::Buffer::Length(value);
     133          12 :           PersistentValue *handle = new PersistentValue(value);
     134          12 :           resources->handles.push_back(unique_ptr<PersistentValue>(handle));
     135             :         } else {
     136             :           return false;
     137             :         }
     138             :       } else {
     139         156 :         if (value->IsString()) {
     140          78 :           Local<String> string_value = Nan::To<String>(value).ToLocalChecked();
     141         156 :           Utf8String *utf8_value = new Utf8String(string_value);
     142          78 :           resources->strings.push_back(unique_ptr<Utf8String>(utf8_value));
     143          78 :           current->value = **utf8_value;
     144          78 :           current->value_length = string_value->Length();
     145             :         } else {
     146             :           return false;
     147             :         }
     148             :       }
     149          84 :       array->count += 1;
     150             :     }
     151             :   }
     152             :   return true;
     153             : }
     154             : 
     155         272 : Local<Value> ParseMetadata(const grpc_metadata_array *metadata_array) {
     156             :   EscapableHandleScope scope;
     157         272 :   grpc_metadata *metadata_elements = metadata_array->metadata;
     158         272 :   size_t length = metadata_array->count;
     159             :   std::map<const char*, size_t> size_map;
     160             :   std::map<const char*, size_t> index_map;
     161             : 
     162         439 :   for (unsigned int i = 0; i < length; i++) {
     163         167 :     const char *key = metadata_elements[i].key;
     164         167 :     if (size_map.count(key)) {
     165           2 :       size_map[key] += 1;
     166             :     } else {
     167         165 :       size_map[key] = 1;
     168             :     }
     169         167 :     index_map[key] = 0;
     170             :   }
     171         272 :   Local<Object> metadata_object = Nan::New<Object>();
     172         439 :   for (unsigned int i = 0; i < length; i++) {
     173         167 :     grpc_metadata* elem = &metadata_elements[i];
     174         334 :     Local<String> key_string = Nan::New(elem->key).ToLocalChecked();
     175             :     Local<Array> array;
     176         167 :     MaybeLocal<Value> maybe_array = Nan::Get(metadata_object, key_string);
     177         334 :     if (maybe_array.IsEmpty() || !maybe_array.ToLocalChecked()->IsArray()) {
     178         165 :       array = Nan::New<Array>(size_map[elem->key]);
     179             :       Nan::Set(metadata_object, key_string, array);
     180             :     } else {
     181             :       array = Local<Array>::Cast(maybe_array.ToLocalChecked());
     182             :     }
     183         167 :     if (EndsWith(elem->key, "-bin")) {
     184           4 :       Nan::Set(array, index_map[elem->key],
     185             :                MakeFastBuffer(
     186             :                    Nan::CopyBuffer(elem->value,
     187          20 :                                    elem->value_length).ToLocalChecked()));
     188             :     } else {
     189         163 :       Nan::Set(array, index_map[elem->key],
     190         489 :                Nan::New(elem->value).ToLocalChecked());
     191             :     }
     192         167 :     index_map[elem->key] += 1;
     193             :   }
     194         544 :   return scope.Escape(metadata_object);
     195             : }
     196             : 
     197        1121 : Local<Value> Op::GetOpType() const {
     198             :   EscapableHandleScope scope;
     199        4484 :   return scope.Escape(Nan::New(GetTypeString()).ToLocalChecked());
     200             : }
     201             : 
     202        1166 : Op::~Op() {
     203        1166 : }
     204             : 
     205         531 : class SendMetadataOp : public Op {
     206             :  public:
     207         166 :   Local<Value> GetNodeValue() const {
     208             :     EscapableHandleScope scope;
     209         332 :     return scope.Escape(Nan::True());
     210             :   }
     211         177 :   bool ParseOp(Local<Value> value, grpc_op *out,
     212             :                shared_ptr<Resources> resources) {
     213         177 :     if (!value->IsObject()) {
     214             :       return false;
     215             :     }
     216             :     grpc_metadata_array array;
     217         173 :     MaybeLocal<Object> maybe_metadata = Nan::To<Object>(value);
     218         173 :     if (maybe_metadata.IsEmpty()) {
     219             :       return false;
     220             :     }
     221         346 :     if (!CreateMetadataArray(maybe_metadata.ToLocalChecked(),
     222         346 :                              &array, resources)) {
     223             :       return false;
     224             :     }
     225         173 :     out->data.send_initial_metadata.count = array.count;
     226         173 :     out->data.send_initial_metadata.metadata = array.metadata;
     227         173 :     return true;
     228             :   }
     229             :  protected:
     230         166 :   std::string GetTypeString() const {
     231         332 :     return "send_metadata";
     232             :   }
     233             : };
     234             : 
     235             : class SendMessageOp : public Op {
     236             :  public:
     237         382 :   SendMessageOp() {
     238         191 :     send_message = NULL;
     239             :   }
     240         573 :   ~SendMessageOp() {
     241         191 :     if (send_message != NULL) {
     242         191 :       grpc_byte_buffer_destroy(send_message);
     243             :     }
     244         382 :   }
     245         190 :   Local<Value> GetNodeValue() const {
     246             :     EscapableHandleScope scope;
     247         380 :     return scope.Escape(Nan::True());
     248             :   }
     249         191 :   bool ParseOp(Local<Value> value, grpc_op *out,
     250             :                shared_ptr<Resources> resources) {
     251         191 :     if (!::node::Buffer::HasInstance(value)) {
     252             :       return false;
     253             :     }
     254         191 :     Local<Object> object_value = Nan::To<Object>(value).ToLocalChecked();
     255             :     MaybeLocal<Value> maybe_flag_value = Nan::Get(
     256         382 :         object_value, Nan::New("grpcWriteFlags").ToLocalChecked());
     257         191 :     if (!maybe_flag_value.IsEmpty()) {
     258         191 :       Local<Value> flag_value = maybe_flag_value.ToLocalChecked();
     259         191 :       if (flag_value->IsUint32()) {
     260           0 :         Maybe<uint32_t> maybe_flag = Nan::To<uint32_t>(flag_value);
     261           0 :         out->flags = maybe_flag.FromMaybe(0) & GRPC_WRITE_USED_MASK;
     262             :       }
     263             :     }
     264         191 :     send_message = BufferToByteBuffer(value);
     265         191 :     out->data.send_message = send_message;
     266         382 :     PersistentValue *handle = new PersistentValue(value);
     267         382 :     resources->handles.push_back(unique_ptr<PersistentValue>(handle));
     268         191 :     return true;
     269             :   }
     270             :  protected:
     271         190 :   std::string GetTypeString() const {
     272         380 :     return "send_message";
     273             :   }
     274             :  private:
     275             :   grpc_byte_buffer *send_message;
     276             : };
     277             : 
     278         240 : class SendClientCloseOp : public Op {
     279             :  public:
     280          80 :   Local<Value> GetNodeValue() const {
     281             :     EscapableHandleScope scope;
     282         160 :     return scope.Escape(Nan::True());
     283             :   }
     284          80 :   bool ParseOp(Local<Value> value, grpc_op *out,
     285             :                shared_ptr<Resources> resources) {
     286          80 :     return true;
     287             :   }
     288             :  protected:
     289          80 :   std::string GetTypeString() const {
     290         160 :     return "client_close";
     291             :   }
     292             : };
     293             : 
     294         234 : class SendServerStatusOp : public Op {
     295             :  public:
     296          72 :   Local<Value> GetNodeValue() const {
     297             :     EscapableHandleScope scope;
     298         144 :     return scope.Escape(Nan::True());
     299             :   }
     300          78 :   bool ParseOp(Local<Value> value, grpc_op *out,
     301             :                shared_ptr<Resources> resources) {
     302          78 :     if (!value->IsObject()) {
     303             :       return false;
     304             :     }
     305          78 :     Local<Object> server_status = Nan::To<Object>(value).ToLocalChecked();
     306             :     MaybeLocal<Value> maybe_metadata = Nan::Get(
     307         156 :         server_status, Nan::New("metadata").ToLocalChecked());
     308          78 :     if (maybe_metadata.IsEmpty()) {
     309             :       return false;
     310             :     }
     311          78 :     if (!maybe_metadata.ToLocalChecked()->IsObject()) {
     312             :       return false;
     313             :     }
     314             :     Local<Object> metadata = Nan::To<Object>(
     315          78 :         maybe_metadata.ToLocalChecked()).ToLocalChecked();
     316             :     MaybeLocal<Value> maybe_code = Nan::Get(server_status,
     317         156 :                                             Nan::New("code").ToLocalChecked());
     318          78 :     if (maybe_code.IsEmpty()) {
     319             :       return false;
     320             :     }
     321          78 :     if (!maybe_code.ToLocalChecked()->IsUint32()) {
     322             :       return false;
     323             :     }
     324          78 :     uint32_t code = Nan::To<uint32_t>(maybe_code.ToLocalChecked()).FromJust();
     325             :     MaybeLocal<Value> maybe_details = Nan::Get(
     326         156 :         server_status, Nan::New("details").ToLocalChecked());
     327          78 :     if (maybe_details.IsEmpty()) {
     328             :       return false;
     329             :     }
     330         156 :     if (!maybe_details.ToLocalChecked()->IsString()) {
     331             :       return false;
     332             :     }
     333             :     Local<String> details = Nan::To<String>(
     334          78 :         maybe_details.ToLocalChecked()).ToLocalChecked();
     335             :     grpc_metadata_array array;
     336         156 :     if (!CreateMetadataArray(metadata, &array, resources)) {
     337             :       return false;
     338             :     }
     339          78 :     out->data.send_status_from_server.trailing_metadata_count = array.count;
     340          78 :     out->data.send_status_from_server.trailing_metadata = array.metadata;
     341             :     out->data.send_status_from_server.status =
     342          78 :         static_cast<grpc_status_code>(code);
     343         156 :     Utf8String *str = new Utf8String(details);
     344         156 :     resources->strings.push_back(unique_ptr<Utf8String>(str));
     345          78 :     out->data.send_status_from_server.status_details = **str;
     346          78 :     return true;
     347             :   }
     348             :  protected:
     349          72 :   std::string GetTypeString() const {
     350         144 :     return "send_status";
     351             :   }
     352             : };
     353             : 
     354             : class GetMetadataOp : public Op {
     355             :  public:
     356         186 :   GetMetadataOp() {
     357          93 :     grpc_metadata_array_init(&recv_metadata);
     358             :   }
     359             : 
     360         186 :   ~GetMetadataOp() {
     361          93 :     grpc_metadata_array_destroy(&recv_metadata);
     362          93 :   }
     363             : 
     364          90 :   Local<Value> GetNodeValue() const {
     365             :     EscapableHandleScope scope;
     366         180 :     return scope.Escape(ParseMetadata(&recv_metadata));
     367             :   }
     368             : 
     369          93 :   bool ParseOp(Local<Value> value, grpc_op *out,
     370             :                shared_ptr<Resources> resources) {
     371          93 :     out->data.recv_initial_metadata = &recv_metadata;
     372          93 :     return true;
     373             :   }
     374             : 
     375             :  protected:
     376          90 :   std::string GetTypeString() const {
     377         180 :     return "metadata";
     378             :   }
     379             : 
     380             :  private:
     381             :   grpc_metadata_array recv_metadata;
     382             : };
     383             : 
     384             : class ReadMessageOp : public Op {
     385             :  public:
     386         508 :   ReadMessageOp() {
     387         254 :     recv_message = NULL;
     388             :   }
     389         762 :   ~ReadMessageOp() {
     390         254 :     if (recv_message != NULL) {
     391         185 :       grpc_byte_buffer_destroy(recv_message);
     392             :     }
     393         508 :   }
     394         254 :   Local<Value> GetNodeValue() const {
     395             :     EscapableHandleScope scope;
     396         508 :     return scope.Escape(ByteBufferToBuffer(recv_message));
     397             :   }
     398             : 
     399         254 :   bool ParseOp(Local<Value> value, grpc_op *out,
     400             :                shared_ptr<Resources> resources) {
     401         254 :     out->data.recv_message = &recv_message;
     402         254 :     return true;
     403             :   }
     404             : 
     405             :  protected:
     406         254 :   std::string GetTypeString() const {
     407         508 :     return "read";
     408             :   }
     409             : 
     410             :  private:
     411             :   grpc_byte_buffer *recv_message;
     412             : };
     413             : 
     414             : class ClientStatusOp : public Op {
     415             :  public:
     416         190 :   ClientStatusOp() {
     417          95 :     grpc_metadata_array_init(&metadata_array);
     418          95 :     status_details = NULL;
     419          95 :     details_capacity = 0;
     420             :   }
     421             : 
     422         285 :   ~ClientStatusOp() {
     423          95 :     grpc_metadata_array_destroy(&metadata_array);
     424          95 :     gpr_free(status_details);
     425         190 :   }
     426             : 
     427          95 :   bool ParseOp(Local<Value> value, grpc_op *out,
     428             :                shared_ptr<Resources> resources) {
     429          95 :     out->data.recv_status_on_client.trailing_metadata = &metadata_array;
     430          95 :     out->data.recv_status_on_client.status = &status;
     431          95 :     out->data.recv_status_on_client.status_details = &status_details;
     432          95 :     out->data.recv_status_on_client.status_details_capacity = &details_capacity;
     433          95 :     return true;
     434             :   }
     435             : 
     436          95 :   Local<Value> GetNodeValue() const {
     437             :     EscapableHandleScope scope;
     438          95 :     Local<Object> status_obj = Nan::New<Object>();
     439          95 :     Nan::Set(status_obj, Nan::New("code").ToLocalChecked(),
     440         285 :                     Nan::New<Number>(status));
     441          95 :     if (status_details != NULL) {
     442          95 :       Nan::Set(status_obj, Nan::New("details").ToLocalChecked(),
     443         285 :                Nan::New(status_details).ToLocalChecked());
     444             :     }
     445          95 :     Nan::Set(status_obj, Nan::New("metadata").ToLocalChecked(),
     446         285 :              ParseMetadata(&metadata_array));
     447         190 :     return scope.Escape(status_obj);
     448             :   }
     449             :  protected:
     450          95 :   std::string GetTypeString() const {
     451         190 :     return "status";
     452             :   }
     453             :  private:
     454             :   grpc_metadata_array metadata_array;
     455             :   grpc_status_code status;
     456             :   char *status_details;
     457             :   size_t details_capacity;
     458             : };
     459             : 
     460         261 : class ServerCloseResponseOp : public Op {
     461             :  public:
     462          87 :   Local<Value> GetNodeValue() const {
     463             :     EscapableHandleScope scope;
     464         261 :     return scope.Escape(Nan::New<Boolean>(cancelled));
     465             :   }
     466             : 
     467          87 :   bool ParseOp(Local<Value> value, grpc_op *out,
     468             :                shared_ptr<Resources> resources) {
     469          87 :     out->data.recv_close_on_server.cancelled = &cancelled;
     470          87 :     return true;
     471             :   }
     472             : 
     473             :  protected:
     474          87 :   std::string GetTypeString() const {
     475         174 :     return "cancelled";
     476             :   }
     477             : 
     478             :  private:
     479             :   int cancelled;
     480             : };
     481             : 
     482         787 : tag::tag(Callback *callback, OpVec *ops,
     483             :          shared_ptr<Resources> resources) :
     484         787 :     callback(callback), ops(ops), resources(resources){
     485         787 : }
     486             : 
     487        1574 : tag::~tag() {
     488        1574 :   delete callback;
     489         787 :   delete ops;
     490         787 : }
     491             : 
     492         751 : Local<Value> GetTagNodeValue(void *tag) {
     493             :   EscapableHandleScope scope;
     494         751 :   struct tag *tag_struct = reinterpret_cast<struct tag *>(tag);
     495         751 :   Local<Object> tag_obj = Nan::New<Object>();
     496        3374 :   for (vector<unique_ptr<Op> >::iterator it = tag_struct->ops->begin();
     497        3744 :        it != tag_struct->ops->end(); ++it) {
     498        2242 :     Op *op_ptr = it->get();
     499        2242 :     Nan::Set(tag_obj, op_ptr->GetOpType(), op_ptr->GetNodeValue());
     500             :   }
     501        1502 :   return scope.Escape(tag_obj);
     502             : }
     503             : 
     504         787 : Callback *GetTagCallback(void *tag) {
     505         787 :   struct tag *tag_struct = reinterpret_cast<struct tag *>(tag);
     506         787 :   return tag_struct->callback;
     507             : }
     508             : 
     509         787 : void DestroyTag(void *tag) {
     510         787 :   struct tag *tag_struct = reinterpret_cast<struct tag *>(tag);
     511         787 :   delete tag_struct;
     512         787 : }
     513             : 
     514         396 : Call::Call(grpc_call *call) : wrapped_call(call) {
     515         198 : }
     516             : 
     517         189 : Call::~Call() {
     518          63 :   grpc_call_destroy(wrapped_call);
     519         126 : }
     520             : 
     521           1 : void Call::Init(Local<Object> exports) {
     522             :   HandleScope scope;
     523           1 :   Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
     524           1 :   tpl->SetClassName(Nan::New("Call").ToLocalChecked());
     525           2 :   tpl->InstanceTemplate()->SetInternalFieldCount(1);
     526             :   Nan::SetPrototypeMethod(tpl, "startBatch", StartBatch);
     527             :   Nan::SetPrototypeMethod(tpl, "cancel", Cancel);
     528             :   Nan::SetPrototypeMethod(tpl, "cancelWithStatus", CancelWithStatus);
     529             :   Nan::SetPrototypeMethod(tpl, "getPeer", GetPeer);
     530             :   Nan::SetPrototypeMethod(tpl, "setCredentials", SetCredentials);
     531             :   fun_tpl.Reset(tpl);
     532           1 :   Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked();
     533           1 :   Nan::Set(exports, Nan::New("Call").ToLocalChecked(), ctr);
     534           1 :   constructor = new Callback(ctr);
     535           1 : }
     536             : 
     537         808 : bool Call::HasInstance(Local<Value> val) {
     538             :   HandleScope scope;
     539        2424 :   return Nan::New(fun_tpl)->HasInstance(val);
     540             : }
     541             : 
     542          87 : Local<Value> Call::WrapStruct(grpc_call *call) {
     543             :   EscapableHandleScope scope;
     544          87 :   if (call == NULL) {
     545           0 :     return scope.Escape(Nan::Null());
     546             :   }
     547          87 :   const int argc = 1;
     548             :   Local<Value> argv[argc] = {Nan::New<External>(
     549          87 :       reinterpret_cast<void *>(call))};
     550             :   MaybeLocal<Object> maybe_instance = Nan::NewInstance(
     551         174 :       constructor->GetFunction(), argc, argv);
     552          87 :   if (maybe_instance.IsEmpty()) {
     553           0 :     return scope.Escape(Nan::Null());
     554             :   } else {
     555          87 :     return scope.Escape(maybe_instance.ToLocalChecked());
     556             :   }
     557             : }
     558             : 
     559         206 : NAN_METHOD(Call::New) {
     560         412 :   if (info.IsConstructCall()) {
     561             :     Call *call;
     562         410 :     if (info[0]->IsExternal()) {
     563         261 :       Local<External> ext = info[0].As<External>();
     564             :       // This option is used for wrapping an existing call
     565             :       grpc_call *call_value =
     566          87 :           reinterpret_cast<grpc_call *>(ext->Value());
     567          87 :       call = new Call(call_value);
     568             :     } else {
     569         236 :       if (!Channel::HasInstance(info[0])) {
     570           7 :         return Nan::ThrowTypeError("Call's first argument must be a Channel");
     571             :       }
     572         348 :       if (!info[1]->IsString()) {
     573             :         return Nan::ThrowTypeError("Call's second argument must be a string");
     574             :       }
     575         250 :       if (!(info[2]->IsNumber() || info[2]->IsDate())) {
     576             :         return Nan::ThrowTypeError(
     577             :             "Call's third argument must be a date or a number");
     578             :       }
     579             :       // These arguments are at the end because they are optional
     580         111 :       grpc_call *parent_call = NULL;
     581         222 :       if (Call::HasInstance(info[4])) {
     582             :         Call *parent_obj = ObjectWrap::Unwrap<Call>(
     583          12 :             Nan::To<Object>(info[4]).ToLocalChecked());
     584           6 :         parent_call = parent_obj->wrapped_call;
     585         315 :       } else if (!(info[4]->IsUndefined() || info[4]->IsNull())) {
     586             :         return Nan::ThrowTypeError(
     587             :             "Call's fifth argument must be another call, if provided");
     588             :       }
     589         111 :       gpr_uint32 propagate_flags = GRPC_PROPAGATE_DEFAULTS;
     590         222 :       if (info[5]->IsUint32()) {
     591           4 :         propagate_flags = Nan::To<uint32_t>(info[5]).FromJust();
     592         327 :       } else if (!(info[5]->IsUndefined() || info[5]->IsNull())) {
     593             :         return Nan::ThrowTypeError(
     594             :             "Call's sixth argument must be propagate flags, if provided");
     595             :       }
     596         222 :       Local<Object> channel_object = Nan::To<Object>(info[0]).ToLocalChecked();
     597         111 :       Channel *channel = ObjectWrap::Unwrap<Channel>(channel_object);
     598         111 :       if (channel->GetWrappedChannel() == NULL) {
     599             :         return Nan::ThrowError("Call cannot be created from a closed channel");
     600             :       }
     601         111 :       Utf8String method(info[1]);
     602         222 :       double deadline = Nan::To<double>(info[2]).FromJust();
     603         111 :       grpc_channel *wrapped_channel = channel->GetWrappedChannel();
     604             :       grpc_call *wrapped_call;
     605         333 :       if (info[3]->IsString()) {
     606           1 :         Utf8String host_override(info[3]);
     607             :         wrapped_call = grpc_channel_create_call(
     608             :             wrapped_channel, parent_call, propagate_flags,
     609           1 :             CompletionQueueAsyncWorker::GetQueue(), *method,
     610           2 :             *host_override, MillisecondsToTimespec(deadline), NULL);
     611         330 :       } else if (info[3]->IsUndefined() || info[3]->IsNull()) {
     612             :         wrapped_call = grpc_channel_create_call(
     613             :             wrapped_channel, parent_call, propagate_flags,
     614         110 :             CompletionQueueAsyncWorker::GetQueue(), *method,
     615         220 :             NULL, MillisecondsToTimespec(deadline), NULL);
     616             :       } else {
     617             :         return Nan::ThrowTypeError("Call's fourth argument must be a string");
     618             :       }
     619         111 :       call = new Call(wrapped_call);
     620         111 :       info.This()->SetHiddenValue(Nan::New("channel_").ToLocalChecked(),
     621         222 :                                   channel_object);
     622             :     }
     623         396 :     call->Wrap(info.This());
     624         594 :     info.GetReturnValue().Set(info.This());
     625             :   } else {
     626           1 :     const int argc = 4;
     627           5 :     Local<Value> argv[argc] = {info[0], info[1], info[2], info[3]};
     628           1 :     MaybeLocal<Object> maybe_instance = constructor->GetFunction()->NewInstance(
     629           2 :         argc, argv);
     630           1 :     if (maybe_instance.IsEmpty()) {
     631             :       // There's probably a pending exception
     632           0 :       return;
     633             :     } else {
     634           2 :       info.GetReturnValue().Set(maybe_instance.ToLocalChecked());
     635             :     }
     636             :   }
     637             : }
     638             : 
     639         666 : NAN_METHOD(Call::StartBatch) {
     640        1332 :   if (!Call::HasInstance(info.This())) {
     641           7 :     return Nan::ThrowTypeError("startBatch can only be called on Call objects");
     642             :   }
     643        1332 :   if (!info[0]->IsObject()) {
     644             :     return Nan::ThrowError("startBatch's first argument must be an object");
     645             :   }
     646        1328 :   if (!info[1]->IsFunction()) {
     647             :     return Nan::ThrowError("startBatch's second argument must be a callback");
     648             :   }
     649        1989 :   Local<Function> callback_func = info[1].As<Function>();
     650        1326 :   Call *call = ObjectWrap::Unwrap<Call>(info.This());
     651         663 :   shared_ptr<Resources> resources(new Resources);
     652        1326 :   Local<Object> obj = Nan::To<Object>(info[0]).ToLocalChecked();
     653         663 :   Local<Array> keys = Nan::GetOwnPropertyNames(obj).ToLocalChecked();
     654         663 :   size_t nops = keys->Length();
     655        1326 :   vector<grpc_op> ops(nops);
     656         663 :   unique_ptr<OpVec> op_vector(new OpVec());
     657        3428 :   for (unsigned int i = 0; i < nops; i++) {
     658        1055 :     unique_ptr<Op> op;
     659        1055 :     MaybeLocal<Value> maybe_key = Nan::Get(keys, i);
     660        2110 :     if (maybe_key.IsEmpty() || (!maybe_key.ToLocalChecked()->IsUint32())) {
     661             :       return Nan::ThrowError(
     662             :           "startBatch's first argument's keys must be integers");
     663             :     }
     664        1055 :     uint32_t type = Nan::To<uint32_t>(maybe_key.ToLocalChecked()).FromJust();
     665        2110 :     ops[i].op = static_cast<grpc_op_type>(type);
     666        1055 :     ops[i].flags = 0;
     667        1055 :     ops[i].reserved = NULL;
     668        1055 :     switch (type) {
     669             :       case GRPC_OP_SEND_INITIAL_METADATA:
     670         177 :         op.reset(new SendMetadataOp());
     671             :         break;
     672             :       case GRPC_OP_SEND_MESSAGE:
     673         191 :         op.reset(new SendMessageOp());
     674             :         break;
     675             :       case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
     676          80 :         op.reset(new SendClientCloseOp());
     677             :         break;
     678             :       case GRPC_OP_SEND_STATUS_FROM_SERVER:
     679          78 :         op.reset(new SendServerStatusOp());
     680             :         break;
     681             :       case GRPC_OP_RECV_INITIAL_METADATA:
     682          93 :         op.reset(new GetMetadataOp());
     683             :         break;
     684             :       case GRPC_OP_RECV_MESSAGE:
     685         254 :         op.reset(new ReadMessageOp());
     686             :         break;
     687             :       case GRPC_OP_RECV_STATUS_ON_CLIENT:
     688          95 :         op.reset(new ClientStatusOp());
     689             :         break;
     690             :       case GRPC_OP_RECV_CLOSE_ON_SERVER:
     691          87 :         op.reset(new ServerCloseResponseOp());
     692             :         break;
     693             :       default:
     694             :         return Nan::ThrowError("Argument object had an unrecognized key");
     695             :     }
     696        4220 :     if (!op->ParseOp(obj->Get(type), &ops[i], resources)) {
     697             :       return Nan::ThrowTypeError("Incorrectly typed arguments to startBatch");
     698             :     }
     699        2102 :     op_vector->push_back(std::move(op));
     700             :   }
     701         659 :   Callback *callback = new Callback(callback_func);
     702             :   grpc_call_error error = grpc_call_start_batch(
     703         659 :       call->wrapped_call, &ops[0], nops, new struct tag(
     704        2636 :           callback, op_vector.release(), resources), NULL);
     705         659 :   if (error != GRPC_CALL_OK) {
     706           0 :     return Nan::ThrowError(nanErrorWithCode("startBatch failed", error));
     707             :   }
     708         659 :   CompletionQueueAsyncWorker::Next();
     709             : }
     710             : 
     711          12 : NAN_METHOD(Call::Cancel) {
     712          24 :   if (!Call::HasInstance(info.This())) {
     713             :     return Nan::ThrowTypeError("cancel can only be called on Call objects");
     714             :   }
     715          24 :   Call *call = ObjectWrap::Unwrap<Call>(info.This());
     716          12 :   grpc_call_error error = grpc_call_cancel(call->wrapped_call, NULL);
     717          12 :   if (error != GRPC_CALL_OK) {
     718           0 :     return Nan::ThrowError(nanErrorWithCode("cancel failed", error));
     719             :   }
     720             : }
     721             : 
     722           8 : NAN_METHOD(Call::CancelWithStatus) {
     723             :   Nan::HandleScope scope;
     724          16 :   if (!HasInstance(info.This())) {
     725             :     return Nan::ThrowTypeError("cancel can only be called on Call objects");
     726             :   }
     727          16 :   if (!info[0]->IsUint32()) {
     728             :     return Nan::ThrowTypeError(
     729             :         "cancelWithStatus's first argument must be a status code");
     730             :   }
     731          18 :   if (!info[1]->IsString()) {
     732             :     return Nan::ThrowTypeError(
     733             :         "cancelWithStatus's second argument must be a string");
     734             :   }
     735          10 :   Call *call = ObjectWrap::Unwrap<Call>(info.This());
     736             :   grpc_status_code code = static_cast<grpc_status_code>(
     737          10 :       Nan::To<uint32_t>(info[0]).FromJust());
     738           5 :   if (code == GRPC_STATUS_OK) {
     739             :     return Nan::ThrowRangeError(
     740             :         "cancelWithStatus cannot be called with OK status");
     741             :   }
     742           4 :   Utf8String details(info[1]);
     743           4 :   grpc_call_cancel_with_status(call->wrapped_call, code, *details, NULL);
     744             : }
     745             : 
     746           6 : NAN_METHOD(Call::GetPeer) {
     747             :   Nan::HandleScope scope;
     748          12 :   if (!HasInstance(info.This())) {
     749           6 :     return Nan::ThrowTypeError("getPeer can only be called on Call objects");
     750             :   }
     751          12 :   Call *call = ObjectWrap::Unwrap<Call>(info.This());
     752           6 :   char *peer = grpc_call_get_peer(call->wrapped_call);
     753           6 :   Local<Value> peer_value = Nan::New(peer).ToLocalChecked();
     754           6 :   gpr_free(peer);
     755          18 :   info.GetReturnValue().Set(peer_value);
     756             : }
     757             : 
     758           5 : NAN_METHOD(Call::SetCredentials) {
     759             :   Nan::HandleScope scope;
     760          10 :   if (!HasInstance(info.This())) {
     761             :     return Nan::ThrowTypeError(
     762             :         "setCredentials can only be called on Call objects");
     763             :   }
     764          10 :   if (!CallCredentials::HasInstance(info[0])) {
     765             :     return Nan::ThrowTypeError(
     766             :         "setCredentials' first argument must be a CallCredentials");
     767             :   }
     768          10 :   Call *call = ObjectWrap::Unwrap<Call>(info.This());
     769             :   CallCredentials *creds_object = ObjectWrap::Unwrap<CallCredentials>(
     770          10 :       Nan::To<Object>(info[0]).ToLocalChecked());
     771           5 :   grpc_call_credentials *creds = creds_object->GetWrappedCredentials();
     772           5 :   grpc_call_error error = GRPC_CALL_ERROR;
     773           5 :   if (creds) {
     774           5 :     error = grpc_call_set_credentials(call->wrapped_call, creds);
     775             :   }
     776          15 :   info.GetReturnValue().Set(Nan::New<Uint32>(error));
     777             : }
     778             : 
     779             : }  // namespace node
     780           3 : }  // namespace grpc

Generated by: LCOV version 1.11