|  | @@ -1,6 +1,6 @@
 | 
	
		
			
				|  |  |  /*
 | 
	
		
			
				|  |  |   *
 | 
	
		
			
				|  |  | - * Copyright 2014, Google Inc.
 | 
	
		
			
				|  |  | + * Copyright 2015, Google Inc.
 | 
	
		
			
				|  |  |   * All rights reserved.
 | 
	
		
			
				|  |  |   *
 | 
	
		
			
				|  |  |   * Redistribution and use in source and binary forms, with or without
 | 
	
	
		
			
				|  | @@ -31,17 +31,25 @@
 | 
	
		
			
				|  |  |   *
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +#include <memory>
 | 
	
		
			
				|  |  | +#include <vector>
 | 
	
		
			
				|  |  | +#include <map>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  #include <node.h>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #include "grpc/support/log.h"
 | 
	
		
			
				|  |  |  #include "grpc/grpc.h"
 | 
	
		
			
				|  |  | +#include "grpc/support/alloc.h"
 | 
	
		
			
				|  |  |  #include "grpc/support/time.h"
 | 
	
		
			
				|  |  |  #include "byte_buffer.h"
 | 
	
		
			
				|  |  |  #include "call.h"
 | 
	
		
			
				|  |  |  #include "channel.h"
 | 
	
		
			
				|  |  |  #include "completion_queue_async_worker.h"
 | 
	
		
			
				|  |  |  #include "timeval.h"
 | 
	
		
			
				|  |  | -#include "tag.h"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +using std::unique_ptr;
 | 
	
		
			
				|  |  | +using std::shared_ptr;
 | 
	
		
			
				|  |  | +using std::vector;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  namespace grpc {
 | 
	
		
			
				|  |  |  namespace node {
 | 
	
	
		
			
				|  | @@ -49,6 +57,7 @@ namespace node {
 | 
	
		
			
				|  |  |  using ::node::Buffer;
 | 
	
		
			
				|  |  |  using v8::Arguments;
 | 
	
		
			
				|  |  |  using v8::Array;
 | 
	
		
			
				|  |  | +using v8::Boolean;
 | 
	
		
			
				|  |  |  using v8::Exception;
 | 
	
		
			
				|  |  |  using v8::External;
 | 
	
		
			
				|  |  |  using v8::Function;
 | 
	
	
		
			
				|  | @@ -68,37 +77,372 @@ using v8::Value;
 | 
	
		
			
				|  |  |  Persistent<Function> Call::constructor;
 | 
	
		
			
				|  |  |  Persistent<FunctionTemplate> Call::fun_tpl;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -Call::Call(grpc_call *call) : wrapped_call(call) {}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -Call::~Call() { grpc_call_destroy(wrapped_call); }
 | 
	
		
			
				|  |  | +bool CreateMetadataArray(Handle<Object> metadata, grpc_metadata_array *array,
 | 
	
		
			
				|  |  | +                         shared_ptr<Resources> resources) {
 | 
	
		
			
				|  |  | +  NanScope();
 | 
	
		
			
				|  |  | +  grpc_metadata_array_init(array);
 | 
	
		
			
				|  |  | +  Handle<Array> keys(metadata->GetOwnPropertyNames());
 | 
	
		
			
				|  |  | +  for (unsigned int i = 0; i < keys->Length(); i++) {
 | 
	
		
			
				|  |  | +    Handle<String> current_key(keys->Get(i)->ToString());
 | 
	
		
			
				|  |  | +    if (!metadata->Get(current_key)->IsArray()) {
 | 
	
		
			
				|  |  | +      return false;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    array->capacity += Local<Array>::Cast(metadata->Get(current_key))->Length();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  array->metadata = reinterpret_cast<grpc_metadata*>(
 | 
	
		
			
				|  |  | +      gpr_malloc(array->capacity * sizeof(grpc_metadata)));
 | 
	
		
			
				|  |  | +  for (unsigned int i = 0; i < keys->Length(); i++) {
 | 
	
		
			
				|  |  | +    Handle<String> current_key(keys->Get(i)->ToString());
 | 
	
		
			
				|  |  | +    NanUtf8String *utf8_key = new NanUtf8String(current_key);
 | 
	
		
			
				|  |  | +    resources->strings.push_back(unique_ptr<NanUtf8String>(utf8_key));
 | 
	
		
			
				|  |  | +    Handle<Array> values = Local<Array>::Cast(metadata->Get(current_key));
 | 
	
		
			
				|  |  | +    for (unsigned int j = 0; j < values->Length(); j++) {
 | 
	
		
			
				|  |  | +      Handle<Value> value = values->Get(j);
 | 
	
		
			
				|  |  | +      grpc_metadata *current = &array->metadata[array->count];
 | 
	
		
			
				|  |  | +      current->key = **utf8_key;
 | 
	
		
			
				|  |  | +      if (Buffer::HasInstance(value)) {
 | 
	
		
			
				|  |  | +        current->value = Buffer::Data(value);
 | 
	
		
			
				|  |  | +        current->value_length = Buffer::Length(value);
 | 
	
		
			
				|  |  | +        Persistent<Value> handle;
 | 
	
		
			
				|  |  | +        NanAssignPersistent(handle, value);
 | 
	
		
			
				|  |  | +        resources->handles.push_back(unique_ptr<PersistentHolder>(
 | 
	
		
			
				|  |  | +            new PersistentHolder(handle)));
 | 
	
		
			
				|  |  | +      } else if (value->IsString()) {
 | 
	
		
			
				|  |  | +        Handle<String> string_value = value->ToString();
 | 
	
		
			
				|  |  | +        NanUtf8String *utf8_value = new NanUtf8String(string_value);
 | 
	
		
			
				|  |  | +        resources->strings.push_back(unique_ptr<NanUtf8String>(utf8_value));
 | 
	
		
			
				|  |  | +        current->value = **utf8_value;
 | 
	
		
			
				|  |  | +        current->value_length = string_value->Length();
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        return false;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      array->count += 1;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return true;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +Handle<Value> ParseMetadata(const grpc_metadata_array *metadata_array) {
 | 
	
		
			
				|  |  | +  NanEscapableScope();
 | 
	
		
			
				|  |  | +  grpc_metadata *metadata_elements = metadata_array->metadata;
 | 
	
		
			
				|  |  | +  size_t length = metadata_array->count;
 | 
	
		
			
				|  |  | +  std::map<const char*, size_t> size_map;
 | 
	
		
			
				|  |  | +  std::map<const char*, size_t> index_map;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  for (unsigned int i = 0; i < length; i++) {
 | 
	
		
			
				|  |  | +    const char *key = metadata_elements[i].key;
 | 
	
		
			
				|  |  | +    if (size_map.count(key)) {
 | 
	
		
			
				|  |  | +      size_map[key] += 1;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    index_map[key] = 0;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  Handle<Object> metadata_object = NanNew<Object>();
 | 
	
		
			
				|  |  | +  for (unsigned int i = 0; i < length; i++) {
 | 
	
		
			
				|  |  | +    grpc_metadata* elem = &metadata_elements[i];
 | 
	
		
			
				|  |  | +    Handle<String> key_string = String::New(elem->key);
 | 
	
		
			
				|  |  | +    Handle<Array> array;
 | 
	
		
			
				|  |  | +    if (metadata_object->Has(key_string)) {
 | 
	
		
			
				|  |  | +      array = Handle<Array>::Cast(metadata_object->Get(key_string));
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      array = NanNew<Array>(size_map[elem->key]);
 | 
	
		
			
				|  |  | +      metadata_object->Set(key_string, array);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    array->Set(index_map[elem->key],
 | 
	
		
			
				|  |  | +               MakeFastBuffer(
 | 
	
		
			
				|  |  | +                   NanNewBufferHandle(elem->value, elem->value_length)));
 | 
	
		
			
				|  |  | +    index_map[elem->key] += 1;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return NanEscapeScope(metadata_object);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +Handle<Value> Op::GetOpType() const {
 | 
	
		
			
				|  |  | +  NanEscapableScope();
 | 
	
		
			
				|  |  | +  return NanEscapeScope(NanNew<String>(GetTypeString()));
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class SendMetadataOp : public Op {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  Handle<Value> GetNodeValue() const {
 | 
	
		
			
				|  |  | +    NanEscapableScope();
 | 
	
		
			
				|  |  | +    return NanEscapeScope(NanTrue());
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  bool ParseOp(Handle<Value> value, grpc_op *out,
 | 
	
		
			
				|  |  | +               shared_ptr<Resources> resources) {
 | 
	
		
			
				|  |  | +    if (!value->IsObject()) {
 | 
	
		
			
				|  |  | +      return false;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    grpc_metadata_array array;
 | 
	
		
			
				|  |  | +    if (!CreateMetadataArray(value->ToObject(), &array, resources)) {
 | 
	
		
			
				|  |  | +      return false;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    out->data.send_initial_metadata.count = array.count;
 | 
	
		
			
				|  |  | +    out->data.send_initial_metadata.metadata = array.metadata;
 | 
	
		
			
				|  |  | +    return true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | + protected:
 | 
	
		
			
				|  |  | +  std::string GetTypeString() const {
 | 
	
		
			
				|  |  | +    return "send metadata";
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class SendMessageOp : public Op {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  Handle<Value> GetNodeValue() const {
 | 
	
		
			
				|  |  | +    NanEscapableScope();
 | 
	
		
			
				|  |  | +    return NanEscapeScope(NanTrue());
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  bool ParseOp(Handle<Value> value, grpc_op *out,
 | 
	
		
			
				|  |  | +               shared_ptr<Resources> resources) {
 | 
	
		
			
				|  |  | +    if (!Buffer::HasInstance(value)) {
 | 
	
		
			
				|  |  | +      return false;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    out->data.send_message = BufferToByteBuffer(value);
 | 
	
		
			
				|  |  | +    Persistent<Value> handle;
 | 
	
		
			
				|  |  | +    NanAssignPersistent(handle, value);
 | 
	
		
			
				|  |  | +    resources->handles.push_back(unique_ptr<PersistentHolder>(
 | 
	
		
			
				|  |  | +        new PersistentHolder(handle)));
 | 
	
		
			
				|  |  | +    return true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | + protected:
 | 
	
		
			
				|  |  | +  std::string GetTypeString() const {
 | 
	
		
			
				|  |  | +    return "send message";
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class SendClientCloseOp : public Op {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  Handle<Value> GetNodeValue() const {
 | 
	
		
			
				|  |  | +    NanEscapableScope();
 | 
	
		
			
				|  |  | +    return NanEscapeScope(NanTrue());
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  bool ParseOp(Handle<Value> value, grpc_op *out,
 | 
	
		
			
				|  |  | +               shared_ptr<Resources> resources) {
 | 
	
		
			
				|  |  | +    return true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | + protected:
 | 
	
		
			
				|  |  | +  std::string GetTypeString() const {
 | 
	
		
			
				|  |  | +    return "client close";
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class SendServerStatusOp : public Op {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  Handle<Value> GetNodeValue() const {
 | 
	
		
			
				|  |  | +    NanEscapableScope();
 | 
	
		
			
				|  |  | +    return NanEscapeScope(NanTrue());
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  bool ParseOp(Handle<Value> value, grpc_op *out,
 | 
	
		
			
				|  |  | +               shared_ptr<Resources> resources) {
 | 
	
		
			
				|  |  | +    if (!value->IsObject()) {
 | 
	
		
			
				|  |  | +      return false;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    Handle<Object> server_status = value->ToObject();
 | 
	
		
			
				|  |  | +    if (!server_status->Get(NanNew("metadata"))->IsObject()) {
 | 
	
		
			
				|  |  | +      return false;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if (!server_status->Get(NanNew("code"))->IsUint32()) {
 | 
	
		
			
				|  |  | +      return false;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if (!server_status->Get(NanNew("details"))->IsString()) {
 | 
	
		
			
				|  |  | +      return false;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    grpc_metadata_array array;
 | 
	
		
			
				|  |  | +    if (!CreateMetadataArray(server_status->Get(NanNew("metadata"))->
 | 
	
		
			
				|  |  | +                             ToObject(),
 | 
	
		
			
				|  |  | +                             &array, resources)) {
 | 
	
		
			
				|  |  | +      return false;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    out->data.send_status_from_server.trailing_metadata_count = array.count;
 | 
	
		
			
				|  |  | +    out->data.send_status_from_server.trailing_metadata = array.metadata;
 | 
	
		
			
				|  |  | +    out->data.send_status_from_server.status =
 | 
	
		
			
				|  |  | +        static_cast<grpc_status_code>(
 | 
	
		
			
				|  |  | +            server_status->Get(NanNew("code"))->Uint32Value());
 | 
	
		
			
				|  |  | +    NanUtf8String *str = new NanUtf8String(
 | 
	
		
			
				|  |  | +        server_status->Get(NanNew("details")));
 | 
	
		
			
				|  |  | +    resources->strings.push_back(unique_ptr<NanUtf8String>(str));
 | 
	
		
			
				|  |  | +    out->data.send_status_from_server.status_details = **str;
 | 
	
		
			
				|  |  | +    return true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | + protected:
 | 
	
		
			
				|  |  | +  std::string GetTypeString() const {
 | 
	
		
			
				|  |  | +    return "send status";
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class GetMetadataOp : public Op {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  GetMetadataOp() {
 | 
	
		
			
				|  |  | +    grpc_metadata_array_init(&recv_metadata);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  ~GetMetadataOp() {
 | 
	
		
			
				|  |  | +    grpc_metadata_array_destroy(&recv_metadata);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  Handle<Value> GetNodeValue() const {
 | 
	
		
			
				|  |  | +    NanEscapableScope();
 | 
	
		
			
				|  |  | +    return NanEscapeScope(ParseMetadata(&recv_metadata));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  bool ParseOp(Handle<Value> value, grpc_op *out,
 | 
	
		
			
				|  |  | +               shared_ptr<Resources> resources) {
 | 
	
		
			
				|  |  | +    out->data.recv_initial_metadata = &recv_metadata;
 | 
	
		
			
				|  |  | +    return true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + protected:
 | 
	
		
			
				|  |  | +  std::string GetTypeString() const {
 | 
	
		
			
				|  |  | +    return "metadata";
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  grpc_metadata_array recv_metadata;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class ReadMessageOp : public Op {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  ReadMessageOp() {
 | 
	
		
			
				|  |  | +    recv_message = NULL;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  ~ReadMessageOp() {
 | 
	
		
			
				|  |  | +    if (recv_message != NULL) {
 | 
	
		
			
				|  |  | +      gpr_free(recv_message);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  Handle<Value> GetNodeValue() const {
 | 
	
		
			
				|  |  | +    NanEscapableScope();
 | 
	
		
			
				|  |  | +    return NanEscapeScope(ByteBufferToBuffer(recv_message));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  bool ParseOp(Handle<Value> value, grpc_op *out,
 | 
	
		
			
				|  |  | +               shared_ptr<Resources> resources) {
 | 
	
		
			
				|  |  | +    out->data.recv_message = &recv_message;
 | 
	
		
			
				|  |  | +    return true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + protected:
 | 
	
		
			
				|  |  | +  std::string GetTypeString() const {
 | 
	
		
			
				|  |  | +    return "read";
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  grpc_byte_buffer *recv_message;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class ClientStatusOp : public Op {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  ClientStatusOp() {
 | 
	
		
			
				|  |  | +    grpc_metadata_array_init(&metadata_array);
 | 
	
		
			
				|  |  | +    status_details = NULL;
 | 
	
		
			
				|  |  | +    details_capacity = 0;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  ~ClientStatusOp() {
 | 
	
		
			
				|  |  | +    grpc_metadata_array_destroy(&metadata_array);
 | 
	
		
			
				|  |  | +    gpr_free(status_details);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  bool ParseOp(Handle<Value> value, grpc_op *out,
 | 
	
		
			
				|  |  | +               shared_ptr<Resources> resources) {
 | 
	
		
			
				|  |  | +    out->data.recv_status_on_client.trailing_metadata = &metadata_array;
 | 
	
		
			
				|  |  | +    out->data.recv_status_on_client.status = &status;
 | 
	
		
			
				|  |  | +    out->data.recv_status_on_client.status_details = &status_details;
 | 
	
		
			
				|  |  | +    out->data.recv_status_on_client.status_details_capacity = &details_capacity;
 | 
	
		
			
				|  |  | +    return true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  Handle<Value> GetNodeValue() const {
 | 
	
		
			
				|  |  | +    NanEscapableScope();
 | 
	
		
			
				|  |  | +    Handle<Object> status_obj = NanNew<Object>();
 | 
	
		
			
				|  |  | +    status_obj->Set(NanNew("code"), NanNew<Number>(status));
 | 
	
		
			
				|  |  | +    if (status_details != NULL) {
 | 
	
		
			
				|  |  | +      status_obj->Set(NanNew("details"), String::New(status_details));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    status_obj->Set(NanNew("metadata"), ParseMetadata(&metadata_array));
 | 
	
		
			
				|  |  | +    return NanEscapeScope(status_obj);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | + protected:
 | 
	
		
			
				|  |  | +  std::string GetTypeString() const {
 | 
	
		
			
				|  |  | +    return "status";
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  grpc_metadata_array metadata_array;
 | 
	
		
			
				|  |  | +  grpc_status_code status;
 | 
	
		
			
				|  |  | +  char *status_details;
 | 
	
		
			
				|  |  | +  size_t details_capacity;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class ServerCloseResponseOp : public Op {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  Handle<Value> GetNodeValue() const {
 | 
	
		
			
				|  |  | +    NanEscapableScope();
 | 
	
		
			
				|  |  | +    return NanEscapeScope(NanNew<Boolean>(cancelled));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  bool ParseOp(Handle<Value> value, grpc_op *out,
 | 
	
		
			
				|  |  | +               shared_ptr<Resources> resources) {
 | 
	
		
			
				|  |  | +    out->data.recv_close_on_server.cancelled = &cancelled;
 | 
	
		
			
				|  |  | +    return true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + protected:
 | 
	
		
			
				|  |  | +  std::string GetTypeString() const {
 | 
	
		
			
				|  |  | +    return "cancelled";
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  int cancelled;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +tag::tag(NanCallback *callback, OpVec *ops,
 | 
	
		
			
				|  |  | +         shared_ptr<Resources> resources) :
 | 
	
		
			
				|  |  | +    callback(callback), ops(ops), resources(resources){
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +tag::~tag() {
 | 
	
		
			
				|  |  | +  delete callback;
 | 
	
		
			
				|  |  | +  delete ops;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +Handle<Value> GetTagNodeValue(void *tag) {
 | 
	
		
			
				|  |  | +  NanEscapableScope();
 | 
	
		
			
				|  |  | +  struct tag *tag_struct = reinterpret_cast<struct tag *>(tag);
 | 
	
		
			
				|  |  | +  Handle<Object> tag_obj = NanNew<Object>();
 | 
	
		
			
				|  |  | +  for (vector<unique_ptr<Op> >::iterator it = tag_struct->ops->begin();
 | 
	
		
			
				|  |  | +       it != tag_struct->ops->end(); ++it) {
 | 
	
		
			
				|  |  | +    Op *op_ptr = it->get();
 | 
	
		
			
				|  |  | +    tag_obj->Set(op_ptr->GetOpType(), op_ptr->GetNodeValue());
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return NanEscapeScope(tag_obj);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +NanCallback *GetTagCallback(void *tag) {
 | 
	
		
			
				|  |  | +  struct tag *tag_struct = reinterpret_cast<struct tag *>(tag);
 | 
	
		
			
				|  |  | +  return tag_struct->callback;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void DestroyTag(void *tag) {
 | 
	
		
			
				|  |  | +  struct tag *tag_struct = reinterpret_cast<struct tag *>(tag);
 | 
	
		
			
				|  |  | +  delete tag_struct;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +Call::Call(grpc_call *call) : wrapped_call(call) {
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +Call::~Call() {
 | 
	
		
			
				|  |  | +  grpc_call_destroy(wrapped_call);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void Call::Init(Handle<Object> exports) {
 | 
	
		
			
				|  |  |    NanScope();
 | 
	
		
			
				|  |  |    Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
 | 
	
		
			
				|  |  |    tpl->SetClassName(NanNew("Call"));
 | 
	
		
			
				|  |  |    tpl->InstanceTemplate()->SetInternalFieldCount(1);
 | 
	
		
			
				|  |  | -  NanSetPrototypeTemplate(tpl, "addMetadata",
 | 
	
		
			
				|  |  | -                          FunctionTemplate::New(AddMetadata)->GetFunction());
 | 
	
		
			
				|  |  | -  NanSetPrototypeTemplate(tpl, "invoke",
 | 
	
		
			
				|  |  | -                          FunctionTemplate::New(Invoke)->GetFunction());
 | 
	
		
			
				|  |  | -  NanSetPrototypeTemplate(tpl, "serverAccept",
 | 
	
		
			
				|  |  | -                          FunctionTemplate::New(ServerAccept)->GetFunction());
 | 
	
		
			
				|  |  | -  NanSetPrototypeTemplate(
 | 
	
		
			
				|  |  | -      tpl, "serverEndInitialMetadata",
 | 
	
		
			
				|  |  | -      FunctionTemplate::New(ServerEndInitialMetadata)->GetFunction());
 | 
	
		
			
				|  |  | +  NanSetPrototypeTemplate(tpl, "startBatch",
 | 
	
		
			
				|  |  | +                          FunctionTemplate::New(StartBatch)->GetFunction());
 | 
	
		
			
				|  |  |    NanSetPrototypeTemplate(tpl, "cancel",
 | 
	
		
			
				|  |  |                            FunctionTemplate::New(Cancel)->GetFunction());
 | 
	
		
			
				|  |  | -  NanSetPrototypeTemplate(tpl, "startWrite",
 | 
	
		
			
				|  |  | -                          FunctionTemplate::New(StartWrite)->GetFunction());
 | 
	
		
			
				|  |  | -  NanSetPrototypeTemplate(
 | 
	
		
			
				|  |  | -      tpl, "startWriteStatus",
 | 
	
		
			
				|  |  | -      FunctionTemplate::New(StartWriteStatus)->GetFunction());
 | 
	
		
			
				|  |  | -  NanSetPrototypeTemplate(tpl, "writesDone",
 | 
	
		
			
				|  |  | -                          FunctionTemplate::New(WritesDone)->GetFunction());
 | 
	
		
			
				|  |  | -  NanSetPrototypeTemplate(tpl, "startReadMetadata",
 | 
	
		
			
				|  |  | -                          FunctionTemplate::New(WritesDone)->GetFunction());
 | 
	
		
			
				|  |  | -  NanSetPrototypeTemplate(tpl, "startRead",
 | 
	
		
			
				|  |  | -                          FunctionTemplate::New(StartRead)->GetFunction());
 | 
	
		
			
				|  |  |    NanAssignPersistent(fun_tpl, tpl);
 | 
	
		
			
				|  |  |    NanAssignPersistent(constructor, tpl->GetFunction());
 | 
	
		
			
				|  |  |    constructor->Set(NanNew("WRITE_BUFFER_HINT"),
 | 
	
	
		
			
				|  | @@ -152,9 +496,9 @@ NAN_METHOD(Call::New) {
 | 
	
		
			
				|  |  |        NanUtf8String method(args[1]);
 | 
	
		
			
				|  |  |        double deadline = args[2]->NumberValue();
 | 
	
		
			
				|  |  |        grpc_channel *wrapped_channel = channel->GetWrappedChannel();
 | 
	
		
			
				|  |  | -      grpc_call *wrapped_call = grpc_channel_create_call_old(
 | 
	
		
			
				|  |  | -          wrapped_channel, *method, channel->GetHost(),
 | 
	
		
			
				|  |  | -          MillisecondsToTimespec(deadline));
 | 
	
		
			
				|  |  | +      grpc_call *wrapped_call = grpc_channel_create_call(
 | 
	
		
			
				|  |  | +          wrapped_channel, CompletionQueueAsyncWorker::GetQueue(), *method,
 | 
	
		
			
				|  |  | +          channel->GetHost(), MillisecondsToTimespec(deadline));
 | 
	
		
			
				|  |  |        call = new Call(wrapped_call);
 | 
	
		
			
				|  |  |        args.This()->SetHiddenValue(String::NewSymbol("channel_"),
 | 
	
		
			
				|  |  |                                    channel_object);
 | 
	
	
		
			
				|  | @@ -168,119 +512,74 @@ NAN_METHOD(Call::New) {
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -NAN_METHOD(Call::AddMetadata) {
 | 
	
		
			
				|  |  | +NAN_METHOD(Call::StartBatch) {
 | 
	
		
			
				|  |  |    NanScope();
 | 
	
		
			
				|  |  |    if (!HasInstance(args.This())) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError("addMetadata can only be called on Call objects");
 | 
	
		
			
				|  |  | +    return NanThrowTypeError("startBatch can only be called on Call objects");
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  Call *call = ObjectWrap::Unwrap<Call>(args.This());
 | 
	
		
			
				|  |  |    if (!args[0]->IsObject()) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError("addMetadata's first argument must be an object");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  Handle<Object> metadata = args[0]->ToObject();
 | 
	
		
			
				|  |  | -  Handle<Array> keys(metadata->GetOwnPropertyNames());
 | 
	
		
			
				|  |  | -  for (unsigned int i = 0; i < keys->Length(); i++) {
 | 
	
		
			
				|  |  | -    Handle<String> current_key(keys->Get(i)->ToString());
 | 
	
		
			
				|  |  | -    if (!metadata->Get(current_key)->IsArray()) {
 | 
	
		
			
				|  |  | -      return NanThrowTypeError(
 | 
	
		
			
				|  |  | -          "addMetadata's first argument's values must be arrays");
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    NanUtf8String utf8_key(current_key);
 | 
	
		
			
				|  |  | -    Handle<Array> values = Local<Array>::Cast(metadata->Get(current_key));
 | 
	
		
			
				|  |  | -    for (unsigned int j = 0; j < values->Length(); j++) {
 | 
	
		
			
				|  |  | -      Handle<Value> value = values->Get(j);
 | 
	
		
			
				|  |  | -      grpc_metadata metadata;
 | 
	
		
			
				|  |  | -      grpc_call_error error;
 | 
	
		
			
				|  |  | -      metadata.key = *utf8_key;
 | 
	
		
			
				|  |  | -      if (Buffer::HasInstance(value)) {
 | 
	
		
			
				|  |  | -        metadata.value = Buffer::Data(value);
 | 
	
		
			
				|  |  | -        metadata.value_length = Buffer::Length(value);
 | 
	
		
			
				|  |  | -        error = grpc_call_add_metadata_old(call->wrapped_call, &metadata, 0);
 | 
	
		
			
				|  |  | -      } else if (value->IsString()) {
 | 
	
		
			
				|  |  | -        Handle<String> string_value = value->ToString();
 | 
	
		
			
				|  |  | -        NanUtf8String utf8_value(string_value);
 | 
	
		
			
				|  |  | -        metadata.value = *utf8_value;
 | 
	
		
			
				|  |  | -        metadata.value_length = string_value->Length();
 | 
	
		
			
				|  |  | -        gpr_log(GPR_DEBUG, "adding metadata: %s, %s, %d", metadata.key,
 | 
	
		
			
				|  |  | -                metadata.value, metadata.value_length);
 | 
	
		
			
				|  |  | -        error = grpc_call_add_metadata_old(call->wrapped_call, &metadata, 0);
 | 
	
		
			
				|  |  | -      } else {
 | 
	
		
			
				|  |  | -        return NanThrowTypeError(
 | 
	
		
			
				|  |  | -            "addMetadata values must be strings or buffers");
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      if (error != GRPC_CALL_OK) {
 | 
	
		
			
				|  |  | -        return NanThrowError("addMetadata failed", error);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  NanReturnUndefined();
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -NAN_METHOD(Call::Invoke) {
 | 
	
		
			
				|  |  | -  NanScope();
 | 
	
		
			
				|  |  | -  if (!HasInstance(args.This())) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError("invoke can only be called on Call objects");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (!args[0]->IsFunction()) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError("invoke's first argument must be a function");
 | 
	
		
			
				|  |  | +    return NanThrowError("startBatch's first argument must be an object");
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    if (!args[1]->IsFunction()) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError("invoke's second argument must be a function");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (!args[2]->IsUint32()) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError("invoke's third argument must be integer flags");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  Call *call = ObjectWrap::Unwrap<Call>(args.This());
 | 
	
		
			
				|  |  | -  unsigned int flags = args[3]->Uint32Value();
 | 
	
		
			
				|  |  | -  grpc_call_error error = grpc_call_invoke_old(
 | 
	
		
			
				|  |  | -      call->wrapped_call, CompletionQueueAsyncWorker::GetQueue(),
 | 
	
		
			
				|  |  | -      CreateTag(args[0], args.This()), CreateTag(args[1], args.This()), flags);
 | 
	
		
			
				|  |  | -  if (error == GRPC_CALL_OK) {
 | 
	
		
			
				|  |  | -    CompletionQueueAsyncWorker::Next();
 | 
	
		
			
				|  |  | -    CompletionQueueAsyncWorker::Next();
 | 
	
		
			
				|  |  | -  } else {
 | 
	
		
			
				|  |  | -    return NanThrowError("invoke failed", error);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  NanReturnUndefined();
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -NAN_METHOD(Call::ServerAccept) {
 | 
	
		
			
				|  |  | -  NanScope();
 | 
	
		
			
				|  |  | -  if (!HasInstance(args.This())) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError("accept can only be called on Call objects");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (!args[0]->IsFunction()) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError("accept's first argument must be a function");
 | 
	
		
			
				|  |  | +    return NanThrowError("startBatch's second argument must be a callback");
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +  Handle<Function> callback_func = args[1].As<Function>();
 | 
	
		
			
				|  |  |    Call *call = ObjectWrap::Unwrap<Call>(args.This());
 | 
	
		
			
				|  |  | -  grpc_call_error error = grpc_call_server_accept_old(
 | 
	
		
			
				|  |  | -      call->wrapped_call, CompletionQueueAsyncWorker::GetQueue(),
 | 
	
		
			
				|  |  | -      CreateTag(args[0], args.This()));
 | 
	
		
			
				|  |  | -  if (error == GRPC_CALL_OK) {
 | 
	
		
			
				|  |  | -    CompletionQueueAsyncWorker::Next();
 | 
	
		
			
				|  |  | -  } else {
 | 
	
		
			
				|  |  | -    return NanThrowError("serverAccept failed", error);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  NanReturnUndefined();
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -NAN_METHOD(Call::ServerEndInitialMetadata) {
 | 
	
		
			
				|  |  | -  NanScope();
 | 
	
		
			
				|  |  | -  if (!HasInstance(args.This())) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError(
 | 
	
		
			
				|  |  | -        "serverEndInitialMetadata can only be called on Call objects");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (!args[0]->IsUint32()) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError(
 | 
	
		
			
				|  |  | -        "serverEndInitialMetadata's second argument must be integer flags");
 | 
	
		
			
				|  |  | +  shared_ptr<Resources> resources(new Resources);
 | 
	
		
			
				|  |  | +  Handle<Object> obj = args[0]->ToObject();
 | 
	
		
			
				|  |  | +  Handle<Array> keys = obj->GetOwnPropertyNames();
 | 
	
		
			
				|  |  | +  size_t nops = keys->Length();
 | 
	
		
			
				|  |  | +  vector<grpc_op> ops(nops);
 | 
	
		
			
				|  |  | +  unique_ptr<OpVec> op_vector(new OpVec());
 | 
	
		
			
				|  |  | +  for (unsigned int i = 0; i < nops; i++) {
 | 
	
		
			
				|  |  | +    unique_ptr<Op> op;
 | 
	
		
			
				|  |  | +    if (!keys->Get(i)->IsUint32()) {
 | 
	
		
			
				|  |  | +      return NanThrowError(
 | 
	
		
			
				|  |  | +          "startBatch's first argument's keys must be integers");
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    uint32_t type = keys->Get(i)->Uint32Value();
 | 
	
		
			
				|  |  | +    ops[i].op = static_cast<grpc_op_type>(type);
 | 
	
		
			
				|  |  | +    switch (type) {
 | 
	
		
			
				|  |  | +      case GRPC_OP_SEND_INITIAL_METADATA:
 | 
	
		
			
				|  |  | +        op.reset(new SendMetadataOp());
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  | +      case GRPC_OP_SEND_MESSAGE:
 | 
	
		
			
				|  |  | +        op.reset(new SendMessageOp());
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  | +      case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
 | 
	
		
			
				|  |  | +        op.reset(new SendClientCloseOp());
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  | +      case GRPC_OP_SEND_STATUS_FROM_SERVER:
 | 
	
		
			
				|  |  | +        op.reset(new SendServerStatusOp());
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  | +      case GRPC_OP_RECV_INITIAL_METADATA:
 | 
	
		
			
				|  |  | +        op.reset(new GetMetadataOp());
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  | +      case GRPC_OP_RECV_MESSAGE:
 | 
	
		
			
				|  |  | +        op.reset(new ReadMessageOp());
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  | +      case GRPC_OP_RECV_STATUS_ON_CLIENT:
 | 
	
		
			
				|  |  | +        op.reset(new ClientStatusOp());
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  | +      case GRPC_OP_RECV_CLOSE_ON_SERVER:
 | 
	
		
			
				|  |  | +        op.reset(new ServerCloseResponseOp());
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  | +      default:
 | 
	
		
			
				|  |  | +        return NanThrowError("Argument object had an unrecognized key");
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if (!op->ParseOp(obj->Get(type), &ops[i], resources)) {
 | 
	
		
			
				|  |  | +      return NanThrowTypeError("Incorrectly typed arguments to startBatch");
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    op_vector->push_back(std::move(op));
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  Call *call = ObjectWrap::Unwrap<Call>(args.This());
 | 
	
		
			
				|  |  | -  unsigned int flags = args[1]->Uint32Value();
 | 
	
		
			
				|  |  | -  grpc_call_error error =
 | 
	
		
			
				|  |  | -      grpc_call_server_end_initial_metadata_old(call->wrapped_call, flags);
 | 
	
		
			
				|  |  | +  NanCallback *callback = new NanCallback(callback_func);
 | 
	
		
			
				|  |  | +  grpc_call_error error = grpc_call_start_batch(
 | 
	
		
			
				|  |  | +      call->wrapped_call, &ops[0], nops, new struct tag(
 | 
	
		
			
				|  |  | +          callback, op_vector.release(), resources));
 | 
	
		
			
				|  |  |    if (error != GRPC_CALL_OK) {
 | 
	
		
			
				|  |  | -    return NanThrowError("serverEndInitialMetadata failed", error);
 | 
	
		
			
				|  |  | +    return NanThrowError("startBatch failed", error);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +  CompletionQueueAsyncWorker::Next();
 | 
	
		
			
				|  |  |    NanReturnUndefined();
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -297,102 +596,5 @@ NAN_METHOD(Call::Cancel) {
 | 
	
		
			
				|  |  |    NanReturnUndefined();
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -NAN_METHOD(Call::StartWrite) {
 | 
	
		
			
				|  |  | -  NanScope();
 | 
	
		
			
				|  |  | -  if (!HasInstance(args.This())) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError("startWrite can only be called on Call objects");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (!Buffer::HasInstance(args[0])) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError("startWrite's first argument must be a Buffer");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (!args[1]->IsFunction()) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError("startWrite's second argument must be a function");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (!args[2]->IsUint32()) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError(
 | 
	
		
			
				|  |  | -        "startWrite's third argument must be integer flags");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  Call *call = ObjectWrap::Unwrap<Call>(args.This());
 | 
	
		
			
				|  |  | -  grpc_byte_buffer *buffer = BufferToByteBuffer(args[0]);
 | 
	
		
			
				|  |  | -  unsigned int flags = args[2]->Uint32Value();
 | 
	
		
			
				|  |  | -  grpc_call_error error = grpc_call_start_write_old(
 | 
	
		
			
				|  |  | -      call->wrapped_call, buffer, CreateTag(args[1], args.This()), flags);
 | 
	
		
			
				|  |  | -  if (error == GRPC_CALL_OK) {
 | 
	
		
			
				|  |  | -    CompletionQueueAsyncWorker::Next();
 | 
	
		
			
				|  |  | -  } else {
 | 
	
		
			
				|  |  | -    return NanThrowError("startWrite failed", error);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  NanReturnUndefined();
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -NAN_METHOD(Call::StartWriteStatus) {
 | 
	
		
			
				|  |  | -  NanScope();
 | 
	
		
			
				|  |  | -  if (!HasInstance(args.This())) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError(
 | 
	
		
			
				|  |  | -        "startWriteStatus can only be called on Call objects");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (!args[0]->IsUint32()) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError(
 | 
	
		
			
				|  |  | -        "startWriteStatus's first argument must be a status code");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (!args[1]->IsString()) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError(
 | 
	
		
			
				|  |  | -        "startWriteStatus's second argument must be a string");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (!args[2]->IsFunction()) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError(
 | 
	
		
			
				|  |  | -        "startWriteStatus's third argument must be a function");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  Call *call = ObjectWrap::Unwrap<Call>(args.This());
 | 
	
		
			
				|  |  | -  NanUtf8String details(args[1]);
 | 
	
		
			
				|  |  | -  grpc_call_error error = grpc_call_start_write_status_old(
 | 
	
		
			
				|  |  | -      call->wrapped_call, (grpc_status_code)args[0]->Uint32Value(), *details,
 | 
	
		
			
				|  |  | -      CreateTag(args[2], args.This()));
 | 
	
		
			
				|  |  | -  if (error == GRPC_CALL_OK) {
 | 
	
		
			
				|  |  | -    CompletionQueueAsyncWorker::Next();
 | 
	
		
			
				|  |  | -  } else {
 | 
	
		
			
				|  |  | -    return NanThrowError("startWriteStatus failed", error);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  NanReturnUndefined();
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -NAN_METHOD(Call::WritesDone) {
 | 
	
		
			
				|  |  | -  NanScope();
 | 
	
		
			
				|  |  | -  if (!HasInstance(args.This())) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError("writesDone can only be called on Call objects");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (!args[0]->IsFunction()) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError("writesDone's first argument must be a function");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  Call *call = ObjectWrap::Unwrap<Call>(args.This());
 | 
	
		
			
				|  |  | -  grpc_call_error error = grpc_call_writes_done_old(
 | 
	
		
			
				|  |  | -      call->wrapped_call, CreateTag(args[0], args.This()));
 | 
	
		
			
				|  |  | -  if (error == GRPC_CALL_OK) {
 | 
	
		
			
				|  |  | -    CompletionQueueAsyncWorker::Next();
 | 
	
		
			
				|  |  | -  } else {
 | 
	
		
			
				|  |  | -    return NanThrowError("writesDone failed", error);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  NanReturnUndefined();
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -NAN_METHOD(Call::StartRead) {
 | 
	
		
			
				|  |  | -  NanScope();
 | 
	
		
			
				|  |  | -  if (!HasInstance(args.This())) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError("startRead can only be called on Call objects");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  if (!args[0]->IsFunction()) {
 | 
	
		
			
				|  |  | -    return NanThrowTypeError("startRead's first argument must be a function");
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  Call *call = ObjectWrap::Unwrap<Call>(args.This());
 | 
	
		
			
				|  |  | -  grpc_call_error error = grpc_call_start_read_old(
 | 
	
		
			
				|  |  | -      call->wrapped_call, CreateTag(args[0], args.This()));
 | 
	
		
			
				|  |  | -  if (error == GRPC_CALL_OK) {
 | 
	
		
			
				|  |  | -    CompletionQueueAsyncWorker::Next();
 | 
	
		
			
				|  |  | -  } else {
 | 
	
		
			
				|  |  | -    return NanThrowError("startRead failed", error);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  NanReturnUndefined();
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  }  // namespace node
 | 
	
		
			
				|  |  |  }  // namespace grpc
 |