From 40aae07bea8b5a8a8e167142b16ca053833289ea Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Mon, 2 Feb 2015 10:16:30 -0800 Subject: [PATCH 01/91] Prepare for the new batch call API. Rename all core API functions that are on their way to deprecation with an _old tag across all wrappings. --- .gitignore | 2 ++ ext/call.cc | 26 +++++++++++++------------- ext/server.cc | 2 +- 3 files changed, 16 insertions(+), 14 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e3fbd983 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build +node_modules diff --git a/ext/call.cc b/ext/call.cc index 3261b780..23aead07 100644 --- a/ext/call.cc +++ b/ext/call.cc @@ -152,9 +152,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(wrapped_channel, *method, channel->GetHost(), - MillisecondsToTimespec(deadline)); + grpc_call *wrapped_call = grpc_channel_create_call_old( + wrapped_channel, *method, channel->GetHost(), + MillisecondsToTimespec(deadline)); call = new Call(wrapped_call); args.This()->SetHiddenValue(String::NewSymbol("channel_"), channel_object); @@ -195,7 +195,7 @@ NAN_METHOD(Call::AddMetadata) { if (Buffer::HasInstance(value)) { metadata.value = Buffer::Data(value); metadata.value_length = Buffer::Length(value); - error = grpc_call_add_metadata(call->wrapped_call, &metadata, 0); + error = grpc_call_add_metadata_old(call->wrapped_call, &metadata, 0); } else if (value->IsString()) { Handle string_value = value->ToString(); NanUtf8String utf8_value(string_value); @@ -203,7 +203,7 @@ NAN_METHOD(Call::AddMetadata) { 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(call->wrapped_call, &metadata, 0); + error = grpc_call_add_metadata_old(call->wrapped_call, &metadata, 0); } else { return NanThrowTypeError( "addMetadata values must be strings or buffers"); @@ -232,7 +232,7 @@ NAN_METHOD(Call::Invoke) { } Call *call = ObjectWrap::Unwrap(args.This()); unsigned int flags = args[3]->Uint32Value(); - grpc_call_error error = grpc_call_invoke( + 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) { @@ -253,7 +253,7 @@ NAN_METHOD(Call::ServerAccept) { return NanThrowTypeError("accept's first argument must be a function"); } Call *call = ObjectWrap::Unwrap(args.This()); - grpc_call_error error = grpc_call_server_accept( + grpc_call_error error = grpc_call_server_accept_old( call->wrapped_call, CompletionQueueAsyncWorker::GetQueue(), CreateTag(args[0], args.This())); if (error == GRPC_CALL_OK) { @@ -277,7 +277,7 @@ NAN_METHOD(Call::ServerEndInitialMetadata) { Call *call = ObjectWrap::Unwrap(args.This()); unsigned int flags = args[1]->Uint32Value(); grpc_call_error error = - grpc_call_server_end_initial_metadata(call->wrapped_call, flags); + grpc_call_server_end_initial_metadata_old(call->wrapped_call, flags); if (error != GRPC_CALL_OK) { return NanThrowError("serverEndInitialMetadata failed", error); } @@ -315,7 +315,7 @@ NAN_METHOD(Call::StartWrite) { Call *call = ObjectWrap::Unwrap(args.This()); grpc_byte_buffer *buffer = BufferToByteBuffer(args[0]); unsigned int flags = args[2]->Uint32Value(); - grpc_call_error error = grpc_call_start_write( + 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(); @@ -345,7 +345,7 @@ NAN_METHOD(Call::StartWriteStatus) { } Call *call = ObjectWrap::Unwrap(args.This()); NanUtf8String details(args[1]); - grpc_call_error error = grpc_call_start_write_status( + 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) { @@ -365,7 +365,7 @@ NAN_METHOD(Call::WritesDone) { return NanThrowTypeError("writesDone's first argument must be a function"); } Call *call = ObjectWrap::Unwrap(args.This()); - grpc_call_error error = grpc_call_writes_done( + grpc_call_error error = grpc_call_writes_done_old( call->wrapped_call, CreateTag(args[0], args.This())); if (error == GRPC_CALL_OK) { CompletionQueueAsyncWorker::Next(); @@ -384,8 +384,8 @@ NAN_METHOD(Call::StartRead) { return NanThrowTypeError("startRead's first argument must be a function"); } Call *call = ObjectWrap::Unwrap(args.This()); - grpc_call_error error = - grpc_call_start_read(call->wrapped_call, CreateTag(args[0], 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 { diff --git a/ext/server.cc b/ext/server.cc index b102775d..6b8ccef9 100644 --- a/ext/server.cc +++ b/ext/server.cc @@ -175,7 +175,7 @@ NAN_METHOD(Server::RequestCall) { return NanThrowTypeError("requestCall can only be called on a Server"); } Server *server = ObjectWrap::Unwrap(args.This()); - grpc_call_error error = grpc_server_request_call( + grpc_call_error error = grpc_server_request_call_old( server->wrapped_server, CreateTag(args[0], NanNull())); if (error == GRPC_CALL_OK) { CompletionQueueAsyncWorker::Next(); From a736903ba22edc6394d2826688b41e3e20d395dc Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Mon, 2 Feb 2015 10:16:30 -0800 Subject: [PATCH 02/91] Prepare for the new batch call API. Rename all core API functions that are on their way to deprecation with an _old tag across all wrappings. --- .gitignore | 2 ++ ext/call.cc | 26 +++++++++++++------------- ext/server.cc | 2 +- 3 files changed, 16 insertions(+), 14 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e3fbd983 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build +node_modules diff --git a/ext/call.cc b/ext/call.cc index 3261b780..23aead07 100644 --- a/ext/call.cc +++ b/ext/call.cc @@ -152,9 +152,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(wrapped_channel, *method, channel->GetHost(), - MillisecondsToTimespec(deadline)); + grpc_call *wrapped_call = grpc_channel_create_call_old( + wrapped_channel, *method, channel->GetHost(), + MillisecondsToTimespec(deadline)); call = new Call(wrapped_call); args.This()->SetHiddenValue(String::NewSymbol("channel_"), channel_object); @@ -195,7 +195,7 @@ NAN_METHOD(Call::AddMetadata) { if (Buffer::HasInstance(value)) { metadata.value = Buffer::Data(value); metadata.value_length = Buffer::Length(value); - error = grpc_call_add_metadata(call->wrapped_call, &metadata, 0); + error = grpc_call_add_metadata_old(call->wrapped_call, &metadata, 0); } else if (value->IsString()) { Handle string_value = value->ToString(); NanUtf8String utf8_value(string_value); @@ -203,7 +203,7 @@ NAN_METHOD(Call::AddMetadata) { 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(call->wrapped_call, &metadata, 0); + error = grpc_call_add_metadata_old(call->wrapped_call, &metadata, 0); } else { return NanThrowTypeError( "addMetadata values must be strings or buffers"); @@ -232,7 +232,7 @@ NAN_METHOD(Call::Invoke) { } Call *call = ObjectWrap::Unwrap(args.This()); unsigned int flags = args[3]->Uint32Value(); - grpc_call_error error = grpc_call_invoke( + 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) { @@ -253,7 +253,7 @@ NAN_METHOD(Call::ServerAccept) { return NanThrowTypeError("accept's first argument must be a function"); } Call *call = ObjectWrap::Unwrap(args.This()); - grpc_call_error error = grpc_call_server_accept( + grpc_call_error error = grpc_call_server_accept_old( call->wrapped_call, CompletionQueueAsyncWorker::GetQueue(), CreateTag(args[0], args.This())); if (error == GRPC_CALL_OK) { @@ -277,7 +277,7 @@ NAN_METHOD(Call::ServerEndInitialMetadata) { Call *call = ObjectWrap::Unwrap(args.This()); unsigned int flags = args[1]->Uint32Value(); grpc_call_error error = - grpc_call_server_end_initial_metadata(call->wrapped_call, flags); + grpc_call_server_end_initial_metadata_old(call->wrapped_call, flags); if (error != GRPC_CALL_OK) { return NanThrowError("serverEndInitialMetadata failed", error); } @@ -315,7 +315,7 @@ NAN_METHOD(Call::StartWrite) { Call *call = ObjectWrap::Unwrap(args.This()); grpc_byte_buffer *buffer = BufferToByteBuffer(args[0]); unsigned int flags = args[2]->Uint32Value(); - grpc_call_error error = grpc_call_start_write( + 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(); @@ -345,7 +345,7 @@ NAN_METHOD(Call::StartWriteStatus) { } Call *call = ObjectWrap::Unwrap(args.This()); NanUtf8String details(args[1]); - grpc_call_error error = grpc_call_start_write_status( + 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) { @@ -365,7 +365,7 @@ NAN_METHOD(Call::WritesDone) { return NanThrowTypeError("writesDone's first argument must be a function"); } Call *call = ObjectWrap::Unwrap(args.This()); - grpc_call_error error = grpc_call_writes_done( + grpc_call_error error = grpc_call_writes_done_old( call->wrapped_call, CreateTag(args[0], args.This())); if (error == GRPC_CALL_OK) { CompletionQueueAsyncWorker::Next(); @@ -384,8 +384,8 @@ NAN_METHOD(Call::StartRead) { return NanThrowTypeError("startRead's first argument must be a function"); } Call *call = ObjectWrap::Unwrap(args.This()); - grpc_call_error error = - grpc_call_start_read(call->wrapped_call, CreateTag(args[0], 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 { diff --git a/ext/server.cc b/ext/server.cc index b102775d..6b8ccef9 100644 --- a/ext/server.cc +++ b/ext/server.cc @@ -175,7 +175,7 @@ NAN_METHOD(Server::RequestCall) { return NanThrowTypeError("requestCall can only be called on a Server"); } Server *server = ObjectWrap::Unwrap(args.This()); - grpc_call_error error = grpc_server_request_call( + grpc_call_error error = grpc_server_request_call_old( server->wrapped_server, CreateTag(args[0], NanNull())); if (error == GRPC_CALL_OK) { CompletionQueueAsyncWorker::Next(); From e3559b36a74327333bec1add5e25a03089b7cc53 Mon Sep 17 00:00:00 2001 From: David Klempner Date: Wed, 4 Feb 2015 12:02:17 -0800 Subject: [PATCH 03/91] Remove timeval functions They only had one caller, which could easily be converted to use timespec instead of timeval. --- ext/timeval.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ext/timeval.cc b/ext/timeval.cc index 687e3357..20d52f09 100644 --- a/ext/timeval.cc +++ b/ext/timeval.cc @@ -56,9 +56,8 @@ double TimespecToMilliseconds(gpr_timespec timespec) { } else if (gpr_time_cmp(timespec, gpr_inf_past) == 0) { return -std::numeric_limits::infinity(); } else { - struct timeval time = gpr_timeval_from_timespec(timespec); - return (static_cast(time.tv_sec) * 1000 + - static_cast(time.tv_usec) / 1000); + return (static_cast(timespec.tv_sec) * 1000 + + static_cast(timespec.tv_nsec) / 1000000); } } From 7d9e5349c7a3171be54ad0cb36b0e6eb086c3e18 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 4 Feb 2015 15:10:15 -0800 Subject: [PATCH 04/91] Added a lot more information to README --- README.md | 72 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 55329d8c..c342b7ca 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,70 @@ -# Node.js GRPC extension +# Node.js gRPC Library -The package is built with +## Installation - node-gyp configure - node-gyp build +First, clone this repository (NPM package coming soon). Then follow the instructions in the `INSTALL` file in the root of the repository to install the C core library that this package depends on. -or, for brevity +Then, simply run `npm install` in or referencing this directory. - node-gyp configure build +## Tests -The tests can be run with `npm test` on a dev install. \ No newline at end of file +To run the test suite, simply run `npm test` in the install location. + +## API + +This library internally uses [ProtoBuf.js](https://github.com/dcodeIO/ProtoBuf.js), and some structures it exports match those exported by that library + +If you require this module, you will get an object with the following members + +```javascript +function load(filename) +``` + +Takes a filename of a [Protocol Buffer](https://developers.google.com/protocol-buffers/) file, and returns an object representing the structure of the protocol buffer in the following way: + + - Namespaces become maps from the names of their direct members to those member objects + - Service definitions become client constructors for clients for that service. They also have a `service` member that can be used for constructing servers. + - Message definitions become Message constructors like those that ProtoBuf.js would create + - Enum definitions become Enum objects like those that ProtoBuf.js would create + - Anything else becomes the relevant reflection object that ProtoBuf.js would create + + +```javascript +function loadObject(reflectionObject) +``` + +Returns the same structure that `load` returns, but takes a reflection object from `ProtoBuf.js` instead of a file name. + +```javascript +function buildServer(serviceArray) +``` + +Takes an array of service objects and returns a constructor for a server that handles requests to all of those services. + + +```javascript +status +``` + +An object mapping status names to status code numbers. + + +```javascript +callError +``` + +An object mapping call error names to codes. This is primarily useful for tracking down certain kinds of internal errors. + + +```javascript +Credentials +``` + +An object with factory methods for creating credential objects for clients. + + +```javascript +ServerCredentials +``` + +An object with factory methods fro creating credential objects for servers. From 4c627fb5a7d11b0cf769cfb6e79add2ef1fb98e6 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 5 Feb 2015 17:42:01 -0800 Subject: [PATCH 05/91] Part of the update to the new API --- ext/call.cc | 155 ++++++++++++++++++++++++++- ext/node_grpc.cc | 31 ++++++ ext/tag.cc | 271 +++++++++++++++++++++++++++++++++++++++++------ ext/tag.h | 28 +++-- 4 files changed, 438 insertions(+), 47 deletions(-) diff --git a/ext/call.cc b/ext/call.cc index 23aead07..d2e930bc 100644 --- a/ext/call.cc +++ b/ext/call.cc @@ -31,6 +31,8 @@ * */ +#include + #include #include "grpc/support/log.h" @@ -68,6 +70,50 @@ using v8::Value; Persistent Call::constructor; Persistent Call::fun_tpl; +bool CreateMetadataArray(Handle metadata, grpc_metadata_array *array + vector *string_handles, + vector*> *handles) { + NanScope(); + Handle keys(metadata->GetOwnPropertyNames()); + for (unsigned int i = 0; i < keys->Length(); i++) { + Handle current_key(keys->Get(i)->ToString()); + if (!metadata->Get(current_key)->IsArray()) { + return false; + } + array->capacity += Local::Cast(metadata->Get(current_key))->Length(); + } + array->metadata = calloc(array->capacity, sizeof(grpc_metadata)); + for (unsigned int i = 0; i < keys->Length(); i++) { + Handle current_key(keys->Get(i)->ToString()); + NanUtf8String *utf8_key = new NanUtf8String(current_key); + string_handles->push_back(utf8_key); + Handle values = Local::Cast(metadata->Get(current_key)); + for (unsigned int j = 0; j < values->Length(); j++) { + Handle value = values->Get(j); + grpc_metadata *current = &array[array->count]; + grpc_call_error error; + current->key = **utf8_key; + if (Buffer::HasInstance(value)) { + current->value = Buffer::Data(value); + current->value_length = Buffer::Length(value); + Persistent *handle = new Persistent(); + NanAssignPersistent(handle, object); + handles->push_back(handle); + } else if (value->IsString()) { + Handle string_value = value->ToString(); + NanUtf8String *utf8_value = new NanUtf8String(string_value); + string_handles->push_back(utf8_value); + current->value = **utf8_value; + current->value_length = string_value->Length(); + } else { + return false; + } + array->count += 1; + } + } + return true; +} + Call::Call(grpc_call *call) : wrapped_call(call) {} Call::~Call() { grpc_call_destroy(wrapped_call); } @@ -152,9 +198,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,6 +214,109 @@ NAN_METHOD(Call::New) { } } +NAN_METHOD(Call::StartBatch) { + NanScope(); + if (!HasInstance(args.This())) { + return NanThrowTypeError("startBatch can only be called on Call objects"); + } + if (!args[0]->IsObject()) { + return NanThrowError("startBatch's first argument must be an object"); + } + if (!args[1]->IsFunction()) { + return NanThrowError("startBatch's second argument must be a callback"); + } + vector *> *handles = new vector>(); + vector *strings = new vector(); + Persistent *handle; + Handle keys = args[0]->GetOwnPropertyNames(); + size_t nops = keys->Length(); + grpc_op *ops = calloc(nops, sizeof(grpc_op)); + grpc_metadata_array array; + Handle server_status; + NanUtf8String *str; + for (unsigned int i = 0; i < nops; i++) { + 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 = type; + switch (type) { + case GRPC_OP_SEND_INITIAL_METADATA: + if (!args[0]->Get(type)->IsObject()) { + return NanThrowError("metadata must be an object"); + } + if (!CreateMetadataArray(args[0]->Get(type)->ToObject(), &array, + strings, handles)) { + return NanThrowError("failed to parse metadata"); + } + ops[i].data.send_initial_metadata.count = array.count; + ops[i].data.send_initial_metadata.metadata = array.metadata; + break + case GRPC_OP_SEND_MESSAGE: + if (!Buffer::HasInstance(args[0]->Get(type))) { + return NanThrowError("message must be a Buffer"); + } + ops[i].data.send_message = BufferToByteBuffer(args[0]->Get(type)); + handle = new Persistent(); + NanAssignPersistent(*handle, args[0]->Get(type)); + handles->push_back(handle); + break; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + break; + case GRPC_OP_SEND_STATUS_FROM_SERVER: + if (!args[0]->Get(type)->IsObject()) { + return NanThrowError("server status must be an object"); + } + server_status = args[0]->Get(type)->ToObject(); + if (!server_status->Get("metadata")->IsObject()) { + return NanThrowError("status metadata must be an object"); + } + if (!server_status->Get("code")->IsUInt32()) { + return NanThrowError("status code must be a positive integer"); + } + if (!server_status->Get("details")->IsString()) { + return NanThrowError("status details must be a string"); + } + if (!CreateMetadataArray(server_status->Get("metadata")->ToObject(), + &array, strings, handles)) { + return NanThrowError("Failed to parse status metadata"); + } + ops[i].data.send_status_from_server.trailing_metadata_count = + array.count; + ops[i].data.send_status_from_server.trailing_metadata = array.metadata; + ops[i].data.send_status_from_server.status = + server_status->Get("code")->UInt32Value(); + str = new NanUtf8String(server_status->Get("details")); + strings->push_back(str); + ops[i].data.send_status_from_server.status_details = **str; + break; + case GRPC_OP_RECV_INITIAL_METADATA: + ops[i].data.recv_initial_metadata = malloc(sizeof(grpc_metadata_array)); + grpc_metadata_array_init(ops[i].data.recv_initial_metadata); + break; + case GRPC_OP_RECV_MESSAGE: + ops[i].data.recv_message = malloc(sizeof(grpc_byte_buffer*)); + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + ops[i].data.recv_status_on_client.trailing_metadata = + malloc(sizeof(grpc_metadata_array)); + grpc_metadata_array_init(ops[i].data.recv_status_on_client); + ops[i].data.recv_status_on_client.status = + malloc(sizeof(grpc_status_code)); + ops[i].data.recv_status_on_client.status_details = + malloc(sizeof(char *)); + ops[i].data.recv_status_on_client.status_details_capacity = + malloc(sizeof(size_t)); + break; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + ops[i].data.recv_close_on_server = malloc(sizeof(int)); + break; + + } + } +} + NAN_METHOD(Call::AddMetadata) { NanScope(); if (!HasInstance(args.This())) { diff --git a/ext/node_grpc.cc b/ext/node_grpc.cc index bc1dfaf8..c9388940 100644 --- a/ext/node_grpc.cc +++ b/ext/node_grpc.cc @@ -161,6 +161,36 @@ void InitCompletionTypeConstants(Handle exports) { completion_type->Set(NanNew("SERVER_RPC_NEW"), SERVER_RPC_NEW); } +void InitOpTypeConstants(Handle exports) { + NanScope(); + Handle op_type = Object::New(); + exports->Set(NanNew("opType"), op_type); + Handle SEND_INITIAL_METADATA( + NanNew(GRPC_OP_SEND_INITIAL_METADATA)); + op_type->Set(NanNew("SEND_INITIAL_METADATA"), SEND_INITIAL_METADATA); + Handle SEND_MESSAGE( + NanNew(GRPC_OP_SEND_MESSAGE)); + op_type->Set(NanNew("SEND_MESSAGE"), SEND_MESSAGE); + Handle SEND_CLOSE_FROM_CLIENT( + NanNew(GRPC_OP_SEND_CLOSE_FROM_CLIENT)); + op_type->Set(NanNew("SEND_CLOSE_FROM_CLIENT"), SEND_CLOSE_FROM_CLIENT); + Handle SEND_STATUS_FROM_SERVER( + NanNew(GRPC_OP_SEND_STATUS_FROM_SERVER)); + op_type->Set(NanNew("SEND_STATUS_FROM_SERVER"), SEND_STATUS_FROM_SERVER); + Handle RECV_INITIAL_METADATA( + NanNew(GRPC_OP_RECV_INITIAL_METADATA)); + op_type->Set(NanNew("RECV_INITIAL_METADATA"), RECV_INITIAL_METADATA); + Handle RECV_MESSAGE( + NanNew(GRPC_OP_RECV_MESSAGE)); + op_type->Set(NanNew("RECV_MESSAGE"), RECV_MESSAGE); + Handle RECV_STATUS_ON_CLIENT( + NanNew(GRPC_OP_RECV_STATUS_ON_CLIENT)); + op_type->Set(NanNew("RECV_STATUS_ON_CLIENT"), RECV_STATUS_ON_CLIENT); + Handle RECV_CLOSE_ON_SERVER( + NanNew(GRPC_OP_RECV_CLOSE_ON_SERVER)); + op_type->Set(NanNew("RECV_CLOSE_ON_SERVER"), RECV_CLOSE_ON_SERVER); +} + void init(Handle exports) { NanScope(); grpc_init(); @@ -168,6 +198,7 @@ void init(Handle exports) { InitCallErrorConstants(exports); InitOpErrorConstants(exports); InitCompletionTypeConstants(exports); + InitOpTypeConstants(exports); grpc::node::Call::Init(exports); grpc::node::Channel::Init(exports); diff --git a/ext/tag.cc b/ext/tag.cc index dc8e523e..4c41c3d2 100644 --- a/ext/tag.cc +++ b/ext/tag.cc @@ -31,68 +31,271 @@ * */ +#include +#include + +#include #include #include #include #include "tag.h" +#include "call.h" namespace grpc { namespace node { +using v8::Boolean; +using v8::Function; using v8::Handle; using v8::HandleScope; using v8::Persistent; using v8::Value; -struct tag { - tag(Persistent *tag, Persistent *call) - : persist_tag(tag), persist_call(call) {} +Handle ParseMetadata(grpc_metadata_array *metadata_array) { + NanEscapableScope(); + grpc_metadata *metadata_elements = metadata_array->metadata; + size_t length = metadata_array->count; + std::map size_map; + std::map index_map; - ~tag() { - persist_tag->Dispose(); - if (persist_call != NULL) { - persist_call->Dispose(); + for (unsigned int i = 0; i < length; i++) { + char *key = metadata_elements[i].key; + if (size_map.count(key)) { + size_map[key] += 1; } + index_map[key] = 0; } - Persistent *persist_tag; - Persistent *persist_call; + Handle metadata_object = NanNew(); + for (unsigned int i = 0; i < length; i++) { + grpc_metadata* elem = &metadata_elements[i]; + Handle key_string = String::New(elem->key); + Handle array; + if (metadata_object->Has(key_string)) { + array = Handle::Cast(metadata_object->Get(key_string)); + } else { + array = NanNew(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); +} + +class OpResponse { + public: + explicit OpResponse(char *name): name(name) { + } + virtual Handle GetNodeValue() const = 0; + Handle GetOpType() const { + NanEscapableScope(); + return NanEscapeScope(NanNew(name)); + } + + private: + char *name; }; -void *CreateTag(Handle tag, Handle call) { - NanScope(); - Persistent *persist_tag = new Persistent(); - NanAssignPersistent(*persist_tag, tag); - Persistent *persist_call; - if (call->IsNull() || call->IsUndefined()) { - persist_call = NULL; - } else { - persist_call = new Persistent(); - NanAssignPersistent(*persist_call, call); +class SendResponse : public OpResponse { + public: + explicit SendResponse(char *name): OpResponse(name) { } - struct tag *tag_struct = new struct tag(persist_tag, persist_call); + + Handle GetNodeValue() { + NanEscapableScope(); + return NanEscapeScope(NanTrue()); + } +} + +class MetadataResponse : public OpResponse { + public: + explicit MetadataResponse(grpc_metadata_array *recv_metadata): + recv_metadata(recv_metadata), OpResponse("metadata") { + } + + Handle GetNodeValue() const { + NanEscapableScope(); + return NanEscapeScope(ParseMetadata(recv_metadata)); + } + + private: + grpc_metadata_array *recv_metadata; +}; + +class MessageResponse : public OpResponse { + public: + explicit MessageResponse(grpc_byte_buffer **recv_message): + recv_message(recv_message), OpResponse("read") { + } + + Handle GetNodeValue() const { + NanEscapableScope(); + return NanEscapeScope(ByteBufferToBuffer(*recv_message)); + } + + private: + grpc_byte_buffer **recv_message +}; + +class ClientStatusResponse : public OpResponse { + public: + explicit ClientStatusResponse(grpc_metadata_array *metadata_array, + grpc_status_code *status, + char **status_details): + metadata_array(metadata_array), status(status), + status_details(status_details), OpResponse("status") { + } + + Handle GetNodeValue() const { + NanEscapableScope(); + Handle status_obj = NanNew(); + status_obj->Set(NanNew("code"), NanNew(*status)); + if (event->data.finished.details != NULL) { + status_obj->Set(NanNew("details"), String::New(*status_details)); + } + status_obj->Set(NanNew("metadata"), ParseMetadata(metadata_array)); + return NanEscapeScope(status_obj); + } + private: + grpc_metadata_array *metadata_array; + grpc_status_code *status; + char **status_details; +}; + +class ServerCloseResponse : public OpResponse { + public: + explicit ServerCloseResponse(int *cancelled): cancelled(cancelled), + OpResponse("cancelled") { + } + + Handle GetNodeValue() const { + NanEscapableScope(); + NanEscapeScope(NanNew(*cancelled)); + } + + private: + int *cancelled; +}; + +class NewCallResponse : public OpResponse { + public: + explicit NewCallResponse(grpc_call **call, grpc_call_details *details, + grpc_metadata_array *request_metadata) : + call(call), details(details), request_metadata(request_metadata), + OpResponse("call"){ + } + + Handle GetNodeValue() const { + NanEscapableScope(); + if (*call == NULL) { + return NanEscapeScope(NanNull()); + } + Handle obj = NanNew(); + obj->Set(NanNew("call"), Call::WrapStruct(*call)); + obj->Set(NanNew("method"), NanNew(details->method)); + obj->Set(NanNew("host"), NanNew(details->host)); + obj->Set(NanNew("deadline"), + NanNew(TimespecToMilliseconds(details->deadline))); + obj->Set(NanNew("metadata"), ParseMetadata(request_metadata)); + return NanEscapeScope(obj); + } + private: + grpc_call **call; + grpc_call_details *details; + grpc_metadata_array *request_metadata; +} + +struct tag { + tag(NanCallback *callback, std::vector *responses) : + callback(callback), repsonses(responses) { + } + ~tag() { + for (std::vector::iterator it = responses->begin(); + it != responses->end(); ++it) { + delete *it; + } + delete callback; + delete responses; + } + NanCallback *callback; + std::vector *responses; +}; + +void *CreateTag(Handle callback, grpc_op *ops, size_t nops) { + NanScope(); + NanCallback *cb = new NanCallback(callback); + vector *responses = new vector(); + for (size_t i = 0; i < nops; i++) { + grpc_op *op = &ops[i]; + OpResponse *resp; + // Switching on the TYPE of the op + switch (op->op) { + case GRPC_OP_SEND_INITIAL_METADATA: + resp = new SendResponse("send metadata"); + break; + case GRPC_OP_SEND_MESSAGE: + resp = new SendResponse("write"); + break; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + resp = new SendResponse("client close"); + break; + case GRPC_OP_SEND_STATUS_FROM_SERVER: + resp = new SendResponse("server close"); + break; + case GRPC_OP_RECV_INITIAL_METADATA: + resp = new MetadataResponse(op->data.recv_initial_metadata); + break; + case GRPC_OP_RECV_MESSAGE: + resp = new MessageResponse(op->data.recv_message); + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + resp = new ClientStatusResponse( + op->data.recv_status_on_client.trailing_metadata, + op->data.recv_status_on_client.status, + op->data.recv_status_on_client.status_details); + break; + case GRPC_RECV_CLOSE_ON_SERVER: + resp = new ServerCloseResponse(op->data.recv_close_on_server.cancelled); + break; + default: + continue; + } + responses->push_back(resp); + } + struct tag *tag_struct = new struct tag(cb, responses); return reinterpret_cast(tag_struct); } -Handle GetTagHandle(void *tag) { +void *CreateTag(Handle callback, grpc_call **call, + grpc_call_details *details, + grpc_metadata_array *request_metadata) { NanEscapableScope(); - struct tag *tag_struct = reinterpret_cast(tag); - Handle tag_value = NanNew(*tag_struct->persist_tag); - return NanEscapeScope(tag_value); + NanCallback *cb = new NanCallback(callback); + vector *responses = new vector(); + OpResponse *resp = new NewCallResponse(call, details, request_metadata); + responses->push_back(resp); + struct tag *tag_struct = new struct tag(cb, responses); + return reinterpret_cast(tag_struct); } -bool TagHasCall(void *tag) { - struct tag *tag_struct = reinterpret_cast(tag); - return tag_struct->persist_call != NULL; -} - -Handle TagGetCall(void *tag) { +NanCallback GetCallback(void *tag) { NanEscapableScope(); struct tag *tag_struct = reinterpret_cast(tag); - if (tag_struct->persist_call == NULL) { - return NanEscapeScope(NanNull()); + return NanEscapeScope(*tag_struct->callback); +} + +Handle GetNodeValue(void *tag) { + NanEscapableScope(); + struct tag *tag_struct = reinterpret_cast(tag); + Handle obj = NanNew(); + for (std::vector::iterator it = tag_struct->responses->begin(); + it != tag_struct->responses->end(); ++it) { + OpResponse *resp = *it; + obj->Set(resp->GetOpType(), resp->GetNodeValue()); } - Handle call_value = NanNew(*tag_struct->persist_call); - return NanEscapeScope(call_value); + return NanEscapeScope(obj); } void DestroyTag(void *tag) { delete reinterpret_cast(tag); } diff --git a/ext/tag.h b/ext/tag.h index bdb09252..5c709743 100644 --- a/ext/tag.h +++ b/ext/tag.h @@ -34,21 +34,29 @@ #ifndef NET_GRPC_NODE_TAG_H_ #define NET_GRPC_NODE_TAG_H_ +#include #include +#include namespace grpc { namespace node { -/* Create a void* tag that can be passed to various grpc_call functions from - a javascript value and the javascript wrapper for the call. The call can be - null. */ -void *CreateTag(v8::Handle tag, v8::Handle call); -/* Return the javascript value stored in the tag */ -v8::Handle GetTagHandle(void *tag); -/* Returns true if the call was set (non-null) when the tag was created */ -bool TagHasCall(void *tag); -/* Returns the javascript wrapper for the call associated with this tag */ -v8::Handle TagGetCall(void *call); +/* Create a void* tag that can be passed to grpc_call_start_batch from a callback + function and an ops array */ +void *CreateTag(v8::Handle callback, grpc_op *ops, size_t nops); + +/* Create a void* tag that can be passed to grpc_server_request_call from a + callback and the various out parameters to that function */ +void *CreateTag(v8::Handle callback, grpc_call **call, + grpc_call_details *details, + grpc_metadata_array *request_metadata); + +/* Get the callback from the tag */ +NanCallback GetCallback(void *tag); + +/* Get the combined output value from the tag */ +v8::Handle GetNodevalue(void *tag); + /* Destroy the tag and all resources it is holding. It is illegal to call any of these other functions on a tag after it has been destroyed. */ void DestroyTag(void *tag); From 616e875f23cc98fb1fa7ec350052ab7e0b72c487 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Mon, 9 Feb 2015 10:43:21 -0800 Subject: [PATCH 06/91] More progress towards new API compatibility --- ext/call.cc | 648 ++++++++++++++------------- ext/call.h | 49 +- ext/completion_queue_async_worker.cc | 22 +- ext/completion_queue_async_worker.h | 2 + ext/server.cc | 55 ++- ext/tag.cc | 49 +- ext/tag.h | 13 +- 7 files changed, 494 insertions(+), 344 deletions(-) diff --git a/ext/call.cc b/ext/call.cc index d2e930bc..85dcb3cd 100644 --- a/ext/call.cc +++ b/ext/call.cc @@ -31,24 +31,26 @@ * */ +#include #include #include #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" namespace grpc { namespace node { using ::node::Buffer; +using std::unique_ptr; using v8::Arguments; using v8::Array; using v8::Exception; @@ -70,9 +72,11 @@ using v8::Value; Persistent Call::constructor; Persistent Call::fun_tpl; -bool CreateMetadataArray(Handle metadata, grpc_metadata_array *array - vector *string_handles, - vector*> *handles) { + +bool CreateMetadataArray( + Handle metadata, grpc_metadata_array *array, + std::vector > *string_handles, + std::vector > *handles) { NanScope(); Handle keys(metadata->GetOwnPropertyNames()); for (unsigned int i = 0; i < keys->Length(); i++) { @@ -82,27 +86,27 @@ bool CreateMetadataArray(Handle metadata, grpc_metadata_array *array } array->capacity += Local::Cast(metadata->Get(current_key))->Length(); } - array->metadata = calloc(array->capacity, sizeof(grpc_metadata)); + array->metadata = reinterpret_cast( + gpr_malloc(array->capacity * sizeof(grpc_metadata))); for (unsigned int i = 0; i < keys->Length(); i++) { Handle current_key(keys->Get(i)->ToString()); NanUtf8String *utf8_key = new NanUtf8String(current_key); - string_handles->push_back(utf8_key); + string_handles->push_back(unique_ptr values = Local::Cast(metadata->Get(current_key)); for (unsigned int j = 0; j < values->Length(); j++) { Handle value = values->Get(j); - grpc_metadata *current = &array[array->count]; - grpc_call_error error; + 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 *handle = new Persistent(); - NanAssignPersistent(handle, object); - handles->push_back(handle); + Persistent handle; + NanAssignPersistent(handle, value); + handles->push_back(PersistentHolder(handle)); } else if (value->IsString()) { Handle string_value = value->ToString(); NanUtf8String *utf8_value = new NanUtf8String(string_value); - string_handles->push_back(utf8_value); + string_handles->push_back(unique_ptr(utf8_value)); current->value = **utf8_value; current->value_length = string_value->Length(); } else { @@ -114,6 +118,294 @@ bool CreateMetadataArray(Handle metadata, grpc_metadata_array *array return true; } +Handle ParseMetadata(grpc_metadata_array *metadata_array) { + NanEscapableScope(); + grpc_metadata *metadata_elements = metadata_array->metadata; + size_t length = metadata_array->count; + std::map size_map; + std::map index_map; + + for (unsigned int i = 0; i < length; i++) { + char *key = metadata_elements[i].key; + if (size_map.count(key)) { + size_map[key] += 1; + } + index_map[key] = 0; + } + Handle metadata_object = NanNew(); + for (unsigned int i = 0; i < length; i++) { + grpc_metadata* elem = &metadata_elements[i]; + Handle key_string = String::New(elem->key); + Handle array; + if (metadata_object->Has(key_string)) { + array = Handle::Cast(metadata_object->Get(key_string)); + } else { + array = NanNew(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); +} + +class Op { + public: + Handle GetOpType() const { + NanEscapableScope(); + return NanEscapeScope(NanNew(GetTypeString())); + } +}; + +class SendMetadataOp : public Op { + public: + Handle GetNodeValue() { + NanEscapableScope(); + return NanEscapeScope(NanTrue()); + } + bool ParseOp(Handle value, grpc_op *out, + std::vector > *strings, + std::vector > *handles) { + if (!value->IsObject()) { + return false; + } + grpc_metadata_array array; + if (!CreateMetadataArray(value->ToObject(), &array, strings, handles)) { + return false; + } + out->data.send_initial_metadata.count = array.count; + out->data.send_initial_metadata.metadata = array.metadata; + return true; + } + protected: + char *GetTypeString() { + return "send metadata"; + } +}; + +class SendMessageOp : public Op { + public: + Handle GetNodeValue() { + NanEscapableScope(); + return NanEscapeScope(NanTrue()); + } + bool ParseOp(Handle value, grpc_op *out, + std::vector > *strings, + std::vector > *handles) { + if (!Buffer::HasInstance(value)) { + return false; + } + out->.data.send_message = BufferToByteBuffer(obj->Get(type)); + NanAssignPersistent(handle, value); + handles->push_back(PersistentHolder(handle)); + } + protected: + char *GetTypeString() { + return "send message"; + } +}; + +class SendClientCloseOp : public Op { + public: + Handle GetNodeValue() { + NanEscapableScope(); + return NanEscapeScope(NanTrue()); + } + bool ParseOp(Handle value, grpc_op *out, + std::vector > *strings, + std::vector > *handles) { + return true; + } + protected: + char *GetTypeString() { + return "client close"; + } +}; + +class SendServerStatusOp : public Op { + public: + Handle GetNodeValue() { + NanEscapableScope(); + return NanEscapeScope(NanTrue()); + } + bool ParseOp(Handle value, grpc_op *out, + std::vector > *strings, + std::vector > *handles) { + if (value->IsObject()) { + return false; + } + Handle 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, strings, handles)) { + 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( + server_status->Get(NanNew("code"))->Uint32Value()); + NanUtf8String *str = new NanUtf8String( + server_status->Get(NanNew("details"))); + strings->push_back(unique_ptr(str)); + out->data.send_status_from_server.status_details = **str; + return true; + } + protected: + char *GetTypeString() { + return "send status"; + } +} + +class GetMetadataOp : public Op { + public: + GetMetadataOp() { + grpc_metadata_array_init(&recv_metadata); + } + + ~GetMetadataOp() { + grpc_metadata_array_destroy(&recv_metadata); + } + + Handle GetNodeValue() const { + NanEscapableScope(); + return NanEscapeScope(ParseMetadata(&recv_metadata)); + } + + bool ParseOp(Handle value, grpc_op *out, + std::vector > *strings, + std::vector > *handles) { + out->data.recv_initial_metadata = &recv_metadata; + } + + protected: + char *GetTypeString() { + 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 GetNodeValue() const { + NanEscapableScope(); + return NanEscapeScope(ByteBufferToBuffer(*recv_message)); + } + + bool ParseOp(Handle value, grpc_op *out, + std::vector > *strings, + std::vector > *handles) { + out->data.recv_message = &recv_message; + } + + protected: + char *GetTypeString() { + return "read"; + } + + private: + grpc_byte_buffer *recv_message; +}; + +class ClientStatusOp : public Op { + public: + ClientStatusOp() { + grpc_metadata_array_init(&metadata); + status_details = NULL; + } + + ~ClientStatusOp() { + gprc_metadata_array_destroy(&metadata_array); + gpr_free(status_details); + } + + bool ParseOp(Handle value, grpc_op *out, + std::vector > *strings, + std::vector > *handles) { + 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; + } + + Handle GetNodeValue() const { + NanEscapableScope(); + Handle status_obj = NanNew(); + status_obj->Set(NanNew("code"), NanNew(status)); + if (event->data.finished.details != NULL) { + status_obj->Set(NanNew("details"), String::New(status_details)); + } + status_obj->Set(NanNew("metadata"), ParseMetadata(&metadata_array)); + return NanEscapeScope(status_obj); + } + private: + grpc_metadata_array metadata_array; + grpc_status_code status; + char *status_details; + size_t details_capacity; +}; + +class ServerCloseResponseOp : public Op { + public: + Handle GetNodeValue() const { + NanEscapableScope(); + NanEscapeScope(NanNew(cancelled)); + } + + bool ParseOp(Handle value, grpc_op *out, + std::vector > *strings, + std::vector > *handles) { + out->data.recv_close_on_server.cancelled = &cancelled; + } + + private: + int cancelled; +}; + +struct tag { + tag(NanCallback *callback, std::vector > *ops, + std::vector > *handles, + std::vector > *strings) : + callback(callback), ops(ops), handles(handles), strings(strings){ + } + ~tag() { + if (strings != null) { + for (std::vector::iterator it = strings.begin(); + it != strings.end(); ++it) { + delete *it; + } + delete strings; + } + delete callback; + delete ops; + if (handles != null) { + delete handles; + } + } +}; + Call::Call(grpc_call *call) : wrapped_call(call) {} Call::~Call() { grpc_call_destroy(wrapped_call); } @@ -123,28 +415,10 @@ void Call::Init(Handle exports) { Local 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"), @@ -225,211 +499,64 @@ NAN_METHOD(Call::StartBatch) { if (!args[1]->IsFunction()) { return NanThrowError("startBatch's second argument must be a callback"); } - vector *> *handles = new vector>(); - vector *strings = new vector(); - Persistent *handle; - Handle keys = args[0]->GetOwnPropertyNames(); + Call *call = ObjectWrap::Unwrap(args.This()); + std::vector > *handles = + new std::vector >(); + std::vector > *strings = + new std::vector >(); + Persistent handle; + Handle obj = args[0]->ToObject(); + Handle keys = obj->GetOwnPropertyNames(); size_t nops = keys->Length(); - grpc_op *ops = calloc(nops, sizeof(grpc_op)); - grpc_metadata_array array; - Handle server_status; - NanUtf8String *str; + grpc_op *ops = new grpc_op[nops]; + std::vector > *op_vector = new std::vector >(); for (unsigned int i = 0; i < nops; i++) { - if (!keys->Get(i)->IsUInt32()) { + 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 = type; + uint32_t type = keys->Get(i)->Uint32Value(); + ops[i].op = static_cast(type); switch (type) { case GRPC_OP_SEND_INITIAL_METADATA: - if (!args[0]->Get(type)->IsObject()) { - return NanThrowError("metadata must be an object"); - } - if (!CreateMetadataArray(args[0]->Get(type)->ToObject(), &array, - strings, handles)) { - return NanThrowError("failed to parse metadata"); - } - ops[i].data.send_initial_metadata.count = array.count; - ops[i].data.send_initial_metadata.metadata = array.metadata; - break + op = new SendMetadataOp(); + break; case GRPC_OP_SEND_MESSAGE: - if (!Buffer::HasInstance(args[0]->Get(type))) { - return NanThrowError("message must be a Buffer"); - } - ops[i].data.send_message = BufferToByteBuffer(args[0]->Get(type)); - handle = new Persistent(); - NanAssignPersistent(*handle, args[0]->Get(type)); - handles->push_back(handle); + op = new SendMessageOp(); break; case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + op = new SendClientCloseOp(); break; case GRPC_OP_SEND_STATUS_FROM_SERVER: - if (!args[0]->Get(type)->IsObject()) { - return NanThrowError("server status must be an object"); - } - server_status = args[0]->Get(type)->ToObject(); - if (!server_status->Get("metadata")->IsObject()) { - return NanThrowError("status metadata must be an object"); - } - if (!server_status->Get("code")->IsUInt32()) { - return NanThrowError("status code must be a positive integer"); - } - if (!server_status->Get("details")->IsString()) { - return NanThrowError("status details must be a string"); - } - if (!CreateMetadataArray(server_status->Get("metadata")->ToObject(), - &array, strings, handles)) { - return NanThrowError("Failed to parse status metadata"); - } - ops[i].data.send_status_from_server.trailing_metadata_count = - array.count; - ops[i].data.send_status_from_server.trailing_metadata = array.metadata; - ops[i].data.send_status_from_server.status = - server_status->Get("code")->UInt32Value(); - str = new NanUtf8String(server_status->Get("details")); - strings->push_back(str); - ops[i].data.send_status_from_server.status_details = **str; + op = new SendServerStatusOp(); break; case GRPC_OP_RECV_INITIAL_METADATA: - ops[i].data.recv_initial_metadata = malloc(sizeof(grpc_metadata_array)); - grpc_metadata_array_init(ops[i].data.recv_initial_metadata); + op = new GetMetadataOp(); break; case GRPC_OP_RECV_MESSAGE: - ops[i].data.recv_message = malloc(sizeof(grpc_byte_buffer*)); + op = new ReadMessageOp(); break; case GRPC_OP_RECV_STATUS_ON_CLIENT: - ops[i].data.recv_status_on_client.trailing_metadata = - malloc(sizeof(grpc_metadata_array)); - grpc_metadata_array_init(ops[i].data.recv_status_on_client); - ops[i].data.recv_status_on_client.status = - malloc(sizeof(grpc_status_code)); - ops[i].data.recv_status_on_client.status_details = - malloc(sizeof(char *)); - ops[i].data.recv_status_on_client.status_details_capacity = - malloc(sizeof(size_t)); + op = new ClientStatusOp(); break; case GRPC_OP_RECV_CLOSE_ON_SERVER: - ops[i].data.recv_close_on_server = malloc(sizeof(int)); + op = new ServerCloseResponseOp(); break; - + default: + return NanThrowError("Argument object had an unrecognized key"); } + op.ParseOp(obj.get(type), &ops[i], strings, handles); + op_vector.push_back(unique_ptr(op)); } -} - -NAN_METHOD(Call::AddMetadata) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError("addMetadata can only be called on Call objects"); - } - Call *call = ObjectWrap::Unwrap(args.This()); - if (!args[0]->IsObject()) { - return NanThrowTypeError("addMetadata's first argument must be an object"); - } - Handle metadata = args[0]->ToObject(); - Handle keys(metadata->GetOwnPropertyNames()); - for (unsigned int i = 0; i < keys->Length(); i++) { - Handle 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 values = Local::Cast(metadata->Get(current_key)); - for (unsigned int j = 0; j < values->Length(); j++) { - Handle 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_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"); - } - 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(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"); - } - Call *call = ObjectWrap::Unwrap(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"); - } - Call *call = ObjectWrap::Unwrap(args.This()); - unsigned int flags = args[1]->Uint32Value(); - grpc_call_error error = - grpc_call_server_end_initial_metadata_old(call->wrapped_call, flags); + grpc_call_error error = grpc_call_start_batch( + call->wrapped_call, ops, nops, new struct tag(args[1].As(), + op_vector, nops, handles, + strings)); if (error != GRPC_CALL_OK) { - return NanThrowError("serverEndInitialMetadata failed", error); + return NanThrowError("startBatch failed", error); } + CompletionQueueAsyncWorker::Next(); NanReturnUndefined(); } @@ -446,102 +573,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(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(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(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(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 diff --git a/ext/call.h b/ext/call.h index 1924a1bf..6ae370d0 100644 --- a/ext/call.h +++ b/ext/call.h @@ -34,6 +34,8 @@ #ifndef NET_GRPC_NODE_CALL_H_ #define NET_GRPC_NODE_CALL_H_ +#include + #include #include #include "grpc/grpc.h" @@ -43,6 +45,44 @@ namespace grpc { namespace node { +using std::unique_ptr; + +class PersistentHolder { + public: + explicit PersistentHolder(v8::Persistent persist) : persist(persist) { + } + + ~PersistentHolder() { + persist.Dispose(); + } + + private: + v8::Persistent persist; +}; + +class Op { + public: + virtual Handle GetNodeValue() const = 0; + virtual bool ParseOp(v8::Handle value, grpc_op *out, + std::vector > *strings, + std::vector > *handles) = 0; + Handle GetOpType(); + + protected: + virtual char *GetTypeString(); +}; + +struct tag { + tag(NanCallback *callback, std::vector > *ops, + std::vector > *handles, + std::vector > *strings); + ~tag(); + NanCallback *callback; + std::vector > *ops; + std::vector > *handles; + std::vector > *strings; +}; + /* Wrapper class for grpc_call structs. */ class Call : public ::node::ObjectWrap { public: @@ -60,15 +100,8 @@ class Call : public ::node::ObjectWrap { Call &operator=(const Call &); static NAN_METHOD(New); - static NAN_METHOD(AddMetadata); - static NAN_METHOD(Invoke); - static NAN_METHOD(ServerAccept); - static NAN_METHOD(ServerEndInitialMetadata); + static NAN_METHOD(StartBatch); static NAN_METHOD(Cancel); - static NAN_METHOD(StartWrite); - static NAN_METHOD(StartWriteStatus); - static NAN_METHOD(WritesDone); - static NAN_METHOD(StartRead); static v8::Persistent constructor; // Used for typechecking instances of this javascript class static v8::Persistent fun_tpl; diff --git a/ext/completion_queue_async_worker.cc b/ext/completion_queue_async_worker.cc index 8de7db66..bb0e3918 100644 --- a/ext/completion_queue_async_worker.cc +++ b/ext/completion_queue_async_worker.cc @@ -37,7 +37,6 @@ #include "grpc/grpc.h" #include "grpc/support/time.h" #include "completion_queue_async_worker.h" -#include "event.h" #include "tag.h" namespace grpc { @@ -58,6 +57,9 @@ CompletionQueueAsyncWorker::~CompletionQueueAsyncWorker() {} void CompletionQueueAsyncWorker::Execute() { result = grpc_completion_queue_next(queue, gpr_inf_future); + if (result->data.op_complete != GRPC_OP_OK) { + SetErrorMessage("The batch encountered an error"); + } } grpc_completion_queue *CompletionQueueAsyncWorker::GetQueue() { return queue; } @@ -75,14 +77,26 @@ void CompletionQueueAsyncWorker::Init(Handle exports) { void CompletionQueueAsyncWorker::HandleOKCallback() { NanScope(); - NanCallback event_callback(GetTagHandle(result->tag).As()); - Handle argv[] = {CreateEventObject(result)}; + NanCallback callback = GetTagCallback(result->tag); + Handle argv[] = {NanNull(), GetNodeValue(result->tag)}; DestroyTag(result->tag); grpc_event_finish(result); result = NULL; - event_callback.Call(1, argv); + callback.Call(2, argv); +} + +void CompletionQueueAsyncWorker::HandleErrorCallback() { + NanScope(); + NanCallback callback = GetTagCallback(result->tag); + Handle argv[] = {NanError(ErrorMessage())}; + + DestroyTag(result->tag); + grpc_event_finish(result); + result = NULL; + + callback.Call(1, argv); } } // namespace node diff --git a/ext/completion_queue_async_worker.h b/ext/completion_queue_async_worker.h index 2c928b70..c04a3032 100644 --- a/ext/completion_queue_async_worker.h +++ b/ext/completion_queue_async_worker.h @@ -67,6 +67,8 @@ class CompletionQueueAsyncWorker : public NanAsyncWorker { completion_queue_next */ void HandleOKCallback(); + void HandleErrorCallback(); + private: grpc_event *result; diff --git a/ext/server.cc b/ext/server.cc index 6b8ccef9..c0ccf1f3 100644 --- a/ext/server.cc +++ b/ext/server.cc @@ -31,6 +31,8 @@ * */ +#include + #include "server.h" #include @@ -49,6 +51,7 @@ namespace grpc { namespace node { +using std::unique_ptr; using v8::Arguments; using v8::Array; using v8::Boolean; @@ -67,6 +70,45 @@ using v8::Value; Persistent Server::constructor; Persistent Server::fun_tpl; +class NewCallOp : public Op { + public: + NewCallOp() { + call = NULL; + grpc_call_details_init(&details); + grpc_metadata_array_init(&request_metadata); + } + + ~NewCallOp() { + grpc_call_details_destroy(&details); + grpc_metadata_array_destroy(&details); + } + + Handle GetNodeValue() const { + NanEscapableScope(); + if (*call == NULL) { + return NanEscapeScope(NanNull()); + } + Handle obj = NanNew(); + obj->Set(NanNew("call"), Call::WrapStruct(call)); + obj->Set(NanNew("method"), NanNew(details.method)); + obj->Set(NanNew("host"), NanNew(details.host)); + obj->Set(NanNew("deadline"), + NanNew(TimespecToMilliseconds(details.deadline))); + obj->Set(NanNew("metadata"), ParseMetadata(&request_metadata)); + return NanEscapeScope(obj); + } + + bool ParseOp(Handle value, grpc_op *out, + std::vector > strings, + std::vector > handles) { + return true; + } + + grpc_call *call; + grpc_call_details details; + grpc_metadata_array request_metadata; +} + Server::Server(grpc_server *server) : wrapped_server(server) {} Server::~Server() { grpc_server_destroy(wrapped_server); } @@ -175,13 +217,16 @@ NAN_METHOD(Server::RequestCall) { return NanThrowTypeError("requestCall can only be called on a Server"); } Server *server = ObjectWrap::Unwrap(args.This()); - grpc_call_error error = grpc_server_request_call_old( - server->wrapped_server, CreateTag(args[0], NanNull())); - if (error == GRPC_CALL_OK) { - CompletionQueueAsyncWorker::Next(); - } else { + Op *op = new NewCallOp(); + std::vector > *ops = { unique_ptr(op) }; + grpc_call_error error = grpc_server_request_call( + server->wrapped_server, &op->call, &op->details, &op->metadata, + CompletionQueueAsyncWorker::GetQueue(), + new struct tag(args[0].As(), ops, NULL, NULL)); + if (error != GRPC_CALL_OK) { return NanThrowError("requestCall failed", error); } + CompletionQueueAsyncWorker::Next(); NanReturnUndefined(); } diff --git a/ext/tag.cc b/ext/tag.cc index 4c41c3d2..27baa94a 100644 --- a/ext/tag.cc +++ b/ext/tag.cc @@ -89,6 +89,7 @@ class OpResponse { explicit OpResponse(char *name): name(name) { } virtual Handle GetNodeValue() const = 0; + virtual bool ParseOp() = 0; Handle GetOpType() const { NanEscapableScope(); return NanEscapeScope(NanNew(name)); @@ -136,16 +137,23 @@ class MessageResponse : public OpResponse { } private: - grpc_byte_buffer **recv_message + grpc_byte_buffer **recv_message; }; +switch () { +case GRPC_RECV_CLIENT_STATUS: + op = new ClientStatusResponse; + break; +} + + class ClientStatusResponse : public OpResponse { public: - explicit ClientStatusResponse(grpc_metadata_array *metadata_array, - grpc_status_code *status, - char **status_details): - metadata_array(metadata_array), status(status), - status_details(status_details), OpResponse("status") { + explicit ClientStatusResponse(): + OpResponse("status") { + } + + bool ParseOp(Handle obj, grpc_op *out) { } Handle GetNodeValue() const { @@ -159,9 +167,9 @@ class ClientStatusResponse : public OpResponse { return NanEscapeScope(status_obj); } private: - grpc_metadata_array *metadata_array; - grpc_status_code *status; - char **status_details; + grpc_metadata_array metadata_array; + grpc_status_code status; + char *status_details; }; class ServerCloseResponse : public OpResponse { @@ -208,22 +216,35 @@ class NewCallResponse : public OpResponse { } struct tag { - tag(NanCallback *callback, std::vector *responses) : - callback(callback), repsonses(responses) { + tag(NanCallback *callback, std::vector *responses, + std::vector> *handles, + std::vector *strings) : + callback(callback), repsonses(responses), handles(handles), + strings(strings){ } ~tag() { for (std::vector::iterator it = responses->begin(); it != responses->end(); ++it) { delete *it; } + for (std::vector::iterator it = responses->begin(); + it != responses->end(); ++it) { + delete *it; + } delete callback; delete responses; + delete handles; + delete strings; } NanCallback *callback; std::vector *responses; + std::vector> *handles; + std::vector *strings; }; -void *CreateTag(Handle callback, grpc_op *ops, size_t nops) { +void *CreateTag(Handle callback, grpc_op *ops, size_t nops, + std::vector> *handles, + std::vector *strings) { NanScope(); NanCallback *cb = new NanCallback(callback); vector *responses = new vector(); @@ -264,7 +285,7 @@ void *CreateTag(Handle callback, grpc_op *ops, size_t nops) { } responses->push_back(resp); } - struct tag *tag_struct = new struct tag(cb, responses); + struct tag *tag_struct = new struct tag(cb, responses, handles, strings); return reinterpret_cast(tag_struct); } @@ -280,7 +301,7 @@ void *CreateTag(Handle callback, grpc_call **call, return reinterpret_cast(tag_struct); } -NanCallback GetCallback(void *tag) { +NanCallback GetTagCallback(void *tag) { NanEscapableScope(); struct tag *tag_struct = reinterpret_cast(tag); return NanEscapeScope(*tag_struct->callback); diff --git a/ext/tag.h b/ext/tag.h index 5c709743..9ff8703b 100644 --- a/ext/tag.h +++ b/ext/tag.h @@ -34,6 +34,9 @@ #ifndef NET_GRPC_NODE_TAG_H_ #define NET_GRPC_NODE_TAG_H_ +#include + + #include #include #include @@ -41,9 +44,11 @@ namespace grpc { namespace node { -/* Create a void* tag that can be passed to grpc_call_start_batch from a callback - function and an ops array */ -void *CreateTag(v8::Handle callback, grpc_op *ops, size_t nops); +/* Create a void* tag that can be passed to grpc_call_start_batch from a + callback function and an ops array */ +void *CreateTag(v8::Handle callback, grpc_op *ops, size_t nops, + std::vector > *handles, + std::vector *strings); /* Create a void* tag that can be passed to grpc_server_request_call from a callback and the various out parameters to that function */ @@ -55,7 +60,7 @@ void *CreateTag(v8::Handle callback, grpc_call **call, NanCallback GetCallback(void *tag); /* Get the combined output value from the tag */ -v8::Handle GetNodevalue(void *tag); +v8::Handle GetNodeValue(void *tag); /* Destroy the tag and all resources it is holding. It is illegal to call any of these other functions on a tag after it has been destroyed. */ From 55d5ba6c0561242f391e3514e4617a857934370c Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Mon, 9 Feb 2015 10:56:25 -0800 Subject: [PATCH 07/91] Updated event.cc with new changes --- ext/event.cc | 27 ++++++++++++--------------- test/call_test.js | 8 ++------ 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/ext/event.cc b/ext/event.cc index b9446062..28759957 100644 --- a/ext/event.cc +++ b/ext/event.cc @@ -58,11 +58,11 @@ using v8::Value; Handle ParseMetadata(grpc_metadata *metadata_elements, size_t length) { NanEscapableScope(); - std::map size_map; - std::map index_map; + std::map size_map; + std::map index_map; for (unsigned int i = 0; i < length; i++) { - char *key = metadata_elements[i].key; + const char *key = metadata_elements[i].key; if (size_map.count(key)) { size_map[key] += 1; } @@ -97,8 +97,6 @@ Handle GetEventData(grpc_event *event) { switch (event->type) { case GRPC_READ: return NanEscapeScope(ByteBufferToBuffer(event->data.read)); - case GRPC_INVOKE_ACCEPTED: - return NanEscapeScope(NanNew(event->data.invoke_accepted)); case GRPC_WRITE_ACCEPTED: return NanEscapeScope(NanNew(event->data.write_accepted)); case GRPC_FINISH_ACCEPTED: @@ -124,12 +122,12 @@ Handle GetEventData(grpc_event *event) { return NanEscapeScope(NanNull()); } rpc_new->Set( - NanNew("method"), - NanNew(event->data.server_rpc_new.method)); + NanNew("method"), + NanNew(event->data.server_rpc_new.method)); rpc_new->Set( - NanNew("host"), - NanNew(event->data.server_rpc_new.host)); - rpc_new->Set(NanNew("absolute_deadline"), + NanNew("host"), + NanNew(event->data.server_rpc_new.host)); + rpc_new->Set(NanNew("absolute_deadline"), NanNew(TimespecToMilliseconds( event->data.server_rpc_new.deadline))); count = event->data.server_rpc_new.metadata_count; @@ -137,12 +135,11 @@ Handle GetEventData(grpc_event *event) { metadata = NanNew(static_cast(count)); for (unsigned int i = 0; i < count; i++) { Handle item_obj = Object::New(); - item_obj->Set(NanNew("key"), - NanNew(items[i].key)); + item_obj->Set(NanNew("key"), + NanNew(items[i].key)); item_obj->Set( - NanNew("value"), - NanNew(items[i].value, - static_cast(items[i].value_length))); + NanNew("value"), + NanNew(items[i].value, static_cast(items[i].value_length))); metadata->Set(i, item_obj); } rpc_new->Set(NanNew("metadata"), ParseMetadata(items, count)); diff --git a/test/call_test.js b/test/call_test.js index dfa9aaa1..48db2454 100644 --- a/test/call_test.js +++ b/test/call_test.js @@ -133,15 +133,13 @@ describe('call', function() { call.addMetadata(5); }, TypeError); }); - it('should fail if invoke was already called', function(done) { + it.skip('should fail if invoke was already called', function(done) { var call = new grpc.Call(channel, 'method', getDeadline(1)); call.invoke(function() {}, function() {done();}, 0); assert.throws(function() { call.addMetadata({'key': ['value']}); - }, function(err) { - return err.code === grpc.callError.ALREADY_INVOKED; }); // Cancel to speed up the test call.cancel(); @@ -189,12 +187,10 @@ describe('call', function() { call.serverAccept(); }, TypeError); }); - it('should return an error when called on a client Call', function() { + it.skip('should return an error when called on a client Call', function() { var call = new grpc.Call(channel, 'method', getDeadline(1)); assert.throws(function() { call.serverAccept(function() {}); - }, function(err) { - return err.code === grpc.callError.NOT_ON_CLIENT; }); }); }); From cfa8b0a5609f20aa499fe533b7db8f449712fc74 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Mon, 9 Feb 2015 11:01:20 -0800 Subject: [PATCH 08/91] Minor comment update --- ext/event.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/event.cc b/ext/event.cc index 28759957..d59b68fb 100644 --- a/ext/event.cc +++ b/ext/event.cc @@ -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 From e8c66830d716281ba266e78ce4f18bba961c7f72 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Mon, 9 Feb 2015 11:41:23 -0800 Subject: [PATCH 09/91] Fixed some compiler errors in call.cc --- binding.gyp | 7 +++--- ext/call.cc | 61 ++++++++++++++++++++++++++++++++--------------------- ext/call.h | 12 +++++------ 3 files changed, 47 insertions(+), 33 deletions(-) diff --git a/binding.gyp b/binding.gyp index cf2a6acb..a289b9b9 100644 --- a/binding.gyp +++ b/binding.gyp @@ -9,14 +9,15 @@ 'include_dirs': [ " #include +#include #include @@ -46,11 +47,12 @@ #include "completion_queue_async_worker.h" #include "timeval.h" +using std::unique_ptr; + namespace grpc { namespace node { using ::node::Buffer; -using std::unique_ptr; using v8::Arguments; using v8::Array; using v8::Exception; @@ -91,7 +93,7 @@ bool CreateMetadataArray( for (unsigned int i = 0; i < keys->Length(); i++) { Handle current_key(keys->Get(i)->ToString()); NanUtf8String *utf8_key = new NanUtf8String(current_key); - string_handles->push_back(unique_ptrpush_back(unique_ptr(utf8_key)); Handle values = Local::Cast(metadata->Get(current_key)); for (unsigned int j = 0; j < values->Length(); j++) { Handle value = values->Get(j); @@ -102,7 +104,8 @@ bool CreateMetadataArray( current->value_length = Buffer::Length(value); Persistent handle; NanAssignPersistent(handle, value); - handles->push_back(PersistentHolder(handle)); + handles->push_back(unique_ptr( + new PersistentHolder(handle))); } else if (value->IsString()) { Handle string_value = value->ToString(); NanUtf8String *utf8_value = new NanUtf8String(string_value); @@ -118,15 +121,15 @@ bool CreateMetadataArray( return true; } -Handle ParseMetadata(grpc_metadata_array *metadata_array) { +Handle ParseMetadata(const grpc_metadata_array *metadata_array) { NanEscapableScope(); grpc_metadata *metadata_elements = metadata_array->metadata; size_t length = metadata_array->count; - std::map size_map; - std::map index_map; + std::map size_map; + std::map index_map; for (unsigned int i = 0; i < length; i++) { - char *key = metadata_elements[i].key; + const char *key = metadata_elements[i].key; if (size_map.count(key)) { size_map[key] += 1; } @@ -151,13 +154,10 @@ Handle ParseMetadata(grpc_metadata_array *metadata_array) { return NanEscapeScope(metadata_object); } -class Op { - public: - Handle GetOpType() const { - NanEscapableScope(); - return NanEscapeScope(NanNew(GetTypeString())); - } -}; +Handle Op::GetOpType() const { + NanEscapableScope(); + return NanEscapeScope(NanNew(GetTypeString())); +} class SendMetadataOp : public Op { public: @@ -180,7 +180,7 @@ class SendMetadataOp : public Op { return true; } protected: - char *GetTypeString() { + std::string GetTypeString() { return "send metadata"; } }; @@ -197,12 +197,13 @@ class SendMessageOp : public Op { if (!Buffer::HasInstance(value)) { return false; } - out->.data.send_message = BufferToByteBuffer(obj->Get(type)); + out->data.send_message = BufferToByteBuffer(obj->Get(type)); NanAssignPersistent(handle, value); - handles->push_back(PersistentHolder(handle)); + handles->push_back(unique_ptr( + new PersistentHolder(handle))); } protected: - char *GetTypeString() { + std::string GetTypeString() { return "send message"; } }; @@ -219,7 +220,7 @@ class SendClientCloseOp : public Op { return true; } protected: - char *GetTypeString() { + std::string GetTypeString() { return "client close"; } }; @@ -264,7 +265,7 @@ class SendServerStatusOp : public Op { return true; } protected: - char *GetTypeString() { + std::string GetTypeString() { return "send status"; } } @@ -291,7 +292,7 @@ class GetMetadataOp : public Op { } protected: - char *GetTypeString() { + std::string GetTypeString() { return "metadata"; } @@ -311,17 +312,18 @@ class ReadMessageOp : public Op { } Handle GetNodeValue() const { NanEscapableScope(); - return NanEscapeScope(ByteBufferToBuffer(*recv_message)); + return NanEscapeScope(ByteBufferToBuffer(recv_message)); } bool ParseOp(Handle value, grpc_op *out, std::vector > *strings, std::vector > *handles) { out->data.recv_message = &recv_message; + return true; } protected: - char *GetTypeString() { + std::string GetTypeString() { return "read"; } @@ -348,6 +350,7 @@ class ClientStatusOp : public Op { 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 GetNodeValue() const { @@ -360,6 +363,10 @@ class ClientStatusOp : public Op { 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; @@ -378,6 +385,12 @@ class ServerCloseResponseOp : public Op { std::vector > *strings, std::vector > *handles) { out->data.recv_close_on_server.cancelled = &cancelled; + return true; + } + + protected: + std::string GetTypeString() const { + return "cancelled"; } private: diff --git a/ext/call.h b/ext/call.h index 6ae370d0..6c38877d 100644 --- a/ext/call.h +++ b/ext/call.h @@ -49,27 +49,27 @@ using std::unique_ptr; class PersistentHolder { public: - explicit PersistentHolder(v8::Persistent persist) : persist(persist) { + explicit PersistentHolder(v8::Persistent persist) : persist(persist) { } ~PersistentHolder() { persist.Dispose(); - } +} private: - v8::Persistent persist; + v8::Persistent persist; }; class Op { public: - virtual Handle GetNodeValue() const = 0; + virtual v8::Handle GetNodeValue() const = 0; virtual bool ParseOp(v8::Handle value, grpc_op *out, std::vector > *strings, std::vector > *handles) = 0; - Handle GetOpType(); + v8::Handle GetOpType() const; protected: - virtual char *GetTypeString(); + virtual char *GetTypeString() const; }; struct tag { From 3d35d387a1d147bc7f9491b5a23068b514e6af83 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Mon, 9 Feb 2015 11:43:34 -0800 Subject: [PATCH 10/91] Fixed another compiler error --- ext/call.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/call.h b/ext/call.h index 6c38877d..880ce7c4 100644 --- a/ext/call.h +++ b/ext/call.h @@ -69,7 +69,7 @@ class Op { v8::Handle GetOpType() const; protected: - virtual char *GetTypeString() const; + virtual std::string GetTypeString() const; }; struct tag { From 58777a58124aa3d398c10f6cb77df4d8396f6f38 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Mon, 9 Feb 2015 11:50:19 -0800 Subject: [PATCH 11/91] Fixed math and stock servers --- examples/math_server.js | 3 ++- examples/stock.proto | 14 +++++++------- examples/stock_server.js | 7 ++++++- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/examples/math_server.js b/examples/math_server.js index e65cfe30..e1bd11b5 100644 --- a/examples/math_server.js +++ b/examples/math_server.js @@ -128,7 +128,8 @@ var server = new Server({ }); if (require.main === module) { - server.bind('localhost:7070').listen(); + server.bind('0.0.0.0:7070'); + server.listen(); } /** diff --git a/examples/stock.proto b/examples/stock.proto index efe98d84..2bc5c29d 100644 --- a/examples/stock.proto +++ b/examples/stock.proto @@ -35,28 +35,28 @@ package examples; message StockRequest { optional string symbol = 1; optional int32 num_trades_to_watch = 2 [default=0]; -}; +} message StockReply { optional float price = 1; optional string symbol = 2; -}; +} // Interface exported by the server service Stock { // Simple blocking RPC rpc GetLastTradePrice(StockRequest) returns (StockReply) { - }; + } // Bidirectional streaming RPC rpc GetLastTradePriceMultiple(stream StockRequest) returns (stream StockReply) { - }; + } // Unidirectional server-to-client streaming RPC rpc WatchFutureTrades(StockRequest) returns (stream StockReply) { - }; + } // Unidirectional client-to-server streaming RPC rpc GetHighestTradePrice(stream StockRequest) returns (StockReply) { - }; + } -}; \ No newline at end of file +} \ No newline at end of file diff --git a/examples/stock_server.js b/examples/stock_server.js index c188181b..07cea2ce 100644 --- a/examples/stock_server.js +++ b/examples/stock_server.js @@ -35,7 +35,7 @@ var _ = require('underscore'); var grpc = require('..'); var examples = grpc.load(__dirname + '/stock.proto').examples; -var StockServer = grpc.makeServerConstructor([examples.Stock.service]); +var StockServer = grpc.buildServer([examples.Stock.service]); function getLastTradePrice(call, callback) { callback(null, {price: 88}); @@ -80,4 +80,9 @@ var stockServer = new StockServer({ } }); +if (require.main === module) { + stockServer.bind('0.0.0.0:8080'); + stockServer.listen(); +} + exports.module = stockServer; From 7fab056c995822395d71c59fad820c50238af845 Mon Sep 17 00:00:00 2001 From: Jun Yang Date: Mon, 9 Feb 2015 13:00:17 -0800 Subject: [PATCH 12/91] Completed minimal sample in JsDoc of stock client. --- examples/stock_client.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/stock_client.js b/examples/stock_client.js index 8e99090f..b37e66df 100644 --- a/examples/stock_client.js +++ b/examples/stock_client.js @@ -39,5 +39,8 @@ var examples = grpc.load(__dirname + '/stock.proto').examples; * * var StockClient = require('stock_client.js'); * var stockClient = new StockClient(server_address); + * stockClient.getLastTradePrice({symbol: 'GOOG'}, function(error, response) { + * console.log(error || response); + * }); */ module.exports = examples.Stock; From 187580595ff1259fb33eb7ab4f42b743ba8a147b Mon Sep 17 00:00:00 2001 From: Jun Yang Date: Mon, 9 Feb 2015 13:43:56 -0800 Subject: [PATCH 13/91] Added echoing of requested stock symbol. This makes the example more realistic. --- examples/stock_server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stock_server.js b/examples/stock_server.js index 07cea2ce..61ba2731 100644 --- a/examples/stock_server.js +++ b/examples/stock_server.js @@ -38,7 +38,7 @@ var examples = grpc.load(__dirname + '/stock.proto').examples; var StockServer = grpc.buildServer([examples.Stock.service]); function getLastTradePrice(call, callback) { - callback(null, {price: 88}); + callback(null, {symbol: call.request.symbol, price: 88}); } function watchFutureTrades(call) { From 6f8dde55cca0848a41ecffc701448c93e1624a46 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Mon, 9 Feb 2015 15:55:10 -0800 Subject: [PATCH 14/91] Extension module now compiles and some tests pass --- binding.gyp | 2 - ext/call.cc | 107 +++++++++++++--------- ext/call.h | 14 ++- ext/completion_queue_async_worker.cc | 4 +- ext/node_grpc.cc | 33 ------- ext/server.cc | 27 ++++-- test/call_test.js | 132 +++++++++++---------------- test/constant_test.js | 37 -------- test/end_to_end_test.js | 85 +++++++++-------- 9 files changed, 198 insertions(+), 243 deletions(-) diff --git a/binding.gyp b/binding.gyp index a289b9b9..fb4c779f 100644 --- a/binding.gyp +++ b/binding.gyp @@ -34,11 +34,9 @@ "ext/channel.cc", "ext/completion_queue_async_worker.cc", "ext/credentials.cc", - "ext/event.cc", "ext/node_grpc.cc", "ext/server.cc", "ext/server_credentials.cc", - "ext/tag.cc", "ext/timeval.cc" ], 'conditions' : [ diff --git a/ext/call.cc b/ext/call.cc index 4751621c..785cee8d 100644 --- a/ext/call.cc +++ b/ext/call.cc @@ -55,6 +55,7 @@ namespace node { using ::node::Buffer; using v8::Arguments; using v8::Array; +using v8::Boolean; using v8::Exception; using v8::External; using v8::Function; @@ -80,6 +81,7 @@ bool CreateMetadataArray( std::vector > *string_handles, std::vector > *handles) { NanScope(); + grpc_metadata_array_init(array); Handle keys(metadata->GetOwnPropertyNames()); for (unsigned int i = 0; i < keys->Length(); i++) { Handle current_key(keys->Get(i)->ToString()); @@ -156,12 +158,12 @@ Handle ParseMetadata(const grpc_metadata_array *metadata_array) { Handle Op::GetOpType() const { NanEscapableScope(); - return NanEscapeScope(NanNew(GetTypeString())); + return NanEscapeScope(NanNew(GetTypeString())); } class SendMetadataOp : public Op { public: - Handle GetNodeValue() { + Handle GetNodeValue() const { NanEscapableScope(); return NanEscapeScope(NanTrue()); } @@ -180,14 +182,14 @@ class SendMetadataOp : public Op { return true; } protected: - std::string GetTypeString() { + std::string GetTypeString() const { return "send metadata"; } }; class SendMessageOp : public Op { public: - Handle GetNodeValue() { + Handle GetNodeValue() const { NanEscapableScope(); return NanEscapeScope(NanTrue()); } @@ -197,20 +199,22 @@ class SendMessageOp : public Op { if (!Buffer::HasInstance(value)) { return false; } - out->data.send_message = BufferToByteBuffer(obj->Get(type)); + out->data.send_message = BufferToByteBuffer(value); + Persistent handle; NanAssignPersistent(handle, value); handles->push_back(unique_ptr( new PersistentHolder(handle))); + return true; } protected: - std::string GetTypeString() { + std::string GetTypeString() const { return "send message"; } }; class SendClientCloseOp : public Op { public: - Handle GetNodeValue() { + Handle GetNodeValue() const { NanEscapableScope(); return NanEscapeScope(NanTrue()); } @@ -220,14 +224,14 @@ class SendClientCloseOp : public Op { return true; } protected: - std::string GetTypeString() { + std::string GetTypeString() const { return "client close"; } }; class SendServerStatusOp : public Op { public: - Handle GetNodeValue() { + Handle GetNodeValue() const { NanEscapableScope(); return NanEscapeScope(NanTrue()); } @@ -265,10 +269,10 @@ class SendServerStatusOp : public Op { return true; } protected: - std::string GetTypeString() { + std::string GetTypeString() const { return "send status"; } -} +}; class GetMetadataOp : public Op { public: @@ -289,10 +293,11 @@ class GetMetadataOp : public Op { std::vector > *strings, std::vector > *handles) { out->data.recv_initial_metadata = &recv_metadata; + return true; } protected: - std::string GetTypeString() { + std::string GetTypeString() const { return "metadata"; } @@ -323,7 +328,7 @@ class ReadMessageOp : public Op { } protected: - std::string GetTypeString() { + std::string GetTypeString() const { return "read"; } @@ -334,12 +339,13 @@ class ReadMessageOp : public Op { class ClientStatusOp : public Op { public: ClientStatusOp() { - grpc_metadata_array_init(&metadata); + grpc_metadata_array_init(&metadata_array); status_details = NULL; + details_capacity = 0; } ~ClientStatusOp() { - gprc_metadata_array_destroy(&metadata_array); + grpc_metadata_array_destroy(&metadata_array); gpr_free(status_details); } @@ -357,7 +363,7 @@ class ClientStatusOp : public Op { NanEscapableScope(); Handle status_obj = NanNew(); status_obj->Set(NanNew("code"), NanNew(status)); - if (event->data.finished.details != NULL) { + if (status_details != NULL) { status_obj->Set(NanNew("details"), String::New(status_details)); } status_obj->Set(NanNew("metadata"), ParseMetadata(&metadata_array)); @@ -378,7 +384,7 @@ class ServerCloseResponseOp : public Op { public: Handle GetNodeValue() const { NanEscapableScope(); - NanEscapeScope(NanNew(cancelled)); + return NanEscapeScope(NanNew(cancelled)); } bool ParseOp(Handle value, grpc_op *out, @@ -397,27 +403,43 @@ class ServerCloseResponseOp : public Op { int cancelled; }; -struct tag { - tag(NanCallback *callback, std::vector > *ops, - std::vector > *handles, - std::vector > *strings) : - callback(callback), ops(ops), handles(handles), strings(strings){ +tag::tag(NanCallback *callback, std::vector > *ops, + std::vector > *handles, + std::vector > *strings) : + callback(callback), ops(ops), handles(handles), strings(strings){ +} +tag::~tag() { + delete callback; + delete ops; + if (handles != NULL) { + delete handles; } - ~tag() { - if (strings != null) { - for (std::vector::iterator it = strings.begin(); - it != strings.end(); ++it) { - delete *it; - } - delete strings; - } - delete callback; - delete ops; - if (handles != null) { - delete handles; - } + if (strings != NULL) { + delete strings; } -}; +} + +Handle GetTagNodeValue(void *tag) { + NanEscapableScope(); + struct tag *tag_struct = reinterpret_cast(tag); + Handle tag_obj = NanNew(); + for (std::vector >::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(tag); + return *tag_struct->callback; +} + +void DestroyTag(void *tag) { + struct tag *tag_struct = reinterpret_cast(tag); + delete tag_struct; +} Call::Call(grpc_call *call) : wrapped_call(call) {} @@ -559,13 +581,16 @@ NAN_METHOD(Call::StartBatch) { default: return NanThrowError("Argument object had an unrecognized key"); } - op.ParseOp(obj.get(type), &ops[i], strings, handles); - op_vector.push_back(unique_ptr(op)); + if (!op->ParseOp(obj->Get(type), &ops[i], strings, handles)) { + return NanThrowTypeError("Incorrectly typed arguments to startBatch"); + } + op_vector->push_back(unique_ptr(op)); } grpc_call_error error = grpc_call_start_batch( - call->wrapped_call, ops, nops, new struct tag(args[1].As(), - op_vector, nops, handles, - strings)); + call->wrapped_call, ops, nops, new struct tag( + new NanCallback(args[1].As()), + op_vector, handles, + strings)); if (error != GRPC_CALL_OK) { return NanThrowError("startBatch failed", error); } diff --git a/ext/call.h b/ext/call.h index 880ce7c4..434bcf8a 100644 --- a/ext/call.h +++ b/ext/call.h @@ -35,6 +35,7 @@ #define NET_GRPC_NODE_CALL_H_ #include +#include #include #include @@ -47,9 +48,12 @@ namespace node { using std::unique_ptr; +v8::Handle ParseMetadata(const grpc_metadata_array *metadata_array); + class PersistentHolder { public: - explicit PersistentHolder(v8::Persistent persist) : persist(persist) { + explicit PersistentHolder(v8::Persistent persist) : + persist(persist) { } ~PersistentHolder() { @@ -69,7 +73,7 @@ class Op { v8::Handle GetOpType() const; protected: - virtual std::string GetTypeString() const; + virtual std::string GetTypeString() const = 0; }; struct tag { @@ -83,6 +87,12 @@ struct tag { std::vector > *strings; }; +v8::Handle GetTagNodeValue(void *tag); + +NanCallback GetTagCallback(void *tag); + +void DestroyTag(void *tag); + /* Wrapper class for grpc_call structs. */ class Call : public ::node::ObjectWrap { public: diff --git a/ext/completion_queue_async_worker.cc b/ext/completion_queue_async_worker.cc index bb0e3918..5c0e27e6 100644 --- a/ext/completion_queue_async_worker.cc +++ b/ext/completion_queue_async_worker.cc @@ -37,7 +37,7 @@ #include "grpc/grpc.h" #include "grpc/support/time.h" #include "completion_queue_async_worker.h" -#include "tag.h" +#include "call.h" namespace grpc { namespace node { @@ -78,7 +78,7 @@ void CompletionQueueAsyncWorker::Init(Handle exports) { void CompletionQueueAsyncWorker::HandleOKCallback() { NanScope(); NanCallback callback = GetTagCallback(result->tag); - Handle argv[] = {NanNull(), GetNodeValue(result->tag)}; + Handle argv[] = {NanNull(), GetTagNodeValue(result->tag)}; DestroyTag(result->tag); grpc_event_finish(result); diff --git a/ext/node_grpc.cc b/ext/node_grpc.cc index c9388940..9b0fe829 100644 --- a/ext/node_grpc.cc +++ b/ext/node_grpc.cc @@ -130,37 +130,6 @@ void InitCallErrorConstants(Handle exports) { call_error->Set(NanNew("INVALID_FLAGS"), INVALID_FLAGS); } -void InitOpErrorConstants(Handle exports) { - NanScope(); - Handle op_error = Object::New(); - exports->Set(NanNew("opError"), op_error); - Handle OK(NanNew(GRPC_OP_OK)); - op_error->Set(NanNew("OK"), OK); - Handle ERROR(NanNew(GRPC_OP_ERROR)); - op_error->Set(NanNew("ERROR"), ERROR); -} - -void InitCompletionTypeConstants(Handle exports) { - NanScope(); - Handle completion_type = Object::New(); - exports->Set(NanNew("completionType"), completion_type); - Handle QUEUE_SHUTDOWN(NanNew(GRPC_QUEUE_SHUTDOWN)); - completion_type->Set(NanNew("QUEUE_SHUTDOWN"), QUEUE_SHUTDOWN); - Handle READ(NanNew(GRPC_READ)); - completion_type->Set(NanNew("READ"), READ); - Handle WRITE_ACCEPTED(NanNew(GRPC_WRITE_ACCEPTED)); - completion_type->Set(NanNew("WRITE_ACCEPTED"), WRITE_ACCEPTED); - Handle FINISH_ACCEPTED(NanNew(GRPC_FINISH_ACCEPTED)); - completion_type->Set(NanNew("FINISH_ACCEPTED"), FINISH_ACCEPTED); - Handle CLIENT_METADATA_READ( - NanNew(GRPC_CLIENT_METADATA_READ)); - completion_type->Set(NanNew("CLIENT_METADATA_READ"), CLIENT_METADATA_READ); - Handle FINISHED(NanNew(GRPC_FINISHED)); - completion_type->Set(NanNew("FINISHED"), FINISHED); - Handle SERVER_RPC_NEW(NanNew(GRPC_SERVER_RPC_NEW)); - completion_type->Set(NanNew("SERVER_RPC_NEW"), SERVER_RPC_NEW); -} - void InitOpTypeConstants(Handle exports) { NanScope(); Handle op_type = Object::New(); @@ -196,8 +165,6 @@ void init(Handle exports) { grpc_init(); InitStatusConstants(exports); InitCallErrorConstants(exports); - InitOpErrorConstants(exports); - InitCompletionTypeConstants(exports); InitOpTypeConstants(exports); grpc::node::Call::Init(exports); diff --git a/ext/server.cc b/ext/server.cc index c0ccf1f3..75ea681f 100644 --- a/ext/server.cc +++ b/ext/server.cc @@ -45,8 +45,8 @@ #include "grpc/grpc_security.h" #include "call.h" #include "completion_queue_async_worker.h" -#include "tag.h" #include "server_credentials.h" +#include "timeval.h" namespace grpc { namespace node { @@ -55,6 +55,7 @@ using std::unique_ptr; using v8::Arguments; using v8::Array; using v8::Boolean; +using v8::Date; using v8::Exception; using v8::Function; using v8::FunctionTemplate; @@ -80,12 +81,12 @@ class NewCallOp : public Op { ~NewCallOp() { grpc_call_details_destroy(&details); - grpc_metadata_array_destroy(&details); + grpc_metadata_array_destroy(&request_metadata); } Handle GetNodeValue() const { NanEscapableScope(); - if (*call == NULL) { + if (call == NULL) { return NanEscapeScope(NanNull()); } Handle obj = NanNew(); @@ -99,15 +100,20 @@ class NewCallOp : public Op { } bool ParseOp(Handle value, grpc_op *out, - std::vector > strings, - std::vector > handles) { + std::vector > *strings, + std::vector > *handles) { return true; } grpc_call *call; grpc_call_details details; grpc_metadata_array request_metadata; -} + + protected: + std::string GetTypeString() const { + return "new call"; + } +}; Server::Server(grpc_server *server) : wrapped_server(server) {} @@ -217,12 +223,13 @@ NAN_METHOD(Server::RequestCall) { return NanThrowTypeError("requestCall can only be called on a Server"); } Server *server = ObjectWrap::Unwrap(args.This()); - Op *op = new NewCallOp(); - std::vector > *ops = { unique_ptr(op) }; + NewCallOp *op = new NewCallOp(); + std::vector > *ops = new std::vector >(); + ops->push_back(unique_ptr(op)); grpc_call_error error = grpc_server_request_call( - server->wrapped_server, &op->call, &op->details, &op->metadata, + server->wrapped_server, &op->call, &op->details, &op->request_metadata, CompletionQueueAsyncWorker::GetQueue(), - new struct tag(args[0].As(), ops, NULL, NULL)); + new struct tag(new NanCallback(args[0].As()), ops, NULL, NULL)); if (error != GRPC_CALL_OK) { return NanThrowError("requestCall failed", error); } diff --git a/test/call_test.js b/test/call_test.js index dfa9aaa1..e341092f 100644 --- a/test/call_test.js +++ b/test/call_test.js @@ -98,105 +98,81 @@ describe('call', function() { }, TypeError); }); }); - describe('addMetadata', function() { - it('should succeed with a map from strings to string arrays', function() { + describe('startBatch', function() { + it('should fail without an object and a function', function() { var call = new grpc.Call(channel, 'method', getDeadline(1)); - assert.doesNotThrow(function() { - call.addMetadata({'key': ['value']}); + assert.throws(function() { + call.startBatch(); }); - assert.doesNotThrow(function() { - call.addMetadata({'key1': ['value1'], 'key2': ['value2']}); + assert.throws(function() { + call.startBatch({}); + }); + assert.throws(function() { + call.startBatch(null, function(){}); }); }); - it('should succeed with a map from strings to buffer arrays', function() { + it.skip('should succeed with an empty object', function(done) { var call = new grpc.Call(channel, 'method', getDeadline(1)); assert.doesNotThrow(function() { - call.addMetadata({'key': [new Buffer('value')]}); + call.startBatch({}, function(err) { + assert.ifError(err); + done(); + }); }); + }); + }); + describe('startBatch with metadata', function() { + it('should succeed with a map of strings to string arrays', function(done) { + var call = new grpc.Call(channel, 'method', getDeadline(1)); assert.doesNotThrow(function() { - call.addMetadata({'key1': [new Buffer('value1')], - 'key2': [new Buffer('value2')]}); + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = {'key1': ['value1'], + 'key2': ['value2']}; + call.startBatch(batch, function(err, resp) { + assert.ifError(err); + assert.deepEqual(resp, {'send metadata': true}); + done(); + }); + }); + }); + it('should succeed with a map of strings to buffer arrays', function(done) { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.doesNotThrow(function() { + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = { + 'key1': [new Buffer('value1')], + 'key2': [new Buffer('value2')] + }; + call.startBatch(batch, function(err, resp) { + assert.ifError(err); + assert.deepEqual(resp, {'send metadata': true}); + done(); + }); }); }); it('should fail with other parameter types', function() { var call = new grpc.Call(channel, 'method', getDeadline(1)); assert.throws(function() { - call.addMetadata(); + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = undefined; + call.startBatch(batch, function(){}); }); assert.throws(function() { - call.addMetadata(null); + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = null; + call.startBatch(batch, function(){}); }, TypeError); assert.throws(function() { - call.addMetadata('value'); + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = 'value'; + call.startBatch(batch, function(){}); }, TypeError); assert.throws(function() { - call.addMetadata(5); + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = 5; + call.startBatch(batch, function(){}); }, TypeError); }); - it('should fail if invoke was already called', function(done) { - var call = new grpc.Call(channel, 'method', getDeadline(1)); - call.invoke(function() {}, - function() {done();}, - 0); - assert.throws(function() { - call.addMetadata({'key': ['value']}); - }, function(err) { - return err.code === grpc.callError.ALREADY_INVOKED; - }); - // Cancel to speed up the test - call.cancel(); - }); - }); - describe('invoke', function() { - it('should fail with fewer than 3 arguments', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); - assert.throws(function() { - call.invoke(); - }, TypeError); - assert.throws(function() { - call.invoke(function() {}); - }, TypeError); - assert.throws(function() { - call.invoke(function() {}, - function() {}); - }, TypeError); - }); - it('should work with 2 args and an int', function(done) { - assert.doesNotThrow(function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); - call.invoke(function() {}, - function() {done();}, - 0); - // Cancel to speed up the test - call.cancel(); - }); - }); - it('should reject incorrectly typed arguments', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); - assert.throws(function() { - call.invoke(0, 0, 0); - }, TypeError); - assert.throws(function() { - call.invoke(function() {}, - function() {}, 'test'); - }); - }); - }); - describe('serverAccept', function() { - it('should fail with fewer than 1 argument1', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); - assert.throws(function() { - call.serverAccept(); - }, TypeError); - }); - it('should return an error when called on a client Call', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); - assert.throws(function() { - call.serverAccept(function() {}); - }, function(err) { - return err.code === grpc.callError.NOT_ON_CLIENT; - }); - }); }); describe('cancel', function() { it('should succeed', function() { diff --git a/test/constant_test.js b/test/constant_test.js index 0138a552..4d11e6f5 100644 --- a/test/constant_test.js +++ b/test/constant_test.js @@ -76,31 +76,6 @@ var callErrorNames = [ 'INVALID_FLAGS' ]; -/** - * List of all op error names - * @const - * @type {Array.} - */ -var opErrorNames = [ - 'OK', - 'ERROR' -]; - -/** - * List of all completion type names - * @const - * @type {Array.} - */ -var completionTypeNames = [ - 'QUEUE_SHUTDOWN', - 'READ', - 'WRITE_ACCEPTED', - 'FINISH_ACCEPTED', - 'CLIENT_METADATA_READ', - 'FINISHED', - 'SERVER_RPC_NEW' -]; - describe('constants', function() { it('should have all of the status constants', function() { for (var i = 0; i < statusNames.length; i++) { @@ -114,16 +89,4 @@ describe('constants', function() { 'call error missing: ' + callErrorNames[i]); } }); - it('should have all of the op errors', function() { - for (var i = 0; i < opErrorNames.length; i++) { - assert(grpc.opError.hasOwnProperty(opErrorNames[i]), - 'op error missing: ' + opErrorNames[i]); - } - }); - it('should have all of the completion types', function() { - for (var i = 0; i < completionTypeNames.length; i++) { - assert(grpc.completionType.hasOwnProperty(completionTypeNames[i]), - 'completion type missing: ' + completionTypeNames[i]); - } - }); }); diff --git a/test/end_to_end_test.js b/test/end_to_end_test.js index 1f53df23..e0ad9a88 100644 --- a/test/end_to_end_test.js +++ b/test/end_to_end_test.js @@ -110,52 +110,61 @@ describe('end-to-end', function() { assert.strictEqual(event.data, grpc.opError.OK); }); }); - it('should successfully send and receive metadata', function(complete) { - var done = multiDone(complete, 2); + it.only('should successfully send and receive metadata', function(done) { + debugger; var deadline = new Date(); deadline.setSeconds(deadline.getSeconds() + 3); var status_text = 'xyz'; var call = new grpc.Call(channel, 'dummy_method', deadline); - call.addMetadata({'client_key': ['client_value']}); - call.invoke(function(event) { - assert.strictEqual(event.type, - grpc.completionType.CLIENT_METADATA_READ); - assert.strictEqual(event.data.server_key[0].toString(), 'server_value'); - },function(event) { - assert.strictEqual(event.type, grpc.completionType.FINISHED); - var status = event.data; - assert.strictEqual(status.code, grpc.status.OK); - assert.strictEqual(status.details, status_text); + var client_batch = {}; + client_batch[grpc.opType.SEND_INITIAL_METADATA] = { + 'client_key': ['client_value'] + }; + client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(client_batch, function(err, response) { + assert.ifError(err); + assert.deepEqual(response, { + 'send metadata': true, + 'client close': true, + 'metadata': {'server_key': [new Buffer('server_value')]}, + 'status': { + 'code': grpc.status.OK, + 'details': status_text + } + }); done(); - }, 0); - - server.requestCall(function(event) { - assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW); - assert.strictEqual(event.data.metadata.client_key[0].toString(), - 'client_value'); - var server_call = event.call; - assert.notEqual(server_call, null); - server_call.serverAccept(function(event) { - assert.strictEqual(event.type, grpc.completionType.FINISHED); - }, 0); - server_call.addMetadata({'server_key': ['server_value']}); - server_call.serverEndInitialMetadata(0); - server_call.startWriteStatus( - grpc.status.OK, - status_text, - function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - done(); - }); }); - call.writesDone(function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); + + server.requestCall(function(err, call_details) { + var new_call = call_details['new call']; + assert.notEqual(new_call, null); + assert.strictEqual(new_call.metadata.client_key[0].toString(), + 'client_value'); + var server_call = new_call.call; + assert.notEqual(server_call, null); + var server_batch = {}; + server_batch[grpc.opType.SEND_INITIAL_METADATA] = { + 'server_key': ['server_value'] + }; + server_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { + 'metadata': {}, + 'code': grpc.status.OK, + 'details': status_text + }; + server_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; + console.log(server_batch); + server_call.startBatch(server_batch, function(err, response) { + assert.ifError(err); + assert.deepEqual(response, { + 'send metadata': true, + 'send status': true, + 'cancelled': false + }); + }); }); }); it('should send and receive data without error', function(complete) { From 553889874c2ba904004c9b14a246ec38f724b295 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Mon, 9 Feb 2015 15:58:33 -0800 Subject: [PATCH 15/91] Fixed typo in stock_server.js0 --- examples/stock_server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stock_server.js b/examples/stock_server.js index 61ba2731..b226a715 100644 --- a/examples/stock_server.js +++ b/examples/stock_server.js @@ -85,4 +85,4 @@ if (require.main === module) { stockServer.listen(); } -exports.module = stockServer; +module.exports = stockServer; From f1da0156b79fada4014756dd01297584450d2f41 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Tue, 10 Feb 2015 09:16:49 -0800 Subject: [PATCH 16/91] Fixed end-to-end tests for new changes --- ext/call.cc | 2 +- test/end_to_end_test.js | 195 ++++++++++++++++++++-------------------- 2 files changed, 96 insertions(+), 101 deletions(-) diff --git a/ext/call.cc b/ext/call.cc index 785cee8d..9a6359fe 100644 --- a/ext/call.cc +++ b/ext/call.cc @@ -238,7 +238,7 @@ class SendServerStatusOp : public Op { bool ParseOp(Handle value, grpc_op *out, std::vector > *strings, std::vector > *handles) { - if (value->IsObject()) { + if (!value->IsObject()) { return false; } Handle server_status = value->ToObject(); diff --git a/test/end_to_end_test.js b/test/end_to_end_test.js index e0ad9a88..f1277ff2 100644 --- a/test/end_to_end_test.js +++ b/test/end_to_end_test.js @@ -75,43 +75,51 @@ describe('end-to-end', function() { var call = new grpc.Call(channel, 'dummy_method', deadline); - call.invoke(function(event) { - assert.strictEqual(event.type, - grpc.completionType.CLIENT_METADATA_READ); - },function(event) { - assert.strictEqual(event.type, grpc.completionType.FINISHED); - var status = event.data; - assert.strictEqual(status.code, grpc.status.OK); - assert.strictEqual(status.details, status_text); + var client_batch = {}; + client_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; + client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(client_batch, function(err, response) { + assert.ifError(err); + assert.deepEqual(response, { + 'send metadata': true, + 'client close': true, + 'status': { + 'code': grpc.status.OK, + 'details': status_text, + 'metadata': {} + } + }); done(); - }, 0); - - server.requestCall(function(event) { - assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW); - var server_call = event.call; - assert.notEqual(server_call, null); - server_call.serverAccept(function(event) { - assert.strictEqual(event.type, grpc.completionType.FINISHED); - }, 0); - server_call.serverEndInitialMetadata(0); - server_call.startWriteStatus( - grpc.status.OK, - status_text, - function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - done(); - }); }); - call.writesDone(function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); + + server.requestCall(function(err, call_details) { + var new_call = call_details['new call']; + assert.notEqual(new_call, null); + var server_call = new_call.call; + assert.notEqual(server_call, null); + var server_batch = {}; + server_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; + server_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { + 'metadata': {}, + 'code': grpc.status.OK, + 'details': status_text + }; + server_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; + server_call.startBatch(server_batch, function(err, response) { + assert.ifError(err); + assert.deepEqual(response, { + 'send metadata': true, + 'send status': true, + 'cancelled': false + }); + done(); + }); }); }); - it.only('should successfully send and receive metadata', function(done) { - debugger; + it('should successfully send and receive metadata', function(complete) { + var done = multiDone(complete, 2); var deadline = new Date(); deadline.setSeconds(deadline.getSeconds() + 3); var status_text = 'xyz'; @@ -127,15 +135,14 @@ describe('end-to-end', function() { client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; call.startBatch(client_batch, function(err, response) { assert.ifError(err); - assert.deepEqual(response, { - 'send metadata': true, - 'client close': true, - 'metadata': {'server_key': [new Buffer('server_value')]}, - 'status': { - 'code': grpc.status.OK, - 'details': status_text - } - }); + assert(response['send metadata']); + assert(response['client close']); + assert(response.hasOwnProperty('metadata')); + assert.strictEqual(response.metadata.server_key.toString(), + 'server_value'); + assert.deepEqual(response.status, {'code': grpc.status.OK, + 'details': status_text, + 'metadata': {}}); done(); }); @@ -156,7 +163,6 @@ describe('end-to-end', function() { 'details': status_text }; server_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; - console.log(server_batch); server_call.startBatch(server_batch, function(err, response) { assert.ifError(err); assert.deepEqual(response, { @@ -164,77 +170,66 @@ describe('end-to-end', function() { 'send status': true, 'cancelled': false }); + done(); }); }); }); - it('should send and receive data without error', function(complete) { + it.only('should send and receive data without error', function(complete) { var req_text = 'client_request'; var reply_text = 'server_response'; - var done = multiDone(complete, 6); + var done = multiDone(complete, 2); var deadline = new Date(); deadline.setSeconds(deadline.getSeconds() + 3); var status_text = 'success'; var call = new grpc.Call(channel, 'dummy_method', deadline); - call.invoke(function(event) { - assert.strictEqual(event.type, - grpc.completionType.CLIENT_METADATA_READ); - done(); - },function(event) { - assert.strictEqual(event.type, grpc.completionType.FINISHED); - var status = event.data; - assert.strictEqual(status.code, grpc.status.OK); - assert.strictEqual(status.details, status_text); - done(); - }, 0); - call.startWrite( - new Buffer(req_text), - function(event) { - assert.strictEqual(event.type, - grpc.completionType.WRITE_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - call.writesDone(function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - done(); - }); - }, 0); - call.startRead(function(event) { - assert.strictEqual(event.type, grpc.completionType.READ); - assert.strictEqual(event.data.toString(), reply_text); + var client_batch = {}; + client_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; + client_batch[grpc.opType.SEND_MESSAGE] = new Buffer(req_text); + client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + client_batch[grpc.opType.RECV_MESSAGE] = true; + client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(client_batch, function(err, response) { + assert.ifError(err); + assert(response['send metadata']); + assert(response['client close']); + assert.deepEqual(response.metadata, {}); + assert(response['send message']); + assert.strictEqual(response.read.toString(), reply_text); + assert.deepEqual(response.status, {'code': grpc.status.OK, + 'details': status_text, + 'metadata': {}}); + console.log("OK status"); done(); }); - server.requestCall(function(event) { - assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW); - var server_call = event.call; + + server.requestCall(function(err, call_details) { + var new_call = call_details['new call']; + assert.notEqual(new_call, null); + var server_call = new_call.call; assert.notEqual(server_call, null); - server_call.serverAccept(function(event) { - assert.strictEqual(event.type, grpc.completionType.FINISHED); - done(); - }); - server_call.serverEndInitialMetadata(0); - server_call.startRead(function(event) { - assert.strictEqual(event.type, grpc.completionType.READ); - assert.strictEqual(event.data.toString(), req_text); - server_call.startWrite( - new Buffer(reply_text), - function(event) { - assert.strictEqual(event.type, - grpc.completionType.WRITE_ACCEPTED); - assert.strictEqual(event.data, - grpc.opError.OK); - server_call.startWriteStatus( - grpc.status.OK, - status_text, - function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - done(); - }); - }, 0); + var server_batch = {}; + server_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; + server_batch[grpc.opType.RECV_MESSAGE] = true; + server_call.startBatch(server_batch, function(err, response) { + assert.ifError(err); + assert(response['send metadata']); + assert.strictEqual(response.read.toString(), req_text); + var response_batch = {}; + response_batch[grpc.opType.SEND_MESSAGE] = new Buffer(reply_text); + response_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { + 'metadata': {}, + 'code': grpc.status.OK, + 'details': status_text + }; + response_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; + server_call.startBatch(response_batch, function(err, response) { + assert(response['send status']); + //assert(!response['cancelled']); + done(); + }); }); }); }); From 5bbb836abb1e10d05209f1c2b6e922a24bea3890 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 11 Feb 2015 09:26:25 -0800 Subject: [PATCH 17/91] More end to end test debugging --- ext/call.cc | 19 +++++++++++++------ ext/call.h | 4 ++-- ext/completion_queue_async_worker.cc | 11 +++++++---- ext/server.cc | 2 ++ test/end_to_end_test.js | 17 +++++++++-------- 5 files changed, 33 insertions(+), 20 deletions(-) diff --git a/ext/call.cc b/ext/call.cc index 9a6359fe..3452af94 100644 --- a/ext/call.cc +++ b/ext/call.cc @@ -201,6 +201,8 @@ class SendMessageOp : public Op { } out->data.send_message = BufferToByteBuffer(value); Persistent handle; + Handle temp = NanNew(); + NanAssignPersistent(handle, temp); NanAssignPersistent(handle, value); handles->push_back(unique_ptr( new PersistentHolder(handle))); @@ -441,9 +443,14 @@ void DestroyTag(void *tag) { delete tag_struct; } -Call::Call(grpc_call *call) : wrapped_call(call) {} +Call::Call(grpc_call *call) : wrapped_call(call) { + gpr_log(GPR_DEBUG, "Constructing call, this: %p, pointer: %p", this, call); +} -Call::~Call() { grpc_call_destroy(wrapped_call); } +Call::~Call() { + gpr_log(GPR_DEBUG, "Destructing call, this: %p, pointer: %p", this, wrapped_call); + grpc_call_destroy(wrapped_call); +} void Call::Init(Handle exports) { NanScope(); @@ -473,6 +480,7 @@ Handle Call::WrapStruct(grpc_call *call) { if (call == NULL) { return NanEscapeScope(NanNull()); } + gpr_log(GPR_DEBUG, "Wrapping call: %p", call); const int argc = 1; Handle argv[argc] = {External::New(reinterpret_cast(call))}; return NanEscapeScope(constructor->NewInstance(argc, argv)); @@ -534,12 +542,13 @@ NAN_METHOD(Call::StartBatch) { if (!args[1]->IsFunction()) { return NanThrowError("startBatch's second argument must be a callback"); } + Handle callback_func = args[1].As(); + NanCallback *callback = new NanCallback(callback_func); Call *call = ObjectWrap::Unwrap(args.This()); std::vector > *handles = new std::vector >(); std::vector > *strings = new std::vector >(); - Persistent handle; Handle obj = args[0]->ToObject(); Handle keys = obj->GetOwnPropertyNames(); size_t nops = keys->Length(); @@ -588,9 +597,7 @@ NAN_METHOD(Call::StartBatch) { } grpc_call_error error = grpc_call_start_batch( call->wrapped_call, ops, nops, new struct tag( - new NanCallback(args[1].As()), - op_vector, handles, - strings)); + callback, op_vector, handles, strings)); if (error != GRPC_CALL_OK) { return NanThrowError("startBatch failed", error); } diff --git a/ext/call.h b/ext/call.h index 434bcf8a..b8792713 100644 --- a/ext/call.h +++ b/ext/call.h @@ -57,8 +57,8 @@ class PersistentHolder { } ~PersistentHolder() { - persist.Dispose(); -} + NanDisposePersistent(persist); + } private: v8::Persistent persist; diff --git a/ext/completion_queue_async_worker.cc b/ext/completion_queue_async_worker.cc index 5c0e27e6..dbacdf03 100644 --- a/ext/completion_queue_async_worker.cc +++ b/ext/completion_queue_async_worker.cc @@ -35,6 +35,7 @@ #include #include "grpc/grpc.h" +#include "grpc/support/log.h" #include "grpc/support/time.h" #include "completion_queue_async_worker.h" #include "call.h" @@ -57,6 +58,7 @@ CompletionQueueAsyncWorker::~CompletionQueueAsyncWorker() {} void CompletionQueueAsyncWorker::Execute() { result = grpc_completion_queue_next(queue, gpr_inf_future); + gpr_log(GPR_DEBUG, "Handling response on call %p", result->call); if (result->data.op_complete != GRPC_OP_OK) { SetErrorMessage("The batch encountered an error"); } @@ -77,14 +79,15 @@ void CompletionQueueAsyncWorker::Init(Handle exports) { void CompletionQueueAsyncWorker::HandleOKCallback() { NanScope(); + gpr_log(GPR_DEBUG, "Handling response on call %p", result->call); NanCallback callback = GetTagCallback(result->tag); Handle argv[] = {NanNull(), GetTagNodeValue(result->tag)}; + callback.Call(2, argv); + DestroyTag(result->tag); grpc_event_finish(result); result = NULL; - - callback.Call(2, argv); } void CompletionQueueAsyncWorker::HandleErrorCallback() { @@ -92,11 +95,11 @@ void CompletionQueueAsyncWorker::HandleErrorCallback() { NanCallback callback = GetTagCallback(result->tag); Handle argv[] = {NanError(ErrorMessage())}; + callback.Call(1, argv); + DestroyTag(result->tag); grpc_event_finish(result); result = NULL; - - callback.Call(1, argv); } } // namespace node diff --git a/ext/server.cc b/ext/server.cc index 75ea681f..93aa9ec4 100644 --- a/ext/server.cc +++ b/ext/server.cc @@ -43,6 +43,7 @@ #include #include "grpc/grpc.h" #include "grpc/grpc_security.h" +#include "grpc/support/log.h" #include "call.h" #include "completion_queue_async_worker.h" #include "server_credentials.h" @@ -90,6 +91,7 @@ class NewCallOp : public Op { return NanEscapeScope(NanNull()); } Handle obj = NanNew(); + gpr_log(GPR_DEBUG, "Wrapping server call: %p", call); obj->Set(NanNew("call"), Call::WrapStruct(call)); obj->Set(NanNew("method"), NanNew(details.method)); obj->Set(NanNew("host"), NanNew(details.host)); diff --git a/test/end_to_end_test.js b/test/end_to_end_test.js index f1277ff2..d4344608 100644 --- a/test/end_to_end_test.js +++ b/test/end_to_end_test.js @@ -67,14 +67,14 @@ describe('end-to-end', function() { after(function() { server.shutdown(); }); - it('should start and end a request without error', function(complete) { + it.skip('should start and end a request without error', function(complete) { var done = multiDone(complete, 2); var deadline = new Date(); deadline.setSeconds(deadline.getSeconds() + 3); var status_text = 'xyz'; var call = new grpc.Call(channel, 'dummy_method', - deadline); + Infinity); var client_batch = {}; client_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; @@ -85,6 +85,7 @@ describe('end-to-end', function() { assert.deepEqual(response, { 'send metadata': true, 'client close': true, + 'metadata': {}, 'status': { 'code': grpc.status.OK, 'details': status_text, @@ -125,7 +126,7 @@ describe('end-to-end', function() { var status_text = 'xyz'; var call = new grpc.Call(channel, 'dummy_method', - deadline); + Infinity); var client_batch = {}; client_batch[grpc.opType.SEND_INITIAL_METADATA] = { 'client_key': ['client_value'] @@ -138,7 +139,7 @@ describe('end-to-end', function() { assert(response['send metadata']); assert(response['client close']); assert(response.hasOwnProperty('metadata')); - assert.strictEqual(response.metadata.server_key.toString(), + assert.strictEqual(response.metadata.server_key[0].toString(), 'server_value'); assert.deepEqual(response.status, {'code': grpc.status.OK, 'details': status_text, @@ -147,6 +148,7 @@ describe('end-to-end', function() { }); server.requestCall(function(err, call_details) { + console.log("Server received new call"); var new_call = call_details['new call']; assert.notEqual(new_call, null); assert.strictEqual(new_call.metadata.client_key[0].toString(), @@ -174,7 +176,7 @@ describe('end-to-end', function() { }); }); }); - it.only('should send and receive data without error', function(complete) { + it('should send and receive data without error', function(complete) { var req_text = 'client_request'; var reply_text = 'server_response'; var done = multiDone(complete, 2); @@ -183,7 +185,7 @@ describe('end-to-end', function() { var status_text = 'success'; var call = new grpc.Call(channel, 'dummy_method', - deadline); + Infinity); var client_batch = {}; client_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; client_batch[grpc.opType.SEND_MESSAGE] = new Buffer(req_text); @@ -201,7 +203,6 @@ describe('end-to-end', function() { assert.deepEqual(response.status, {'code': grpc.status.OK, 'details': status_text, 'metadata': {}}); - console.log("OK status"); done(); }); @@ -227,7 +228,7 @@ describe('end-to-end', function() { response_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; server_call.startBatch(response_batch, function(err, response) { assert(response['send status']); - //assert(!response['cancelled']); + assert(!response['cancelled']); done(); }); }); From 8cb4f5cce1d51b2abaa211ed771f42888d096dc2 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 11 Feb 2015 11:15:31 -0800 Subject: [PATCH 18/91] Added a performance test --- examples/perf_test.js | 104 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 examples/perf_test.js diff --git a/examples/perf_test.js b/examples/perf_test.js new file mode 100644 index 00000000..379b14e0 --- /dev/null +++ b/examples/perf_test.js @@ -0,0 +1,104 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +var grpc = require('..'); +var testProto = grpc.load(__dirname + '/../interop/test.proto').grpc.testing; +var _ = require('underscore'); +var interop_server = require('../interop/interop_server.js'); + +function runTest(iterations, callback) { + var testServer = interop_server.getServer(0, false); + testServer.server.listen(); + var client = new testProto.TestService('localhost:' + testServer.port); + + function runIterations(finish) { + var start = process.hrtime(); + var intervals = []; + var pending = iterations; + function next(i) { + if (i >= iterations) { + testServer.server.shutdown(); + var totalDiff = process.hrtime(start); + finish({ + total: totalDiff[0] * 1000000 + totalDiff[1] / 1000, + intervals: intervals + }); + } else{ + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 3); + var startTime = process.hrtime(); + client.emptyCall({}, function(err, resp) { + var timeDiff = process.hrtime(startTime); + intervals[i] = timeDiff[0] * 1000000 + timeDiff[1] / 1000; + next(i+1); + }, {}, deadline); + } + } + next(0); + } + + function warmUp(num) { + var pending = num; + for (var i = 0; i < num; i++) { + (function(i) { + client.emptyCall({}, function(err, resp) { + pending--; + if (pending === 0) { + runIterations(callback); + } + }); + })(i); + } + } + warmUp(100); +} + +if (require.main === module) { + var count; + if (process.argv.length >= 3) { + count = process.argv[2]; + } else { + count = 100; + } + runTest(count, function(results) { + console.log('count:', count); + console.log('total time:', results.total, 'us'); + console.log('min latency:', _.min(results.intervals), 'us'); + console.log('max latency:', _.max(results.intervals), 'us'); + console.log('average latency:', _.reduce(results.intervals, function(a, b){ + return a+b; + }) / count, 'us'); + }); +} + +module.exports = runTest; From 6fea4ccb0a24b0a5850cefe89e9245d421b5044b Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 11 Feb 2015 11:17:10 -0800 Subject: [PATCH 19/91] Updated copyright date --- examples/perf_test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/perf_test.js b/examples/perf_test.js index 379b14e0..7da600c6 100644 --- a/examples/perf_test.js +++ b/examples/perf_test.js @@ -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 From 4f0bd5a63fb1d0a6461548b98928f5d5ce3d4d47 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 11 Feb 2015 12:28:34 -0800 Subject: [PATCH 20/91] Added standard performance metrics --- examples/perf_test.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/examples/perf_test.js b/examples/perf_test.js index 7da600c6..c5e28727 100644 --- a/examples/perf_test.js +++ b/examples/perf_test.js @@ -83,6 +83,16 @@ function runTest(iterations, callback) { warmUp(100); } +function percentile(arr, percentile) { + if (percentile > 99) { + percentile = 99; + } + if (percentile < 0) { + percentile = 0; + } + return arr[(arr.length * percentile / 100)|0]; +} + if (require.main === module) { var count; if (process.argv.length >= 3) { @@ -91,13 +101,14 @@ if (require.main === module) { count = 100; } runTest(count, function(results) { + var sorted_intervals = _.sortBy(results.intervals, _.identity); console.log('count:', count); console.log('total time:', results.total, 'us'); - console.log('min latency:', _.min(results.intervals), 'us'); - console.log('max latency:', _.max(results.intervals), 'us'); - console.log('average latency:', _.reduce(results.intervals, function(a, b){ - return a+b; - }) / count, 'us'); + console.log('median:', percentile(sorted_intervals, 50), 'us'); + console.log('90th percentile:', percentile(sorted_intervals, 90), 'us'); + console.log('95th percentile:', percentile(sorted_intervals, 95), 'us'); + console.log('99th percentile:', percentile(sorted_intervals, 99), 'us'); + console.log('QPS:', (count / results.total) * 1000000); }); } From d35c92d4735c315fb5a5ebdfad9358fbf94327e4 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 11 Feb 2015 16:19:55 -0800 Subject: [PATCH 21/91] Fixed end to end tests --- ext/call.cc | 8 ++++++-- ext/call.h | 2 +- ext/completion_queue_async_worker.cc | 8 ++++---- test/call_test.js | 2 +- test/end_to_end_test.js | 2 +- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/ext/call.cc b/ext/call.cc index 3452af94..cdc34b52 100644 --- a/ext/call.cc +++ b/ext/call.cc @@ -98,6 +98,9 @@ bool CreateMetadataArray( string_handles->push_back(unique_ptr(utf8_key)); Handle values = Local::Cast(metadata->Get(current_key)); for (unsigned int j = 0; j < values->Length(); j++) { + if (array->count >= array->capacity) { + gpr_log(GPR_ERROR, "Metadata array grew past capacity"); + } Handle value = values->Get(j); grpc_metadata *current = &array->metadata[array->count]; current->key = **utf8_key; @@ -433,9 +436,9 @@ Handle GetTagNodeValue(void *tag) { return NanEscapeScope(tag_obj); } -NanCallback GetTagCallback(void *tag) { +NanCallback *GetTagCallback(void *tag) { struct tag *tag_struct = reinterpret_cast(tag); - return *tag_struct->callback; + return tag_struct->callback; } void DestroyTag(void *tag) { @@ -598,6 +601,7 @@ NAN_METHOD(Call::StartBatch) { grpc_call_error error = grpc_call_start_batch( call->wrapped_call, ops, nops, new struct tag( callback, op_vector, handles, strings)); + delete ops; if (error != GRPC_CALL_OK) { return NanThrowError("startBatch failed", error); } diff --git a/ext/call.h b/ext/call.h index b8792713..f443a046 100644 --- a/ext/call.h +++ b/ext/call.h @@ -89,7 +89,7 @@ struct tag { v8::Handle GetTagNodeValue(void *tag); -NanCallback GetTagCallback(void *tag); +NanCallback *GetTagCallback(void *tag); void DestroyTag(void *tag); diff --git a/ext/completion_queue_async_worker.cc b/ext/completion_queue_async_worker.cc index dbacdf03..3c32b07c 100644 --- a/ext/completion_queue_async_worker.cc +++ b/ext/completion_queue_async_worker.cc @@ -80,10 +80,10 @@ void CompletionQueueAsyncWorker::Init(Handle exports) { void CompletionQueueAsyncWorker::HandleOKCallback() { NanScope(); gpr_log(GPR_DEBUG, "Handling response on call %p", result->call); - NanCallback callback = GetTagCallback(result->tag); + NanCallback *callback = GetTagCallback(result->tag); Handle argv[] = {NanNull(), GetTagNodeValue(result->tag)}; - callback.Call(2, argv); + callback->Call(2, argv); DestroyTag(result->tag); grpc_event_finish(result); @@ -92,10 +92,10 @@ void CompletionQueueAsyncWorker::HandleOKCallback() { void CompletionQueueAsyncWorker::HandleErrorCallback() { NanScope(); - NanCallback callback = GetTagCallback(result->tag); + NanCallback *callback = GetTagCallback(result->tag); Handle argv[] = {NanError(ErrorMessage())}; - callback.Call(1, argv); + callback->Call(1, argv); DestroyTag(result->tag); grpc_event_finish(result); diff --git a/test/call_test.js b/test/call_test.js index e341092f..1cbfc228 100644 --- a/test/call_test.js +++ b/test/call_test.js @@ -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 diff --git a/test/end_to_end_test.js b/test/end_to_end_test.js index d4344608..34ce2500 100644 --- a/test/end_to_end_test.js +++ b/test/end_to_end_test.js @@ -67,7 +67,7 @@ describe('end-to-end', function() { after(function() { server.shutdown(); }); - it.skip('should start and end a request without error', function(complete) { + it('should start and end a request without error', function(complete) { var done = multiDone(complete, 2); var deadline = new Date(); deadline.setSeconds(deadline.getSeconds() + 3); From e95b7ff1979b0a2be4661dd857bab4f7300eb034 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 11 Feb 2015 17:29:09 -0800 Subject: [PATCH 22/91] Removed extra extension files --- ext/event.cc | 173 --------------------------- ext/event.h | 48 -------- ext/tag.cc | 325 --------------------------------------------------- ext/tag.h | 72 ------------ 4 files changed, 618 deletions(-) delete mode 100644 ext/event.cc delete mode 100644 ext/event.h delete mode 100644 ext/tag.cc delete mode 100644 ext/tag.h diff --git a/ext/event.cc b/ext/event.cc deleted file mode 100644 index d59b68fb..00000000 --- a/ext/event.cc +++ /dev/null @@ -1,173 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#include -#include -#include "grpc/grpc.h" -#include "byte_buffer.h" -#include "call.h" -#include "event.h" -#include "tag.h" -#include "timeval.h" - -namespace grpc { -namespace node { - -using ::node::Buffer; -using v8::Array; -using v8::Date; -using v8::Handle; -using v8::HandleScope; -using v8::Number; -using v8::Object; -using v8::Persistent; -using v8::String; -using v8::Value; - -Handle ParseMetadata(grpc_metadata *metadata_elements, size_t length) { - NanEscapableScope(); - std::map size_map; - std::map 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 metadata_object = NanNew(); - for (unsigned int i = 0; i < length; i++) { - grpc_metadata* elem = &metadata_elements[i]; - Handle key_string = String::New(elem->key); - Handle array; - if (metadata_object->Has(key_string)) { - array = Handle::Cast(metadata_object->Get(key_string)); - } else { - array = NanNew(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 GetEventData(grpc_event *event) { - NanEscapableScope(); - size_t count; - grpc_metadata *items; - Handle metadata; - Handle status; - Handle rpc_new; - switch (event->type) { - case GRPC_READ: - return NanEscapeScope(ByteBufferToBuffer(event->data.read)); - case GRPC_WRITE_ACCEPTED: - return NanEscapeScope(NanNew(event->data.write_accepted)); - case GRPC_FINISH_ACCEPTED: - return NanEscapeScope(NanNew(event->data.finish_accepted)); - case GRPC_CLIENT_METADATA_READ: - count = event->data.client_metadata_read.count; - items = event->data.client_metadata_read.elements; - return NanEscapeScope(ParseMetadata(items, count)); - case GRPC_FINISHED: - status = NanNew(); - status->Set(NanNew("code"), NanNew(event->data.finished.status)); - if (event->data.finished.details != NULL) { - status->Set(NanNew("details"), - String::New(event->data.finished.details)); - } - count = event->data.finished.metadata_count; - items = event->data.finished.metadata_elements; - status->Set(NanNew("metadata"), ParseMetadata(items, count)); - return NanEscapeScope(status); - case GRPC_SERVER_RPC_NEW: - rpc_new = NanNew(); - if (event->data.server_rpc_new.method == NULL) { - return NanEscapeScope(NanNull()); - } - rpc_new->Set( - NanNew("method"), - NanNew(event->data.server_rpc_new.method)); - rpc_new->Set( - NanNew("host"), - NanNew(event->data.server_rpc_new.host)); - rpc_new->Set(NanNew("absolute_deadline"), - NanNew(TimespecToMilliseconds( - event->data.server_rpc_new.deadline))); - count = event->data.server_rpc_new.metadata_count; - items = event->data.server_rpc_new.metadata_elements; - metadata = NanNew(static_cast(count)); - for (unsigned int i = 0; i < count; i++) { - Handle item_obj = Object::New(); - item_obj->Set(NanNew("key"), - NanNew(items[i].key)); - item_obj->Set( - NanNew("value"), - NanNew(items[i].value, static_cast(items[i].value_length))); - metadata->Set(i, item_obj); - } - rpc_new->Set(NanNew("metadata"), ParseMetadata(items, count)); - return NanEscapeScope(rpc_new); - default: - return NanEscapeScope(NanNull()); - } -} - -Handle CreateEventObject(grpc_event *event) { - NanEscapableScope(); - if (event == NULL) { - return NanEscapeScope(NanNull()); - } - Handle event_obj = NanNew(); - Handle call; - if (TagHasCall(event->tag)) { - call = TagGetCall(event->tag); - } else { - call = Call::WrapStruct(event->call); - } - event_obj->Set(NanNew("call"), call); - event_obj->Set(NanNew("type"), - NanNew(event->type)); - event_obj->Set(NanNew("data"), GetEventData(event)); - - return NanEscapeScope(event_obj); -} - -} // namespace node -} // namespace grpc diff --git a/ext/event.h b/ext/event.h deleted file mode 100644 index e06d8f01..00000000 --- a/ext/event.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * Copyright 2014, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef NET_GRPC_NODE_EVENT_H_ -#define NET_GRPC_NODE_EVENT_H_ - -#include -#include "grpc/grpc.h" - -namespace grpc { -namespace node { - -v8::Handle CreateEventObject(grpc_event *event); - -} // namespace node -} // namespace grpc - -#endif // NET_GRPC_NODE_EVENT_H_ diff --git a/ext/tag.cc b/ext/tag.cc deleted file mode 100644 index 27baa94a..00000000 --- a/ext/tag.cc +++ /dev/null @@ -1,325 +0,0 @@ -/* - * - * Copyright 2014, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -#include - -#include -#include -#include -#include -#include "tag.h" -#include "call.h" - -namespace grpc { -namespace node { - -using v8::Boolean; -using v8::Function; -using v8::Handle; -using v8::HandleScope; -using v8::Persistent; -using v8::Value; - -Handle ParseMetadata(grpc_metadata_array *metadata_array) { - NanEscapableScope(); - grpc_metadata *metadata_elements = metadata_array->metadata; - size_t length = metadata_array->count; - std::map size_map; - std::map index_map; - - for (unsigned int i = 0; i < length; i++) { - char *key = metadata_elements[i].key; - if (size_map.count(key)) { - size_map[key] += 1; - } - index_map[key] = 0; - } - Handle metadata_object = NanNew(); - for (unsigned int i = 0; i < length; i++) { - grpc_metadata* elem = &metadata_elements[i]; - Handle key_string = String::New(elem->key); - Handle array; - if (metadata_object->Has(key_string)) { - array = Handle::Cast(metadata_object->Get(key_string)); - } else { - array = NanNew(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); -} - -class OpResponse { - public: - explicit OpResponse(char *name): name(name) { - } - virtual Handle GetNodeValue() const = 0; - virtual bool ParseOp() = 0; - Handle GetOpType() const { - NanEscapableScope(); - return NanEscapeScope(NanNew(name)); - } - - private: - char *name; -}; - -class SendResponse : public OpResponse { - public: - explicit SendResponse(char *name): OpResponse(name) { - } - - Handle GetNodeValue() { - NanEscapableScope(); - return NanEscapeScope(NanTrue()); - } -} - -class MetadataResponse : public OpResponse { - public: - explicit MetadataResponse(grpc_metadata_array *recv_metadata): - recv_metadata(recv_metadata), OpResponse("metadata") { - } - - Handle GetNodeValue() const { - NanEscapableScope(); - return NanEscapeScope(ParseMetadata(recv_metadata)); - } - - private: - grpc_metadata_array *recv_metadata; -}; - -class MessageResponse : public OpResponse { - public: - explicit MessageResponse(grpc_byte_buffer **recv_message): - recv_message(recv_message), OpResponse("read") { - } - - Handle GetNodeValue() const { - NanEscapableScope(); - return NanEscapeScope(ByteBufferToBuffer(*recv_message)); - } - - private: - grpc_byte_buffer **recv_message; -}; - -switch () { -case GRPC_RECV_CLIENT_STATUS: - op = new ClientStatusResponse; - break; -} - - -class ClientStatusResponse : public OpResponse { - public: - explicit ClientStatusResponse(): - OpResponse("status") { - } - - bool ParseOp(Handle obj, grpc_op *out) { - } - - Handle GetNodeValue() const { - NanEscapableScope(); - Handle status_obj = NanNew(); - status_obj->Set(NanNew("code"), NanNew(*status)); - if (event->data.finished.details != NULL) { - status_obj->Set(NanNew("details"), String::New(*status_details)); - } - status_obj->Set(NanNew("metadata"), ParseMetadata(metadata_array)); - return NanEscapeScope(status_obj); - } - private: - grpc_metadata_array metadata_array; - grpc_status_code status; - char *status_details; -}; - -class ServerCloseResponse : public OpResponse { - public: - explicit ServerCloseResponse(int *cancelled): cancelled(cancelled), - OpResponse("cancelled") { - } - - Handle GetNodeValue() const { - NanEscapableScope(); - NanEscapeScope(NanNew(*cancelled)); - } - - private: - int *cancelled; -}; - -class NewCallResponse : public OpResponse { - public: - explicit NewCallResponse(grpc_call **call, grpc_call_details *details, - grpc_metadata_array *request_metadata) : - call(call), details(details), request_metadata(request_metadata), - OpResponse("call"){ - } - - Handle GetNodeValue() const { - NanEscapableScope(); - if (*call == NULL) { - return NanEscapeScope(NanNull()); - } - Handle obj = NanNew(); - obj->Set(NanNew("call"), Call::WrapStruct(*call)); - obj->Set(NanNew("method"), NanNew(details->method)); - obj->Set(NanNew("host"), NanNew(details->host)); - obj->Set(NanNew("deadline"), - NanNew(TimespecToMilliseconds(details->deadline))); - obj->Set(NanNew("metadata"), ParseMetadata(request_metadata)); - return NanEscapeScope(obj); - } - private: - grpc_call **call; - grpc_call_details *details; - grpc_metadata_array *request_metadata; -} - -struct tag { - tag(NanCallback *callback, std::vector *responses, - std::vector> *handles, - std::vector *strings) : - callback(callback), repsonses(responses), handles(handles), - strings(strings){ - } - ~tag() { - for (std::vector::iterator it = responses->begin(); - it != responses->end(); ++it) { - delete *it; - } - for (std::vector::iterator it = responses->begin(); - it != responses->end(); ++it) { - delete *it; - } - delete callback; - delete responses; - delete handles; - delete strings; - } - NanCallback *callback; - std::vector *responses; - std::vector> *handles; - std::vector *strings; -}; - -void *CreateTag(Handle callback, grpc_op *ops, size_t nops, - std::vector> *handles, - std::vector *strings) { - NanScope(); - NanCallback *cb = new NanCallback(callback); - vector *responses = new vector(); - for (size_t i = 0; i < nops; i++) { - grpc_op *op = &ops[i]; - OpResponse *resp; - // Switching on the TYPE of the op - switch (op->op) { - case GRPC_OP_SEND_INITIAL_METADATA: - resp = new SendResponse("send metadata"); - break; - case GRPC_OP_SEND_MESSAGE: - resp = new SendResponse("write"); - break; - case GRPC_OP_SEND_CLOSE_FROM_CLIENT: - resp = new SendResponse("client close"); - break; - case GRPC_OP_SEND_STATUS_FROM_SERVER: - resp = new SendResponse("server close"); - break; - case GRPC_OP_RECV_INITIAL_METADATA: - resp = new MetadataResponse(op->data.recv_initial_metadata); - break; - case GRPC_OP_RECV_MESSAGE: - resp = new MessageResponse(op->data.recv_message); - break; - case GRPC_OP_RECV_STATUS_ON_CLIENT: - resp = new ClientStatusResponse( - op->data.recv_status_on_client.trailing_metadata, - op->data.recv_status_on_client.status, - op->data.recv_status_on_client.status_details); - break; - case GRPC_RECV_CLOSE_ON_SERVER: - resp = new ServerCloseResponse(op->data.recv_close_on_server.cancelled); - break; - default: - continue; - } - responses->push_back(resp); - } - struct tag *tag_struct = new struct tag(cb, responses, handles, strings); - return reinterpret_cast(tag_struct); -} - -void *CreateTag(Handle callback, grpc_call **call, - grpc_call_details *details, - grpc_metadata_array *request_metadata) { - NanEscapableScope(); - NanCallback *cb = new NanCallback(callback); - vector *responses = new vector(); - OpResponse *resp = new NewCallResponse(call, details, request_metadata); - responses->push_back(resp); - struct tag *tag_struct = new struct tag(cb, responses); - return reinterpret_cast(tag_struct); -} - -NanCallback GetTagCallback(void *tag) { - NanEscapableScope(); - struct tag *tag_struct = reinterpret_cast(tag); - return NanEscapeScope(*tag_struct->callback); -} - -Handle GetNodeValue(void *tag) { - NanEscapableScope(); - struct tag *tag_struct = reinterpret_cast(tag); - Handle obj = NanNew(); - for (std::vector::iterator it = tag_struct->responses->begin(); - it != tag_struct->responses->end(); ++it) { - OpResponse *resp = *it; - obj->Set(resp->GetOpType(), resp->GetNodeValue()); - } - return NanEscapeScope(obj); -} - -void DestroyTag(void *tag) { delete reinterpret_cast(tag); } - -} // namespace node -} // namespace grpc diff --git a/ext/tag.h b/ext/tag.h deleted file mode 100644 index 9ff8703b..00000000 --- a/ext/tag.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * - * Copyright 2014, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef NET_GRPC_NODE_TAG_H_ -#define NET_GRPC_NODE_TAG_H_ - -#include - - -#include -#include -#include - -namespace grpc { -namespace node { - -/* Create a void* tag that can be passed to grpc_call_start_batch from a - callback function and an ops array */ -void *CreateTag(v8::Handle callback, grpc_op *ops, size_t nops, - std::vector > *handles, - std::vector *strings); - -/* Create a void* tag that can be passed to grpc_server_request_call from a - callback and the various out parameters to that function */ -void *CreateTag(v8::Handle callback, grpc_call **call, - grpc_call_details *details, - grpc_metadata_array *request_metadata); - -/* Get the callback from the tag */ -NanCallback GetCallback(void *tag); - -/* Get the combined output value from the tag */ -v8::Handle GetNodeValue(void *tag); - -/* Destroy the tag and all resources it is holding. It is illegal to call any - of these other functions on a tag after it has been destroyed. */ -void DestroyTag(void *tag); - -} // namespace node -} // namespace grpc - -#endif // NET_GRPC_NODE_TAG_H_ From 4dd6c0996841a7e2225cb85cb92c282924ca0d5b Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 12 Feb 2015 12:21:15 -0800 Subject: [PATCH 23/91] Fixed most of surface tests --- examples/math_server.js | 1 + index.js | 10 +- src/client.js | 517 +++++++++++++++++++++++++++----------- src/common.js | 25 ++ src/server.js | 522 ++++++++++++++++++++++++++++----------- test/math_client_test.js | 5 +- 6 files changed, 778 insertions(+), 302 deletions(-) diff --git a/examples/math_server.js b/examples/math_server.js index e1bd11b5..e0104453 100644 --- a/examples/math_server.js +++ b/examples/math_server.js @@ -69,6 +69,7 @@ function mathDiv(call, cb) { * @param {stream} stream The stream for sending responses. */ function mathFib(stream) { + console.log(stream); // Here, call is a standard writable Node object Stream var previous = 0, current = 1; for (var i = 0; i < stream.request.limit; i++) { diff --git a/index.js b/index.js index 0627e7f5..baef4d03 100644 --- a/index.js +++ b/index.js @@ -35,9 +35,9 @@ var _ = require('underscore'); var ProtoBuf = require('protobufjs'); -var surface_client = require('./src/surface_client.js'); +var client = require('./src/client.js'); -var surface_server = require('./src/surface_server.js'); +var server = require('./src/server.js'); var grpc = require('bindings')('grpc'); @@ -54,7 +54,7 @@ function loadObject(value) { }); return result; } else if (value.className === 'Service') { - return surface_client.makeClientConstructor(value); + return client.makeClientConstructor(value); } else if (value.className === 'Message' || value.className === 'Enum') { return value.build(); } else { @@ -84,9 +84,9 @@ exports.loadObject = loadObject; exports.load = load; /** - * See docs for surface_server.makeServerConstructor + * See docs for server.makeServerConstructor */ -exports.buildServer = surface_server.makeServerConstructor; +exports.buildServer = server.makeServerConstructor; /** * Status name to code number mapping diff --git a/src/client.js b/src/client.js index 3a1c9eef..88fa9dc2 100644 --- a/src/client.js +++ b/src/client.js @@ -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,14 +31,104 @@ * */ +var _ = require('underscore'); + +var capitalize = require('underscore.string/capitalize'); +var decapitalize = require('underscore.string/decapitalize'); + var grpc = require('bindings')('grpc.node'); -var common = require('./common'); +var common = require('./common.js'); -var Duplex = require('stream').Duplex; +var EventEmitter = require('events').EventEmitter; + +var stream = require('stream'); + +var Readable = stream.Readable; +var Writable = stream.Writable; +var Duplex = stream.Duplex; var util = require('util'); -util.inherits(GrpcClientStream, Duplex); +util.inherits(ClientWritableStream, Writable); + +function ClientWritableStream(call, serialize) { + Writable.call(this, {objectMode: true}); + this.call = call; + this.serialize = common.wrapIgnoreNull(serialize); + this.on('finish', function() { + var batch = {}; + batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + call.startBatch(batch, function() {}); + }); +} + +/** + * Attempt to write the given chunk. Calls the callback when done. This is an + * implementation of a method needed for implementing stream.Writable. + * @param {Buffer} chunk The chunk to write + * @param {string} encoding Ignored + * @param {function(Error=)} callback Called when the write is complete + */ +function _write(chunk, encoding, callback) { + var batch = {}; + batch[grpc.opType.SEND_MESSAGE] = this.serialize(chunk); + console.log(batch); + this.call.startBatch(batch, function(err, event) { + if (err) { + throw err; + } + callback(); + }); +}; + +ClientWritableStream.prototype._write = _write; + +util.inherits(ClientReadableStream, Readable); + +function ClientReadableStream(call, deserialize) { + Readable.call(this, {objectMode: true}); + this.call = call; + this.finished = false; + this.reading = false; + this.serialize = common.wrapIgnoreNull(deserialize); +} + +function _read(size) { + var self = this; + /** + * Callback to be called when a READ event is received. Pushes the data onto + * the read queue and starts reading again if applicable + * @param {grpc.Event} event READ event object + */ + function readCallback(event) { + if (self.finished) { + self.push(null); + return; + } + var data = event.data; + if (self.push(self.deserialize(data)) && data != null) { + var read_batch = {}; + read_batch[grpc.opType.RECV_MESSAGE] = true; + self.call.startBatch(read_batch, readCallback); + } else { + self.reading = false; + } + } + if (self.finished) { + self.push(null); + } else { + if (!self.reading) { + self.reading = true; + var read_batch = {}; + read_batch[grpc.opType.RECV_MESSAGE] = true; + self.call.startBatch(read_batch, readCallback); + } + } +}; + +ClientReadableStream.prototype._read = _read; + +util.inherits(ClientDuplexStream, Duplex); /** * Class for representing a gRPC client side stream as a Node stream. Extends @@ -49,167 +139,310 @@ util.inherits(GrpcClientStream, Duplex); * @param {function(Buffer):*=} deserialize Deserialization function for * responses */ -function GrpcClientStream(call, serialize, deserialize) { +function ClientDuplexStream(call, serialize, deserialize) { Duplex.call(this, {objectMode: true}); - if (!serialize) { - serialize = function(value) { - return value; - }; - } - if (!deserialize) { - deserialize = function(value) { - return value; - }; - } + this.serialize = common.wrapIgnoreNull(serialize); + this.serialize = common.wrapIgnoreNull(deserialize); var self = this; var finished = false; // Indicates that a read is currently pending var reading = false; - // Indicates that a write is currently pending - var writing = false; - this._call = call; + this.call = call; +} - /** - * Serialize a request value to a buffer. Always maps null to null. Otherwise - * uses the provided serialize function - * @param {*} value The value to serialize - * @return {Buffer} The serialized value - */ - this.serialize = function(value) { - if (value === null || value === undefined) { - return null; - } - return serialize(value); - }; +ClientDuplexStream.prototype._read = _read; +ClientDuplexStream.prototype._write = _write; +function cancel() { + this.call.cancel(); +} + +ClientReadableStream.prototype.cancel = cancel; +ClientWritableStream.prototype.cancel = cancel; +ClientDuplexStream.prototype.cancel = cancel; + +/** + * Get a function that can make unary requests to the specified method. + * @param {string} method The name of the method to request + * @param {function(*):Buffer} serialize The serialization function for inputs + * @param {function(Buffer)} deserialize The deserialization function for + * outputs + * @return {Function} makeUnaryRequest + */ +function makeUnaryRequestFunction(method, serialize, deserialize) { /** - * Deserialize a response buffer to a value. Always maps null to null. - * Otherwise uses the provided deserialize function. - * @param {Buffer} buffer The buffer to deserialize - * @return {*} The deserialized value + * Make a unary request with this method on the given channel with the given + * argument, callback, etc. + * @this {Client} Client object. Must have a channel member. + * @param {*} argument The argument to the call. Should be serializable with + * serialize + * @param {function(?Error, value=)} callback The callback to for when the + * response is received + * @param {array=} metadata Array of metadata key/value pairs to add to the + * call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events */ - this.deserialize = function(buffer) { - if (buffer === null) { - return null; + function makeUnaryRequest(argument, callback, metadata, deadline) { + if (deadline === undefined) { + deadline = Infinity; } - return deserialize(buffer); - }; - /** - * Callback to be called when a READ event is received. Pushes the data onto - * the read queue and starts reading again if applicable - * @param {grpc.Event} event READ event object - */ - function readCallback(event) { - if (finished) { - self.push(null); - return; - } - var data = event.data; - if (self.push(self.deserialize(data)) && data != null) { - self._call.startRead(readCallback); - } else { - reading = false; + var emitter = new EventEmitter(); + var call = new grpc.Call(this.channel, method, deadline); + if (metadata === null || metadata === undefined) { + metadata = {}; } + emitter.cancel = function cancel() { + call.cancel(); + }; + var client_batch = {}; + client_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + client_batch[grpc.opType.SEND_MESSAGE] = serialize(argument); + client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + client_batch[grpc.opType.RECV_MESSAGE] = true; + client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(client_batch, function(err, response) { + if (err) { + callback(err); + return; + } + emitter.emit('status', response.status); + emitter.emit('metadata', response.metadata); + callback(null, deserialize(response.read)); + }); + return emitter; } - call.invoke(function(event) { - self.emit('metadata', event.data); - }, function(event) { - finished = true; - self.emit('status', event.data); - }, 0); - this.on('finish', function() { - call.writesDone(function() {}); - }); + return makeUnaryRequest; +} + +/** + * Get a function that can make client stream requests to the specified method. + * @param {string} method The name of the method to request + * @param {function(*):Buffer} serialize The serialization function for inputs + * @param {function(Buffer)} deserialize The deserialization function for + * outputs + * @return {Function} makeClientStreamRequest + */ +function makeClientStreamRequestFunction(method, serialize, deserialize) { /** - * Start reading if there is not already a pending read. Reading will - * continue until self.push returns false (indicating reads should slow - * down) or the read data is null (indicating that there is no more data). + * Make a client stream request with this method on the given channel with the + * given callback, etc. + * @this {Client} Client object. Must have a channel member. + * @param {function(?Error, value=)} callback The callback to for when the + * response is received + * @param {array=} metadata Array of metadata key/value pairs to add to the + * call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events */ - this.startReading = function() { - if (finished) { - self.push(null); + function makeClientStreamRequest(callback, metadata, deadline) { + if (deadline === undefined) { + deadline = Infinity; + } + var call = new grpc.Call(this.channel, method, deadline); + if (metadata === null || metadata === undefined) { + metadata = {}; + } + var stream = new ClientWritableStream(call, serialize); + var metadata_batch = {}; + metadata_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + metadata_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + call.startBatch(metadata_batch, function(err, response) { + if (err) { + callback(err); + return; + } + stream.emit('metadata', response.metadata); + }); + var client_batch = {}; + client_batch[grpc.opType.RECV_MESSAGE] = true; + client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(client_batch, function(err, response) { + if (err) { + callback(err); + return; + } + stream.emit('status', response.status); + callback(null, deserialize(response.read)); + }); + return stream; + } + return makeClientStreamRequest; +} + +/** + * Get a function that can make server stream requests to the specified method. + * @param {string} method The name of the method to request + * @param {function(*):Buffer} serialize The serialization function for inputs + * @param {function(Buffer)} deserialize The deserialization function for + * outputs + * @return {Function} makeServerStreamRequest + */ +function makeServerStreamRequestFunction(method, serialize, deserialize) { + /** + * Make a server stream request with this method on the given channel with the + * given argument, etc. + * @this {SurfaceClient} Client object. Must have a channel member. + * @param {*} argument The argument to the call. Should be serializable with + * serialize + * @param {array=} metadata Array of metadata key/value pairs to add to the + * call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events + */ + function makeServerStreamRequest(argument, metadata, deadline) { + if (deadline === undefined) { + deadline = Infinity; + } + var call = new grpc.Call(this.channel, method, deadline); + if (metadata === null || metadata === undefined) { + metadata = {}; + } + var stream = new ClientReadableStream(call, deserialize); + var start_batch = {}; + console.log('Starting server streaming request on', method); + start_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + start_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + start_batch[grpc.opType.SEND_MESSAGE] = serialize(argument); + start_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + call.startBatch(start_batch, function(err, response) { + if (err) { + throw err; + } + console.log(response); + stream.emit('metadata', response.metadata); + }); + var status_batch = {}; + status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(status_batch, function(err, response) { + if (err) { + throw err; + } + stream.emit('status', response.status); + }); + return stream; + } + return makeServerStreamRequest; +} + +/** + * Get a function that can make bidirectional stream requests to the specified + * method. + * @param {string} method The name of the method to request + * @param {function(*):Buffer} serialize The serialization function for inputs + * @param {function(Buffer)} deserialize The deserialization function for + * outputs + * @return {Function} makeBidiStreamRequest + */ +function makeBidiStreamRequestFunction(method, serialize, deserialize) { + /** + * Make a bidirectional stream request with this method on the given channel. + * @this {SurfaceClient} Client object. Must have a channel member. + * @param {array=} metadata Array of metadata key/value pairs to add to the + * call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events + */ + function makeBidiStreamRequest(metadata, deadline) { + if (deadline === undefined) { + deadline = Infinity; + } + var call = new grpc.Call(this.channel, method, deadline); + if (metadata === null || metadata === undefined) { + metadata = {}; + } + var stream = new ClientDuplexStream(call, serialize, deserialize); + var start_batch = {}; + start_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + start_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + call.startBatch(start_batch, function(err, response) { + if (err) { + throw err; + } + stream.emit('metadata', response.metadata); + }); + var status_batch = {}; + status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(status_batch, function(err, response) { + if (err) { + throw err; + } + stream.emit('status', response.status); + }); + return stream; + } + return makeBidiStreamRequest; +} + + +/** + * Map with short names for each of the requester maker functions. Used in + * makeClientConstructor + */ +var requester_makers = { + unary: makeUnaryRequestFunction, + server_stream: makeServerStreamRequestFunction, + client_stream: makeClientStreamRequestFunction, + bidi: makeBidiStreamRequestFunction +}; + +/** + * Creates a constructor for clients for the given service + * @param {ProtoBuf.Reflect.Service} service The service to generate a client + * for + * @return {function(string, Object)} New client constructor + */ +function makeClientConstructor(service) { + var prefix = '/' + common.fullyQualifiedName(service) + '/'; + /** + * Create a client with the given methods + * @constructor + * @param {string} address The address of the server to connect to + * @param {Object} options Options to pass to the underlying channel + */ + function Client(address, options) { + this.channel = new grpc.Channel(address, options); + } + + _.each(service.children, function(method) { + var method_type; + if (method.requestStream) { + if (method.responseStream) { + method_type = 'bidi'; + } else { + method_type = 'client_stream'; + } } else { - if (!reading) { - reading = true; - self._call.startRead(readCallback); + if (method.responseStream) { + method_type = 'server_stream'; + } else { + method_type = 'unary'; } } - }; + Client.prototype[decapitalize(method.name)] = + requester_makers[method_type]( + prefix + capitalize(method.name), + common.serializeCls(method.resolvedRequestType.build()), + common.deserializeCls(method.resolvedResponseType.build())); + }); + + Client.service = service; + + return Client; } -/** - * Start reading. This is an implementation of a method needed for implementing - * stream.Readable. - * @param {number} size Ignored - */ -GrpcClientStream.prototype._read = function(size) { - this.startReading(); -}; +exports.makeClientConstructor = makeClientConstructor; /** - * Attempt to write the given chunk. Calls the callback when done. This is an - * implementation of a method needed for implementing stream.Writable. - * @param {Buffer} chunk The chunk to write - * @param {string} encoding Ignored - * @param {function(Error=)} callback Ignored - */ -GrpcClientStream.prototype._write = function(chunk, encoding, callback) { - var self = this; - self._call.startWrite(self.serialize(chunk), function(event) { - callback(); - }, 0); -}; - -/** - * Cancel the ongoing call. If the call has not already finished, it will finish - * with status CANCELLED. - */ -GrpcClientStream.prototype.cancel = function() { - this._call.cancel(); -}; - -/** - * Make a request on the channel to the given method with the given arguments - * @param {grpc.Channel} channel The channel on which to make the request - * @param {string} method The method to request - * @param {function(*):Buffer} serialize Serialization function for requests - * @param {function(Buffer):*} deserialize Deserialization function for - * responses - * @param {array=} metadata Array of metadata key/value pairs to add to the call - * @param {(number|Date)=} deadline The deadline for processing this request. - * Defaults to infinite future. - * @return {stream=} The stream of responses - */ -function makeRequest(channel, - method, - serialize, - deserialize, - metadata, - deadline) { - if (deadline === undefined) { - deadline = Infinity; - } - var call = new grpc.Call(channel, method, deadline); - if (metadata) { - call.addMetadata(metadata); - } - return new GrpcClientStream(call, serialize, deserialize); -} - -/** - * See documentation for makeRequest above - */ -exports.makeRequest = makeRequest; - -/** - * Represents a client side gRPC channel associated with a single host. - */ -exports.Channel = grpc.Channel; -/** - * Status name to code number mapping + * See docs for client.status */ exports.status = grpc.status; /** - * Call error name to code number mapping + * See docs for client.callError */ exports.callError = grpc.callError; diff --git a/src/common.js b/src/common.js index 54247e3f..7560cf1b 100644 --- a/src/common.js +++ b/src/common.js @@ -31,6 +31,8 @@ * */ +var _ = require('underscore'); + var capitalize = require('underscore.string/capitalize'); /** @@ -87,6 +89,24 @@ function fullyQualifiedName(value) { return name; } +/** + * Wrap a function to pass null-like values through without calling it. If no + * function is given, just uses the identity; + * @param {?function} func The function to wrap + * @return {function} The wrapped function + */ +function wrapIgnoreNull(func) { + if (!func) { + return _.identity; + } + return function(arg) { + if (arg === null || arg === undefined) { + return null; + } + return func(arg); + }; +} + /** * See docs for deserializeCls */ @@ -101,3 +121,8 @@ exports.serializeCls = serializeCls; * See docs for fullyQualifiedName */ exports.fullyQualifiedName = fullyQualifiedName; + +/** + * See docs for wrapIgnoreNull + */ +exports.wrapIgnoreNull = wrapIgnoreNull; diff --git a/src/server.js b/src/server.js index e4f71ff0..2d5396e3 100644 --- a/src/server.js +++ b/src/server.js @@ -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 @@ -33,80 +33,72 @@ var _ = require('underscore'); +var capitalize = require('underscore.string/capitalize'); +var decapitalize = require('underscore.string/decapitalize'); + var grpc = require('bindings')('grpc.node'); var common = require('./common'); -var Duplex = require('stream').Duplex; +var stream = require('stream'); + +var Readable = stream.Readable; +var Writable = stream.Writable; +var Duplex = stream.Duplex; var util = require('util'); -util.inherits(GrpcServerStream, Duplex); +var EventEmitter = require('events').EventEmitter; -/** - * Class for representing a gRPC server side stream as a Node stream. Extends - * from stream.Duplex. - * @constructor - * @param {grpc.Call} call Call object to proxy - * @param {function(*):Buffer=} serialize Serialization function for responses - * @param {function(Buffer):*=} deserialize Deserialization function for - * requests - */ -function GrpcServerStream(call, serialize, deserialize) { - Duplex.call(this, {objectMode: true}); - if (!serialize) { - serialize = function(value) { - return value; - }; - } - if (!deserialize) { - deserialize = function(value) { - return value; - }; - } - this._call = call; - // Indicate that a status has been sent - var finished = false; - var self = this; - var status = { +var common = require('./common.js'); + +function handleError(call, error) { + var error_batch = {}; + error_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { + code: grpc.status.INTERNAL, + details: 'Unknown Error', + metadata: {} + }; + call.startBatch(error_batch, function(){}); +} + +function waitForCancel(call, emitter) { + var cancel_batch = {}; + cancel_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; + call.startBatch(cancel_batch, function(err, result) { + if (err) { + emitter.emit('error', err); + } + if (result.cancelled) { + emitter.cancelled = true; + emitter.emit('cancelled'); + } + }); +} + +function sendUnaryResponse(call, value, serialize) { + var end_batch = {}; + end_batch[grpc.opType.SEND_MESSAGE] = serialize(value); + end_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { + code: grpc.status.OK, + details: 'OK', + metadata: {} + }; + call.startBatch(end_batch, function (){}); +} + +function setUpWritable(stream, serialize) { + stream.finished = false; + stream.status = { 'code' : grpc.status.OK, 'details' : 'OK' }; - - /** - * Serialize a response value to a buffer. Always maps null to null. Otherwise - * uses the provided serialize function - * @param {*} value The value to serialize - * @return {Buffer} The serialized value - */ - this.serialize = function(value) { - if (value === null || value === undefined) { - return null; - } - return serialize(value); - }; - - /** - * Deserialize a request buffer to a value. Always maps null to null. - * Otherwise uses the provided deserialize function. - * @param {Buffer} buffer The buffer to deserialize - * @return {*} The deserialized value - */ - this.deserialize = function(buffer) { - if (buffer === null) { - return null; - } - return deserialize(buffer); - }; - - /** - * Send the pending status - */ + stream.serialize = common.wrapIgnoreNull(serialize); function sendStatus() { - call.startWriteStatus(status.code, status.details, function() { - }); - finished = true; + var batch = {}; + batch[grpc.opType.SEND_STATUS_FROM_SERVER] = stream.status; + stream.call.startBatch(batch, function(){}); } - this.on('finish', sendStatus); + stream.on('finish', sendStatus); /** * Set the pending status to a given error status. If the error does not have * code or details properties, the code will be set to grpc.status.INTERNAL @@ -123,7 +115,7 @@ function GrpcServerStream(call, serialize, deserialize) { details = err.details; } } - status = {'code': code, 'details': details}; + stream.status = {'code': code, 'details': details}; } /** * Terminate the call. This includes indicating that reads are done, draining @@ -133,55 +125,36 @@ function GrpcServerStream(call, serialize, deserialize) { */ function terminateCall(err) { // Drain readable data - this.on('data', function() {}); setStatus(err); - this.end(); + stream.end(); } - this.on('error', terminateCall); - // Indicates that a read is pending - var reading = false; - /** - * Callback to be called when a READ event is received. Pushes the data onto - * the read queue and starts reading again if applicable - * @param {grpc.Event} event READ event object - */ - function readCallback(event) { - if (finished) { - self.push(null); - return; - } - var data = event.data; - if (self.push(self.deserialize(data)) && data != null) { - self._call.startRead(readCallback); - } else { - reading = false; - } - } - /** - * Start reading if there is not already a pending read. Reading will - * continue until self.push returns false (indicating reads should slow - * down) or the read data is null (indicating that there is no more data). - */ - this.startReading = function() { - if (finished) { - self.push(null); - } else { - if (!reading) { - reading = true; - self._call.startRead(readCallback); - } - } - }; + stream.on('error', terminateCall); } -/** - * Start reading from the gRPC data source. This is an implementation of a - * method required for implementing stream.Readable - * @param {number} size Ignored - */ -GrpcServerStream.prototype._read = function(size) { - this.startReading(); -}; +function setUpReadable(stream, deserialize) { + stream.deserialize = common.wrapIgnoreNull(deserialize); + stream.finished = false; + stream.reading = false; + + stream.terminate = function() { + stream.finished = true; + stream.on('data', function() {}); + }; + + stream.on('cancelled', function() { + stream.terminate(); + }); +} + +util.inherits(ServerWritableStream, Writable); + +function ServerWritableStream(call, serialize) { + Writable.call(this, {objectMode: true}); + this.call = call; + + this.finished = false; + setUpWritable(this, serialize); +} /** * Start writing a chunk of data. This is an implementation of a method required @@ -191,11 +164,157 @@ GrpcServerStream.prototype._read = function(size) { * @param {function(Error=)} callback Callback to indicate that the write is * complete */ -GrpcServerStream.prototype._write = function(chunk, encoding, callback) { - var self = this; - self._call.startWrite(self.serialize(chunk), function(event) { +function _write(chunk, encoding, callback) { + var batch = {}; + batch[grpc.opType.SEND_MESSAGE] = this.serialize(chunk); + this.call.startBatch(batch, function(err, value) { + if (err) { + this.emit('error', err); + return; + } callback(); - }, 0); + }); +} + +ServerWritableStream.prototype._write = _write; + +util.inherits(ServerReadableStream, Readable); + +function ServerReadableStream(call, deserialize) { + Readable.call(this, {objectMode: true}); + this.call = call; + setUpReadable(this, deserialize); +} + +/** + * Start reading from the gRPC data source. This is an implementation of a + * method required for implementing stream.Readable + * @param {number} size Ignored + */ +function _read(size) { + var self = this; + /** + * Callback to be called when a READ event is received. Pushes the data onto + * the read queue and starts reading again if applicable + * @param {grpc.Event} event READ event object + */ + function readCallback(err, event) { + if (err) { + self.terminate(); + return; + } + if (self.finished) { + self.push(null); + return; + } + var data = event.read; + if (self.push(self.deserialize(data)) && data != null) { + var read_batch = {}; + read_batch[grpc.opType.RECV_MESSAGE] = true; + self.call.startBatch(read_batch, readCallback); + } else { + self.reading = false; + } + } + if (self.finished) { + self.push(null); + } else { + if (!self.reading) { + self.reading = true; + var batch = {}; + batch[grpc.opType.RECV_MESSAGE] = true; + self.call.startBatch(batch, readCallback); + } + } +} + +ServerReadableStream.prototype._read = _read; + +util.inherits(ServerDuplexStream, Duplex); + +function ServerDuplexStream(call, serialize, deserialize) { + Duplex.call(this, {objectMode: true}); + setUpWritable(this, serialize); + setUpReadable(this, deserialize); +} + +ServerDuplexStream.prototype._read = _read; +ServerDuplexStream.prototype._write = _write; + +function handleUnary(call, handler, metadata) { + var emitter = new EventEmitter(); + emitter.on('error', function(error) { + handleError(call, error); + }); + waitForCancel(call, emitter); + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + batch[grpc.opType.RECV_MESSAGE] = true; + call.startBatch(batch, function(err, result) { + if (err) { + handleError(call, err); + return; + } + emitter.request = handler.deserialize(result.read); + if (emitter.cancelled) { + return; + } + handler.func(emitter, function sendUnaryData(err, value) { + if (err) { + handleError(call, err); + } + sendUnaryResponse(call, value, handler.serialize); + }); + }); +} + +function handleServerStreaming(call, handler, metadata) { + console.log('Handling server streaming call'); + var stream = new ServerWritableStream(call, handler.serialize); + waitForCancel(call, stream); + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + batch[grpc.opType.RECV_MESSAGE] = true; + call.startBatch(batch, function(err, result) { + if (err) { + stream.emit('error', err); + return; + } + stream.request = result.read; + handler.func(stream); + }); +} + +function handleClientStreaming(call, handler, metadata) { + var stream = new ServerReadableStream(call, handler.deserialize); + waitForCancel(call, stream); + var metadata_batch = {}; + metadata_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + call.startBatch(metadata_batch, function() {}); + handler.func(stream, function(err, value) { + stream.terminate(); + if (err) { + handleError(call, err); + } + sendUnaryResponse(call, value, handler.serialize); + }); +} + +function handleBidiStreaming(call, handler, metadata) { + var stream = new ServerDuplexStream(call, handler.serialize, + handler.deserialize); + waitForCancel(call, stream); + var metadata_batch = {}; + metadata_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + call.startBatch(metadata_batch, function() {}); + handler.func(stream); +} + +var streamHandlers = { + unary: handleUnary, + server_stream: handleServerStreaming, + client_stream: handleClientStreaming, + bidi: handleBidiStreaming }; /** @@ -218,7 +337,7 @@ function Server(getMetadata, options) { * Start the server and begin handling requests * @this Server */ - this.start = function() { + this.listen = function() { console.log('Server starting'); _.each(handlers, function(handler, handler_name) { console.log('Serving', handler_name); @@ -233,48 +352,42 @@ function Server(getMetadata, options) { * wait for the next request * @param {grpc.Event} event The event to handle with tag SERVER_RPC_NEW */ - function handleNewCall(event) { - var call = event.call; - var data = event.data; - if (data === null) { + function handleNewCall(err, event) { + console.log('Handling new call'); + if (err) { + return; + } + var details = event['new call']; + var call = details.call; + var method = details.method; + var metadata = details.metadata; + if (method === null) { return; } server.requestCall(handleNewCall); var handler = undefined; - var deadline = data.absolute_deadline; - var cancelled = false; - call.serverAccept(function(event) { - if (event.data.code === grpc.status.CANCELLED) { - cancelled = true; - if (stream) { - stream.emit('cancelled'); - } - } - }, 0); - if (handlers.hasOwnProperty(data.method)) { - handler = handlers[data.method]; + var deadline = details.deadline; + if (handlers.hasOwnProperty(method)) { + handler = handlers[method]; + console.log(handler); } else { - call.serverEndInitialMetadata(0); - call.startWriteStatus( - grpc.status.UNIMPLEMENTED, - "This method is not available on this server.", - function() {}); + console.log(handlers); + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = {}; + batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { + code: grpc.status.UNIMPLEMENTED, + details: "This method is not available on this server.", + metadata: {} + }; + batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; + call.startBatch(batch, function() {}); return; } + var response_metadata = {}; if (getMetadata) { - call.addMetadata(getMetadata(data.method, data.metadata)); - } - call.serverEndInitialMetadata(0); - var stream = new GrpcServerStream(call, handler.serialize, - handler.deserialize); - Object.defineProperty(stream, 'cancelled', { - get: function() { return cancelled;} - }); - try { - handler.func(stream, data.metadata); - } catch (e) { - stream.emit('error', e); + response_metadata = getMetadata(method, metadata); } + streamHandlers[handler.type](call, handler, response_metadata); } server.requestCall(handleNewCall); }; @@ -294,17 +407,20 @@ function Server(getMetadata, options) { * returns a stream of response values * @param {function(*):Buffer} serialize Serialization function for responses * @param {function(Buffer):*} deserialize Deserialization function for requests + * @param {string} type The streaming type of method that this handles * @return {boolean} True if the handler was set. False if a handler was already * set for that name. */ -Server.prototype.register = function(name, handler, serialize, deserialize) { +Server.prototype.register = function(name, handler, serialize, deserialize, + type) { if (this.handlers.hasOwnProperty(name)) { return false; } this.handlers[name] = { func: handler, serialize: serialize, - deserialize: deserialize + deserialize: deserialize, + type: type }; return true; }; @@ -324,6 +440,110 @@ Server.prototype.bind = function(port, secure) { }; /** - * See documentation for Server + * Creates a constructor for servers with a service defined by the methods + * object. The methods object has string keys and values of this form: + * {serialize: function, deserialize: function, client_stream: bool, + * server_stream: bool} + * @param {Object} methods Method descriptor for each method the server should + * expose + * @param {string} prefix The prefex to prepend to each method name + * @return {function(Object, Object)} New server constructor */ -module.exports = Server; +function makeServerConstructor(services) { + var qual_names = []; + _.each(services, function(service) { + _.each(service.children, function(method) { + var name = common.fullyQualifiedName(method); + if (_.indexOf(qual_names, name) !== -1) { + throw new Error('Method ' + name + ' exposed by more than one service'); + } + qual_names.push(name); + }); + }); + /** + * Create a server with the given handlers for all of the methods. + * @constructor + * @param {Object} service_handlers Map from service names to map from method + * names to handlers + * @param {function(string, Object>): + Object>=} getMetadata Callback that + * gets metatada for a given method + * @param {Object=} options Options to pass to the underlying server + */ + function SurfaceServer(service_handlers, getMetadata, options) { + var server = new Server(getMetadata, options); + this.inner_server = server; + _.each(services, function(service) { + var service_name = common.fullyQualifiedName(service); + if (service_handlers[service_name] === undefined) { + throw new Error('Handlers for service ' + + service_name + ' not provided.'); + } + var prefix = '/' + common.fullyQualifiedName(service) + '/'; + _.each(service.children, function(method) { + var method_type; + if (method.requestStream) { + if (method.responseStream) { + method_type = 'bidi'; + } else { + method_type = 'client_stream'; + } + } else { + if (method.responseStream) { + method_type = 'server_stream'; + } else { + method_type = 'unary'; + } + } + if (service_handlers[service_name][decapitalize(method.name)] === + undefined) { + throw new Error('Method handler for ' + + common.fullyQualifiedName(method) + ' not provided.'); + } + var serialize = common.serializeCls( + method.resolvedResponseType.build()); + var deserialize = common.deserializeCls( + method.resolvedRequestType.build()); + server.register( + prefix + capitalize(method.name), + service_handlers[service_name][decapitalize(method.name)], + serialize, deserialize, method_type); + }); + }, this); + } + + /** + * Binds the server to the given port, with SSL enabled if secure is specified + * @param {string} port The port that the server should bind on, in the format + * "address:port" + * @param {boolean=} secure Whether the server should open a secure port + * @return {SurfaceServer} this + */ + SurfaceServer.prototype.bind = function(port, secure) { + return this.inner_server.bind(port, secure); + }; + + /** + * Starts the server listening on any bound ports + * @return {SurfaceServer} this + */ + SurfaceServer.prototype.listen = function() { + this.inner_server.listen(); + return this; + }; + + /** + * Shuts the server down; tells it to stop listening for new requests and to + * kill old requests. + */ + SurfaceServer.prototype.shutdown = function() { + this.inner_server.shutdown(); + }; + + return SurfaceServer; +} + +/** + * See documentation for makeServerConstructor + */ +exports.makeServerConstructor = makeServerConstructor; diff --git a/test/math_client_test.js b/test/math_client_test.js index 0e365bf8..f347b18e 100644 --- a/test/math_client_test.js +++ b/test/math_client_test.js @@ -63,13 +63,10 @@ describe('Math client', function() { assert.ifError(err); assert.equal(value.quotient, 1); assert.equal(value.remainder, 3); - }); - call.on('status', function checkStatus(status) { - assert.strictEqual(status.code, grpc.status.OK); done(); }); }); - it('should handle a server streaming request', function(done) { + it.only('should handle a server streaming request', function(done) { var call = math_client.fib({limit: 7}); var expected_results = [1, 1, 2, 3, 5, 8, 13]; var next_expected = 0; From 312bab200a6c0a8f5a8a93f2a64c795678b0f8aa Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 12 Feb 2015 13:28:25 -0800 Subject: [PATCH 24/91] All tests but one now pass against new API --- interop/interop_client.js | 2 +- interop/interop_server.js | 1 + src/client.js | 27 ++- src/server.js | 15 +- src/surface_client.js | 357 ------------------------------------ src/surface_server.js | 340 ---------------------------------- test/client_server_test.js | 255 -------------------------- test/interop_sanity_test.js | 2 +- test/math_client_test.js | 2 +- test/server_test.js | 122 ------------ test/surface_test.js | 4 +- 11 files changed, 40 insertions(+), 1087 deletions(-) delete mode 100644 src/surface_client.js delete mode 100644 src/surface_server.js delete mode 100644 test/client_server_test.js delete mode 100644 test/server_test.js diff --git a/interop/interop_client.js b/interop/interop_client.js index ce18f77f..8737af6c 100644 --- a/interop/interop_client.js +++ b/interop/interop_client.js @@ -145,8 +145,8 @@ function serverStreaming(client, done) { resp_index += 1; }); call.on('status', function(status) { - assert.strictEqual(resp_index, 4); assert.strictEqual(status.code, grpc.status.OK); + assert.strictEqual(resp_index, 4); if (done) { done(); } diff --git a/interop/interop_server.js b/interop/interop_server.js index 54e9715d..271fe596 100644 --- a/interop/interop_server.js +++ b/interop/interop_server.js @@ -106,6 +106,7 @@ function handleStreamingOutput(call) { testProto.PayloadType.COMPRESSABLE, testProto.PayloadType.UNCOMPRESSABLE][Math.random() < 0.5 ? 0 : 1]; } + console.log('req:', req); _.each(req.response_parameters, function(resp_param) { call.write({ payload: { diff --git a/src/client.js b/src/client.js index 88fa9dc2..36a6f41d 100644 --- a/src/client.js +++ b/src/client.js @@ -56,6 +56,7 @@ function ClientWritableStream(call, serialize) { this.call = call; this.serialize = common.wrapIgnoreNull(serialize); this.on('finish', function() { + console.log('Send close from client'); var batch = {}; batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; call.startBatch(batch, function() {}); @@ -90,7 +91,7 @@ function ClientReadableStream(call, deserialize) { this.call = call; this.finished = false; this.reading = false; - this.serialize = common.wrapIgnoreNull(deserialize); + this.deserialize = common.wrapIgnoreNull(deserialize); } function _read(size) { @@ -100,12 +101,15 @@ function _read(size) { * the read queue and starts reading again if applicable * @param {grpc.Event} event READ event object */ - function readCallback(event) { + function readCallback(err, event) { + if (err) { + throw err; + } if (self.finished) { self.push(null); return; } - var data = event.data; + var data = event.read; if (self.push(self.deserialize(data)) && data != null) { var read_batch = {}; read_batch[grpc.opType.RECV_MESSAGE] = true; @@ -142,12 +146,18 @@ util.inherits(ClientDuplexStream, Duplex); function ClientDuplexStream(call, serialize, deserialize) { Duplex.call(this, {objectMode: true}); this.serialize = common.wrapIgnoreNull(serialize); - this.serialize = common.wrapIgnoreNull(deserialize); + this.deserialize = common.wrapIgnoreNull(deserialize); var self = this; var finished = false; // Indicates that a read is currently pending var reading = false; this.call = call; + this.on('finish', function() { + console.log('Send close from client'); + var batch = {}; + batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + call.startBatch(batch, function() {}); + }); } ClientDuplexStream.prototype._read = _read; @@ -208,6 +218,10 @@ function makeUnaryRequestFunction(method, serialize, deserialize) { callback(err); return; } + if (response.status.code != grpc.status.OK) { + callback(response.status); + return; + } emitter.emit('status', response.status); emitter.emit('metadata', response.metadata); callback(null, deserialize(response.read)); @@ -265,6 +279,11 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) { callback(err); return; } + console.log(response); + if (response.status.code != grpc.status.OK) { + callback(response.status); + return; + } stream.emit('status', response.status); callback(null, deserialize(response.read)); }); diff --git a/src/server.js b/src/server.js index 2d5396e3..3c214913 100644 --- a/src/server.js +++ b/src/server.js @@ -89,11 +89,13 @@ function sendUnaryResponse(call, value, serialize) { function setUpWritable(stream, serialize) { stream.finished = false; stream.status = { - 'code' : grpc.status.OK, - 'details' : 'OK' + code : grpc.status.OK, + details : 'OK', + metadata : {} }; stream.serialize = common.wrapIgnoreNull(serialize); function sendStatus() { + console.log('Server sending status'); var batch = {}; batch[grpc.opType.SEND_STATUS_FROM_SERVER] = stream.status; stream.call.startBatch(batch, function(){}); @@ -115,7 +117,7 @@ function setUpWritable(stream, serialize) { details = err.details; } } - stream.status = {'code': code, 'details': details}; + stream.status = {code: code, details: details, metadata: {}}; } /** * Terminate the call. This includes indicating that reads are done, draining @@ -167,6 +169,7 @@ function ServerWritableStream(call, serialize) { function _write(chunk, encoding, callback) { var batch = {}; batch[grpc.opType.SEND_MESSAGE] = this.serialize(chunk); + console.log('Server writing', batch); this.call.startBatch(batch, function(err, value) { if (err) { this.emit('error', err); @@ -204,11 +207,14 @@ function _read(size) { return; } if (self.finished) { + console.log('Pushing null'); self.push(null); return; } var data = event.read; + console.log(data); if (self.push(self.deserialize(data)) && data != null) { + console.log('Reading again'); var read_batch = {}; read_batch[grpc.opType.RECV_MESSAGE] = true; self.call.startBatch(read_batch, readCallback); @@ -234,6 +240,7 @@ util.inherits(ServerDuplexStream, Duplex); function ServerDuplexStream(call, serialize, deserialize) { Duplex.call(this, {objectMode: true}); + this.call = call; setUpWritable(this, serialize); setUpReadable(this, deserialize); } @@ -280,7 +287,7 @@ function handleServerStreaming(call, handler, metadata) { stream.emit('error', err); return; } - stream.request = result.read; + stream.request = handler.deserialize(result.read); handler.func(stream); }); } diff --git a/src/surface_client.js b/src/surface_client.js deleted file mode 100644 index 16c31809..00000000 --- a/src/surface_client.js +++ /dev/null @@ -1,357 +0,0 @@ -/* - * - * Copyright 2014, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -var _ = require('underscore'); - -var capitalize = require('underscore.string/capitalize'); -var decapitalize = require('underscore.string/decapitalize'); - -var client = require('./client.js'); - -var common = require('./common.js'); - -var EventEmitter = require('events').EventEmitter; - -var stream = require('stream'); - -var Readable = stream.Readable; -var Writable = stream.Writable; -var Duplex = stream.Duplex; -var util = require('util'); - - -function forwardEvent(fromEmitter, toEmitter, event) { - fromEmitter.on(event, function forward() { - _.partial(toEmitter.emit, event).apply(toEmitter, arguments); - }); -} - -util.inherits(ClientReadableObjectStream, Readable); - -/** - * Class for representing a gRPC server streaming call as a Node stream on the - * client side. Extends from stream.Readable. - * @constructor - * @param {stream} stream Underlying binary Duplex stream for the call - */ -function ClientReadableObjectStream(stream) { - var options = {objectMode: true}; - Readable.call(this, options); - this._stream = stream; - var self = this; - forwardEvent(stream, this, 'status'); - forwardEvent(stream, this, 'metadata'); - this._stream.on('data', function forwardData(chunk) { - if (!self.push(chunk)) { - self._stream.pause(); - } - }); - this._stream.pause(); -} - -/** - * _read implementation for both types of streams that allow reading. - * @this {ClientReadableObjectStream} - * @param {number} size Ignored - */ -function _read(size) { - this._stream.resume(); -} - -/** - * See docs for _read - */ -ClientReadableObjectStream.prototype._read = _read; - -util.inherits(ClientWritableObjectStream, Writable); - -/** - * Class for representing a gRPC client streaming call as a Node stream on the - * client side. Extends from stream.Writable. - * @constructor - * @param {stream} stream Underlying binary Duplex stream for the call - */ -function ClientWritableObjectStream(stream) { - var options = {objectMode: true}; - Writable.call(this, options); - this._stream = stream; - forwardEvent(stream, this, 'status'); - forwardEvent(stream, this, 'metadata'); - this.on('finish', function() { - this._stream.end(); - }); -} - -/** - * _write implementation for both types of streams that allow writing - * @this {ClientWritableObjectStream} - * @param {*} chunk The value to write to the stream - * @param {string} encoding Ignored - * @param {function(Error)} callback Callback to call when finished writing - */ -function _write(chunk, encoding, callback) { - this._stream.write(chunk, encoding, callback); -} - -/** - * See docs for _write - */ -ClientWritableObjectStream.prototype._write = _write; - -/** - * Cancel the underlying call - */ -function cancel() { - this._stream.cancel(); -} - -ClientReadableObjectStream.prototype.cancel = cancel; -ClientWritableObjectStream.prototype.cancel = cancel; - -/** - * Get a function that can make unary requests to the specified method. - * @param {string} method The name of the method to request - * @param {function(*):Buffer} serialize The serialization function for inputs - * @param {function(Buffer)} deserialize The deserialization function for - * outputs - * @return {Function} makeUnaryRequest - */ -function makeUnaryRequestFunction(method, serialize, deserialize) { - /** - * Make a unary request with this method on the given channel with the given - * argument, callback, etc. - * @this {SurfaceClient} Client object. Must have a channel member. - * @param {*} argument The argument to the call. Should be serializable with - * serialize - * @param {function(?Error, value=)} callback The callback to for when the - * response is received - * @param {array=} metadata Array of metadata key/value pairs to add to the - * call - * @param {(number|Date)=} deadline The deadline for processing this request. - * Defaults to infinite future - * @return {EventEmitter} An event emitter for stream related events - */ - function makeUnaryRequest(argument, callback, metadata, deadline) { - var stream = client.makeRequest(this.channel, method, serialize, - deserialize, metadata, deadline); - var emitter = new EventEmitter(); - emitter.cancel = function cancel() { - stream.cancel(); - }; - forwardEvent(stream, emitter, 'status'); - forwardEvent(stream, emitter, 'metadata'); - stream.write(argument); - stream.end(); - stream.on('data', function forwardData(chunk) { - try { - callback(null, chunk); - } catch (e) { - callback(e); - } - }); - stream.on('status', function forwardStatus(status) { - if (status.code !== client.status.OK) { - callback(status); - } - }); - return emitter; - } - return makeUnaryRequest; -} - -/** - * Get a function that can make client stream requests to the specified method. - * @param {string} method The name of the method to request - * @param {function(*):Buffer} serialize The serialization function for inputs - * @param {function(Buffer)} deserialize The deserialization function for - * outputs - * @return {Function} makeClientStreamRequest - */ -function makeClientStreamRequestFunction(method, serialize, deserialize) { - /** - * Make a client stream request with this method on the given channel with the - * given callback, etc. - * @this {SurfaceClient} Client object. Must have a channel member. - * @param {function(?Error, value=)} callback The callback to for when the - * response is received - * @param {array=} metadata Array of metadata key/value pairs to add to the - * call - * @param {(number|Date)=} deadline The deadline for processing this request. - * Defaults to infinite future - * @return {EventEmitter} An event emitter for stream related events - */ - function makeClientStreamRequest(callback, metadata, deadline) { - var stream = client.makeRequest(this.channel, method, serialize, - deserialize, metadata, deadline); - var obj_stream = new ClientWritableObjectStream(stream); - stream.on('data', function forwardData(chunk) { - try { - callback(null, chunk); - } catch (e) { - callback(e); - } - }); - stream.on('status', function forwardStatus(status) { - if (status.code !== client.status.OK) { - callback(status); - } - }); - return obj_stream; - } - return makeClientStreamRequest; -} - -/** - * Get a function that can make server stream requests to the specified method. - * @param {string} method The name of the method to request - * @param {function(*):Buffer} serialize The serialization function for inputs - * @param {function(Buffer)} deserialize The deserialization function for - * outputs - * @return {Function} makeServerStreamRequest - */ -function makeServerStreamRequestFunction(method, serialize, deserialize) { - /** - * Make a server stream request with this method on the given channel with the - * given argument, etc. - * @this {SurfaceClient} Client object. Must have a channel member. - * @param {*} argument The argument to the call. Should be serializable with - * serialize - * @param {array=} metadata Array of metadata key/value pairs to add to the - * call - * @param {(number|Date)=} deadline The deadline for processing this request. - * Defaults to infinite future - * @return {EventEmitter} An event emitter for stream related events - */ - function makeServerStreamRequest(argument, metadata, deadline) { - var stream = client.makeRequest(this.channel, method, serialize, - deserialize, metadata, deadline); - var obj_stream = new ClientReadableObjectStream(stream); - stream.write(argument); - stream.end(); - return obj_stream; - } - return makeServerStreamRequest; -} - -/** - * Get a function that can make bidirectional stream requests to the specified - * method. - * @param {string} method The name of the method to request - * @param {function(*):Buffer} serialize The serialization function for inputs - * @param {function(Buffer)} deserialize The deserialization function for - * outputs - * @return {Function} makeBidiStreamRequest - */ -function makeBidiStreamRequestFunction(method, serialize, deserialize) { - /** - * Make a bidirectional stream request with this method on the given channel. - * @this {SurfaceClient} Client object. Must have a channel member. - * @param {array=} metadata Array of metadata key/value pairs to add to the - * call - * @param {(number|Date)=} deadline The deadline for processing this request. - * Defaults to infinite future - * @return {EventEmitter} An event emitter for stream related events - */ - function makeBidiStreamRequest(metadata, deadline) { - return client.makeRequest(this.channel, method, serialize, - deserialize, metadata, deadline); - } - return makeBidiStreamRequest; -} - -/** - * Map with short names for each of the requester maker functions. Used in - * makeClientConstructor - */ -var requester_makers = { - unary: makeUnaryRequestFunction, - server_stream: makeServerStreamRequestFunction, - client_stream: makeClientStreamRequestFunction, - bidi: makeBidiStreamRequestFunction -} - -/** - * Creates a constructor for clients for the given service - * @param {ProtoBuf.Reflect.Service} service The service to generate a client - * for - * @return {function(string, Object)} New client constructor - */ -function makeClientConstructor(service) { - var prefix = '/' + common.fullyQualifiedName(service) + '/'; - /** - * Create a client with the given methods - * @constructor - * @param {string} address The address of the server to connect to - * @param {Object} options Options to pass to the underlying channel - */ - function SurfaceClient(address, options) { - this.channel = new client.Channel(address, options); - } - - _.each(service.children, function(method) { - var method_type; - if (method.requestStream) { - if (method.responseStream) { - method_type = 'bidi'; - } else { - method_type = 'client_stream'; - } - } else { - if (method.responseStream) { - method_type = 'server_stream'; - } else { - method_type = 'unary'; - } - } - SurfaceClient.prototype[decapitalize(method.name)] = - requester_makers[method_type]( - prefix + capitalize(method.name), - common.serializeCls(method.resolvedRequestType.build()), - common.deserializeCls(method.resolvedResponseType.build())); - }); - - SurfaceClient.service = service; - - return SurfaceClient; -} - -exports.makeClientConstructor = makeClientConstructor; - -/** - * See docs for client.status - */ -exports.status = client.status; -/** - * See docs for client.callError - */ -exports.callError = client.callError; diff --git a/src/surface_server.js b/src/surface_server.js deleted file mode 100644 index a47d1fa2..00000000 --- a/src/surface_server.js +++ /dev/null @@ -1,340 +0,0 @@ -/* - * - * Copyright 2014, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -var _ = require('underscore'); - -var capitalize = require('underscore.string/capitalize'); -var decapitalize = require('underscore.string/decapitalize'); - -var Server = require('./server.js'); - -var stream = require('stream'); - -var Readable = stream.Readable; -var Writable = stream.Writable; -var Duplex = stream.Duplex; -var util = require('util'); - -var common = require('./common.js'); - -util.inherits(ServerReadableObjectStream, Readable); - -/** - * Class for representing a gRPC client streaming call as a Node stream on the - * server side. Extends from stream.Readable. - * @constructor - * @param {stream} stream Underlying binary Duplex stream for the call - */ -function ServerReadableObjectStream(stream) { - var options = {objectMode: true}; - Readable.call(this, options); - this._stream = stream; - Object.defineProperty(this, 'cancelled', { - get: function() { return stream.cancelled; } - }); - var self = this; - this._stream.on('cancelled', function() { - self.emit('cancelled'); - }); - this._stream.on('data', function forwardData(chunk) { - if (!self.push(chunk)) { - self._stream.pause(); - } - }); - this._stream.on('end', function forwardEnd() { - self.push(null); - }); - this._stream.pause(); -} - -/** - * _read implementation for both types of streams that allow reading. - * @this {ServerReadableObjectStream|ServerBidiObjectStream} - * @param {number} size Ignored - */ -function _read(size) { - this._stream.resume(); -} - -/** - * See docs for _read - */ -ServerReadableObjectStream.prototype._read = _read; - -util.inherits(ServerWritableObjectStream, Writable); - -/** - * Class for representing a gRPC server streaming call as a Node stream on the - * server side. Extends from stream.Writable. - * @constructor - * @param {stream} stream Underlying binary Duplex stream for the call - */ -function ServerWritableObjectStream(stream) { - var options = {objectMode: true}; - Writable.call(this, options); - this._stream = stream; - this._stream.on('cancelled', function() { - self.emit('cancelled'); - }); - this.on('finish', function() { - this._stream.end(); - }); -} - -/** - * _write implementation for both types of streams that allow writing - * @this {ServerWritableObjectStream} - * @param {*} chunk The value to write to the stream - * @param {string} encoding Ignored - * @param {function(Error)} callback Callback to call when finished writing - */ -function _write(chunk, encoding, callback) { - this._stream.write(chunk, encoding, callback); -} - -/** - * See docs for _write - */ -ServerWritableObjectStream.prototype._write = _write; - -/** - * Creates a binary stream handler function from a unary handler function - * @param {function(Object, function(Error, *), metadata=)} handler Unary call - * handler - * @return {function(stream, metadata=)} Binary stream handler - */ -function makeUnaryHandler(handler) { - /** - * Handles a stream by reading a single data value, passing it to the handler, - * and writing the response back to the stream. - * @param {stream} stream Binary data stream - * @param {metadata=} metadata Incoming metadata array - */ - return function handleUnaryCall(stream, metadata) { - stream.on('data', function handleUnaryData(value) { - var call = {request: value}; - Object.defineProperty(call, 'cancelled', { - get: function() { return stream.cancelled;} - }); - stream.on('cancelled', function() { - call.emit('cancelled'); - }); - handler(call, function sendUnaryData(err, value) { - if (err) { - stream.emit('error', err); - } else { - stream.write(value); - stream.end(); - } - }, metadata); - }); - }; -} - -/** - * Creates a binary stream handler function from a client stream handler - * function - * @param {function(Readable, function(Error, *), metadata=)} handler Client - * stream call handler - * @return {function(stream, metadata=)} Binary stream handler - */ -function makeClientStreamHandler(handler) { - /** - * Handles a stream by passing a deserializing stream to the handler and - * writing the response back to the stream. - * @param {stream} stream Binary data stream - * @param {metadata=} metadata Incoming metadata array - */ - return function handleClientStreamCall(stream, metadata) { - var object_stream = new ServerReadableObjectStream(stream); - handler(object_stream, function sendClientStreamData(err, value) { - if (err) { - stream.emit('error', err); - } else { - stream.write(value); - stream.end(); - } - }, metadata); - }; -} - -/** - * Creates a binary stream handler function from a server stream handler - * function - * @param {function(Writable, metadata=)} handler Server stream call handler - * @return {function(stream, metadata=)} Binary stream handler - */ -function makeServerStreamHandler(handler) { - /** - * Handles a stream by attaching it to a serializing stream, and passing it to - * the handler. - * @param {stream} stream Binary data stream - * @param {metadata=} metadata Incoming metadata array - */ - return function handleServerStreamCall(stream, metadata) { - stream.on('data', function handleClientData(value) { - var object_stream = new ServerWritableObjectStream(stream); - object_stream.request = value; - handler(object_stream, metadata); - }); - }; -} - -/** - * Creates a binary stream handler function from a bidi stream handler function - * @param {function(Duplex, metadata=)} handler Unary call handler - * @return {function(stream, metadata=)} Binary stream handler - */ -function makeBidiStreamHandler(handler) { - return handler; -} - -/** - * Map with short names for each of the handler maker functions. Used in - * makeServerConstructor - */ -var handler_makers = { - unary: makeUnaryHandler, - server_stream: makeServerStreamHandler, - client_stream: makeClientStreamHandler, - bidi: makeBidiStreamHandler -}; - -/** - * Creates a constructor for servers with a service defined by the methods - * object. The methods object has string keys and values of this form: - * {serialize: function, deserialize: function, client_stream: bool, - * server_stream: bool} - * @param {Object} methods Method descriptor for each method the server should - * expose - * @param {string} prefix The prefex to prepend to each method name - * @return {function(Object, Object)} New server constructor - */ -function makeServerConstructor(services) { - var qual_names = []; - _.each(services, function(service) { - _.each(service.children, function(method) { - var name = common.fullyQualifiedName(method); - if (_.indexOf(qual_names, name) !== -1) { - throw new Error('Method ' + name + ' exposed by more than one service'); - } - qual_names.push(name); - }); - }); - /** - * Create a server with the given handlers for all of the methods. - * @constructor - * @param {Object} service_handlers Map from service names to map from method - * names to handlers - * @param {function(string, Object>): - Object>=} getMetadata Callback that - * gets metatada for a given method - * @param {Object=} options Options to pass to the underlying server - */ - function SurfaceServer(service_handlers, getMetadata, options) { - var server = new Server(getMetadata, options); - this.inner_server = server; - _.each(services, function(service) { - var service_name = common.fullyQualifiedName(service); - if (service_handlers[service_name] === undefined) { - throw new Error('Handlers for service ' + - service_name + ' not provided.'); - } - var prefix = '/' + common.fullyQualifiedName(service) + '/'; - _.each(service.children, function(method) { - var method_type; - if (method.requestStream) { - if (method.responseStream) { - method_type = 'bidi'; - } else { - method_type = 'client_stream'; - } - } else { - if (method.responseStream) { - method_type = 'server_stream'; - } else { - method_type = 'unary'; - } - } - if (service_handlers[service_name][decapitalize(method.name)] === - undefined) { - throw new Error('Method handler for ' + - common.fullyQualifiedName(method) + ' not provided.'); - } - var binary_handler = handler_makers[method_type]( - service_handlers[service_name][decapitalize(method.name)]); - var serialize = common.serializeCls( - method.resolvedResponseType.build()); - var deserialize = common.deserializeCls( - method.resolvedRequestType.build()); - server.register(prefix + capitalize(method.name), binary_handler, - serialize, deserialize); - }); - }, this); - } - - /** - * Binds the server to the given port, with SSL enabled if secure is specified - * @param {string} port The port that the server should bind on, in the format - * "address:port" - * @param {boolean=} secure Whether the server should open a secure port - * @return {SurfaceServer} this - */ - SurfaceServer.prototype.bind = function(port, secure) { - return this.inner_server.bind(port, secure); - }; - - /** - * Starts the server listening on any bound ports - * @return {SurfaceServer} this - */ - SurfaceServer.prototype.listen = function() { - this.inner_server.start(); - return this; - }; - - /** - * Shuts the server down; tells it to stop listening for new requests and to - * kill old requests. - */ - SurfaceServer.prototype.shutdown = function() { - this.inner_server.shutdown(); - }; - - return SurfaceServer; -} - -/** - * See documentation for makeServerConstructor - */ -exports.makeServerConstructor = makeServerConstructor; diff --git a/test/client_server_test.js b/test/client_server_test.js deleted file mode 100644 index 1db9f694..00000000 --- a/test/client_server_test.js +++ /dev/null @@ -1,255 +0,0 @@ -/* - * - * Copyright 2014, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -var assert = require('assert'); -var fs = require('fs'); -var path = require('path'); -var grpc = require('bindings')('grpc.node'); -var Server = require('../src/server'); -var client = require('../src/client'); -var common = require('../src/common'); - -var ca_path = path.join(__dirname, 'data/ca.pem'); - -var key_path = path.join(__dirname, 'data/server1.key'); - -var pem_path = path.join(__dirname, 'data/server1.pem'); - -/** - * Helper function to return an absolute deadline given a relative timeout in - * seconds. - * @param {number} timeout_secs The number of seconds to wait before timing out - * @return {Date} A date timeout_secs in the future - */ -function getDeadline(timeout_secs) { - var deadline = new Date(); - deadline.setSeconds(deadline.getSeconds() + timeout_secs); - return deadline; -} - -/** - * Responds to every request with the same data as a response - * @param {Stream} stream - */ -function echoHandler(stream) { - stream.pipe(stream); -} - -/** - * Responds to every request with an error status - * @param {Stream} stream - */ -function errorHandler(stream) { - throw { - 'code' : grpc.status.UNIMPLEMENTED, - 'details' : 'error details' - }; -} - -/** - * Wait for a cancellation instead of responding - * @param {Stream} stream - */ -function cancelHandler(stream) { - // do nothing -} - -function metadataHandler(stream, metadata) { - stream.end(); -} - -/** - * Serialize a string to a Buffer - * @param {string} value The string to serialize - * @return {Buffer} The serialized value - */ -function stringSerialize(value) { - return new Buffer(value); -} - -/** - * Deserialize a Buffer to a string - * @param {Buffer} buffer The buffer to deserialize - * @return {string} The string value of the buffer - */ -function stringDeserialize(buffer) { - return buffer.toString(); -} - -describe('echo client', function() { - var server; - var channel; - before(function() { - server = new Server(function getMetadata(method, metadata) { - return {method: [method]}; - }); - var port_num = server.bind('0.0.0.0:0'); - server.register('echo', echoHandler); - server.register('error', errorHandler); - server.register('cancellation', cancelHandler); - server.register('metadata', metadataHandler); - server.start(); - - channel = new grpc.Channel('localhost:' + port_num); - }); - after(function() { - server.shutdown(); - }); - it('should receive echo responses', function(done) { - var messages = ['echo1', 'echo2', 'echo3', 'echo4']; - var stream = client.makeRequest( - channel, - 'echo', - stringSerialize, - stringDeserialize); - for (var i = 0; i < messages.length; i++) { - stream.write(messages[i]); - } - stream.end(); - var index = 0; - stream.on('data', function(chunk) { - assert.equal(messages[index], chunk); - index += 1; - }); - stream.on('status', function(status) { - assert.equal(status.code, client.status.OK); - }); - stream.on('end', function() { - assert.equal(index, messages.length); - done(); - }); - }); - it('should recieve metadata set by the server', function(done) { - var stream = client.makeRequest(channel, 'metadata'); - stream.on('metadata', function(metadata) { - assert.strictEqual(metadata.method[0].toString(), 'metadata'); - }); - stream.on('status', function(status) { - assert.equal(status.code, client.status.OK); - done(); - }); - stream.end(); - }); - it('should get an error status that the server throws', function(done) { - var stream = client.makeRequest(channel, 'error'); - - stream.on('data', function() {}); - stream.write(new Buffer('test')); - stream.end(); - stream.on('status', function(status) { - assert.equal(status.code, grpc.status.UNIMPLEMENTED); - assert.equal(status.details, 'error details'); - done(); - }); - }); - it('should be able to cancel a call', function(done) { - var stream = client.makeRequest( - channel, - 'cancellation', - null, - getDeadline(1)); - - stream.cancel(); - stream.on('status', function(status) { - assert.equal(status.code, grpc.status.CANCELLED); - done(); - }); - }); - it('should get correct status for unimplemented method', function(done) { - var stream = client.makeRequest(channel, 'unimplemented_method'); - stream.end(); - stream.on('status', function(status) { - assert.equal(status.code, grpc.status.UNIMPLEMENTED); - done(); - }); - }); -}); -/* TODO(mlumish): explore options for reducing duplication between this test - * and the insecure echo client test */ -describe('secure echo client', function() { - var server; - var channel; - before(function(done) { - fs.readFile(ca_path, function(err, ca_data) { - assert.ifError(err); - fs.readFile(key_path, function(err, key_data) { - assert.ifError(err); - fs.readFile(pem_path, function(err, pem_data) { - assert.ifError(err); - var creds = grpc.Credentials.createSsl(ca_data); - var server_creds = grpc.ServerCredentials.createSsl(null, - key_data, - pem_data); - - server = new Server(null, {'credentials' : server_creds}); - var port_num = server.bind('0.0.0.0:0', true); - server.register('echo', echoHandler); - server.start(); - - channel = new grpc.Channel('localhost:' + port_num, { - 'grpc.ssl_target_name_override' : 'foo.test.google.com', - 'credentials' : creds - }); - done(); - }); - }); - }); - }); - after(function() { - server.shutdown(); - }); - it('should recieve echo responses', function(done) { - var messages = ['echo1', 'echo2', 'echo3', 'echo4']; - var stream = client.makeRequest( - channel, - 'echo', - stringSerialize, - stringDeserialize); - for (var i = 0; i < messages.length; i++) { - stream.write(messages[i]); - } - stream.end(); - var index = 0; - stream.on('data', function(chunk) { - assert.equal(messages[index], chunk); - index += 1; - }); - stream.on('status', function(status) { - assert.equal(status.code, client.status.OK); - }); - stream.on('end', function() { - assert.equal(index, messages.length); - done(); - }); - }); -}); diff --git a/test/interop_sanity_test.js b/test/interop_sanity_test.js index 7ecaad83..81cd9fa5 100644 --- a/test/interop_sanity_test.js +++ b/test/interop_sanity_test.js @@ -56,7 +56,7 @@ describe('Interop tests', function() { interop_client.runTest(port, name_override, 'empty_unary', true, done); }); // This fails due to an unknown bug - it.skip('should pass large_unary', function(done) { + it('should pass large_unary', function(done) { interop_client.runTest(port, name_override, 'large_unary', true, done); }); it('should pass client_streaming', function(done) { diff --git a/test/math_client_test.js b/test/math_client_test.js index f347b18e..61b4a2fa 100644 --- a/test/math_client_test.js +++ b/test/math_client_test.js @@ -66,7 +66,7 @@ describe('Math client', function() { done(); }); }); - it.only('should handle a server streaming request', function(done) { + it('should handle a server streaming request', function(done) { var call = math_client.fib({limit: 7}); var expected_results = [1, 1, 2, 3, 5, 8, 13]; var next_expected = 0; diff --git a/test/server_test.js b/test/server_test.js deleted file mode 100644 index a3e1edf5..00000000 --- a/test/server_test.js +++ /dev/null @@ -1,122 +0,0 @@ -/* - * - * Copyright 2014, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -var assert = require('assert'); -var grpc = require('bindings')('grpc.node'); -var Server = require('../src/server'); - -/** - * This is used for testing functions with multiple asynchronous calls that - * can happen in different orders. This should be passed the number of async - * function invocations that can occur last, and each of those should call this - * function's return value - * @param {function()} done The function that should be called when a test is - * complete. - * @param {number} count The number of calls to the resulting function if the - * test passes. - * @return {function()} The function that should be called at the end of each - * sequence of asynchronous functions. - */ -function multiDone(done, count) { - return function() { - count -= 1; - if (count <= 0) { - done(); - } - }; -} - -/** - * Responds to every request with the same data as a response - * @param {Stream} stream - */ -function echoHandler(stream) { - stream.pipe(stream); -} - -describe('echo server', function() { - var server; - var channel; - before(function() { - server = new Server(); - var port_num = server.bind('[::]:0'); - server.register('echo', echoHandler); - server.start(); - - channel = new grpc.Channel('localhost:' + port_num); - }); - after(function() { - server.shutdown(); - }); - it('should echo inputs as responses', function(done) { - done = multiDone(done, 4); - - var req_text = 'echo test string'; - var status_text = 'OK'; - - var deadline = new Date(); - deadline.setSeconds(deadline.getSeconds() + 3); - var call = new grpc.Call(channel, - 'echo', - deadline); - call.invoke(function(event) { - assert.strictEqual(event.type, - grpc.completionType.CLIENT_METADATA_READ); - done(); - },function(event) { - assert.strictEqual(event.type, grpc.completionType.FINISHED); - var status = event.data; - assert.strictEqual(status.code, grpc.status.OK); - assert.strictEqual(status.details, status_text); - done(); - }, 0); - call.startWrite( - new Buffer(req_text), - function(event) { - assert.strictEqual(event.type, - grpc.completionType.WRITE_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - call.writesDone(function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - done(); - }); - }, 0); - call.startRead(function(event) { - assert.strictEqual(event.type, grpc.completionType.READ); - assert.strictEqual(event.data.toString(), req_text); - done(); - }); - }); -}); diff --git a/test/surface_test.js b/test/surface_test.js index 1038f9ab..34e4ab40 100644 --- a/test/surface_test.js +++ b/test/surface_test.js @@ -33,9 +33,9 @@ var assert = require('assert'); -var surface_server = require('../src/surface_server.js'); +var surface_server = require('../src/server.js'); -var surface_client = require('../src/surface_client.js'); +var surface_client = require('../src/client.js'); var ProtoBuf = require('protobufjs'); From 7f654db5b85abe045c636a3fec79e572942f06c6 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 12 Feb 2015 13:32:18 -0800 Subject: [PATCH 25/91] Removed extra log lines --- examples/math_server.js | 1 - interop/interop_server.js | 1 - src/client.js | 6 ------ src/server.js | 9 --------- test/end_to_end_test.js | 1 - 5 files changed, 18 deletions(-) diff --git a/examples/math_server.js b/examples/math_server.js index e0104453..e1bd11b5 100644 --- a/examples/math_server.js +++ b/examples/math_server.js @@ -69,7 +69,6 @@ function mathDiv(call, cb) { * @param {stream} stream The stream for sending responses. */ function mathFib(stream) { - console.log(stream); // Here, call is a standard writable Node object Stream var previous = 0, current = 1; for (var i = 0; i < stream.request.limit; i++) { diff --git a/interop/interop_server.js b/interop/interop_server.js index 271fe596..54e9715d 100644 --- a/interop/interop_server.js +++ b/interop/interop_server.js @@ -106,7 +106,6 @@ function handleStreamingOutput(call) { testProto.PayloadType.COMPRESSABLE, testProto.PayloadType.UNCOMPRESSABLE][Math.random() < 0.5 ? 0 : 1]; } - console.log('req:', req); _.each(req.response_parameters, function(resp_param) { call.write({ payload: { diff --git a/src/client.js b/src/client.js index 36a6f41d..4b7eda32 100644 --- a/src/client.js +++ b/src/client.js @@ -56,7 +56,6 @@ function ClientWritableStream(call, serialize) { this.call = call; this.serialize = common.wrapIgnoreNull(serialize); this.on('finish', function() { - console.log('Send close from client'); var batch = {}; batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; call.startBatch(batch, function() {}); @@ -73,7 +72,6 @@ function ClientWritableStream(call, serialize) { function _write(chunk, encoding, callback) { var batch = {}; batch[grpc.opType.SEND_MESSAGE] = this.serialize(chunk); - console.log(batch); this.call.startBatch(batch, function(err, event) { if (err) { throw err; @@ -153,7 +151,6 @@ function ClientDuplexStream(call, serialize, deserialize) { var reading = false; this.call = call; this.on('finish', function() { - console.log('Send close from client'); var batch = {}; batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; call.startBatch(batch, function() {}); @@ -279,7 +276,6 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) { callback(err); return; } - console.log(response); if (response.status.code != grpc.status.OK) { callback(response.status); return; @@ -323,7 +319,6 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) { } var stream = new ClientReadableStream(call, deserialize); var start_batch = {}; - console.log('Starting server streaming request on', method); start_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; start_batch[grpc.opType.RECV_INITIAL_METADATA] = true; start_batch[grpc.opType.SEND_MESSAGE] = serialize(argument); @@ -332,7 +327,6 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) { if (err) { throw err; } - console.log(response); stream.emit('metadata', response.metadata); }); var status_batch = {}; diff --git a/src/server.js b/src/server.js index 3c214913..82d521dd 100644 --- a/src/server.js +++ b/src/server.js @@ -95,7 +95,6 @@ function setUpWritable(stream, serialize) { }; stream.serialize = common.wrapIgnoreNull(serialize); function sendStatus() { - console.log('Server sending status'); var batch = {}; batch[grpc.opType.SEND_STATUS_FROM_SERVER] = stream.status; stream.call.startBatch(batch, function(){}); @@ -169,7 +168,6 @@ function ServerWritableStream(call, serialize) { function _write(chunk, encoding, callback) { var batch = {}; batch[grpc.opType.SEND_MESSAGE] = this.serialize(chunk); - console.log('Server writing', batch); this.call.startBatch(batch, function(err, value) { if (err) { this.emit('error', err); @@ -207,14 +205,11 @@ function _read(size) { return; } if (self.finished) { - console.log('Pushing null'); self.push(null); return; } var data = event.read; - console.log(data); if (self.push(self.deserialize(data)) && data != null) { - console.log('Reading again'); var read_batch = {}; read_batch[grpc.opType.RECV_MESSAGE] = true; self.call.startBatch(read_batch, readCallback); @@ -276,7 +271,6 @@ function handleUnary(call, handler, metadata) { } function handleServerStreaming(call, handler, metadata) { - console.log('Handling server streaming call'); var stream = new ServerWritableStream(call, handler.serialize); waitForCancel(call, stream); var batch = {}; @@ -360,7 +354,6 @@ function Server(getMetadata, options) { * @param {grpc.Event} event The event to handle with tag SERVER_RPC_NEW */ function handleNewCall(err, event) { - console.log('Handling new call'); if (err) { return; } @@ -376,9 +369,7 @@ function Server(getMetadata, options) { var deadline = details.deadline; if (handlers.hasOwnProperty(method)) { handler = handlers[method]; - console.log(handler); } else { - console.log(handlers); var batch = {}; batch[grpc.opType.SEND_INITIAL_METADATA] = {}; batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { diff --git a/test/end_to_end_test.js b/test/end_to_end_test.js index 34ce2500..f8899bea 100644 --- a/test/end_to_end_test.js +++ b/test/end_to_end_test.js @@ -148,7 +148,6 @@ describe('end-to-end', function() { }); server.requestCall(function(err, call_details) { - console.log("Server received new call"); var new_call = call_details['new call']; assert.notEqual(new_call, null); assert.strictEqual(new_call.metadata.client_key[0].toString(), From 5bcc358e5339ff83446c3e394eb672b8713a7a30 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 12 Feb 2015 13:58:24 -0800 Subject: [PATCH 26/91] Last test now passes --- test/call_test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/call_test.js b/test/call_test.js index 1cbfc228..c1a7e95f 100644 --- a/test/call_test.js +++ b/test/call_test.js @@ -111,7 +111,7 @@ describe('call', function() { call.startBatch(null, function(){}); }); }); - it.skip('should succeed with an empty object', function(done) { + it('should succeed with an empty object', function(done) { var call = new grpc.Call(channel, 'method', getDeadline(1)); assert.doesNotThrow(function() { call.startBatch({}, function(err) { From c1ec85fc43aa71a48f27d34218b7bb12cd9b1c01 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 12 Feb 2015 15:48:51 -0800 Subject: [PATCH 27/91] Removed extraneous log messages --- ext/call.cc | 6 ------ ext/completion_queue_async_worker.cc | 2 -- ext/credentials.cc | 1 - ext/server.cc | 1 - ext/server_credentials.cc | 1 - 5 files changed, 11 deletions(-) diff --git a/ext/call.cc b/ext/call.cc index cdc34b52..4401698b 100644 --- a/ext/call.cc +++ b/ext/call.cc @@ -98,9 +98,6 @@ bool CreateMetadataArray( string_handles->push_back(unique_ptr(utf8_key)); Handle values = Local::Cast(metadata->Get(current_key)); for (unsigned int j = 0; j < values->Length(); j++) { - if (array->count >= array->capacity) { - gpr_log(GPR_ERROR, "Metadata array grew past capacity"); - } Handle value = values->Get(j); grpc_metadata *current = &array->metadata[array->count]; current->key = **utf8_key; @@ -447,11 +444,9 @@ void DestroyTag(void *tag) { } Call::Call(grpc_call *call) : wrapped_call(call) { - gpr_log(GPR_DEBUG, "Constructing call, this: %p, pointer: %p", this, call); } Call::~Call() { - gpr_log(GPR_DEBUG, "Destructing call, this: %p, pointer: %p", this, wrapped_call); grpc_call_destroy(wrapped_call); } @@ -483,7 +478,6 @@ Handle Call::WrapStruct(grpc_call *call) { if (call == NULL) { return NanEscapeScope(NanNull()); } - gpr_log(GPR_DEBUG, "Wrapping call: %p", call); const int argc = 1; Handle argv[argc] = {External::New(reinterpret_cast(call))}; return NanEscapeScope(constructor->NewInstance(argc, argv)); diff --git a/ext/completion_queue_async_worker.cc b/ext/completion_queue_async_worker.cc index 3c32b07c..a1f390f6 100644 --- a/ext/completion_queue_async_worker.cc +++ b/ext/completion_queue_async_worker.cc @@ -58,7 +58,6 @@ CompletionQueueAsyncWorker::~CompletionQueueAsyncWorker() {} void CompletionQueueAsyncWorker::Execute() { result = grpc_completion_queue_next(queue, gpr_inf_future); - gpr_log(GPR_DEBUG, "Handling response on call %p", result->call); if (result->data.op_complete != GRPC_OP_OK) { SetErrorMessage("The batch encountered an error"); } @@ -79,7 +78,6 @@ void CompletionQueueAsyncWorker::Init(Handle exports) { void CompletionQueueAsyncWorker::HandleOKCallback() { NanScope(); - gpr_log(GPR_DEBUG, "Handling response on call %p", result->call); NanCallback *callback = GetTagCallback(result->tag); Handle argv[] = {NanNull(), GetTagNodeValue(result->tag)}; diff --git a/ext/credentials.cc b/ext/credentials.cc index c8859ed9..b79c3e30 100644 --- a/ext/credentials.cc +++ b/ext/credentials.cc @@ -63,7 +63,6 @@ Credentials::Credentials(grpc_credentials *credentials) : wrapped_credentials(credentials) {} Credentials::~Credentials() { - gpr_log(GPR_DEBUG, "Destroying credentials object"); grpc_credentials_release(wrapped_credentials); } diff --git a/ext/server.cc b/ext/server.cc index 93aa9ec4..51904479 100644 --- a/ext/server.cc +++ b/ext/server.cc @@ -91,7 +91,6 @@ class NewCallOp : public Op { return NanEscapeScope(NanNull()); } Handle obj = NanNew(); - gpr_log(GPR_DEBUG, "Wrapping server call: %p", call); obj->Set(NanNew("call"), Call::WrapStruct(call)); obj->Set(NanNew("method"), NanNew(details.method)); obj->Set(NanNew("host"), NanNew(details.host)); diff --git a/ext/server_credentials.cc b/ext/server_credentials.cc index 393f3a63..3add43c4 100644 --- a/ext/server_credentials.cc +++ b/ext/server_credentials.cc @@ -63,7 +63,6 @@ ServerCredentials::ServerCredentials(grpc_server_credentials *credentials) : wrapped_credentials(credentials) {} ServerCredentials::~ServerCredentials() { - gpr_log(GPR_DEBUG, "Destroying server credentials object"); grpc_server_credentials_release(wrapped_credentials); } From 8e10540169f65a8d0ddfdc68fc5d18b2e1bb5c21 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 13 Feb 2015 10:19:10 -0800 Subject: [PATCH 28/91] Made changes based on comments --- ext/call.cc | 86 ++++++++++++++++++++------------------------------- ext/call.h | 15 +++++---- ext/server.cc | 6 ++-- 3 files changed, 46 insertions(+), 61 deletions(-) diff --git a/ext/call.cc b/ext/call.cc index 4401698b..18f40f24 100644 --- a/ext/call.cc +++ b/ext/call.cc @@ -48,6 +48,7 @@ #include "timeval.h" using std::unique_ptr; +using std::shared_ptr; namespace grpc { namespace node { @@ -76,10 +77,8 @@ Persistent Call::constructor; Persistent Call::fun_tpl; -bool CreateMetadataArray( - Handle metadata, grpc_metadata_array *array, - std::vector > *string_handles, - std::vector > *handles) { +bool CreateMetadataArray(Handle metadata, grpc_metadata_array *array, + shared_ptr resources) { NanScope(); grpc_metadata_array_init(array); Handle keys(metadata->GetOwnPropertyNames()); @@ -95,7 +94,7 @@ bool CreateMetadataArray( for (unsigned int i = 0; i < keys->Length(); i++) { Handle current_key(keys->Get(i)->ToString()); NanUtf8String *utf8_key = new NanUtf8String(current_key); - string_handles->push_back(unique_ptr(utf8_key)); + resources->strings.push_back(unique_ptr(utf8_key)); Handle values = Local::Cast(metadata->Get(current_key)); for (unsigned int j = 0; j < values->Length(); j++) { Handle value = values->Get(j); @@ -106,12 +105,12 @@ bool CreateMetadataArray( current->value_length = Buffer::Length(value); Persistent handle; NanAssignPersistent(handle, value); - handles->push_back(unique_ptr( + resources->handles.push_back(unique_ptr( new PersistentHolder(handle))); } else if (value->IsString()) { Handle string_value = value->ToString(); NanUtf8String *utf8_value = new NanUtf8String(string_value); - string_handles->push_back(unique_ptr(utf8_value)); + resources->strings.push_back(unique_ptr(utf8_value)); current->value = **utf8_value; current->value_length = string_value->Length(); } else { @@ -168,13 +167,12 @@ class SendMetadataOp : public Op { return NanEscapeScope(NanTrue()); } bool ParseOp(Handle value, grpc_op *out, - std::vector > *strings, - std::vector > *handles) { + shared_ptr resources) { if (!value->IsObject()) { return false; } grpc_metadata_array array; - if (!CreateMetadataArray(value->ToObject(), &array, strings, handles)) { + if (!CreateMetadataArray(value->ToObject(), &array, resources)) { return false; } out->data.send_initial_metadata.count = array.count; @@ -194,8 +192,7 @@ class SendMessageOp : public Op { return NanEscapeScope(NanTrue()); } bool ParseOp(Handle value, grpc_op *out, - std::vector > *strings, - std::vector > *handles) { + shared_ptr resources) { if (!Buffer::HasInstance(value)) { return false; } @@ -204,7 +201,7 @@ class SendMessageOp : public Op { Handle temp = NanNew(); NanAssignPersistent(handle, temp); NanAssignPersistent(handle, value); - handles->push_back(unique_ptr( + resources->handles.push_back(unique_ptr( new PersistentHolder(handle))); return true; } @@ -221,8 +218,7 @@ class SendClientCloseOp : public Op { return NanEscapeScope(NanTrue()); } bool ParseOp(Handle value, grpc_op *out, - std::vector > *strings, - std::vector > *handles) { + shared_ptr resources) { return true; } protected: @@ -238,8 +234,7 @@ class SendServerStatusOp : public Op { return NanEscapeScope(NanTrue()); } bool ParseOp(Handle value, grpc_op *out, - std::vector > *strings, - std::vector > *handles) { + shared_ptr resources) { if (!value->IsObject()) { return false; } @@ -256,7 +251,7 @@ class SendServerStatusOp : public Op { grpc_metadata_array array; if (!CreateMetadataArray(server_status->Get(NanNew("metadata"))-> ToObject(), - &array, strings, handles)) { + &array, resources)) { return false; } out->data.send_status_from_server.trailing_metadata_count = array.count; @@ -266,7 +261,7 @@ class SendServerStatusOp : public Op { server_status->Get(NanNew("code"))->Uint32Value()); NanUtf8String *str = new NanUtf8String( server_status->Get(NanNew("details"))); - strings->push_back(unique_ptr(str)); + resources->strings.push_back(unique_ptr(str)); out->data.send_status_from_server.status_details = **str; return true; } @@ -292,8 +287,7 @@ class GetMetadataOp : public Op { } bool ParseOp(Handle value, grpc_op *out, - std::vector > *strings, - std::vector > *handles) { + shared_ptr resources) { out->data.recv_initial_metadata = &recv_metadata; return true; } @@ -323,8 +317,7 @@ class ReadMessageOp : public Op { } bool ParseOp(Handle value, grpc_op *out, - std::vector > *strings, - std::vector > *handles) { + shared_ptr resources) { out->data.recv_message = &recv_message; return true; } @@ -352,8 +345,7 @@ class ClientStatusOp : public Op { } bool ParseOp(Handle value, grpc_op *out, - std::vector > *strings, - std::vector > *handles) { + shared_ptr 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; @@ -390,8 +382,7 @@ class ServerCloseResponseOp : public Op { } bool ParseOp(Handle value, grpc_op *out, - std::vector > *strings, - std::vector > *handles) { + shared_ptr resources) { out->data.recv_close_on_server.cancelled = &cancelled; return true; } @@ -406,19 +397,13 @@ class ServerCloseResponseOp : public Op { }; tag::tag(NanCallback *callback, std::vector > *ops, - std::vector > *handles, - std::vector > *strings) : - callback(callback), ops(ops), handles(handles), strings(strings){ + shared_ptr resources) : + callback(callback), ops(ops), resources(resources){ } + tag::~tag() { delete callback; delete ops; - if (handles != NULL) { - delete handles; - } - if (strings != NULL) { - delete strings; - } } Handle GetTagNodeValue(void *tag) { @@ -542,17 +527,14 @@ NAN_METHOD(Call::StartBatch) { Handle callback_func = args[1].As(); NanCallback *callback = new NanCallback(callback_func); Call *call = ObjectWrap::Unwrap(args.This()); - std::vector > *handles = - new std::vector >(); - std::vector > *strings = - new std::vector >(); + shared_ptr resources(new Resources); Handle obj = args[0]->ToObject(); Handle keys = obj->GetOwnPropertyNames(); size_t nops = keys->Length(); grpc_op *ops = new grpc_op[nops]; std::vector > *op_vector = new std::vector >(); for (unsigned int i = 0; i < nops; i++) { - Op *op; + unique_ptr op; if (!keys->Get(i)->IsUint32()) { return NanThrowError( "startBatch's first argument's keys must be integers"); @@ -561,40 +543,40 @@ NAN_METHOD(Call::StartBatch) { ops[i].op = static_cast(type); switch (type) { case GRPC_OP_SEND_INITIAL_METADATA: - op = new SendMetadataOp(); + op.reset(new SendMetadataOp()); break; case GRPC_OP_SEND_MESSAGE: - op = new SendMessageOp(); + op.reset(new SendMessageOp()); break; case GRPC_OP_SEND_CLOSE_FROM_CLIENT: - op = new SendClientCloseOp(); + op.reset(new SendClientCloseOp()); break; case GRPC_OP_SEND_STATUS_FROM_SERVER: - op = new SendServerStatusOp(); + op.reset(new SendServerStatusOp()); break; case GRPC_OP_RECV_INITIAL_METADATA: - op = new GetMetadataOp(); + op.reset(new GetMetadataOp()); break; case GRPC_OP_RECV_MESSAGE: - op = new ReadMessageOp(); + op.reset(new ReadMessageOp()); break; case GRPC_OP_RECV_STATUS_ON_CLIENT: - op = new ClientStatusOp(); + op.reset(new ClientStatusOp()); break; case GRPC_OP_RECV_CLOSE_ON_SERVER: - op = new ServerCloseResponseOp(); + op.reset(new ServerCloseResponseOp()); break; default: return NanThrowError("Argument object had an unrecognized key"); } - if (!op->ParseOp(obj->Get(type), &ops[i], strings, handles)) { + if (!op->ParseOp(obj->Get(type), &ops[i], resources)) { return NanThrowTypeError("Incorrectly typed arguments to startBatch"); } - op_vector->push_back(unique_ptr(op)); + op_vector->push_back(std::move(op)); } grpc_call_error error = grpc_call_start_batch( call->wrapped_call, ops, nops, new struct tag( - callback, op_vector, handles, strings)); + callback, op_vector, resources)); delete ops; if (error != GRPC_CALL_OK) { return NanThrowError("startBatch failed", error); diff --git a/ext/call.h b/ext/call.h index f443a046..4074f150 100644 --- a/ext/call.h +++ b/ext/call.h @@ -47,6 +47,7 @@ namespace grpc { namespace node { using std::unique_ptr; +using std::shared_ptr; v8::Handle ParseMetadata(const grpc_metadata_array *metadata_array); @@ -64,12 +65,16 @@ class PersistentHolder { v8::Persistent persist; }; +struct Resources { + std::vector > strings; + std::vector > handles; +}; + class Op { public: virtual v8::Handle GetNodeValue() const = 0; virtual bool ParseOp(v8::Handle value, grpc_op *out, - std::vector > *strings, - std::vector > *handles) = 0; + shared_ptr resources) = 0; v8::Handle GetOpType() const; protected: @@ -78,13 +83,11 @@ class Op { struct tag { tag(NanCallback *callback, std::vector > *ops, - std::vector > *handles, - std::vector > *strings); + shared_ptr resources); ~tag(); NanCallback *callback; std::vector > *ops; - std::vector > *handles; - std::vector > *strings; + shared_ptr resources; }; v8::Handle GetTagNodeValue(void *tag); diff --git a/ext/server.cc b/ext/server.cc index 51904479..6a4a9511 100644 --- a/ext/server.cc +++ b/ext/server.cc @@ -101,8 +101,7 @@ class NewCallOp : public Op { } bool ParseOp(Handle value, grpc_op *out, - std::vector > *strings, - std::vector > *handles) { + shared_ptr resources) { return true; } @@ -230,7 +229,8 @@ NAN_METHOD(Server::RequestCall) { grpc_call_error error = grpc_server_request_call( server->wrapped_server, &op->call, &op->details, &op->request_metadata, CompletionQueueAsyncWorker::GetQueue(), - new struct tag(new NanCallback(args[0].As()), ops, NULL, NULL)); + new struct tag(new NanCallback(args[0].As()), ops, + shared_ptr(nullptr))); if (error != GRPC_CALL_OK) { return NanThrowError("requestCall failed", error); } From 4f82d0021d2e3548953ce413127560423a8d171f Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 13 Feb 2015 10:40:07 -0800 Subject: [PATCH 29/91] Improved memory management --- ext/call.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ext/call.cc b/ext/call.cc index 18f40f24..4d719802 100644 --- a/ext/call.cc +++ b/ext/call.cc @@ -531,7 +531,7 @@ NAN_METHOD(Call::StartBatch) { Handle obj = args[0]->ToObject(); Handle keys = obj->GetOwnPropertyNames(); size_t nops = keys->Length(); - grpc_op *ops = new grpc_op[nops]; + std::vector ops(nops); std::vector > *op_vector = new std::vector >(); for (unsigned int i = 0; i < nops; i++) { unique_ptr op; @@ -575,9 +575,8 @@ NAN_METHOD(Call::StartBatch) { op_vector->push_back(std::move(op)); } grpc_call_error error = grpc_call_start_batch( - call->wrapped_call, ops, nops, new struct tag( + call->wrapped_call, &ops[0], nops, new struct tag( callback, op_vector, resources)); - delete ops; if (error != GRPC_CALL_OK) { return NanThrowError("startBatch failed", error); } From b8a1daf4edf4acc6846e6e52c81246479be091e4 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 13 Feb 2015 10:41:25 -0800 Subject: [PATCH 30/91] Further improved memory management --- ext/call.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/call.cc b/ext/call.cc index 4d719802..e6701efb 100644 --- a/ext/call.cc +++ b/ext/call.cc @@ -525,7 +525,6 @@ NAN_METHOD(Call::StartBatch) { return NanThrowError("startBatch's second argument must be a callback"); } Handle callback_func = args[1].As(); - NanCallback *callback = new NanCallback(callback_func); Call *call = ObjectWrap::Unwrap(args.This()); shared_ptr resources(new Resources); Handle obj = args[0]->ToObject(); @@ -574,6 +573,7 @@ NAN_METHOD(Call::StartBatch) { } op_vector->push_back(std::move(op)); } + 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, resources)); From d64985bc75d90be38931a1df99b00d15a046f7b9 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 13 Feb 2015 11:14:03 -0800 Subject: [PATCH 31/91] Improved op_vector memory management --- ext/call.cc | 11 ++++++----- ext/call.h | 7 +++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/ext/call.cc b/ext/call.cc index e6701efb..a2333fa4 100644 --- a/ext/call.cc +++ b/ext/call.cc @@ -49,6 +49,7 @@ using std::unique_ptr; using std::shared_ptr; +using std::vector; namespace grpc { namespace node { @@ -396,7 +397,7 @@ class ServerCloseResponseOp : public Op { int cancelled; }; -tag::tag(NanCallback *callback, std::vector > *ops, +tag::tag(NanCallback *callback, OpVec *ops, shared_ptr resources) : callback(callback), ops(ops), resources(resources){ } @@ -410,7 +411,7 @@ Handle GetTagNodeValue(void *tag) { NanEscapableScope(); struct tag *tag_struct = reinterpret_cast(tag); Handle tag_obj = NanNew(); - for (std::vector >::iterator it = tag_struct->ops->begin(); + for (vector >::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()); @@ -530,8 +531,8 @@ NAN_METHOD(Call::StartBatch) { Handle obj = args[0]->ToObject(); Handle keys = obj->GetOwnPropertyNames(); size_t nops = keys->Length(); - std::vector ops(nops); - std::vector > *op_vector = new std::vector >(); + vector ops(nops); + unique_ptr op_vector(new OpVec()); for (unsigned int i = 0; i < nops; i++) { unique_ptr op; if (!keys->Get(i)->IsUint32()) { @@ -576,7 +577,7 @@ NAN_METHOD(Call::StartBatch) { 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, resources)); + callback, op_vector.release(), resources)); if (error != GRPC_CALL_OK) { return NanThrowError("startBatch failed", error); } diff --git a/ext/call.h b/ext/call.h index 4074f150..dbdb8e2f 100644 --- a/ext/call.h +++ b/ext/call.h @@ -43,6 +43,7 @@ #include "channel.h" + namespace grpc { namespace node { @@ -81,12 +82,14 @@ class Op { virtual std::string GetTypeString() const = 0; }; +typedef std::vector> OpVec; + struct tag { - tag(NanCallback *callback, std::vector > *ops, + tag(NanCallback *callback, OpVec *ops, shared_ptr resources); ~tag(); NanCallback *callback; - std::vector > *ops; + OpVec *ops; shared_ptr resources; }; From 928322992c140c3009e9944eafcc5f57fbeead65 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 13 Feb 2015 12:21:59 -0800 Subject: [PATCH 32/91] Updated server.cc to match call.cc changes --- ext/server.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/server.cc b/ext/server.cc index 6a4a9511..ee3e1087 100644 --- a/ext/server.cc +++ b/ext/server.cc @@ -224,12 +224,12 @@ NAN_METHOD(Server::RequestCall) { } Server *server = ObjectWrap::Unwrap(args.This()); NewCallOp *op = new NewCallOp(); - std::vector > *ops = new std::vector >(); + unique_ptr ops(new OpVec()); ops->push_back(unique_ptr(op)); grpc_call_error error = grpc_server_request_call( server->wrapped_server, &op->call, &op->details, &op->request_metadata, CompletionQueueAsyncWorker::GetQueue(), - new struct tag(new NanCallback(args[0].As()), ops, + new struct tag(new NanCallback(args[0].As()), ops.release(), shared_ptr(nullptr))); if (error != GRPC_CALL_OK) { return NanThrowError("requestCall failed", error); From 45a929f3bc84ead6ebae39ae15c0a917e4231bd5 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 13 Feb 2015 13:02:47 -0800 Subject: [PATCH 33/91] Removed debugging code --- ext/call.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/ext/call.cc b/ext/call.cc index a2333fa4..9ed389f3 100644 --- a/ext/call.cc +++ b/ext/call.cc @@ -199,8 +199,6 @@ class SendMessageOp : public Op { } out->data.send_message = BufferToByteBuffer(value); Persistent handle; - Handle temp = NanNew(); - NanAssignPersistent(handle, temp); NanAssignPersistent(handle, value); resources->handles.push_back(unique_ptr( new PersistentHolder(handle))); From 3781a8958972a9fb5b8c5ca08c5a2e2ae70486c8 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 13 Feb 2015 14:10:13 -0800 Subject: [PATCH 34/91] Version bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 028dc205..8f81014c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "grpc", - "version": "0.1.0", + "version": "0.2.0", "description": "gRPC Library for Node", "scripts": { "test": "./node_modules/mocha/bin/mocha" From a052d1253bf79c6200124b3d67ac1f0be9300746 Mon Sep 17 00:00:00 2001 From: Nathaniel Manista Date: Sun, 15 Feb 2015 01:21:18 +0000 Subject: [PATCH 35/91] The Python interoperability testing server. --- interop/test.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interop/test.proto b/interop/test.proto index 8380ebb3..996f11aa 100644 --- a/interop/test.proto +++ b/interop/test.proto @@ -14,7 +14,7 @@ service TestService { rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty); // One request followed by one response. - // The server returns the client payload as-is. + // TODO(Issue 527): Describe required server behavior. rpc UnaryCall(SimpleRequest) returns (SimpleResponse); // One request followed by a sequence of responses (streamed download). From 2403c6f6e8dba8361e4a7175a1f6b4174c9a7956 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Mon, 16 Feb 2015 12:23:04 -0800 Subject: [PATCH 36/91] Add proto copyrights --- examples/math.proto | 30 ++++++++++++++++++++++++++++++ interop/empty.proto | 30 ++++++++++++++++++++++++++++++ interop/messages.proto | 30 ++++++++++++++++++++++++++++++ interop/test.proto | 30 ++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+) diff --git a/examples/math.proto b/examples/math.proto index c49787ad..2cf6a036 100644 --- a/examples/math.proto +++ b/examples/math.proto @@ -1,3 +1,33 @@ + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + syntax = "proto3"; package math; diff --git a/interop/empty.proto b/interop/empty.proto index c9920a22..98fc3a39 100644 --- a/interop/empty.proto +++ b/interop/empty.proto @@ -1,3 +1,33 @@ + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + syntax = "proto2"; package grpc.testing; diff --git a/interop/messages.proto b/interop/messages.proto index 29db0dd8..f53d99ab 100644 --- a/interop/messages.proto +++ b/interop/messages.proto @@ -1,3 +1,33 @@ + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + // Message definitions to be used by integration test service definitions. syntax = "proto2"; diff --git a/interop/test.proto b/interop/test.proto index 8380ebb3..cce0889b 100644 --- a/interop/test.proto +++ b/interop/test.proto @@ -1,3 +1,33 @@ + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + // An integration test service that covers all the method signature permutations // of unary/streaming requests/responses. syntax = "proto2"; From 61abcf3f155d40534b3c5f27a0161944073cfb47 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Tue, 17 Feb 2015 16:23:06 -0800 Subject: [PATCH 37/91] Added missing documentation --- src/client.js | 25 +++++++++++-- src/server.js | 97 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 117 insertions(+), 5 deletions(-) diff --git a/src/client.js b/src/client.js index 4b7eda32..81fa65eb 100644 --- a/src/client.js +++ b/src/client.js @@ -51,6 +51,13 @@ var util = require('util'); util.inherits(ClientWritableStream, Writable); +/** + * A stream that the client can write to. Used for calls that are streaming from + * the client side. + * @constructor + * @param {grpc.Call} call The call object to send data with + * @param {function(*):Buffer=} serialize Serialization function for writes. + */ function ClientWritableStream(call, serialize) { Writable.call(this, {objectMode: true}); this.call = call; @@ -84,6 +91,13 @@ ClientWritableStream.prototype._write = _write; util.inherits(ClientReadableStream, Readable); +/** + * A stream that the client can read from. Used for calls that are streaming + * from the server side. + * @constructor + * @param {grpc.Call} call The call object to read data with + * @param {function(Buffer):*=} deserialize Deserialization function for reads + */ function ClientReadableStream(call, deserialize) { Readable.call(this, {objectMode: true}); this.call = call; @@ -92,6 +106,10 @@ function ClientReadableStream(call, deserialize) { this.deserialize = common.wrapIgnoreNull(deserialize); } +/** + * Read the next object from the stream. + * @param {*} size Ignored because we use objectMode=true + */ function _read(size) { var self = this; /** @@ -133,8 +151,8 @@ ClientReadableStream.prototype._read = _read; util.inherits(ClientDuplexStream, Duplex); /** - * Class for representing a gRPC client side stream as a Node stream. Extends - * from stream.Duplex. + * A stream that the client can read from or write to. Used for calls with + * duplex streaming. * @constructor * @param {grpc.Call} call Call object to proxy * @param {function(*):Buffer=} serialize Serialization function for requests @@ -160,6 +178,9 @@ function ClientDuplexStream(call, serialize, deserialize) { ClientDuplexStream.prototype._read = _read; ClientDuplexStream.prototype._write = _write; +/** + * Cancel the ongoing call + */ function cancel() { this.call.cancel(); } diff --git a/src/server.js b/src/server.js index 82d521dd..48c349ef 100644 --- a/src/server.js +++ b/src/server.js @@ -51,16 +51,38 @@ var EventEmitter = require('events').EventEmitter; var common = require('./common.js'); +/** + * Handle an error on a call by sending it as a status + * @param {grpc.Call} call The call to send the error on + * @param {Object} error The error object + */ function handleError(call, error) { - var error_batch = {}; - error_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { + var status = { code: grpc.status.INTERNAL, details: 'Unknown Error', metadata: {} }; + if (error.hasOwnProperty('message')) { + status.details = error.message; + } + if (error.hasOwnProperty('code')) { + status.code = error.code; + if (error.hasOwnProperty('details')) { + status.details = error.details; + } + } + var error_batch = {}; + error_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = status; call.startBatch(error_batch, function(){}); } +/** + * Wait for the client to close, then emit a cancelled event if the client + * cancelled. + * @param {grpc.Call} call The call object to wait on + * @param {EventEmitter} emitter The event emitter to emit the cancelled event + * on + */ function waitForCancel(call, emitter) { var cancel_batch = {}; cancel_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; @@ -75,6 +97,13 @@ function waitForCancel(call, emitter) { }); } +/** + * Send a response to a unary or client streaming call. + * @param {grpc.Call} call The call to respond on + * @param {*} value The value to respond with + * @param {function(*):Buffer=} serialize Serialization function for the + * response + */ function sendUnaryResponse(call, value, serialize) { var end_batch = {}; end_batch[grpc.opType.SEND_MESSAGE] = serialize(value); @@ -86,6 +115,12 @@ function sendUnaryResponse(call, value, serialize) { call.startBatch(end_batch, function (){}); } +/** + * Initialize a writable stream. This is used for both the writable and duplex + * stream constructors. + * @param {Writable} stream The stream to set up + * @param {function(*):Buffer=} Serialization function for responses + */ function setUpWritable(stream, serialize) { stream.finished = false; stream.status = { @@ -109,7 +144,9 @@ function setUpWritable(stream, serialize) { function setStatus(err) { var code = grpc.status.INTERNAL; var details = 'Unknown Error'; - + if (err.hasOwnProperty('message')) { + details = err.message; + } if (err.hasOwnProperty('code')) { code = err.code; if (err.hasOwnProperty('details')) { @@ -132,6 +169,13 @@ function setUpWritable(stream, serialize) { stream.on('error', terminateCall); } +/** + * Initialize a readable stream. This is used for both the readable and duplex + * stream constructors. + * @param {Readable} stream The stream to initialize + * @param {function(Buffer):*=} deserialize Deserialization function for + * incoming data. + */ function setUpReadable(stream, deserialize) { stream.deserialize = common.wrapIgnoreNull(deserialize); stream.finished = false; @@ -149,6 +193,13 @@ function setUpReadable(stream, deserialize) { util.inherits(ServerWritableStream, Writable); +/** + * A stream that the server can write to. Used for calls that are streaming from + * the server side. + * @constructor + * @param {grpc.Call} call The call object to send data with + * @param {function(*):Buffer=} serialize Serialization function for writes + */ function ServerWritableStream(call, serialize) { Writable.call(this, {objectMode: true}); this.call = call; @@ -181,6 +232,13 @@ ServerWritableStream.prototype._write = _write; util.inherits(ServerReadableStream, Readable); +/** + * A stream that the server can read from. Used for calls that are streaming + * from the client side. + * @constructor + * @param {grpc.Call} call The call object to read data with + * @param {function(Buffer):*=} deserialize Deserialization function for reads + */ function ServerReadableStream(call, deserialize) { Readable.call(this, {objectMode: true}); this.call = call; @@ -233,6 +291,15 @@ ServerReadableStream.prototype._read = _read; util.inherits(ServerDuplexStream, Duplex); +/** + * A stream that the server can read from or write to. Used for calls with + * duplex streaming. + * @constructor + * @param {grpc.Call} call Call object to proxy + * @param {function(*):Buffer=} serialize Serialization function for requests + * @param {function(Buffer):*=} deserialize Deserialization function for + * responses + */ function ServerDuplexStream(call, serialize, deserialize) { Duplex.call(this, {objectMode: true}); this.call = call; @@ -243,6 +310,12 @@ function ServerDuplexStream(call, serialize, deserialize) { ServerDuplexStream.prototype._read = _read; ServerDuplexStream.prototype._write = _write; +/** + * Fully handle a unary call + * @param {grpc.Call} call The call to handle + * @param {Object} handler Request handler object for the method that was called + * @param {Object} metadata Metadata from the client + */ function handleUnary(call, handler, metadata) { var emitter = new EventEmitter(); emitter.on('error', function(error) { @@ -270,6 +343,12 @@ function handleUnary(call, handler, metadata) { }); } +/** + * Fully handle a server streaming call + * @param {grpc.Call} call The call to handle + * @param {Object} handler Request handler object for the method that was called + * @param {Object} metadata Metadata from the client + */ function handleServerStreaming(call, handler, metadata) { var stream = new ServerWritableStream(call, handler.serialize); waitForCancel(call, stream); @@ -286,6 +365,12 @@ function handleServerStreaming(call, handler, metadata) { }); } +/** + * Fully handle a client streaming call + * @param {grpc.Call} call The call to handle + * @param {Object} handler Request handler object for the method that was called + * @param {Object} metadata Metadata from the client + */ function handleClientStreaming(call, handler, metadata) { var stream = new ServerReadableStream(call, handler.deserialize); waitForCancel(call, stream); @@ -301,6 +386,12 @@ function handleClientStreaming(call, handler, metadata) { }); } +/** + * Fully handle a bidirectional streaming call + * @param {grpc.Call} call The call to handle + * @param {Object} handler Request handler object for the method that was called + * @param {Object} metadata Metadata from the client + */ function handleBidiStreaming(call, handler, metadata) { var stream = new ServerDuplexStream(call, handler.serialize, handler.deserialize); From 42af13c9563196416211ed265ac8992cf9efccc6 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 18 Feb 2015 08:34:56 -0800 Subject: [PATCH 38/91] Update copyright to 2015 --- examples/math_server.js | 4 ++-- ext/byte_buffer.cc | 4 ++-- ext/byte_buffer.h | 4 ++-- ext/call.h | 4 ++-- ext/channel.cc | 4 ++-- ext/channel.h | 4 ++-- ext/completion_queue_async_worker.cc | 4 ++-- ext/completion_queue_async_worker.h | 4 ++-- ext/credentials.cc | 4 ++-- ext/credentials.h | 4 ++-- ext/node_grpc.cc | 4 ++-- ext/server.cc | 4 ++-- ext/server.h | 4 ++-- ext/server_credentials.cc | 4 ++-- ext/server_credentials.h | 4 ++-- ext/timeval.cc | 4 ++-- ext/timeval.h | 4 ++-- index.js | 4 ++-- interop/interop_client.js | 4 ++-- interop/interop_server.js | 4 ++-- src/common.js | 4 ++-- test/channel_test.js | 4 ++-- test/constant_test.js | 4 ++-- test/end_to_end_test.js | 4 ++-- test/interop_sanity_test.js | 4 ++-- test/math_client_test.js | 4 ++-- test/surface_test.js | 4 ++-- 27 files changed, 54 insertions(+), 54 deletions(-) diff --git a/examples/math_server.js b/examples/math_server.js index e1bd11b5..42728d0b 100644 --- a/examples/math_server.js +++ b/examples/math_server.js @@ -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 @@ -135,4 +135,4 @@ if (require.main === module) { /** * See docs for server */ -module.exports = server; +module.exports = server; \ No newline at end of file diff --git a/ext/byte_buffer.cc b/ext/byte_buffer.cc index 695ecedd..8180c273 100644 --- a/ext/byte_buffer.cc +++ b/ext/byte_buffer.cc @@ -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 @@ -92,4 +92,4 @@ Handle MakeFastBuffer(Handle slowBuffer) { return NanEscapeScope(fastBuffer); } } // namespace node -} // namespace grpc +} // namespace grpc \ No newline at end of file diff --git a/ext/byte_buffer.h b/ext/byte_buffer.h index 5f1903a4..52fee70a 100644 --- a/ext/byte_buffer.h +++ b/ext/byte_buffer.h @@ -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 @@ -57,4 +57,4 @@ v8::Handle MakeFastBuffer(v8::Handle slowBuffer); } // namespace node } // namespace grpc -#endif // NET_GRPC_NODE_BYTE_BUFFER_H_ +#endif // NET_GRPC_NODE_BYTE_BUFFER_H_ \ No newline at end of file diff --git a/ext/call.h b/ext/call.h index dbdb8e2f..e93349d6 100644 --- a/ext/call.h +++ b/ext/call.h @@ -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 @@ -128,4 +128,4 @@ class Call : public ::node::ObjectWrap { } // namespace node } // namespace grpc -#endif // NET_GRPC_NODE_CALL_H_ +#endif // NET_GRPC_NODE_CALL_H_ \ No newline at end of file diff --git a/ext/channel.cc b/ext/channel.cc index 9087d6f9..edeebe97 100644 --- a/ext/channel.cc +++ b/ext/channel.cc @@ -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 @@ -179,4 +179,4 @@ NAN_METHOD(Channel::Close) { } } // namespace node -} // namespace grpc +} // namespace grpc \ No newline at end of file diff --git a/ext/channel.h b/ext/channel.h index 140cbf20..3c059771 100644 --- a/ext/channel.h +++ b/ext/channel.h @@ -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 @@ -76,4 +76,4 @@ class Channel : public ::node::ObjectWrap { } // namespace node } // namespace grpc -#endif // NET_GRPC_NODE_CHANNEL_H_ +#endif // NET_GRPC_NODE_CHANNEL_H_ \ No newline at end of file diff --git a/ext/completion_queue_async_worker.cc b/ext/completion_queue_async_worker.cc index a1f390f6..bc5896b5 100644 --- a/ext/completion_queue_async_worker.cc +++ b/ext/completion_queue_async_worker.cc @@ -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 @@ -101,4 +101,4 @@ void CompletionQueueAsyncWorker::HandleErrorCallback() { } } // namespace node -} // namespace grpc +} // namespace grpc \ No newline at end of file diff --git a/ext/completion_queue_async_worker.h b/ext/completion_queue_async_worker.h index c04a3032..1c02a345 100644 --- a/ext/completion_queue_async_worker.h +++ b/ext/completion_queue_async_worker.h @@ -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 @@ -78,4 +78,4 @@ class CompletionQueueAsyncWorker : public NanAsyncWorker { } // namespace node } // namespace grpc -#endif // NET_GRPC_NODE_COMPLETION_QUEUE_ASYNC_WORKER_H_ +#endif // NET_GRPC_NODE_COMPLETION_QUEUE_ASYNC_WORKER_H_ \ No newline at end of file diff --git a/ext/credentials.cc b/ext/credentials.cc index b79c3e30..cb1e8a79 100644 --- a/ext/credentials.cc +++ b/ext/credentials.cc @@ -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 @@ -200,4 +200,4 @@ NAN_METHOD(Credentials::CreateIam) { } } // namespace node -} // namespace grpc +} // namespace grpc \ No newline at end of file diff --git a/ext/credentials.h b/ext/credentials.h index 981e5a99..576a5dbd 100644 --- a/ext/credentials.h +++ b/ext/credentials.h @@ -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 @@ -78,4 +78,4 @@ class Credentials : public ::node::ObjectWrap { } // namespace node } // namespace grpc -#endif // NET_GRPC_NODE_CREDENTIALS_H_ +#endif // NET_GRPC_NODE_CREDENTIALS_H_ \ No newline at end of file diff --git a/ext/node_grpc.cc b/ext/node_grpc.cc index 9b0fe829..4e1553fe 100644 --- a/ext/node_grpc.cc +++ b/ext/node_grpc.cc @@ -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 @@ -175,4 +175,4 @@ void init(Handle exports) { grpc::node::ServerCredentials::Init(exports); } -NODE_MODULE(grpc, init) +NODE_MODULE(grpc, init) \ No newline at end of file diff --git a/ext/server.cc b/ext/server.cc index ee3e1087..c2e85df5 100644 --- a/ext/server.cc +++ b/ext/server.cc @@ -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 @@ -286,4 +286,4 @@ NAN_METHOD(Server::Shutdown) { } } // namespace node -} // namespace grpc +} // namespace grpc \ No newline at end of file diff --git a/ext/server.h b/ext/server.h index d50f1fb6..e4bb4d88 100644 --- a/ext/server.h +++ b/ext/server.h @@ -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 @@ -76,4 +76,4 @@ class Server : public ::node::ObjectWrap { } // namespace node } // namespace grpc -#endif // NET_GRPC_NODE_SERVER_H_ +#endif // NET_GRPC_NODE_SERVER_H_ \ No newline at end of file diff --git a/ext/server_credentials.cc b/ext/server_credentials.cc index 3add43c4..9c9a1b38 100644 --- a/ext/server_credentials.cc +++ b/ext/server_credentials.cc @@ -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 @@ -151,4 +151,4 @@ NAN_METHOD(ServerCredentials::CreateFake) { } } // namespace node -} // namespace grpc +} // namespace grpc \ No newline at end of file diff --git a/ext/server_credentials.h b/ext/server_credentials.h index 8baae3f1..7c916e77 100644 --- a/ext/server_credentials.h +++ b/ext/server_credentials.h @@ -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 @@ -74,4 +74,4 @@ class ServerCredentials : public ::node::ObjectWrap { } // namespace node } // namespace grpc -#endif // NET_GRPC_NODE_SERVER_CREDENTIALS_H_ +#endif // NET_GRPC_NODE_SERVER_CREDENTIALS_H_ \ No newline at end of file diff --git a/ext/timeval.cc b/ext/timeval.cc index 20d52f09..5cece4a9 100644 --- a/ext/timeval.cc +++ b/ext/timeval.cc @@ -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 @@ -62,4 +62,4 @@ double TimespecToMilliseconds(gpr_timespec timespec) { } } // namespace node -} // namespace grpc +} // namespace grpc \ No newline at end of file diff --git a/ext/timeval.h b/ext/timeval.h index 1fb0f2c6..a85949f2 100644 --- a/ext/timeval.h +++ b/ext/timeval.h @@ -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 @@ -45,4 +45,4 @@ gpr_timespec MillisecondsToTimespec(double millis); } // namespace node } // namespace grpc -#endif // NET_GRPC_NODE_TIMEVAL_H_ +#endif // NET_GRPC_NODE_TIMEVAL_H_ \ No newline at end of file diff --git a/index.js b/index.js index baef4d03..167be3a7 100644 --- a/index.js +++ b/index.js @@ -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 @@ -105,4 +105,4 @@ exports.Credentials = grpc.Credentials; /** * ServerCredentials factories */ -exports.ServerCredentials = grpc.ServerCredentials; +exports.ServerCredentials = grpc.ServerCredentials; \ No newline at end of file diff --git a/interop/interop_client.js b/interop/interop_client.js index 8737af6c..4efc9667 100644 --- a/interop/interop_client.js +++ b/interop/interop_client.js @@ -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 @@ -310,4 +310,4 @@ if (require.main === module) { /** * See docs for runTest */ -exports.runTest = runTest; +exports.runTest = runTest; \ No newline at end of file diff --git a/interop/interop_server.js b/interop/interop_server.js index 54e9715d..2c9cf04c 100644 --- a/interop/interop_server.js +++ b/interop/interop_server.js @@ -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 @@ -201,4 +201,4 @@ if (require.main === module) { /** * See docs for getServer */ -exports.getServer = getServer; +exports.getServer = getServer; \ No newline at end of file diff --git a/src/common.js b/src/common.js index 7560cf1b..c5b83623 100644 --- a/src/common.js +++ b/src/common.js @@ -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 @@ -125,4 +125,4 @@ exports.fullyQualifiedName = fullyQualifiedName; /** * See docs for wrapIgnoreNull */ -exports.wrapIgnoreNull = wrapIgnoreNull; +exports.wrapIgnoreNull = wrapIgnoreNull; \ No newline at end of file diff --git a/test/channel_test.js b/test/channel_test.js index 4d8cfc4d..77708d16 100644 --- a/test/channel_test.js +++ b/test/channel_test.js @@ -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 @@ -85,4 +85,4 @@ describe('channel', function() { }); }); }); -}); +}); \ No newline at end of file diff --git a/test/constant_test.js b/test/constant_test.js index 4d11e6f5..0affa403 100644 --- a/test/constant_test.js +++ b/test/constant_test.js @@ -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 @@ -89,4 +89,4 @@ describe('constants', function() { 'call error missing: ' + callErrorNames[i]); } }); -}); +}); \ No newline at end of file diff --git a/test/end_to_end_test.js b/test/end_to_end_test.js index f8899bea..4fd6d8d2 100644 --- a/test/end_to_end_test.js +++ b/test/end_to_end_test.js @@ -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 @@ -233,4 +233,4 @@ describe('end-to-end', function() { }); }); }); -}); +}); \ No newline at end of file diff --git a/test/interop_sanity_test.js b/test/interop_sanity_test.js index 81cd9fa5..92e87b5d 100644 --- a/test/interop_sanity_test.js +++ b/test/interop_sanity_test.js @@ -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 @@ -79,4 +79,4 @@ describe('Interop tests', function() { interop_client.runTest(port, name_override, 'cancel_after_first_response', true, done); }); -}); +}); \ No newline at end of file diff --git a/test/math_client_test.js b/test/math_client_test.js index 61b4a2fa..97b95377 100644 --- a/test/math_client_test.js +++ b/test/math_client_test.js @@ -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 @@ -113,4 +113,4 @@ describe('Math client', function() { done(); }); }); -}); +}); \ No newline at end of file diff --git a/test/surface_test.js b/test/surface_test.js index 34e4ab40..e6a63b1e 100644 --- a/test/surface_test.js +++ b/test/surface_test.js @@ -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 @@ -125,4 +125,4 @@ describe('Cancelling surface client', function() { }); call.cancel(); }); -}); +}); \ No newline at end of file From da0983d56aa55a6532ccd7dcb22ee233c4e487e5 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 18 Feb 2015 09:23:38 -0800 Subject: [PATCH 39/91] Add missing new-lines at end of file --- ext/byte_buffer.cc | 2 +- ext/byte_buffer.h | 2 +- ext/call.h | 2 +- ext/channel.cc | 2 +- ext/channel.h | 2 +- ext/completion_queue_async_worker.cc | 2 +- ext/completion_queue_async_worker.h | 2 +- ext/credentials.cc | 2 +- ext/credentials.h | 2 +- ext/node_grpc.cc | 2 +- ext/server.cc | 2 +- ext/server.h | 2 +- ext/server_credentials.cc | 2 +- ext/server_credentials.h | 2 +- ext/timeval.cc | 2 +- ext/timeval.h | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ext/byte_buffer.cc b/ext/byte_buffer.cc index 8180c273..c165d26e 100644 --- a/ext/byte_buffer.cc +++ b/ext/byte_buffer.cc @@ -92,4 +92,4 @@ Handle MakeFastBuffer(Handle slowBuffer) { return NanEscapeScope(fastBuffer); } } // namespace node -} // namespace grpc \ No newline at end of file +} // namespace grpc diff --git a/ext/byte_buffer.h b/ext/byte_buffer.h index 52fee70a..5083674d 100644 --- a/ext/byte_buffer.h +++ b/ext/byte_buffer.h @@ -57,4 +57,4 @@ v8::Handle MakeFastBuffer(v8::Handle slowBuffer); } // namespace node } // namespace grpc -#endif // NET_GRPC_NODE_BYTE_BUFFER_H_ \ No newline at end of file +#endif // NET_GRPC_NODE_BYTE_BUFFER_H_ diff --git a/ext/call.h b/ext/call.h index e93349d6..933541ce 100644 --- a/ext/call.h +++ b/ext/call.h @@ -128,4 +128,4 @@ class Call : public ::node::ObjectWrap { } // namespace node } // namespace grpc -#endif // NET_GRPC_NODE_CALL_H_ \ No newline at end of file +#endif // NET_GRPC_NODE_CALL_H_ diff --git a/ext/channel.cc b/ext/channel.cc index edeebe97..6c7a89e5 100644 --- a/ext/channel.cc +++ b/ext/channel.cc @@ -179,4 +179,4 @@ NAN_METHOD(Channel::Close) { } } // namespace node -} // namespace grpc \ No newline at end of file +} // namespace grpc diff --git a/ext/channel.h b/ext/channel.h index 3c059771..bf793194 100644 --- a/ext/channel.h +++ b/ext/channel.h @@ -76,4 +76,4 @@ class Channel : public ::node::ObjectWrap { } // namespace node } // namespace grpc -#endif // NET_GRPC_NODE_CHANNEL_H_ \ No newline at end of file +#endif // NET_GRPC_NODE_CHANNEL_H_ diff --git a/ext/completion_queue_async_worker.cc b/ext/completion_queue_async_worker.cc index bc5896b5..ca22527e 100644 --- a/ext/completion_queue_async_worker.cc +++ b/ext/completion_queue_async_worker.cc @@ -101,4 +101,4 @@ void CompletionQueueAsyncWorker::HandleErrorCallback() { } } // namespace node -} // namespace grpc \ No newline at end of file +} // namespace grpc diff --git a/ext/completion_queue_async_worker.h b/ext/completion_queue_async_worker.h index 1c02a345..0ddb5b4c 100644 --- a/ext/completion_queue_async_worker.h +++ b/ext/completion_queue_async_worker.h @@ -78,4 +78,4 @@ class CompletionQueueAsyncWorker : public NanAsyncWorker { } // namespace node } // namespace grpc -#endif // NET_GRPC_NODE_COMPLETION_QUEUE_ASYNC_WORKER_H_ \ No newline at end of file +#endif // NET_GRPC_NODE_COMPLETION_QUEUE_ASYNC_WORKER_H_ diff --git a/ext/credentials.cc b/ext/credentials.cc index cb1e8a79..4b95c72b 100644 --- a/ext/credentials.cc +++ b/ext/credentials.cc @@ -200,4 +200,4 @@ NAN_METHOD(Credentials::CreateIam) { } } // namespace node -} // namespace grpc \ No newline at end of file +} // namespace grpc diff --git a/ext/credentials.h b/ext/credentials.h index 576a5dbd..e60be3d4 100644 --- a/ext/credentials.h +++ b/ext/credentials.h @@ -78,4 +78,4 @@ class Credentials : public ::node::ObjectWrap { } // namespace node } // namespace grpc -#endif // NET_GRPC_NODE_CREDENTIALS_H_ \ No newline at end of file +#endif // NET_GRPC_NODE_CREDENTIALS_H_ diff --git a/ext/node_grpc.cc b/ext/node_grpc.cc index 4e1553fe..965186e0 100644 --- a/ext/node_grpc.cc +++ b/ext/node_grpc.cc @@ -175,4 +175,4 @@ void init(Handle exports) { grpc::node::ServerCredentials::Init(exports); } -NODE_MODULE(grpc, init) \ No newline at end of file +NODE_MODULE(grpc, init) diff --git a/ext/server.cc b/ext/server.cc index c2e85df5..ab45da8d 100644 --- a/ext/server.cc +++ b/ext/server.cc @@ -286,4 +286,4 @@ NAN_METHOD(Server::Shutdown) { } } // namespace node -} // namespace grpc \ No newline at end of file +} // namespace grpc diff --git a/ext/server.h b/ext/server.h index e4bb4d88..2056fe7d 100644 --- a/ext/server.h +++ b/ext/server.h @@ -76,4 +76,4 @@ class Server : public ::node::ObjectWrap { } // namespace node } // namespace grpc -#endif // NET_GRPC_NODE_SERVER_H_ \ No newline at end of file +#endif // NET_GRPC_NODE_SERVER_H_ diff --git a/ext/server_credentials.cc b/ext/server_credentials.cc index 9c9a1b38..f75a2bf7 100644 --- a/ext/server_credentials.cc +++ b/ext/server_credentials.cc @@ -151,4 +151,4 @@ NAN_METHOD(ServerCredentials::CreateFake) { } } // namespace node -} // namespace grpc \ No newline at end of file +} // namespace grpc diff --git a/ext/server_credentials.h b/ext/server_credentials.h index 7c916e77..f0990242 100644 --- a/ext/server_credentials.h +++ b/ext/server_credentials.h @@ -74,4 +74,4 @@ class ServerCredentials : public ::node::ObjectWrap { } // namespace node } // namespace grpc -#endif // NET_GRPC_NODE_SERVER_CREDENTIALS_H_ \ No newline at end of file +#endif // NET_GRPC_NODE_SERVER_CREDENTIALS_H_ diff --git a/ext/timeval.cc b/ext/timeval.cc index 5cece4a9..bc3237f7 100644 --- a/ext/timeval.cc +++ b/ext/timeval.cc @@ -62,4 +62,4 @@ double TimespecToMilliseconds(gpr_timespec timespec) { } } // namespace node -} // namespace grpc \ No newline at end of file +} // namespace grpc diff --git a/ext/timeval.h b/ext/timeval.h index a85949f2..0cada5ac 100644 --- a/ext/timeval.h +++ b/ext/timeval.h @@ -45,4 +45,4 @@ gpr_timespec MillisecondsToTimespec(double millis); } // namespace node } // namespace grpc -#endif // NET_GRPC_NODE_TIMEVAL_H_ \ No newline at end of file +#endif // NET_GRPC_NODE_TIMEVAL_H_ From b56ee7c87142fa383d03dbd5089346498b8f8bbe Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 18 Feb 2015 09:25:21 -0800 Subject: [PATCH 40/91] Add missing new-lines at end of file --- examples/math.proto | 6 +++--- examples/math_server.js | 2 +- examples/stock.proto | 2 +- index.js | 2 +- interop/empty.proto | 6 +++--- interop/interop_client.js | 2 +- interop/interop_server.js | 2 +- interop/messages.proto | 6 +++--- interop/test.proto | 6 +++--- src/common.js | 2 +- test/channel_test.js | 2 +- test/constant_test.js | 2 +- test/end_to_end_test.js | 2 +- test/interop_sanity_test.js | 2 +- test/math_client_test.js | 2 +- test/surface_test.js | 2 +- 16 files changed, 24 insertions(+), 24 deletions(-) diff --git a/examples/math.proto b/examples/math.proto index 2cf6a036..e34ad5e9 100644 --- a/examples/math.proto +++ b/examples/math.proto @@ -1,11 +1,11 @@ // Copyright 2015, Google Inc. // All rights reserved. -// +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: -// +// // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above @@ -15,7 +15,7 @@ // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. -// +// // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR diff --git a/examples/math_server.js b/examples/math_server.js index 42728d0b..89bc0de3 100644 --- a/examples/math_server.js +++ b/examples/math_server.js @@ -135,4 +135,4 @@ if (require.main === module) { /** * See docs for server */ -module.exports = server; \ No newline at end of file +module.exports = server; diff --git a/examples/stock.proto b/examples/stock.proto index 2bc5c29d..328e050a 100644 --- a/examples/stock.proto +++ b/examples/stock.proto @@ -59,4 +59,4 @@ service Stock { rpc GetHighestTradePrice(stream StockRequest) returns (StockReply) { } -} \ No newline at end of file +} diff --git a/index.js b/index.js index 167be3a7..fe1fb1d3 100644 --- a/index.js +++ b/index.js @@ -105,4 +105,4 @@ exports.Credentials = grpc.Credentials; /** * ServerCredentials factories */ -exports.ServerCredentials = grpc.ServerCredentials; \ No newline at end of file +exports.ServerCredentials = grpc.ServerCredentials; diff --git a/interop/empty.proto b/interop/empty.proto index 98fc3a39..f66a108c 100644 --- a/interop/empty.proto +++ b/interop/empty.proto @@ -1,11 +1,11 @@ // Copyright 2015, Google Inc. // All rights reserved. -// +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: -// +// // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above @@ -15,7 +15,7 @@ // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. -// +// // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR diff --git a/interop/interop_client.js b/interop/interop_client.js index 4efc9667..d00724b2 100644 --- a/interop/interop_client.js +++ b/interop/interop_client.js @@ -310,4 +310,4 @@ if (require.main === module) { /** * See docs for runTest */ -exports.runTest = runTest; \ No newline at end of file +exports.runTest = runTest; diff --git a/interop/interop_server.js b/interop/interop_server.js index 2c9cf04c..c97d2344 100644 --- a/interop/interop_server.js +++ b/interop/interop_server.js @@ -201,4 +201,4 @@ if (require.main === module) { /** * See docs for getServer */ -exports.getServer = getServer; \ No newline at end of file +exports.getServer = getServer; diff --git a/interop/messages.proto b/interop/messages.proto index f53d99ab..eb652646 100644 --- a/interop/messages.proto +++ b/interop/messages.proto @@ -1,11 +1,11 @@ // Copyright 2015, Google Inc. // All rights reserved. -// +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: -// +// // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above @@ -15,7 +15,7 @@ // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. -// +// // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR diff --git a/interop/test.proto b/interop/test.proto index c2437630..927a3a83 100644 --- a/interop/test.proto +++ b/interop/test.proto @@ -1,11 +1,11 @@ // Copyright 2015, Google Inc. // All rights reserved. -// +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: -// +// // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above @@ -15,7 +15,7 @@ // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. -// +// // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR diff --git a/src/common.js b/src/common.js index c5b83623..848c9674 100644 --- a/src/common.js +++ b/src/common.js @@ -125,4 +125,4 @@ exports.fullyQualifiedName = fullyQualifiedName; /** * See docs for wrapIgnoreNull */ -exports.wrapIgnoreNull = wrapIgnoreNull; \ No newline at end of file +exports.wrapIgnoreNull = wrapIgnoreNull; diff --git a/test/channel_test.js b/test/channel_test.js index 77708d16..449a8cc4 100644 --- a/test/channel_test.js +++ b/test/channel_test.js @@ -85,4 +85,4 @@ describe('channel', function() { }); }); }); -}); \ No newline at end of file +}); diff --git a/test/constant_test.js b/test/constant_test.js index 0affa403..4a403868 100644 --- a/test/constant_test.js +++ b/test/constant_test.js @@ -89,4 +89,4 @@ describe('constants', function() { 'call error missing: ' + callErrorNames[i]); } }); -}); \ No newline at end of file +}); diff --git a/test/end_to_end_test.js b/test/end_to_end_test.js index 4fd6d8d2..8e99d6f1 100644 --- a/test/end_to_end_test.js +++ b/test/end_to_end_test.js @@ -233,4 +233,4 @@ describe('end-to-end', function() { }); }); }); -}); \ No newline at end of file +}); diff --git a/test/interop_sanity_test.js b/test/interop_sanity_test.js index 92e87b5d..16def1fa 100644 --- a/test/interop_sanity_test.js +++ b/test/interop_sanity_test.js @@ -79,4 +79,4 @@ describe('Interop tests', function() { interop_client.runTest(port, name_override, 'cancel_after_first_response', true, done); }); -}); \ No newline at end of file +}); diff --git a/test/math_client_test.js b/test/math_client_test.js index 97b95377..fd946e03 100644 --- a/test/math_client_test.js +++ b/test/math_client_test.js @@ -113,4 +113,4 @@ describe('Math client', function() { done(); }); }); -}); \ No newline at end of file +}); diff --git a/test/surface_test.js b/test/surface_test.js index e6a63b1e..d6694724 100644 --- a/test/surface_test.js +++ b/test/surface_test.js @@ -125,4 +125,4 @@ describe('Cancelling surface client', function() { }); call.cancel(); }); -}); \ No newline at end of file +}); From 924cd36e18587a16ae0033ba42d0544ca5fb9382 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 18 Feb 2015 10:21:57 -0800 Subject: [PATCH 41/91] Added interop support for default root SSL certs --- interop/interop_client.js | 14 +++++++++++--- test/interop_sanity_test.js | 21 +++++++++++++-------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/interop/interop_client.js b/interop/interop_client.js index 8737af6c..00284d08 100644 --- a/interop/interop_client.js +++ b/interop/interop_client.js @@ -280,11 +280,16 @@ var test_cases = { * @param {function} done Callback to call when the test is completed. Included * primarily for use with mocha */ -function runTest(address, host_override, test_case, tls, done) { +function runTest(address, host_override, test_case, tls, test_ca, done) { // TODO(mlumish): enable TLS functionality var options = {}; if (tls) { - var ca_path = path.join(__dirname, '../test/data/ca.pem'); + var ca_path; + if (test_ca) { + ca_path = path.join(__dirname, '../test/data/ca.pem'); + } else { + ca_path = process.env.SSL_CERT_FILE; + } var ca_data = fs.readFileSync(ca_path); var creds = grpc.Credentials.createSsl(ca_data); options.credentials = creds; @@ -304,7 +309,10 @@ if (require.main === module) { 'use_tls', 'use_test_ca'] }); runTest(argv.server_host + ':' + argv.server_port, argv.server_host_override, - argv.test_case, argv.use_tls === 'true'); + argv.test_case, argv.use_tls === 'true', argv.use_test_ca === 'true', + function () { + console.log('OK:', argv.test_case); + }); } /** diff --git a/test/interop_sanity_test.js b/test/interop_sanity_test.js index 81cd9fa5..070a02e0 100644 --- a/test/interop_sanity_test.js +++ b/test/interop_sanity_test.js @@ -53,30 +53,35 @@ describe('Interop tests', function() { }); // This depends on not using a binary stream it('should pass empty_unary', function(done) { - interop_client.runTest(port, name_override, 'empty_unary', true, done); + interop_client.runTest(port, name_override, 'empty_unary', true, true, + done); }); // This fails due to an unknown bug it('should pass large_unary', function(done) { - interop_client.runTest(port, name_override, 'large_unary', true, done); + interop_client.runTest(port, name_override, 'large_unary', true, true, + done); }); it('should pass client_streaming', function(done) { - interop_client.runTest(port, name_override, 'client_streaming', true, done); + interop_client.runTest(port, name_override, 'client_streaming', true, true, + done); }); it('should pass server_streaming', function(done) { - interop_client.runTest(port, name_override, 'server_streaming', true, done); + interop_client.runTest(port, name_override, 'server_streaming', true, true, + done); }); it('should pass ping_pong', function(done) { - interop_client.runTest(port, name_override, 'ping_pong', true, done); + interop_client.runTest(port, name_override, 'ping_pong', true, true, done); }); it('should pass empty_stream', function(done) { - interop_client.runTest(port, name_override, 'empty_stream', true, done); + interop_client.runTest(port, name_override, 'empty_stream', true, true, + done); }); it('should pass cancel_after_begin', function(done) { interop_client.runTest(port, name_override, 'cancel_after_begin', true, - done); + true, done); }); it('should pass cancel_after_first_response', function(done) { interop_client.runTest(port, name_override, 'cancel_after_first_response', - true, done); + true, true, done); }); }); From 5ab7deb5e4fd43823c02fbc20133ded3cf447812 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 18 Feb 2015 15:00:56 -0800 Subject: [PATCH 42/91] Added auth functionality and interop tests --- index.js | 32 +++++++ interop/interop_client.js | 48 +++++++++- interop/messages.proto | 10 +- package.json | 3 +- src/client.js | 193 +++++++++++++++++++++++--------------- 5 files changed, 206 insertions(+), 80 deletions(-) diff --git a/index.js b/index.js index baef4d03..8b5b1ea9 100644 --- a/index.js +++ b/index.js @@ -73,6 +73,36 @@ function load(filename) { return loadObject(builder.ns); } +/** + * Get a function that a client can use to update metadata with authentication + * information from a Google Auth credential object. + * @param {Object} credential The credential object to use + * @return {function(Object, callback)} Metadata updater function + */ +function getGoogleAuthDelegate(credential) { + /** + * Update a metadata object with authentication information. + * @param {Object} metadata Metadata object + * @param {function(Error, Object)} callback + */ + return function updateMetadata(metadata, callback) { + metadata = _.clone(metadata); + if (metadata.Authorization) { + metadata.Authorization = _.clone(metadata.Authorization); + } else { + metadata.Authorization = []; + } + credential.getAccessToken(function(err, token) { + if (err) { + callback(err); + return; + } + metadata.Authorization.push('Bearer ' + token); + callback(null, metadata); + }); + }; +} + /** * See docs for loadObject */ @@ -106,3 +136,5 @@ exports.Credentials = grpc.Credentials; * ServerCredentials factories */ exports.ServerCredentials = grpc.ServerCredentials; + +exports.getGoogleAuthDelegate = getGoogleAuthDelegate; diff --git a/interop/interop_client.js b/interop/interop_client.js index 00284d08..9a19a509 100644 --- a/interop/interop_client.js +++ b/interop/interop_client.js @@ -35,9 +35,14 @@ var fs = require('fs'); var path = require('path'); var grpc = require('..'); var testProto = grpc.load(__dirname + '/test.proto').grpc.testing; +var GoogleAuth = require('googleauth'); var assert = require('assert'); +var AUTH_SCOPE = 'https://www.googleapis.com/auth/xapi.zoo'; +var AUTH_SCOPE_RESPONSE = 'xapi.zoo'; +var AUTH_USER = '155450119199-3psnrh1sdr3d8cpj1v46naggf81mhdnk@developer.gserviceaccount.com'; + /** * Create a buffer filled with size zeroes * @param {number} size The length of the buffer @@ -255,6 +260,45 @@ function cancelAfterFirstResponse(client, done) { }); } +/** + * Run one of the authentication tests. + * @param {Client} client The client to test against + * @param {function} done Callback to call when the test is completed. Included + * primarily for use with mocha + */ +function authTest(client, done) { + (new GoogleAuth()).getApplicationDefault(function(err, credential) { + assert.ifError(err); + if (credential.createScopedRequired()) { + credential = credential.createScoped(AUTH_SCOPE); + } + client.updateMetadata = grpc.getGoogleAuthDelegate(credential); + var arg = { + response_type: testProto.PayloadType.COMPRESSABLE, + response_size: 314159, + payload: { + body: zeroBuffer(271828) + }, + fill_username: true, + fill_oauth_scope: true + }; + var call = client.unaryCall(arg, function(err, resp) { + assert.ifError(err); + assert.strictEqual(resp.payload.type, testProto.PayloadType.COMPRESSABLE); + assert.strictEqual(resp.payload.body.limit - resp.payload.body.offset, + 314159); + assert.strictEqual(resp.username, AUTH_USER); + assert.strictEqual(resp.oauth_scope, AUTH_SCOPE_RESPONSE); + }); + call.on('status', function(status) { + assert.strictEqual(status.code, grpc.status.OK); + if (done) { + done(); + } + }); + }); +} + /** * Map from test case names to test functions */ @@ -266,7 +310,9 @@ var test_cases = { ping_pong: pingPong, empty_stream: emptyStream, cancel_after_begin: cancelAfterBegin, - cancel_after_first_response: cancelAfterFirstResponse + cancel_after_first_response: cancelAfterFirstResponse, + compute_engine_creds: authTest, + service_account_creds: authTest }; /** diff --git a/interop/messages.proto b/interop/messages.proto index 29db0dd8..1d95154c 100644 --- a/interop/messages.proto +++ b/interop/messages.proto @@ -36,6 +36,12 @@ message SimpleRequest { // Optional input payload sent along with the request. optional Payload payload = 3; + + // Whether SimpleResponse should include username. + optional bool fill_username = 4; + + // Whether SimpleResponse should include OAuth scope. + optional bool fill_oauth_scope = 5; } // Unary response, as configured by the request. @@ -44,7 +50,9 @@ message SimpleResponse { optional Payload payload = 1; // The user the request came from, for verifying authentication was // successful when the client expected it. - optional int64 effective_gaia_user_id = 2; + optional string username = 2; + // OAuth scope. + optional string oauth_scope = 3; } // Client-streaming request. diff --git a/package.json b/package.json index 8f81014c..821641ce 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ }, "devDependencies": { "mocha": "~1.21.0", - "minimist": "^1.1.0" + "minimist": "^1.1.0", + "googleauth": "google/google-auth-library-nodejs" }, "main": "index.js" } diff --git a/src/client.js b/src/client.js index 81fa65eb..19c3144c 100644 --- a/src/client.js +++ b/src/client.js @@ -224,25 +224,32 @@ function makeUnaryRequestFunction(method, serialize, deserialize) { emitter.cancel = function cancel() { call.cancel(); }; - var client_batch = {}; - client_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; - client_batch[grpc.opType.SEND_MESSAGE] = serialize(argument); - client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; - client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; - client_batch[grpc.opType.RECV_MESSAGE] = true; - client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; - call.startBatch(client_batch, function(err, response) { - if (err) { - callback(err); + this.updateMetadata(metadata, function(error, metadata) { + if (error) { + call.cancel(); + callback(error); return; } - if (response.status.code != grpc.status.OK) { - callback(response.status); - return; - } - emitter.emit('status', response.status); - emitter.emit('metadata', response.metadata); - callback(null, deserialize(response.read)); + var client_batch = {}; + client_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + client_batch[grpc.opType.SEND_MESSAGE] = serialize(argument); + client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + client_batch[grpc.opType.RECV_MESSAGE] = true; + client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(client_batch, function(err, response) { + if (err) { + callback(err); + return; + } + if (response.status.code != grpc.status.OK) { + callback(response.status); + return; + } + emitter.emit('status', response.status); + emitter.emit('metadata', response.metadata); + callback(null, deserialize(response.read)); + }); }); return emitter; } @@ -279,30 +286,37 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) { metadata = {}; } var stream = new ClientWritableStream(call, serialize); - var metadata_batch = {}; - metadata_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; - metadata_batch[grpc.opType.RECV_INITIAL_METADATA] = true; - call.startBatch(metadata_batch, function(err, response) { - if (err) { - callback(err); + this.updateMetadata(metadata, function(error, metadata) { + if (error) { + call.cancel(); + callback(error); return; } - stream.emit('metadata', response.metadata); - }); - var client_batch = {}; - client_batch[grpc.opType.RECV_MESSAGE] = true; - client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; - call.startBatch(client_batch, function(err, response) { - if (err) { - callback(err); - return; - } - if (response.status.code != grpc.status.OK) { - callback(response.status); - return; - } - stream.emit('status', response.status); - callback(null, deserialize(response.read)); + var metadata_batch = {}; + metadata_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + metadata_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + call.startBatch(metadata_batch, function(err, response) { + if (err) { + callback(err); + return; + } + stream.emit('metadata', response.metadata); + }); + var client_batch = {}; + client_batch[grpc.opType.RECV_MESSAGE] = true; + client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(client_batch, function(err, response) { + if (err) { + callback(err); + return; + } + if (response.status.code != grpc.status.OK) { + callback(response.status); + return; + } + stream.emit('status', response.status); + callback(null, deserialize(response.read)); + }); }); return stream; } @@ -339,24 +353,31 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) { metadata = {}; } var stream = new ClientReadableStream(call, deserialize); - var start_batch = {}; - start_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; - start_batch[grpc.opType.RECV_INITIAL_METADATA] = true; - start_batch[grpc.opType.SEND_MESSAGE] = serialize(argument); - start_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; - call.startBatch(start_batch, function(err, response) { - if (err) { - throw err; + this.updateMetadata(metadata, function(error, metadata) { + if (error) { + call.cancel(); + stream.emit('error', error); + return; } - stream.emit('metadata', response.metadata); - }); - var status_batch = {}; - status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; - call.startBatch(status_batch, function(err, response) { - if (err) { - throw err; - } - stream.emit('status', response.status); + var start_batch = {}; + start_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + start_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + start_batch[grpc.opType.SEND_MESSAGE] = serialize(argument); + start_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + call.startBatch(start_batch, function(err, response) { + if (err) { + throw err; + } + stream.emit('metadata', response.metadata); + }); + var status_batch = {}; + status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(status_batch, function(err, response) { + if (err) { + throw err; + } + stream.emit('status', response.status); + }); }); return stream; } @@ -391,22 +412,29 @@ function makeBidiStreamRequestFunction(method, serialize, deserialize) { metadata = {}; } var stream = new ClientDuplexStream(call, serialize, deserialize); - var start_batch = {}; - start_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; - start_batch[grpc.opType.RECV_INITIAL_METADATA] = true; - call.startBatch(start_batch, function(err, response) { - if (err) { - throw err; + this.updateMetadata(metadata, function(error, metadata) { + if (error) { + call.cancel(); + stream.emit('error', error); + return; } - stream.emit('metadata', response.metadata); - }); - var status_batch = {}; - status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; - call.startBatch(status_batch, function(err, response) { - if (err) { - throw err; - } - stream.emit('status', response.status); + var start_batch = {}; + start_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + start_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + call.startBatch(start_batch, function(err, response) { + if (err) { + throw err; + } + stream.emit('metadata', response.metadata); + }); + var status_batch = {}; + status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(status_batch, function(err, response) { + if (err) { + throw err; + } + stream.emit('status', response.status); + }); }); return stream; } @@ -438,8 +466,17 @@ function makeClientConstructor(service) { * @constructor * @param {string} address The address of the server to connect to * @param {Object} options Options to pass to the underlying channel + * @param {function(Object, function)=} updateMetadata function to update the + * metadata for each request */ - function Client(address, options) { + function Client(address, options, updateMetadata) { + if (updateMetadata) { + this.updateMetadata = updateMetadata; + } else { + this.updateMetadata = function(metadata, callback) { + callback(null, metadata); + }; + } this.channel = new grpc.Channel(address, options); } @@ -458,11 +495,13 @@ function makeClientConstructor(service) { method_type = 'unary'; } } - Client.prototype[decapitalize(method.name)] = - requester_makers[method_type]( - prefix + capitalize(method.name), - common.serializeCls(method.resolvedRequestType.build()), - common.deserializeCls(method.resolvedResponseType.build())); + var serialize = common.serializeCls(method.resolvedRequestType.build()); + var deserialize = common.deserializeCls( + method.resolvedResponseType.build()); + Client.prototype[decapitalize(method.name)] = requester_makers[method_type]( + prefix + capitalize(method.name), serialize, deserialize); + Client.prototype[decapitalize(method.name)].serialize = serialize; + Client.prototype[decapitalize(method.name)].deserialize = deserialize; }); Client.service = service; From ef1bcc4320983f8644b6176de676dc994f197230 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 18 Feb 2015 17:03:03 -0800 Subject: [PATCH 43/91] Added route_guide server example implementation --- examples/route_guide.proto | 120 +++++++++++++++++++++ examples/route_guide_server.js | 186 +++++++++++++++++++++++++++++++++ 2 files changed, 306 insertions(+) create mode 100644 examples/route_guide.proto create mode 100644 examples/route_guide_server.js diff --git a/examples/route_guide.proto b/examples/route_guide.proto new file mode 100644 index 00000000..b648a058 --- /dev/null +++ b/examples/route_guide.proto @@ -0,0 +1,120 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +option java_package = "ex.grpc"; + +package examples; + +// Interface exported by the server. +service RouteGuide { + // A simple RPC. + // + // Obtains the feature at a given position. + rpc GetFeature(Point) returns (Feature) {} + + // A server-to-client streaming RPC. + // + // Obtains the Features available within the given Rectangle. Results are + // streamed rather than returned at once (e.g. in a response message with a + // repeated field), as the rectangle may cover a large area and contain a + // huge number of features. + rpc ListFeatures(Rectangle) returns (stream Feature) {} + + // A client-to-server streaming RPC. + // + // Accepts a stream of Points on a route being traversed, returning a + // RouteSummary when traversal is completed. + rpc RecordRoute(stream Point) returns (RouteSummary) {} + + // A Bidirectional streaming RPC. + // + // Accepts a stream of RouteNotes sent while a route is being traversed, + // while receiving other RouteNotes (e.g. from other users). + rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} +} + +// Points are represented as latitude-longitude pairs in the E7 representation +// (degrees multiplied by 10**7 and rounded to the nearest integer). +// Latitudes should be in the range +/- 90 degrees and longitude should be in +// the range +/- 180 degrees (inclusive). +message Point { + optional int32 latitude = 1; + optional int32 longitude = 2; +} + +// A latitude-longitude rectangle, represented as two diagonally opposite +// points "lo" and "hi". +message Rectangle { + // One corner of the rectangle. + optional Point lo = 1; + + // The other corner of the rectangle. + optional Point hi = 2; +} + +// A feature names something at a given point. +// +// If a feature could not be named, the name is empty. +message Feature { + // The name of the feature. + optional string name = 1; + + // The point where the feature is detected. + optional Point location = 2; +} + +// A RouteNote is a message sent while at a given point. +message RouteNote { + // The location from which the message is sent. + optional Point location = 1; + + // The message to be sent. + optional string message = 2; +} + +// A RouteSummary is received in response to a RecordRoute rpc. +// +// It contains the number of individual points received, the number of +// detected features, and the total distance covered as the cumulative sum of +// the distance between each point. +message RouteSummary { + // The number of points received. + optional int32 point_count = 1; + + // The number of known features passed while traversing the route. + optional int32 feature_count = 2; + + // The distance covered in metres. + optional int32 distance = 3; + + // The duration of the traversal in seconds. + optional int32 elapsed_time = 4; +} \ No newline at end of file diff --git a/examples/route_guide_server.js b/examples/route_guide_server.js new file mode 100644 index 00000000..3df90e91 --- /dev/null +++ b/examples/route_guide_server.js @@ -0,0 +1,186 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +var _ = require('underscore'); +var grpc = require('..'); +var examples = grpc.load(__dirname + '/route_guide.proto').examples; + +var Server = grpc.buildServer([examples.RouteGuide.service]); + +var COORD_FACTOR = 1e7; + +var feature_list = []; + +function randomWord(length) { + var alphabet = 'abcdefghijklmnopqrstuvwxyz'; + var word = ''; + for (var i = 0; i < length; i++) { + word += alphabet[_.random(0, alphabet.length - 1)]; + } + return word; +} + +function checkFeature(point) { + var feature; + for (var i = 0; i < feature_list.length; i++) { + feature = feature_list[i]; + if (feature.point.latitude === point.latitude && + feature.point.longitude === point.longitude) { + return feature; + } + } + var name; + if (_.random(0,1) === 0) { + name = ''; + } else { + name = randomWord(5); + } + feature = { + name: name, + location: point + }; + feature_list.push(feature); + return feature; +} + +function getFeature(call, callback) { + callback(null, checkFeature(call.request)); +} + +function listFeatures(call) { + var lo = call.request.lo; + var hi = call.request.hi; + var left = _.min(lo.longitude, hi.longitude); + var right = _.max(lo.longitude, hi.longitude); + var top = _.max(lo.latitude, hi.latitude); + var bottom = _.max(lo.latitude, hi.latitude); + _.each(feature_list, function(feature) { + if (feature.location.longitude >= left && + feature.location.longitude <= right && + feature.location.latitude >= bottom && + feature.location.latitude <= top) { + call.write(feature); + } + }); + call.end(); +} + +/** + * Calculate the distance between two points using the "haversine" formula. + * This code was taken from http://www.movable-type.co.uk/scripts/latlong.html. + * @param start The starting point + * @param end The end point + * @return The distance between the points in meters + */ +function getDistance(start, end) { + var lat1 = start.latitude / COORD_FACTOR; + var lat2 = end.latitude / COORD_FACTOR; + var lon1 = start.longitude / COORD_FACTOR; + var lon2 = end.longitude / COORD_FACTOR; + var R = 6371000; // metres + var φ1 = lat1.toRadians(); + var φ2 = lat2.toRadians(); + var Δφ = (lat2-lat1).toRadians(); + var Δλ = (lon2-lon1).toRadians(); + + var a = Math.sin(Δφ/2) * Math.sin(Δφ/2) + + Math.cos(φ1) * Math.cos(φ2) * + Math.sin(Δλ/2) * Math.sin(Δλ/2); + var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); + + return R * c; +} + +function recordRoute(call, callback) { + var point_count = 0; + var feature_count = 0; + var distance = 0; + var previous = null; + var start_time = process.hrtime(); + call.on('data', function(point) { + point_count += 1; + if (checkFeature(point) !== '') { + feature_count += 1; + } + if (previous != null) { + distance += getDistance(previous, point); + } + previous = point; + }); + call.on('end', function() { + callback(null, { + point_count: point_count, + feature_count: feature_count, + distance: distance|0, + elapsed_time: process.hrtime(start_time)[0] + }); + }); +} + +var route_notes = {}; + +function pointKey(point) { + return point.latitude + ' ' + point.longitude; +} + +function routeChat(call, callback) { + call.on('data', function(note) { + var key = pointKey(note.location); + if (route_notes.hasOwnProperty(key)) { + _.each(route_notes[key], function(note) { + call.write(note); + }); + } else { + route_notes[key] = []; + } + route_notes[key].push(note); + }); + call.on('end', function() { + call.end(); + }); +} + +function getServer() { + return new Server({ + 'examples.RouteGuide' : { + getFeature: getFeature, + listFeatures: listFeatures, + recordRoute: recordRoute, + routeChat: routeChat + } + }); +} + +if (require.main === module) { + var routeServer = getServer(); + routeServer.bind('0.0.0.0:0'); + routeServer.listen(); +} + +exports.getServer = getServer; From 084fa910d07cef6ef70be7da60e6f1c978dc4eef Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 18 Feb 2015 17:06:34 -0800 Subject: [PATCH 44/91] Added comment about where Google credentials come from --- index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index f7cbdf00..1bef2072 100644 --- a/index.js +++ b/index.js @@ -75,7 +75,8 @@ function load(filename) { /** * Get a function that a client can use to update metadata with authentication - * information from a Google Auth credential object. + * information from a Google Auth credential object, which comes from the + * googleauth library. * @param {Object} credential The credential object to use * @return {function(Object, callback)} Metadata updater function */ From 88ad8fc6110d6a48302c2dcb3772515c513281eb Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 19 Feb 2015 10:28:41 -0800 Subject: [PATCH 45/91] Added comments and fixed some minor bugs --- examples/route_guide.proto | 2 +- examples/route_guide_server.js | 77 +++++++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/examples/route_guide.proto b/examples/route_guide.proto index b648a058..4c7be175 100644 --- a/examples/route_guide.proto +++ b/examples/route_guide.proto @@ -117,4 +117,4 @@ message RouteSummary { // The duration of the traversal in seconds. optional int32 elapsed_time = 4; -} \ No newline at end of file +} diff --git a/examples/route_guide_server.js b/examples/route_guide_server.js index 3df90e91..b21190d6 100644 --- a/examples/route_guide_server.js +++ b/examples/route_guide_server.js @@ -35,19 +35,42 @@ var Server = grpc.buildServer([examples.RouteGuide.service]); var COORD_FACTOR = 1e7; +/** + * For simplicity, a point is a record type that looks like + * {latitude: number, longitude: number}, and a feature is a record type that + * looks like {name: string, location: point}. feature objects with name==='' are + * points with no feature. + */ + +/** + * List of feature objects at points that have been requested so far. + */ var feature_list = []; +/** + * Return a random "word" (alphabetic character sequence) of the given length. + * @param {number} length The length of the word to create + * @return {string} An alphabetic string with the given length. + */ function randomWord(length) { var alphabet = 'abcdefghijklmnopqrstuvwxyz'; var word = ''; for (var i = 0; i < length; i++) { + // Add a random character from the alphabet to the word word += alphabet[_.random(0, alphabet.length - 1)]; } return word; } +/** + * Get a feature object at the given point, or creates one if it does not exist. + * @param {point} point The point to check + * @return {feature} The feature object at the point. Note that an empty name + * indicates no feature + */ function checkFeature(point) { var feature; + // Check if there is already a feature object for the given point for (var i = 0; i < feature_list.length; i++) { feature = feature_list[i]; if (feature.point.latitude === point.latitude && @@ -55,6 +78,7 @@ function checkFeature(point) { return feature; } } + // If not, create a new one with 50% chance of indicating "no feature present" var name; if (_.random(0,1) === 0) { name = ''; @@ -65,14 +89,27 @@ function checkFeature(point) { name: name, location: point }; + // Add the feature object to the list and return it feature_list.push(feature); return feature; } +/** + * getFeature request handler. Gets a request with a point, and responds with a + * feature object indicating whether there is a feature at that point. + * @param {EventEmitter} call Call object for the handler to process + * @param {function(Error, feature)} callback Response callback + */ function getFeature(call, callback) { callback(null, checkFeature(call.request)); } +/** + * listFeatures request handler. Gets a request with two points, and responds + * with a stream of all features in the bounding box defined by those points. + * @param {Writable} call Writable stream for responses with an additional + * request property for the request value. + */ function listFeatures(call) { var lo = call.request.lo; var hi = call.request.hi; @@ -80,7 +117,11 @@ function listFeatures(call) { var right = _.max(lo.longitude, hi.longitude); var top = _.max(lo.latitude, hi.latitude); var bottom = _.max(lo.latitude, hi.latitude); + // For each feature, check if it is in the given bounding box _.each(feature_list, function(feature) { + if (feature.name === '') { + return; + } if (feature.location.longitude >= left && feature.location.longitude <= right && feature.location.latitude >= bottom && @@ -117,17 +158,28 @@ function getDistance(start, end) { return R * c; } +/** + * recordRoute handler. Gets a stream of points, and responds with statistics + * about the "trip": number of points, number of known features visited, total + * distance traveled, and total time spent. + * @param {Readable} call The request point stream. + * @param {function(Error, routeSummary)} callback The callback to pass the + * response to + */ function recordRoute(call, callback) { var point_count = 0; var feature_count = 0; var distance = 0; var previous = null; + // Start a timer var start_time = process.hrtime(); call.on('data', function(point) { point_count += 1; - if (checkFeature(point) !== '') { + if (checkFeature(point).name !== '') { feature_count += 1; } + /* For each point after the first, add the incremental distance from the + * previous point to the total distance value */ if (previous != null) { distance += getDistance(previous, point); } @@ -137,7 +189,9 @@ function recordRoute(call, callback) { callback(null, { point_count: point_count, feature_count: feature_count, + // Cast the distance to an integer distance: distance|0, + // End the timer elapsed_time: process.hrtime(start_time)[0] }); }); @@ -145,13 +199,25 @@ function recordRoute(call, callback) { var route_notes = {}; +/** + * Turn the point into a dictionary key. + * @param {point} point The point to use + * @return {string} The key for an object + */ function pointKey(point) { return point.latitude + ' ' + point.longitude; } -function routeChat(call, callback) { +/** + * routeChat handler. Receives a stream of message/location pairs, and responds + * with a stream of all previous messages at each of those locations. + * @param {Duplex} call The stream for incoming and outgoing messages + */ +function routeChat(call) { call.on('data', function(note) { var key = pointKey(note.location); + /* For each note sent, respond with all previous notes that correspond to + * the same point */ if (route_notes.hasOwnProperty(key)) { _.each(route_notes[key], function(note) { call.write(note); @@ -159,6 +225,7 @@ function routeChat(call, callback) { } else { route_notes[key] = []; } + // Then add the new note to the list route_notes[key].push(note); }); call.on('end', function() { @@ -166,6 +233,11 @@ function routeChat(call, callback) { }); } +/** + * Get a new server with the handler functions in this file bound to the methods + * it serves. + * @return {Server} The new server object + */ function getServer() { return new Server({ 'examples.RouteGuide' : { @@ -178,6 +250,7 @@ function getServer() { } if (require.main === module) { + // If this is run as a script, start a server on an unused port var routeServer = getServer(); routeServer.bind('0.0.0.0:0'); routeServer.listen(); From 60144224ae62a465eef16eb99f758bd4360ab544 Mon Sep 17 00:00:00 2001 From: Jayant Kolhe Date: Thu, 19 Feb 2015 11:28:57 -0800 Subject: [PATCH 46/91] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index c342b7ca..8880213e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Node.js gRPC Library +## Status + +Alpha : Ready for early adopters + ## Installation First, clone this repository (NPM package coming soon). Then follow the instructions in the `INSTALL` file in the root of the repository to install the C core library that this package depends on. From 66fa6c7b5db37c426ee48ce0e3ff89b6cf29ca7a Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 19 Feb 2015 13:36:56 -0800 Subject: [PATCH 47/91] Added files to the node package --- package.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/package.json b/package.json index 821641ce..642d80e2 100644 --- a/package.json +++ b/package.json @@ -17,5 +17,15 @@ "minimist": "^1.1.0", "googleauth": "google/google-auth-library-nodejs" }, + "files": [ + "README.md", + "index.js", + "binding.gyp", + "examples", + "ext", + "interop", + "src", + "test" + ], "main": "index.js" } From c16a9bdcee265a7afa624c3c44a1c2a1c03000ad Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 19 Feb 2015 13:46:30 -0800 Subject: [PATCH 48/91] Added lint script --- .jshintrc | 28 ++++++++++++++++++++++++++++ package.json | 2 ++ 2 files changed, 30 insertions(+) create mode 100644 .jshintrc diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 00000000..1d930c34 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,28 @@ +{ + "bitwise": true, + "curly": true, + "eqeqeq": true, + "esnext": true, + "freeze": true, + "immed": true, + "indent": 2, + "latedef": "nofunc", + "maxlen": 100, + "newcap": true, + "node": true, + "noarg": true, + "quotmark": "single", + "strict": true, + "trailing": true, + "undef": true, + "unused": true, + "globals": { + /* Mocha-provided globals */ + "describe": false, + "it": false, + "before": false, + "beforeEach": false, + "after": false, + "afterEach": false + } +} \ No newline at end of file diff --git a/package.json b/package.json index 821641ce..1d4c3f6e 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,12 @@ "version": "0.2.0", "description": "gRPC Library for Node", "scripts": { + "lint": "jshint src test examples interop index.js", "test": "./node_modules/mocha/bin/mocha" }, "dependencies": { "bindings": "^1.2.1", + "jshint": "^2.5.5", "nan": "~1.3.0", "protobufjs": "murgatroid99/ProtoBuf.js", "underscore": "^1.7.0", From 1b56bf85235bee7ff6bc1459cb1c808cb1aec8af Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 19 Feb 2015 14:37:18 -0800 Subject: [PATCH 49/91] Fixed lint errors --- .jshintrc | 6 +++--- examples/math_server.js | 5 ++--- examples/perf_test.js | 33 ++++++++++++++++++--------------- examples/stock_server.js | 2 ++ index.js | 2 ++ interop/interop_client.js | 7 +++++-- interop/interop_server.js | 2 ++ package.json | 2 +- src/client.js | 23 ++++++++++++++--------- src/common.js | 2 ++ src/server.js | 12 +++++++----- test/call_test.js | 2 ++ test/channel_test.js | 2 ++ test/constant_test.js | 2 ++ test/end_to_end_test.js | 4 +++- test/interop_sanity_test.js | 2 ++ test/math_client_test.js | 4 +++- test/surface_test.js | 4 ++-- 18 files changed, 74 insertions(+), 42 deletions(-) diff --git a/.jshintrc b/.jshintrc index 1d930c34..8237e0d2 100644 --- a/.jshintrc +++ b/.jshintrc @@ -7,7 +7,7 @@ "immed": true, "indent": 2, "latedef": "nofunc", - "maxlen": 100, + "maxlen": 80, "newcap": true, "node": true, "noarg": true, @@ -15,7 +15,7 @@ "strict": true, "trailing": true, "undef": true, - "unused": true, + "unused": "vars", "globals": { /* Mocha-provided globals */ "describe": false, @@ -25,4 +25,4 @@ "after": false, "afterEach": false } -} \ No newline at end of file +} diff --git a/examples/math_server.js b/examples/math_server.js index 89bc0de3..ae548c89 100644 --- a/examples/math_server.js +++ b/examples/math_server.js @@ -31,9 +31,8 @@ * */ -var _ = require('underscore'); -var ProtoBuf = require('protobufjs'); -var fs = require('fs'); +'use strict'; + var util = require('util'); var Transform = require('stream').Transform; diff --git a/examples/perf_test.js b/examples/perf_test.js index c5e28727..31083e09 100644 --- a/examples/perf_test.js +++ b/examples/perf_test.js @@ -31,6 +31,8 @@ * */ +'use strict'; + var grpc = require('..'); var testProto = grpc.load(__dirname + '/../interop/test.proto').grpc.testing; var _ = require('underscore'); @@ -44,7 +46,6 @@ function runTest(iterations, callback) { function runIterations(finish) { var start = process.hrtime(); var intervals = []; - var pending = iterations; function next(i) { if (i >= iterations) { testServer.server.shutdown(); @@ -69,28 +70,30 @@ function runTest(iterations, callback) { function warmUp(num) { var pending = num; + function startCall() { + client.emptyCall({}, function(err, resp) { + pending--; + if (pending === 0) { + runIterations(callback); + } + }); + } for (var i = 0; i < num; i++) { - (function(i) { - client.emptyCall({}, function(err, resp) { - pending--; - if (pending === 0) { - runIterations(callback); - } - }); - })(i); + startCall(); } } warmUp(100); } -function percentile(arr, percentile) { - if (percentile > 99) { - percentile = 99; +function percentile(arr, pct) { + if (pct > 99) { + pct = 99; } - if (percentile < 0) { - percentile = 0; + if (pct < 0) { + pct = 0; } - return arr[(arr.length * percentile / 100)|0]; + var index = Math.floor(arr.length * pct / 100); + return arr[index]; } if (require.main === module) { diff --git a/examples/stock_server.js b/examples/stock_server.js index b226a715..e475c9cb 100644 --- a/examples/stock_server.js +++ b/examples/stock_server.js @@ -31,6 +31,8 @@ * */ +'use strict'; + var _ = require('underscore'); var grpc = require('..'); var examples = grpc.load(__dirname + '/stock.proto').examples; diff --git a/index.js b/index.js index 1bef2072..4b5302e4 100644 --- a/index.js +++ b/index.js @@ -31,6 +31,8 @@ * */ +'use strict'; + var _ = require('underscore'); var ProtoBuf = require('protobufjs'); diff --git a/interop/interop_client.js b/interop/interop_client.js index fc2fdf4d..eaf254bc 100644 --- a/interop/interop_client.js +++ b/interop/interop_client.js @@ -31,6 +31,8 @@ * */ +'use strict'; + var fs = require('fs'); var path = require('path'); var grpc = require('..'); @@ -41,7 +43,8 @@ var assert = require('assert'); var AUTH_SCOPE = 'https://www.googleapis.com/auth/xapi.zoo'; var AUTH_SCOPE_RESPONSE = 'xapi.zoo'; -var AUTH_USER = '155450119199-3psnrh1sdr3d8cpj1v46naggf81mhdnk@developer.gserviceaccount.com'; +var AUTH_USER = ('155450119199-3psnrh1sdr3d8cpj1v46naggf81mhdnk' + + '@developer.gserviceaccount.com'); /** * Create a buffer filled with size zeroes @@ -318,7 +321,7 @@ var test_cases = { /** * Execute a single test case. * @param {string} address The address of the server to connect to, in the - * format "hostname:port" + * format 'hostname:port' * @param {string} host_overrirde The hostname of the server to use as an SSL * override * @param {string} test_case The name of the test case to run diff --git a/interop/interop_server.js b/interop/interop_server.js index c97d2344..125ede17 100644 --- a/interop/interop_server.js +++ b/interop/interop_server.js @@ -31,6 +31,8 @@ * */ +'use strict'; + var fs = require('fs'); var path = require('path'); var _ = require('underscore'); diff --git a/package.json b/package.json index 1d4c3f6e..7aa0083e 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "gRPC Library for Node", "scripts": { "lint": "jshint src test examples interop index.js", - "test": "./node_modules/mocha/bin/mocha" + "test": "./node_modules/mocha/bin/mocha && npm run-script lint" }, "dependencies": { "bindings": "^1.2.1", diff --git a/src/client.js b/src/client.js index 19c3144c..aaa7be79 100644 --- a/src/client.js +++ b/src/client.js @@ -31,6 +31,8 @@ * */ +'use strict'; + var _ = require('underscore'); var capitalize = require('underscore.string/capitalize'); @@ -77,6 +79,7 @@ function ClientWritableStream(call, serialize) { * @param {function(Error=)} callback Called when the write is complete */ function _write(chunk, encoding, callback) { + /* jshint validthis: true */ var batch = {}; batch[grpc.opType.SEND_MESSAGE] = this.serialize(chunk); this.call.startBatch(batch, function(err, event) { @@ -85,7 +88,7 @@ function _write(chunk, encoding, callback) { } callback(); }); -}; +} ClientWritableStream.prototype._write = _write; @@ -111,6 +114,7 @@ function ClientReadableStream(call, deserialize) { * @param {*} size Ignored because we use objectMode=true */ function _read(size) { + /* jshint validthis: true */ var self = this; /** * Callback to be called when a READ event is received. Pushes the data onto @@ -126,7 +130,7 @@ function _read(size) { return; } var data = event.read; - if (self.push(self.deserialize(data)) && data != null) { + if (self.push(self.deserialize(data)) && data !== null) { var read_batch = {}; read_batch[grpc.opType.RECV_MESSAGE] = true; self.call.startBatch(read_batch, readCallback); @@ -144,7 +148,7 @@ function _read(size) { self.call.startBatch(read_batch, readCallback); } } -}; +} ClientReadableStream.prototype._read = _read; @@ -163,10 +167,6 @@ function ClientDuplexStream(call, serialize, deserialize) { Duplex.call(this, {objectMode: true}); this.serialize = common.wrapIgnoreNull(serialize); this.deserialize = common.wrapIgnoreNull(deserialize); - var self = this; - var finished = false; - // Indicates that a read is currently pending - var reading = false; this.call = call; this.on('finish', function() { var batch = {}; @@ -182,6 +182,7 @@ ClientDuplexStream.prototype._write = _write; * Cancel the ongoing call */ function cancel() { + /* jshint validthis: true */ this.call.cancel(); } @@ -213,6 +214,7 @@ function makeUnaryRequestFunction(method, serialize, deserialize) { * @return {EventEmitter} An event emitter for stream related events */ function makeUnaryRequest(argument, callback, metadata, deadline) { + /* jshint validthis: true */ if (deadline === undefined) { deadline = Infinity; } @@ -242,7 +244,7 @@ function makeUnaryRequestFunction(method, serialize, deserialize) { callback(err); return; } - if (response.status.code != grpc.status.OK) { + if (response.status.code !== grpc.status.OK) { callback(response.status); return; } @@ -278,6 +280,7 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) { * @return {EventEmitter} An event emitter for stream related events */ function makeClientStreamRequest(callback, metadata, deadline) { + /* jshint validthis: true */ if (deadline === undefined) { deadline = Infinity; } @@ -310,7 +313,7 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) { callback(err); return; } - if (response.status.code != grpc.status.OK) { + if (response.status.code !== grpc.status.OK) { callback(response.status); return; } @@ -345,6 +348,7 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) { * @return {EventEmitter} An event emitter for stream related events */ function makeServerStreamRequest(argument, metadata, deadline) { + /* jshint validthis: true */ if (deadline === undefined) { deadline = Infinity; } @@ -404,6 +408,7 @@ function makeBidiStreamRequestFunction(method, serialize, deserialize) { * @return {EventEmitter} An event emitter for stream related events */ function makeBidiStreamRequest(metadata, deadline) { + /* jshint validthis: true */ if (deadline === undefined) { deadline = Infinity; } diff --git a/src/common.js b/src/common.js index 848c9674..eec8f0f9 100644 --- a/src/common.js +++ b/src/common.js @@ -31,6 +31,8 @@ * */ +'use strict'; + var _ = require('underscore'); var capitalize = require('underscore.string/capitalize'); diff --git a/src/server.js b/src/server.js index 48c349ef..91dde022 100644 --- a/src/server.js +++ b/src/server.js @@ -31,6 +31,8 @@ * */ +'use strict'; + var _ = require('underscore'); var capitalize = require('underscore.string/capitalize'); @@ -217,6 +219,7 @@ function ServerWritableStream(call, serialize) { * complete */ function _write(chunk, encoding, callback) { + /* jshint validthis: true */ var batch = {}; batch[grpc.opType.SEND_MESSAGE] = this.serialize(chunk); this.call.startBatch(batch, function(err, value) { @@ -251,6 +254,7 @@ function ServerReadableStream(call, deserialize) { * @param {number} size Ignored */ function _read(size) { + /* jshint validthis: true */ var self = this; /** * Callback to be called when a READ event is received. Pushes the data onto @@ -267,7 +271,7 @@ function _read(size) { return; } var data = event.read; - if (self.push(self.deserialize(data)) && data != null) { + if (self.push(self.deserialize(data)) && data !== null) { var read_batch = {}; read_batch[grpc.opType.RECV_MESSAGE] = true; self.call.startBatch(read_batch, readCallback); @@ -424,7 +428,6 @@ function Server(getMetadata, options) { var handlers = this.handlers; var server = new grpc.Server(options); this._server = server; - var started = false; /** * Start the server and begin handling requests * @this Server @@ -456,8 +459,7 @@ function Server(getMetadata, options) { return; } server.requestCall(handleNewCall); - var handler = undefined; - var deadline = details.deadline; + var handler; if (handlers.hasOwnProperty(method)) { handler = handlers[method]; } else { @@ -465,7 +467,7 @@ function Server(getMetadata, options) { batch[grpc.opType.SEND_INITIAL_METADATA] = {}; batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { code: grpc.status.UNIMPLEMENTED, - details: "This method is not available on this server.", + details: 'This method is not available on this server.', metadata: {} }; batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; diff --git a/test/call_test.js b/test/call_test.js index c1a7e95f..7b2b36ae 100644 --- a/test/call_test.js +++ b/test/call_test.js @@ -31,6 +31,8 @@ * */ +'use strict'; + var assert = require('assert'); var grpc = require('bindings')('grpc.node'); diff --git a/test/channel_test.js b/test/channel_test.js index 449a8cc4..33200c99 100644 --- a/test/channel_test.js +++ b/test/channel_test.js @@ -31,6 +31,8 @@ * */ +'use strict'; + var assert = require('assert'); var grpc = require('bindings')('grpc.node'); diff --git a/test/constant_test.js b/test/constant_test.js index 4a403868..ecc98ec4 100644 --- a/test/constant_test.js +++ b/test/constant_test.js @@ -31,6 +31,8 @@ * */ +'use strict'; + var assert = require('assert'); var grpc = require('bindings')('grpc.node'); diff --git a/test/end_to_end_test.js b/test/end_to_end_test.js index 8e99d6f1..1cc19286 100644 --- a/test/end_to_end_test.js +++ b/test/end_to_end_test.js @@ -31,6 +31,8 @@ * */ +'use strict'; + var assert = require('assert'); var grpc = require('bindings')('grpc.node'); @@ -227,7 +229,7 @@ describe('end-to-end', function() { response_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; server_call.startBatch(response_batch, function(err, response) { assert(response['send status']); - assert(!response['cancelled']); + assert(!response.cancelled); done(); }); }); diff --git a/test/interop_sanity_test.js b/test/interop_sanity_test.js index d1bdd166..8dc933ea 100644 --- a/test/interop_sanity_test.js +++ b/test/interop_sanity_test.js @@ -31,6 +31,8 @@ * */ +'use strict'; + var interop_server = require('../interop/interop_server.js'); var interop_client = require('../interop/interop_client.js'); diff --git a/test/math_client_test.js b/test/math_client_test.js index fd946e03..d83f6411 100644 --- a/test/math_client_test.js +++ b/test/math_client_test.js @@ -31,6 +31,8 @@ * */ +'use strict'; + var assert = require('assert'); var grpc = require('..'); @@ -59,7 +61,7 @@ describe('Math client', function() { }); it('should handle a single request', function(done) { var arg = {dividend: 7, divisor: 4}; - var call = math_client.div(arg, function handleDivResult(err, value) { + math_client.div(arg, function handleDivResult(err, value) { assert.ifError(err); assert.equal(value.quotient, 1); assert.equal(value.remainder, 3); diff --git a/test/surface_test.js b/test/surface_test.js index d6694724..91d8197b 100644 --- a/test/surface_test.js +++ b/test/surface_test.js @@ -31,9 +31,9 @@ * */ -var assert = require('assert'); +'use strict'; -var surface_server = require('../src/server.js'); +var assert = require('assert'); var surface_client = require('../src/client.js'); From 25e65921f71b251d3ddc5209238b7f70a2c84802 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 19 Feb 2015 15:40:26 -0800 Subject: [PATCH 50/91] Switched to using pre-defined list of features --- examples/route_guide_db.json | 1 + examples/route_guide_server.js | 32 +++++++------------------------- 2 files changed, 8 insertions(+), 25 deletions(-) create mode 100644 examples/route_guide_db.json diff --git a/examples/route_guide_db.json b/examples/route_guide_db.json new file mode 100644 index 00000000..57f74938 --- /dev/null +++ b/examples/route_guide_db.json @@ -0,0 +1 @@ +[{"location":{"latitude":-898084041,"longitude":-956335530},"name":"Antarctica"},{"location":{"latitude":-146631624,"longitude":-958383511},"name":""},{"location":{"latitude":804112575,"longitude":-442594532},"name":"Greenland"},{"location":{"latitude":-49416972,"longitude":-350954955},"name":"Brazil"},{"location":{"latitude":89468665,"longitude":242327820},"name":"Buram, Sudan"},{"location":{"latitude":-649624710,"longitude":746289111},"name":""},{"location":{"latitude":-505487958,"longitude":-838328104},"name":""},{"location":{"latitude":848121953,"longitude":-298247781},"name":""},{"location":{"latitude":524094837,"longitude":-795584572},"name":"Baffin Region, NU, Canada"},{"location":{"latitude":56485291,"longitude":-187715698},"name":""},{"location":{"latitude":-470089856,"longitude":1789140056},"name":""},{"location":{"latitude":-577486081,"longitude":-1733155598},"name":""},{"location":{"latitude":577741664,"longitude":-873781618},"name":"Keewatin Region, NU, Canada"},{"location":{"latitude":306507262,"longitude":219338963},"name":"Al Wahat, Libya"},{"location":{"latitude":-446911497,"longitude":1679295563},"name":"154 Milford Sound Highway, Fiordland National Park 9679, New Zealand"},{"location":{"latitude":733881426,"longitude":737563031},"name":"Russia, 629705"},{"location":{"latitude":-688087271,"longitude":-262799641},"name":""},{"location":{"latitude":-99379946,"longitude":1774186549},"name":""},{"location":{"latitude":-65992383,"longitude":1099806539},"name":"Indonesia"},{"location":{"latitude":220938950,"longitude":968560584},"name":"41, Republic of the Union of Myanmar"},{"location":{"latitude":-594140442,"longitude":1504883782},"name":""},{"location":{"latitude":-707179151,"longitude":-896729854},"name":""},{"location":{"latitude":-787916310,"longitude":-889044879},"name":""},{"location":{"latitude":-119497900,"longitude":1114024289},"name":""},{"location":{"latitude":-648254087,"longitude":362768502},"name":""},{"location":{"latitude":-402418031,"longitude":117993618},"name":""},{"location":{"latitude":130550282,"longitude":-1032012014},"name":""},{"location":{"latitude":734388781,"longitude":-452362128},"name":""},{"location":{"latitude":-533182420,"longitude":1155727285},"name":""},{"location":{"latitude":741870560,"longitude":585594215},"name":""},{"location":{"latitude":-722890458,"longitude":-812696271},"name":""},{"location":{"latitude":-589756031,"longitude":-640124308},"name":""},{"location":{"latitude":190966959,"longitude":1496287526},"name":""},{"location":{"latitude":-836654415,"longitude":-1662752612},"name":""},{"location":{"latitude":-731583802,"longitude":-574895638},"name":""},{"location":{"latitude":-666407402,"longitude":-1535334105},"name":""},{"location":{"latitude":-790147961,"longitude":-1328725946},"name":""},{"location":{"latitude":858845788,"longitude":-864446073},"name":""},{"location":{"latitude":477627565,"longitude":336161401},"name":""},{"location":{"latitude":878499845,"longitude":-1366230271},"name":""},{"location":{"latitude":161644757,"longitude":1232275165},"name":""},{"location":{"latitude":654854594,"longitude":567182834},"name":""},{"location":{"latitude":572943963,"longitude":1261462818},"name":""},{"location":{"latitude":-679708566,"longitude":244550718},"name":""},{"location":{"latitude":-381192803,"longitude":1745293826},"name":""},{"location":{"latitude":137171282,"longitude":293455452},"name":""},{"location":{"latitude":667714793,"longitude":-26493598},"name":""},{"location":{"latitude":201258356,"longitude":1083312913},"name":""},{"location":{"latitude":-687799996,"longitude":-966880051},"name":""},{"location":{"latitude":-437056011,"longitude":1700457053},"name":""},{"location":{"latitude":-187508133,"longitude":511728352},"name":""},{"location":{"latitude":-512677292,"longitude":-911360176},"name":""},{"location":{"latitude":-208144386,"longitude":449727920},"name":""},{"location":{"latitude":-269716310,"longitude":-1370371395},"name":""},{"location":{"latitude":796592091,"longitude":-122849658},"name":""},{"location":{"latitude":134492646,"longitude":-894681833},"name":""},{"location":{"latitude":894694483,"longitude":-1229015411},"name":""},{"location":{"latitude":567905024,"longitude":-332346260},"name":""},{"location":{"latitude":838345452,"longitude":745395722},"name":""},{"location":{"latitude":108813570,"longitude":1517086626},"name":""},{"location":{"latitude":-273284408,"longitude":1637597498},"name":""},{"location":{"latitude":-221336059,"longitude":-3716325},"name":""},{"location":{"latitude":231027483,"longitude":1469916710},"name":""},{"location":{"latitude":223499746,"longitude":-805682376},"name":"Diego García, Rodas, Cuba"},{"location":{"latitude":-257443435,"longitude":1566640501},"name":""},{"location":{"latitude":402291696,"longitude":-1298402965},"name":""},{"location":{"latitude":-398778441,"longitude":552807095},"name":""},{"location":{"latitude":152404713,"longitude":-1465239382},"name":""},{"location":{"latitude":-582856140,"longitude":-1629641450},"name":""},{"location":{"latitude":-473967125,"longitude":-1652968152},"name":""},{"location":{"latitude":-174439271,"longitude":1422960643},"name":"Strathmore QLD 4871, Australia"},{"location":{"latitude":253782635,"longitude":1030164726},"name":"011 Xiang Dao, Songming Xian, Kunming Shi, Yunnan Sheng, China"},{"location":{"latitude":-621365989,"longitude":-1115532139},"name":""},{"location":{"latitude":862664618,"longitude":1416457351},"name":""},{"location":{"latitude":-383388727,"longitude":-785952718},"name":""},{"location":{"latitude":125664168,"longitude":-291998446},"name":""},{"location":{"latitude":656343840,"longitude":439439764},"name":""},{"location":{"latitude":-655738461,"longitude":-544212722},"name":""},{"location":{"latitude":623251752,"longitude":1488435750},"name":""},{"location":{"latitude":436237502,"longitude":-626387911},"name":""},{"location":{"latitude":515395937,"longitude":-16624701},"name":"Hatchet Hill, Swindon, Swindon SN4 0DP, UK"},{"location":{"latitude":755285640,"longitude":572210361},"name":""},{"location":{"latitude":-52275136,"longitude":-644800186},"name":"Coari - AM, 69460-000, Brazil"},{"location":{"latitude":-402181536,"longitude":1006662754},"name":""},{"location":{"latitude":138914296,"longitude":368173660},"name":""},{"location":{"latitude":-645551826,"longitude":-519728595},"name":""},{"location":{"latitude":548423365,"longitude":-981205907},"name":""},{"location":{"latitude":749998314,"longitude":195688905},"name":""},{"location":{"latitude":861483051,"longitude":990210887},"name":""},{"location":{"latitude":286877612,"longitude":-1112752249},"name":""},{"location":{"latitude":633715864,"longitude":1766634742},"name":""},{"location":{"latitude":625250411,"longitude":-1430210949},"name":""},{"location":{"latitude":146536486,"longitude":-1562487612},"name":""},{"location":{"latitude":-846541096,"longitude":-860062860},"name":""},{"location":{"latitude":-243481285,"longitude":515382553},"name":""},{"location":{"latitude":-851258156,"longitude":373511818},"name":""},{"location":{"latitude":-366270851,"longitude":-52214308},"name":""},{"location":{"latitude":-842736612,"longitude":277021516},"name":""},{"location":{"latitude":753713504,"longitude":490552554},"name":""},{"location":{"latitude":-211675256,"longitude":-1172151400},"name":""}] diff --git a/examples/route_guide_server.js b/examples/route_guide_server.js index b21190d6..a3fb0792 100644 --- a/examples/route_guide_server.js +++ b/examples/route_guide_server.js @@ -27,6 +27,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +var fs = require('fs'); var _ = require('underscore'); var grpc = require('..'); var examples = grpc.load(__dirname + '/route_guide.proto').examples; @@ -47,21 +48,6 @@ var COORD_FACTOR = 1e7; */ var feature_list = []; -/** - * Return a random "word" (alphabetic character sequence) of the given length. - * @param {number} length The length of the word to create - * @return {string} An alphabetic string with the given length. - */ -function randomWord(length) { - var alphabet = 'abcdefghijklmnopqrstuvwxyz'; - var word = ''; - for (var i = 0; i < length; i++) { - // Add a random character from the alphabet to the word - word += alphabet[_.random(0, alphabet.length - 1)]; - } - return word; -} - /** * Get a feature object at the given point, or creates one if it does not exist. * @param {point} point The point to check @@ -78,19 +64,11 @@ function checkFeature(point) { return feature; } } - // If not, create a new one with 50% chance of indicating "no feature present" - var name; - if (_.random(0,1) === 0) { - name = ''; - } else { - name = randomWord(5); - } + var name = ''; feature = { name: name, location: point }; - // Add the feature object to the list and return it - feature_list.push(feature); return feature; } @@ -253,7 +231,11 @@ if (require.main === module) { // If this is run as a script, start a server on an unused port var routeServer = getServer(); routeServer.bind('0.0.0.0:0'); - routeServer.listen(); + fs.readFile(__dirname + '/route_guide_db.json', function(err, data) { + if (err) throw err; + feature_list = JSON.parse(data); + routeServer.listen(); + }); } exports.getServer = getServer; From 532cb3d8aeffd11f61f9cdaa3eeb59dd457aadc4 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 19 Feb 2015 16:06:50 -0800 Subject: [PATCH 51/91] Switched to a smaller area of features --- examples/route_guide_db.json | 2 +- examples/route_guide_server.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/route_guide_db.json b/examples/route_guide_db.json index 57f74938..75c38c6c 100644 --- a/examples/route_guide_db.json +++ b/examples/route_guide_db.json @@ -1 +1 @@ -[{"location":{"latitude":-898084041,"longitude":-956335530},"name":"Antarctica"},{"location":{"latitude":-146631624,"longitude":-958383511},"name":""},{"location":{"latitude":804112575,"longitude":-442594532},"name":"Greenland"},{"location":{"latitude":-49416972,"longitude":-350954955},"name":"Brazil"},{"location":{"latitude":89468665,"longitude":242327820},"name":"Buram, Sudan"},{"location":{"latitude":-649624710,"longitude":746289111},"name":""},{"location":{"latitude":-505487958,"longitude":-838328104},"name":""},{"location":{"latitude":848121953,"longitude":-298247781},"name":""},{"location":{"latitude":524094837,"longitude":-795584572},"name":"Baffin Region, NU, Canada"},{"location":{"latitude":56485291,"longitude":-187715698},"name":""},{"location":{"latitude":-470089856,"longitude":1789140056},"name":""},{"location":{"latitude":-577486081,"longitude":-1733155598},"name":""},{"location":{"latitude":577741664,"longitude":-873781618},"name":"Keewatin Region, NU, Canada"},{"location":{"latitude":306507262,"longitude":219338963},"name":"Al Wahat, Libya"},{"location":{"latitude":-446911497,"longitude":1679295563},"name":"154 Milford Sound Highway, Fiordland National Park 9679, New Zealand"},{"location":{"latitude":733881426,"longitude":737563031},"name":"Russia, 629705"},{"location":{"latitude":-688087271,"longitude":-262799641},"name":""},{"location":{"latitude":-99379946,"longitude":1774186549},"name":""},{"location":{"latitude":-65992383,"longitude":1099806539},"name":"Indonesia"},{"location":{"latitude":220938950,"longitude":968560584},"name":"41, Republic of the Union of Myanmar"},{"location":{"latitude":-594140442,"longitude":1504883782},"name":""},{"location":{"latitude":-707179151,"longitude":-896729854},"name":""},{"location":{"latitude":-787916310,"longitude":-889044879},"name":""},{"location":{"latitude":-119497900,"longitude":1114024289},"name":""},{"location":{"latitude":-648254087,"longitude":362768502},"name":""},{"location":{"latitude":-402418031,"longitude":117993618},"name":""},{"location":{"latitude":130550282,"longitude":-1032012014},"name":""},{"location":{"latitude":734388781,"longitude":-452362128},"name":""},{"location":{"latitude":-533182420,"longitude":1155727285},"name":""},{"location":{"latitude":741870560,"longitude":585594215},"name":""},{"location":{"latitude":-722890458,"longitude":-812696271},"name":""},{"location":{"latitude":-589756031,"longitude":-640124308},"name":""},{"location":{"latitude":190966959,"longitude":1496287526},"name":""},{"location":{"latitude":-836654415,"longitude":-1662752612},"name":""},{"location":{"latitude":-731583802,"longitude":-574895638},"name":""},{"location":{"latitude":-666407402,"longitude":-1535334105},"name":""},{"location":{"latitude":-790147961,"longitude":-1328725946},"name":""},{"location":{"latitude":858845788,"longitude":-864446073},"name":""},{"location":{"latitude":477627565,"longitude":336161401},"name":""},{"location":{"latitude":878499845,"longitude":-1366230271},"name":""},{"location":{"latitude":161644757,"longitude":1232275165},"name":""},{"location":{"latitude":654854594,"longitude":567182834},"name":""},{"location":{"latitude":572943963,"longitude":1261462818},"name":""},{"location":{"latitude":-679708566,"longitude":244550718},"name":""},{"location":{"latitude":-381192803,"longitude":1745293826},"name":""},{"location":{"latitude":137171282,"longitude":293455452},"name":""},{"location":{"latitude":667714793,"longitude":-26493598},"name":""},{"location":{"latitude":201258356,"longitude":1083312913},"name":""},{"location":{"latitude":-687799996,"longitude":-966880051},"name":""},{"location":{"latitude":-437056011,"longitude":1700457053},"name":""},{"location":{"latitude":-187508133,"longitude":511728352},"name":""},{"location":{"latitude":-512677292,"longitude":-911360176},"name":""},{"location":{"latitude":-208144386,"longitude":449727920},"name":""},{"location":{"latitude":-269716310,"longitude":-1370371395},"name":""},{"location":{"latitude":796592091,"longitude":-122849658},"name":""},{"location":{"latitude":134492646,"longitude":-894681833},"name":""},{"location":{"latitude":894694483,"longitude":-1229015411},"name":""},{"location":{"latitude":567905024,"longitude":-332346260},"name":""},{"location":{"latitude":838345452,"longitude":745395722},"name":""},{"location":{"latitude":108813570,"longitude":1517086626},"name":""},{"location":{"latitude":-273284408,"longitude":1637597498},"name":""},{"location":{"latitude":-221336059,"longitude":-3716325},"name":""},{"location":{"latitude":231027483,"longitude":1469916710},"name":""},{"location":{"latitude":223499746,"longitude":-805682376},"name":"Diego García, Rodas, Cuba"},{"location":{"latitude":-257443435,"longitude":1566640501},"name":""},{"location":{"latitude":402291696,"longitude":-1298402965},"name":""},{"location":{"latitude":-398778441,"longitude":552807095},"name":""},{"location":{"latitude":152404713,"longitude":-1465239382},"name":""},{"location":{"latitude":-582856140,"longitude":-1629641450},"name":""},{"location":{"latitude":-473967125,"longitude":-1652968152},"name":""},{"location":{"latitude":-174439271,"longitude":1422960643},"name":"Strathmore QLD 4871, Australia"},{"location":{"latitude":253782635,"longitude":1030164726},"name":"011 Xiang Dao, Songming Xian, Kunming Shi, Yunnan Sheng, China"},{"location":{"latitude":-621365989,"longitude":-1115532139},"name":""},{"location":{"latitude":862664618,"longitude":1416457351},"name":""},{"location":{"latitude":-383388727,"longitude":-785952718},"name":""},{"location":{"latitude":125664168,"longitude":-291998446},"name":""},{"location":{"latitude":656343840,"longitude":439439764},"name":""},{"location":{"latitude":-655738461,"longitude":-544212722},"name":""},{"location":{"latitude":623251752,"longitude":1488435750},"name":""},{"location":{"latitude":436237502,"longitude":-626387911},"name":""},{"location":{"latitude":515395937,"longitude":-16624701},"name":"Hatchet Hill, Swindon, Swindon SN4 0DP, UK"},{"location":{"latitude":755285640,"longitude":572210361},"name":""},{"location":{"latitude":-52275136,"longitude":-644800186},"name":"Coari - AM, 69460-000, Brazil"},{"location":{"latitude":-402181536,"longitude":1006662754},"name":""},{"location":{"latitude":138914296,"longitude":368173660},"name":""},{"location":{"latitude":-645551826,"longitude":-519728595},"name":""},{"location":{"latitude":548423365,"longitude":-981205907},"name":""},{"location":{"latitude":749998314,"longitude":195688905},"name":""},{"location":{"latitude":861483051,"longitude":990210887},"name":""},{"location":{"latitude":286877612,"longitude":-1112752249},"name":""},{"location":{"latitude":633715864,"longitude":1766634742},"name":""},{"location":{"latitude":625250411,"longitude":-1430210949},"name":""},{"location":{"latitude":146536486,"longitude":-1562487612},"name":""},{"location":{"latitude":-846541096,"longitude":-860062860},"name":""},{"location":{"latitude":-243481285,"longitude":515382553},"name":""},{"location":{"latitude":-851258156,"longitude":373511818},"name":""},{"location":{"latitude":-366270851,"longitude":-52214308},"name":""},{"location":{"latitude":-842736612,"longitude":277021516},"name":""},{"location":{"latitude":753713504,"longitude":490552554},"name":""},{"location":{"latitude":-211675256,"longitude":-1172151400},"name":""}] +[{"location":{"latitude":407838351,"longitude":-746143763},"name":"Patriots Path, Mendham, NJ 07945, USA"},{"location":{"latitude":408122808,"longitude":-743999179},"name":"101 New Jersey 10, Whippany, NJ 07981, USA"},{"location":{"latitude":413628156,"longitude":-749015468},"name":"U.S. 6, Shohola, PA 18458, USA"},{"location":{"latitude":419999544,"longitude":-740371136},"name":"5 Conners Road, Kingston, NY 12401, USA"},{"location":{"latitude":414008389,"longitude":-743951297},"name":"Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA"},{"location":{"latitude":419611318,"longitude":-746524769},"name":"287 Flugertown Road, Livingston Manor, NY 12758, USA"},{"location":{"latitude":406109563,"longitude":-742186778},"name":"4001 Tremley Point Road, Linden, NJ 07036, USA"},{"location":{"latitude":416802456,"longitude":-742370183},"name":"352 South Mountain Road, Wallkill, NY 12589, USA"},{"location":{"latitude":412950425,"longitude":-741077389},"name":"Bailey Turn Road, Harriman, NY 10926, USA"},{"location":{"latitude":412144655,"longitude":-743949739},"name":"193-199 Wawayanda Road, Hewitt, NJ 07421, USA"},{"location":{"latitude":415736605,"longitude":-742847522},"name":"406-496 Ward Avenue, Pine Bush, NY 12566, USA"},{"location":{"latitude":413843930,"longitude":-740501726},"name":"162 Merrill Road, Highland Mills, NY 10930, USA"},{"location":{"latitude":410873075,"longitude":-744459023},"name":"Clinton Road, West Milford, NJ 07480, USA"},{"location":{"latitude":412346009,"longitude":-744026814},"name":"16 Old Brook Lane, Warwick, NY 10990, USA"},{"location":{"latitude":402948455,"longitude":-747903913},"name":"3 Drake Lane, Pennington, NJ 08534, USA"},{"location":{"latitude":406337092,"longitude":-740122226},"name":"6324 8th Avenue, Brooklyn, NY 11220, USA"},{"location":{"latitude":406421967,"longitude":-747727624},"name":"1 Merck Access Road, Whitehouse Station, NJ 08889, USA"},{"location":{"latitude":416318082,"longitude":-749677716},"name":"78-98 Schalck Road, Narrowsburg, NY 12764, USA"},{"location":{"latitude":415301720,"longitude":-748416257},"name":"282 Lakeview Drive Road, Highland Lake, NY 12743, USA"},{"location":{"latitude":402647019,"longitude":-747071791},"name":"330 Evelyn Avenue, Hamilton Township, NJ 08619, USA"},{"location":{"latitude":412567807,"longitude":-741058078},"name":"New York State Reference Route 987E, Southfields, NY 10975, USA"},{"location":{"latitude":416855156,"longitude":-744420597},"name":"103-271 Tempaloni Road, Ellenville, NY 12428, USA"},{"location":{"latitude":404663628,"longitude":-744820157},"name":"1300 Airport Road, North Brunswick Township, NJ 08902, USA"},{"location":{"latitude":407113723,"longitude":-749746483},"name":""},{"location":{"latitude":402133926,"longitude":-743613249},"name":""},{"location":{"latitude":400273442,"longitude":-741220915},"name":""},{"location":{"latitude":411236786,"longitude":-744070769},"name":""},{"location":{"latitude":411633782,"longitude":-746784970},"name":"211-225 Plains Road, Augusta, NJ 07822, USA"},{"location":{"latitude":415830701,"longitude":-742952812},"name":""},{"location":{"latitude":413447164,"longitude":-748712898},"name":"165 Pedersen Ridge Road, Milford, PA 18337, USA"},{"location":{"latitude":405047245,"longitude":-749800722},"name":"100-122 Locktown Road, Frenchtown, NJ 08825, USA"},{"location":{"latitude":418858923,"longitude":-746156790},"name":""},{"location":{"latitude":417951888,"longitude":-748484944},"name":"650-652 Willi Hill Road, Swan Lake, NY 12783, USA"},{"location":{"latitude":407033786,"longitude":-743977337},"name":"26 East 3rd Street, New Providence, NJ 07974, USA"},{"location":{"latitude":417548014,"longitude":-740075041},"name":""},{"location":{"latitude":410395868,"longitude":-744972325},"name":""},{"location":{"latitude":404615353,"longitude":-745129803},"name":""},{"location":{"latitude":406589790,"longitude":-743560121},"name":"611 Lawrence Avenue, Westfield, NJ 07090, USA"},{"location":{"latitude":414653148,"longitude":-740477477},"name":"18 Lannis Avenue, New Windsor, NY 12553, USA"},{"location":{"latitude":405957808,"longitude":-743255336},"name":"82-104 Amherst Avenue, Colonia, NJ 07067, USA"},{"location":{"latitude":411733589,"longitude":-741648093},"name":"170 Seven Lakes Drive, Sloatsburg, NY 10974, USA"},{"location":{"latitude":412676291,"longitude":-742606606},"name":"1270 Lakes Road, Monroe, NY 10950, USA"},{"location":{"latitude":409224445,"longitude":-748286738},"name":"509-535 Alphano Road, Great Meadows, NJ 07838, USA"},{"location":{"latitude":406523420,"longitude":-742135517},"name":"652 Garden Street, Elizabeth, NJ 07202, USA"},{"location":{"latitude":401827388,"longitude":-740294537},"name":"349 Sea Spray Court, Neptune City, NJ 07753, USA"},{"location":{"latitude":410564152,"longitude":-743685054},"name":"13-17 Stanley Street, West Milford, NJ 07480, USA"},{"location":{"latitude":408472324,"longitude":-740726046},"name":"47 Industrial Avenue, Teterboro, NJ 07608, USA"},{"location":{"latitude":412452168,"longitude":-740214052},"name":"5 White Oak Lane, Stony Point, NY 10980, USA"},{"location":{"latitude":409146138,"longitude":-746188906},"name":"Berkshire Valley Management Area Trail, Jefferson, NJ, USA"},{"location":{"latitude":404701380,"longitude":-744781745},"name":"1007 Jersey Avenue, New Brunswick, NJ 08901, USA"},{"location":{"latitude":409642566,"longitude":-746017679},"name":"6 East Emerald Isle Drive, Lake Hopatcong, NJ 07849, USA"},{"location":{"latitude":408031728,"longitude":-748645385},"name":"1358-1474 New Jersey 57, Port Murray, NJ 07865, USA"},{"location":{"latitude":413700272,"longitude":-742135189},"name":"367 Prospect Road, Chester, NY 10918, USA"},{"location":{"latitude":404310607,"longitude":-740282632},"name":"10 Simon Lake Drive, Atlantic Highlands, NJ 07716, USA"},{"location":{"latitude":409319800,"longitude":-746201391},"name":"11 Ward Street, Mount Arlington, NJ 07856, USA"},{"location":{"latitude":406685311,"longitude":-742108603},"name":"300-398 Jefferson Avenue, Elizabeth, NJ 07201, USA"},{"location":{"latitude":419018117,"longitude":-749142781},"name":"43 Dreher Road, Roscoe, NY 12776, USA"},{"location":{"latitude":412856162,"longitude":-745148837},"name":"Swan Street, Pine Island, NY 10969, USA"},{"location":{"latitude":416560744,"longitude":-746721964},"name":"66 Pleasantview Avenue, Monticello, NY 12701, USA"},{"location":{"latitude":405314270,"longitude":-749836354},"name":""},{"location":{"latitude":414219548,"longitude":-743327440},"name":""},{"location":{"latitude":415534177,"longitude":-742900616},"name":"565 Winding Hills Road, Montgomery, NY 12549, USA"},{"location":{"latitude":406898530,"longitude":-749127080},"name":"231 Rocky Run Road, Glen Gardner, NJ 08826, USA"},{"location":{"latitude":407586880,"longitude":-741670168},"name":"100 Mount Pleasant Avenue, Newark, NJ 07104, USA"},{"location":{"latitude":400106455,"longitude":-742870190},"name":"517-521 Huntington Drive, Manchester Township, NJ 08759, USA"},{"location":{"latitude":400066188,"longitude":-746793294},"name":""},{"location":{"latitude":418803880,"longitude":-744102673},"name":"40 Mountain Road, Napanoch, NY 12458, USA"},{"location":{"latitude":414204288,"longitude":-747895140},"name":""},{"location":{"latitude":414777405,"longitude":-740615601},"name":""},{"location":{"latitude":415464475,"longitude":-747175374},"name":"48 North Road, Forestburgh, NY 12777, USA"},{"location":{"latitude":404062378,"longitude":-746376177},"name":""},{"location":{"latitude":405688272,"longitude":-749285130},"name":""},{"location":{"latitude":400342070,"longitude":-748788996},"name":""},{"location":{"latitude":401809022,"longitude":-744157964},"name":""},{"location":{"latitude":404226644,"longitude":-740517141},"name":"9 Thompson Avenue, Leonardo, NJ 07737, USA"},{"location":{"latitude":410322033,"longitude":-747871659},"name":""},{"location":{"latitude":407100674,"longitude":-747742727},"name":""},{"location":{"latitude":418811433,"longitude":-741718005},"name":"213 Bush Road, Stone Ridge, NY 12484, USA"},{"location":{"latitude":415034302,"longitude":-743850945},"name":""},{"location":{"latitude":411349992,"longitude":-743694161},"name":""},{"location":{"latitude":404839914,"longitude":-744759616},"name":"1-17 Bergen Court, New Brunswick, NJ 08901, USA"},{"location":{"latitude":414638017,"longitude":-745957854},"name":"35 Oakland Valley Road, Cuddebackville, NY 12729, USA"},{"location":{"latitude":412127800,"longitude":-740173578},"name":""},{"location":{"latitude":401263460,"longitude":-747964303},"name":""},{"location":{"latitude":412843391,"longitude":-749086026},"name":""},{"location":{"latitude":418512773,"longitude":-743067823},"name":""},{"location":{"latitude":404318328,"longitude":-740835638},"name":"42-102 Main Street, Belford, NJ 07718, USA"},{"location":{"latitude":419020746,"longitude":-741172328},"name":""},{"location":{"latitude":404080723,"longitude":-746119569},"name":""},{"location":{"latitude":401012643,"longitude":-744035134},"name":""},{"location":{"latitude":404306372,"longitude":-741079661},"name":""},{"location":{"latitude":403966326,"longitude":-748519297},"name":""},{"location":{"latitude":405002031,"longitude":-748407866},"name":""},{"location":{"latitude":409532885,"longitude":-742200683},"name":""},{"location":{"latitude":416851321,"longitude":-742674555},"name":""},{"location":{"latitude":406411633,"longitude":-741722051},"name":"3387 Richmond Terrace, Staten Island, NY 10303, USA"},{"location":{"latitude":413069058,"longitude":-744597778},"name":"261 Van Sickle Road, Goshen, NY 10924, USA"},{"location":{"latitude":418465462,"longitude":-746859398},"name":""},{"location":{"latitude":411733222,"longitude":-744228360},"name":""},{"location":{"latitude":410248224,"longitude":-747127767},"name":"3 Hasta Way, Newton, NJ 07860, USA"}] diff --git a/examples/route_guide_server.js b/examples/route_guide_server.js index a3fb0792..5b7eda7c 100644 --- a/examples/route_guide_server.js +++ b/examples/route_guide_server.js @@ -39,8 +39,8 @@ var COORD_FACTOR = 1e7; /** * For simplicity, a point is a record type that looks like * {latitude: number, longitude: number}, and a feature is a record type that - * looks like {name: string, location: point}. feature objects with name==='' are - * points with no feature. + * looks like {name: string, location: point}. feature objects with name==='' + * are points with no feature. */ /** From 7d499f8a591f066efaa3e457296eacc860a64575 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 19 Feb 2015 16:12:02 -0800 Subject: [PATCH 52/91] Removed reference to non-existent header --- ext/node_grpc.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/node_grpc.cc b/ext/node_grpc.cc index 965186e0..9f509583 100644 --- a/ext/node_grpc.cc +++ b/ext/node_grpc.cc @@ -38,7 +38,6 @@ #include "call.h" #include "channel.h" -#include "event.h" #include "server.h" #include "completion_queue_async_worker.h" #include "credentials.h" From c4b33e1f83345431a071c599d4f76e9c32b39c49 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 19 Feb 2015 16:33:21 -0800 Subject: [PATCH 53/91] Explicitly use nodejs to run tests --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index bbd89be7..a71577fb 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "version": "0.2.0", "description": "gRPC Library for Node", "scripts": { - "lint": "jshint src test examples interop index.js", - "test": "./node_modules/mocha/bin/mocha && npm run-script lint" + "lint": "nodejs ./node_modules/jshint/bin/jshint src test examples interop index.js", + "test": "nodejs ./node_modules/mocha/bin/mocha && npm run-script lint" }, "dependencies": { "bindings": "^1.2.1", From dd6cc664d0cbf0ba3ef2495141e058dd6eb4292d Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 19 Feb 2015 18:19:10 -0800 Subject: [PATCH 54/91] Added client and fixed some server bugs --- examples/route_guide_client.js | 190 +++++++++++++++++++++++++++++++++ examples/route_guide_server.js | 27 ++--- package.json | 5 +- 3 files changed, 208 insertions(+), 14 deletions(-) create mode 100644 examples/route_guide_client.js diff --git a/examples/route_guide_client.js b/examples/route_guide_client.js new file mode 100644 index 00000000..549c9b78 --- /dev/null +++ b/examples/route_guide_client.js @@ -0,0 +1,190 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +var async = require('async'); +var fs = require('fs'); +var _ = require('underscore'); +var grpc = require('..'); +var examples = grpc.load(__dirname + '/route_guide.proto').examples; +var client = new examples.RouteGuide('localhost:50051'); + +var COORD_FACTOR = 1e7; + +function runGetFeature(callback) { + var next = _.after(2, callback); + function featureCallback(error, feature) { + if (error) { + callback(error); + } + if (feature.name === '') { + console.log('Found no feature at ' + + feature.location.latitude/COORD_FACTOR + ', ' + + feature.location.longitude/COORD_FACTOR); + } else { + console.log('Found feature called "' + feature.name + '" at ' + + feature.location.latitude/COORD_FACTOR + ', ' + + feature.location.longitude/COORD_FACTOR); + } + next(); + } + var point1 = { + latitude: 409146138, + longitude: -746188906 + }; + var point2 = { + latitude: 0, + longitude: 0 + }; + client.getFeature(point1, featureCallback); + client.getFeature(point2, featureCallback); +} + +function runListFeatures(callback) { + var rectangle = { + lo: { + latitude: 400000000, + longitude: -750000000 + }, + hi: { + latitude: 420000000, + longitude: -730000000 + } + }; + console.log('Looking for features between 40, -75 and 42, -73'); + var call = client.listFeatures(rectangle); + call.on('data', function(feature) { + console.log('Found feature called "' + feature.name + '" at ' + + feature.location.latitude/COORD_FACTOR + ', ' + + feature.location.longitude/COORD_FACTOR); + }); + call.on('end', callback); +} + +function runRecordRoute(callback) { + fs.readFile(__dirname + '/route_guide_db.json', function(err, data) { + if (err) callback(err); + var feature_list = JSON.parse(data); + + var num_points = 10; + var call = client.recordRoute(function(error, stats) { + if (error) { + callback(error); + } + console.log('Finished trip with', stats.point_count, 'points'); + console.log('Passed', stats.feature_count, 'features'); + console.log('Travelled', stats.distance, 'meters'); + console.log('It took', stats.elapsed_time, 'seconds'); + callback(); + }); + function pointSender(lat, lng) { + return function(callback) { + console.log('Visiting point ' + lat/COORD_FACTOR + ', ' + + lng/COORD_FACTOR); + call.write({ + latitude: lat, + longitude: lng + }); + _.delay(callback, _.random(500, 1500)); + }; + } + var point_senders = []; + for (var i = 0; i < num_points; i++) { + var rand_point = feature_list[_.random(0, feature_list.length - 1)]; + point_senders[i] = pointSender(rand_point.location.latitude, + rand_point.location.longitude); + } + async.series(point_senders, function() { + call.end(); + }); + }); +} + +function runRouteChat(callback) { + var call = client.routeChat(); + call.on('data', function(note) { + console.log('Got message "' + note.message + '" at ' + + note.location.latitude + ', ' + note.location.longitude); + }); + + call.on('end', callback); + + var notes = [{ + location: { + latitude: 0, + longitude: 0 + }, + message: 'First message' + }, { + location: { + latitude: 0, + longitude: 1 + }, + message: 'Second message' + }, { + location: { + latitude: 1, + longitude: 0 + }, + message: 'Third message' + }, { + location: { + latitude: 0, + longitude: 0 + }, + message: 'Fourth message' + }]; + for (var i = 0; i < notes.length; i++) { + var note = notes[i]; + console.log('Sending message "' + note.message + '" at ' + + note.location.latitude + ', ' + note.location.longitude); + call.write(note); + } + call.end(); +} + +function main() { + async.series([ + runGetFeature, + runListFeatures, + runRecordRoute, + runRouteChat + ]); +} + +if (require.main === module) { + main(); +} + +exports.runGetFeature = runGetFeature; + +exports.runListFeatures = runListFeatures; + +exports.runRecordRoute = runRecordRoute; + +exports.runRouteChat = runRouteChat; diff --git a/examples/route_guide_server.js b/examples/route_guide_server.js index 5b7eda7c..89d8d27c 100644 --- a/examples/route_guide_server.js +++ b/examples/route_guide_server.js @@ -59,8 +59,8 @@ function checkFeature(point) { // Check if there is already a feature object for the given point for (var i = 0; i < feature_list.length; i++) { feature = feature_list[i]; - if (feature.point.latitude === point.latitude && - feature.point.longitude === point.longitude) { + if (feature.location.latitude === point.latitude && + feature.location.longitude === point.longitude) { return feature; } } @@ -91,10 +91,10 @@ function getFeature(call, callback) { function listFeatures(call) { var lo = call.request.lo; var hi = call.request.hi; - var left = _.min(lo.longitude, hi.longitude); - var right = _.max(lo.longitude, hi.longitude); - var top = _.max(lo.latitude, hi.latitude); - var bottom = _.max(lo.latitude, hi.latitude); + var left = _.min([lo.longitude, hi.longitude]); + var right = _.max([lo.longitude, hi.longitude]); + var top = _.max([lo.latitude, hi.latitude]); + var bottom = _.min([lo.latitude, hi.latitude]); // For each feature, check if it is in the given bounding box _.each(feature_list, function(feature) { if (feature.name === '') { @@ -118,15 +118,18 @@ function listFeatures(call) { * @return The distance between the points in meters */ function getDistance(start, end) { + function toRadians(num) { + return num * Math.PI / 180; + } var lat1 = start.latitude / COORD_FACTOR; var lat2 = end.latitude / COORD_FACTOR; var lon1 = start.longitude / COORD_FACTOR; var lon2 = end.longitude / COORD_FACTOR; var R = 6371000; // metres - var φ1 = lat1.toRadians(); - var φ2 = lat2.toRadians(); - var Δφ = (lat2-lat1).toRadians(); - var Δλ = (lon2-lon1).toRadians(); + var φ1 = toRadians(lat1); + var φ2 = toRadians(lat2); + var Δφ = toRadians(lat2-lat1); + var Δλ = toRadians(lon2-lon1); var a = Math.sin(Δφ/2) * Math.sin(Δφ/2) + Math.cos(φ1) * Math.cos(φ2) * @@ -204,7 +207,7 @@ function routeChat(call) { route_notes[key] = []; } // Then add the new note to the list - route_notes[key].push(note); + route_notes[key].push(JSON.parse(JSON.stringify(note))); }); call.on('end', function() { call.end(); @@ -230,7 +233,7 @@ function getServer() { if (require.main === module) { // If this is run as a script, start a server on an unused port var routeServer = getServer(); - routeServer.bind('0.0.0.0:0'); + routeServer.bind('0.0.0.0:50051'); fs.readFile(__dirname + '/route_guide_db.json', function(err, data) { if (err) throw err; feature_list = JSON.parse(data); diff --git a/package.json b/package.json index bbd89be7..898281a9 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,10 @@ "underscore.string": "^3.0.0" }, "devDependencies": { - "mocha": "~1.21.0", + "async": "^0.9.0", + "googleauth": "google/google-auth-library-nodejs", "minimist": "^1.1.0", - "googleauth": "google/google-auth-library-nodejs" + "mocha": "~1.21.0" }, "files": [ "README.md", From 413119058bb3e00ed334d38a6fbb01c65c4b094c Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 20 Feb 2015 10:44:37 -0800 Subject: [PATCH 55/91] Formatted db file --- examples/route_guide_db.json | 602 ++++++++++++++++++++++++++++++++++- 1 file changed, 601 insertions(+), 1 deletion(-) diff --git a/examples/route_guide_db.json b/examples/route_guide_db.json index 75c38c6c..9d6a980a 100644 --- a/examples/route_guide_db.json +++ b/examples/route_guide_db.json @@ -1 +1,601 @@ -[{"location":{"latitude":407838351,"longitude":-746143763},"name":"Patriots Path, Mendham, NJ 07945, USA"},{"location":{"latitude":408122808,"longitude":-743999179},"name":"101 New Jersey 10, Whippany, NJ 07981, USA"},{"location":{"latitude":413628156,"longitude":-749015468},"name":"U.S. 6, Shohola, PA 18458, USA"},{"location":{"latitude":419999544,"longitude":-740371136},"name":"5 Conners Road, Kingston, NY 12401, USA"},{"location":{"latitude":414008389,"longitude":-743951297},"name":"Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA"},{"location":{"latitude":419611318,"longitude":-746524769},"name":"287 Flugertown Road, Livingston Manor, NY 12758, USA"},{"location":{"latitude":406109563,"longitude":-742186778},"name":"4001 Tremley Point Road, Linden, NJ 07036, USA"},{"location":{"latitude":416802456,"longitude":-742370183},"name":"352 South Mountain Road, Wallkill, NY 12589, USA"},{"location":{"latitude":412950425,"longitude":-741077389},"name":"Bailey Turn Road, Harriman, NY 10926, USA"},{"location":{"latitude":412144655,"longitude":-743949739},"name":"193-199 Wawayanda Road, Hewitt, NJ 07421, USA"},{"location":{"latitude":415736605,"longitude":-742847522},"name":"406-496 Ward Avenue, Pine Bush, NY 12566, USA"},{"location":{"latitude":413843930,"longitude":-740501726},"name":"162 Merrill Road, Highland Mills, NY 10930, USA"},{"location":{"latitude":410873075,"longitude":-744459023},"name":"Clinton Road, West Milford, NJ 07480, USA"},{"location":{"latitude":412346009,"longitude":-744026814},"name":"16 Old Brook Lane, Warwick, NY 10990, USA"},{"location":{"latitude":402948455,"longitude":-747903913},"name":"3 Drake Lane, Pennington, NJ 08534, USA"},{"location":{"latitude":406337092,"longitude":-740122226},"name":"6324 8th Avenue, Brooklyn, NY 11220, USA"},{"location":{"latitude":406421967,"longitude":-747727624},"name":"1 Merck Access Road, Whitehouse Station, NJ 08889, USA"},{"location":{"latitude":416318082,"longitude":-749677716},"name":"78-98 Schalck Road, Narrowsburg, NY 12764, USA"},{"location":{"latitude":415301720,"longitude":-748416257},"name":"282 Lakeview Drive Road, Highland Lake, NY 12743, USA"},{"location":{"latitude":402647019,"longitude":-747071791},"name":"330 Evelyn Avenue, Hamilton Township, NJ 08619, USA"},{"location":{"latitude":412567807,"longitude":-741058078},"name":"New York State Reference Route 987E, Southfields, NY 10975, USA"},{"location":{"latitude":416855156,"longitude":-744420597},"name":"103-271 Tempaloni Road, Ellenville, NY 12428, USA"},{"location":{"latitude":404663628,"longitude":-744820157},"name":"1300 Airport Road, North Brunswick Township, NJ 08902, USA"},{"location":{"latitude":407113723,"longitude":-749746483},"name":""},{"location":{"latitude":402133926,"longitude":-743613249},"name":""},{"location":{"latitude":400273442,"longitude":-741220915},"name":""},{"location":{"latitude":411236786,"longitude":-744070769},"name":""},{"location":{"latitude":411633782,"longitude":-746784970},"name":"211-225 Plains Road, Augusta, NJ 07822, USA"},{"location":{"latitude":415830701,"longitude":-742952812},"name":""},{"location":{"latitude":413447164,"longitude":-748712898},"name":"165 Pedersen Ridge Road, Milford, PA 18337, USA"},{"location":{"latitude":405047245,"longitude":-749800722},"name":"100-122 Locktown Road, Frenchtown, NJ 08825, USA"},{"location":{"latitude":418858923,"longitude":-746156790},"name":""},{"location":{"latitude":417951888,"longitude":-748484944},"name":"650-652 Willi Hill Road, Swan Lake, NY 12783, USA"},{"location":{"latitude":407033786,"longitude":-743977337},"name":"26 East 3rd Street, New Providence, NJ 07974, USA"},{"location":{"latitude":417548014,"longitude":-740075041},"name":""},{"location":{"latitude":410395868,"longitude":-744972325},"name":""},{"location":{"latitude":404615353,"longitude":-745129803},"name":""},{"location":{"latitude":406589790,"longitude":-743560121},"name":"611 Lawrence Avenue, Westfield, NJ 07090, USA"},{"location":{"latitude":414653148,"longitude":-740477477},"name":"18 Lannis Avenue, New Windsor, NY 12553, USA"},{"location":{"latitude":405957808,"longitude":-743255336},"name":"82-104 Amherst Avenue, Colonia, NJ 07067, USA"},{"location":{"latitude":411733589,"longitude":-741648093},"name":"170 Seven Lakes Drive, Sloatsburg, NY 10974, USA"},{"location":{"latitude":412676291,"longitude":-742606606},"name":"1270 Lakes Road, Monroe, NY 10950, USA"},{"location":{"latitude":409224445,"longitude":-748286738},"name":"509-535 Alphano Road, Great Meadows, NJ 07838, USA"},{"location":{"latitude":406523420,"longitude":-742135517},"name":"652 Garden Street, Elizabeth, NJ 07202, USA"},{"location":{"latitude":401827388,"longitude":-740294537},"name":"349 Sea Spray Court, Neptune City, NJ 07753, USA"},{"location":{"latitude":410564152,"longitude":-743685054},"name":"13-17 Stanley Street, West Milford, NJ 07480, USA"},{"location":{"latitude":408472324,"longitude":-740726046},"name":"47 Industrial Avenue, Teterboro, NJ 07608, USA"},{"location":{"latitude":412452168,"longitude":-740214052},"name":"5 White Oak Lane, Stony Point, NY 10980, USA"},{"location":{"latitude":409146138,"longitude":-746188906},"name":"Berkshire Valley Management Area Trail, Jefferson, NJ, USA"},{"location":{"latitude":404701380,"longitude":-744781745},"name":"1007 Jersey Avenue, New Brunswick, NJ 08901, USA"},{"location":{"latitude":409642566,"longitude":-746017679},"name":"6 East Emerald Isle Drive, Lake Hopatcong, NJ 07849, USA"},{"location":{"latitude":408031728,"longitude":-748645385},"name":"1358-1474 New Jersey 57, Port Murray, NJ 07865, USA"},{"location":{"latitude":413700272,"longitude":-742135189},"name":"367 Prospect Road, Chester, NY 10918, USA"},{"location":{"latitude":404310607,"longitude":-740282632},"name":"10 Simon Lake Drive, Atlantic Highlands, NJ 07716, USA"},{"location":{"latitude":409319800,"longitude":-746201391},"name":"11 Ward Street, Mount Arlington, NJ 07856, USA"},{"location":{"latitude":406685311,"longitude":-742108603},"name":"300-398 Jefferson Avenue, Elizabeth, NJ 07201, USA"},{"location":{"latitude":419018117,"longitude":-749142781},"name":"43 Dreher Road, Roscoe, NY 12776, USA"},{"location":{"latitude":412856162,"longitude":-745148837},"name":"Swan Street, Pine Island, NY 10969, USA"},{"location":{"latitude":416560744,"longitude":-746721964},"name":"66 Pleasantview Avenue, Monticello, NY 12701, USA"},{"location":{"latitude":405314270,"longitude":-749836354},"name":""},{"location":{"latitude":414219548,"longitude":-743327440},"name":""},{"location":{"latitude":415534177,"longitude":-742900616},"name":"565 Winding Hills Road, Montgomery, NY 12549, USA"},{"location":{"latitude":406898530,"longitude":-749127080},"name":"231 Rocky Run Road, Glen Gardner, NJ 08826, USA"},{"location":{"latitude":407586880,"longitude":-741670168},"name":"100 Mount Pleasant Avenue, Newark, NJ 07104, USA"},{"location":{"latitude":400106455,"longitude":-742870190},"name":"517-521 Huntington Drive, Manchester Township, NJ 08759, USA"},{"location":{"latitude":400066188,"longitude":-746793294},"name":""},{"location":{"latitude":418803880,"longitude":-744102673},"name":"40 Mountain Road, Napanoch, NY 12458, USA"},{"location":{"latitude":414204288,"longitude":-747895140},"name":""},{"location":{"latitude":414777405,"longitude":-740615601},"name":""},{"location":{"latitude":415464475,"longitude":-747175374},"name":"48 North Road, Forestburgh, NY 12777, USA"},{"location":{"latitude":404062378,"longitude":-746376177},"name":""},{"location":{"latitude":405688272,"longitude":-749285130},"name":""},{"location":{"latitude":400342070,"longitude":-748788996},"name":""},{"location":{"latitude":401809022,"longitude":-744157964},"name":""},{"location":{"latitude":404226644,"longitude":-740517141},"name":"9 Thompson Avenue, Leonardo, NJ 07737, USA"},{"location":{"latitude":410322033,"longitude":-747871659},"name":""},{"location":{"latitude":407100674,"longitude":-747742727},"name":""},{"location":{"latitude":418811433,"longitude":-741718005},"name":"213 Bush Road, Stone Ridge, NY 12484, USA"},{"location":{"latitude":415034302,"longitude":-743850945},"name":""},{"location":{"latitude":411349992,"longitude":-743694161},"name":""},{"location":{"latitude":404839914,"longitude":-744759616},"name":"1-17 Bergen Court, New Brunswick, NJ 08901, USA"},{"location":{"latitude":414638017,"longitude":-745957854},"name":"35 Oakland Valley Road, Cuddebackville, NY 12729, USA"},{"location":{"latitude":412127800,"longitude":-740173578},"name":""},{"location":{"latitude":401263460,"longitude":-747964303},"name":""},{"location":{"latitude":412843391,"longitude":-749086026},"name":""},{"location":{"latitude":418512773,"longitude":-743067823},"name":""},{"location":{"latitude":404318328,"longitude":-740835638},"name":"42-102 Main Street, Belford, NJ 07718, USA"},{"location":{"latitude":419020746,"longitude":-741172328},"name":""},{"location":{"latitude":404080723,"longitude":-746119569},"name":""},{"location":{"latitude":401012643,"longitude":-744035134},"name":""},{"location":{"latitude":404306372,"longitude":-741079661},"name":""},{"location":{"latitude":403966326,"longitude":-748519297},"name":""},{"location":{"latitude":405002031,"longitude":-748407866},"name":""},{"location":{"latitude":409532885,"longitude":-742200683},"name":""},{"location":{"latitude":416851321,"longitude":-742674555},"name":""},{"location":{"latitude":406411633,"longitude":-741722051},"name":"3387 Richmond Terrace, Staten Island, NY 10303, USA"},{"location":{"latitude":413069058,"longitude":-744597778},"name":"261 Van Sickle Road, Goshen, NY 10924, USA"},{"location":{"latitude":418465462,"longitude":-746859398},"name":""},{"location":{"latitude":411733222,"longitude":-744228360},"name":""},{"location":{"latitude":410248224,"longitude":-747127767},"name":"3 Hasta Way, Newton, NJ 07860, USA"}] +[{ + "location": { + "latitude": 407838351, + "longitude": -746143763 + }, + "name": "Patriots Path, Mendham, NJ 07945, USA" +}, { + "location": { + "latitude": 408122808, + "longitude": -743999179 + }, + "name": "101 New Jersey 10, Whippany, NJ 07981, USA" +}, { + "location": { + "latitude": 413628156, + "longitude": -749015468 + }, + "name": "U.S. 6, Shohola, PA 18458, USA" +}, { + "location": { + "latitude": 419999544, + "longitude": -740371136 + }, + "name": "5 Conners Road, Kingston, NY 12401, USA" +}, { + "location": { + "latitude": 414008389, + "longitude": -743951297 + }, + "name": "Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA" +}, { + "location": { + "latitude": 419611318, + "longitude": -746524769 + }, + "name": "287 Flugertown Road, Livingston Manor, NY 12758, USA" +}, { + "location": { + "latitude": 406109563, + "longitude": -742186778 + }, + "name": "4001 Tremley Point Road, Linden, NJ 07036, USA" +}, { + "location": { + "latitude": 416802456, + "longitude": -742370183 + }, + "name": "352 South Mountain Road, Wallkill, NY 12589, USA" +}, { + "location": { + "latitude": 412950425, + "longitude": -741077389 + }, + "name": "Bailey Turn Road, Harriman, NY 10926, USA" +}, { + "location": { + "latitude": 412144655, + "longitude": -743949739 + }, + "name": "193-199 Wawayanda Road, Hewitt, NJ 07421, USA" +}, { + "location": { + "latitude": 415736605, + "longitude": -742847522 + }, + "name": "406-496 Ward Avenue, Pine Bush, NY 12566, USA" +}, { + "location": { + "latitude": 413843930, + "longitude": -740501726 + }, + "name": "162 Merrill Road, Highland Mills, NY 10930, USA" +}, { + "location": { + "latitude": 410873075, + "longitude": -744459023 + }, + "name": "Clinton Road, West Milford, NJ 07480, USA" +}, { + "location": { + "latitude": 412346009, + "longitude": -744026814 + }, + "name": "16 Old Brook Lane, Warwick, NY 10990, USA" +}, { + "location": { + "latitude": 402948455, + "longitude": -747903913 + }, + "name": "3 Drake Lane, Pennington, NJ 08534, USA" +}, { + "location": { + "latitude": 406337092, + "longitude": -740122226 + }, + "name": "6324 8th Avenue, Brooklyn, NY 11220, USA" +}, { + "location": { + "latitude": 406421967, + "longitude": -747727624 + }, + "name": "1 Merck Access Road, Whitehouse Station, NJ 08889, USA" +}, { + "location": { + "latitude": 416318082, + "longitude": -749677716 + }, + "name": "78-98 Schalck Road, Narrowsburg, NY 12764, USA" +}, { + "location": { + "latitude": 415301720, + "longitude": -748416257 + }, + "name": "282 Lakeview Drive Road, Highland Lake, NY 12743, USA" +}, { + "location": { + "latitude": 402647019, + "longitude": -747071791 + }, + "name": "330 Evelyn Avenue, Hamilton Township, NJ 08619, USA" +}, { + "location": { + "latitude": 412567807, + "longitude": -741058078 + }, + "name": "New York State Reference Route 987E, Southfields, NY 10975, USA" +}, { + "location": { + "latitude": 416855156, + "longitude": -744420597 + }, + "name": "103-271 Tempaloni Road, Ellenville, NY 12428, USA" +}, { + "location": { + "latitude": 404663628, + "longitude": -744820157 + }, + "name": "1300 Airport Road, North Brunswick Township, NJ 08902, USA" +}, { + "location": { + "latitude": 407113723, + "longitude": -749746483 + }, + "name": "" +}, { + "location": { + "latitude": 402133926, + "longitude": -743613249 + }, + "name": "" +}, { + "location": { + "latitude": 400273442, + "longitude": -741220915 + }, + "name": "" +}, { + "location": { + "latitude": 411236786, + "longitude": -744070769 + }, + "name": "" +}, { + "location": { + "latitude": 411633782, + "longitude": -746784970 + }, + "name": "211-225 Plains Road, Augusta, NJ 07822, USA" +}, { + "location": { + "latitude": 415830701, + "longitude": -742952812 + }, + "name": "" +}, { + "location": { + "latitude": 413447164, + "longitude": -748712898 + }, + "name": "165 Pedersen Ridge Road, Milford, PA 18337, USA" +}, { + "location": { + "latitude": 405047245, + "longitude": -749800722 + }, + "name": "100-122 Locktown Road, Frenchtown, NJ 08825, USA" +}, { + "location": { + "latitude": 418858923, + "longitude": -746156790 + }, + "name": "" +}, { + "location": { + "latitude": 417951888, + "longitude": -748484944 + }, + "name": "650-652 Willi Hill Road, Swan Lake, NY 12783, USA" +}, { + "location": { + "latitude": 407033786, + "longitude": -743977337 + }, + "name": "26 East 3rd Street, New Providence, NJ 07974, USA" +}, { + "location": { + "latitude": 417548014, + "longitude": -740075041 + }, + "name": "" +}, { + "location": { + "latitude": 410395868, + "longitude": -744972325 + }, + "name": "" +}, { + "location": { + "latitude": 404615353, + "longitude": -745129803 + }, + "name": "" +}, { + "location": { + "latitude": 406589790, + "longitude": -743560121 + }, + "name": "611 Lawrence Avenue, Westfield, NJ 07090, USA" +}, { + "location": { + "latitude": 414653148, + "longitude": -740477477 + }, + "name": "18 Lannis Avenue, New Windsor, NY 12553, USA" +}, { + "location": { + "latitude": 405957808, + "longitude": -743255336 + }, + "name": "82-104 Amherst Avenue, Colonia, NJ 07067, USA" +}, { + "location": { + "latitude": 411733589, + "longitude": -741648093 + }, + "name": "170 Seven Lakes Drive, Sloatsburg, NY 10974, USA" +}, { + "location": { + "latitude": 412676291, + "longitude": -742606606 + }, + "name": "1270 Lakes Road, Monroe, NY 10950, USA" +}, { + "location": { + "latitude": 409224445, + "longitude": -748286738 + }, + "name": "509-535 Alphano Road, Great Meadows, NJ 07838, USA" +}, { + "location": { + "latitude": 406523420, + "longitude": -742135517 + }, + "name": "652 Garden Street, Elizabeth, NJ 07202, USA" +}, { + "location": { + "latitude": 401827388, + "longitude": -740294537 + }, + "name": "349 Sea Spray Court, Neptune City, NJ 07753, USA" +}, { + "location": { + "latitude": 410564152, + "longitude": -743685054 + }, + "name": "13-17 Stanley Street, West Milford, NJ 07480, USA" +}, { + "location": { + "latitude": 408472324, + "longitude": -740726046 + }, + "name": "47 Industrial Avenue, Teterboro, NJ 07608, USA" +}, { + "location": { + "latitude": 412452168, + "longitude": -740214052 + }, + "name": "5 White Oak Lane, Stony Point, NY 10980, USA" +}, { + "location": { + "latitude": 409146138, + "longitude": -746188906 + }, + "name": "Berkshire Valley Management Area Trail, Jefferson, NJ, USA" +}, { + "location": { + "latitude": 404701380, + "longitude": -744781745 + }, + "name": "1007 Jersey Avenue, New Brunswick, NJ 08901, USA" +}, { + "location": { + "latitude": 409642566, + "longitude": -746017679 + }, + "name": "6 East Emerald Isle Drive, Lake Hopatcong, NJ 07849, USA" +}, { + "location": { + "latitude": 408031728, + "longitude": -748645385 + }, + "name": "1358-1474 New Jersey 57, Port Murray, NJ 07865, USA" +}, { + "location": { + "latitude": 413700272, + "longitude": -742135189 + }, + "name": "367 Prospect Road, Chester, NY 10918, USA" +}, { + "location": { + "latitude": 404310607, + "longitude": -740282632 + }, + "name": "10 Simon Lake Drive, Atlantic Highlands, NJ 07716, USA" +}, { + "location": { + "latitude": 409319800, + "longitude": -746201391 + }, + "name": "11 Ward Street, Mount Arlington, NJ 07856, USA" +}, { + "location": { + "latitude": 406685311, + "longitude": -742108603 + }, + "name": "300-398 Jefferson Avenue, Elizabeth, NJ 07201, USA" +}, { + "location": { + "latitude": 419018117, + "longitude": -749142781 + }, + "name": "43 Dreher Road, Roscoe, NY 12776, USA" +}, { + "location": { + "latitude": 412856162, + "longitude": -745148837 + }, + "name": "Swan Street, Pine Island, NY 10969, USA" +}, { + "location": { + "latitude": 416560744, + "longitude": -746721964 + }, + "name": "66 Pleasantview Avenue, Monticello, NY 12701, USA" +}, { + "location": { + "latitude": 405314270, + "longitude": -749836354 + }, + "name": "" +}, { + "location": { + "latitude": 414219548, + "longitude": -743327440 + }, + "name": "" +}, { + "location": { + "latitude": 415534177, + "longitude": -742900616 + }, + "name": "565 Winding Hills Road, Montgomery, NY 12549, USA" +}, { + "location": { + "latitude": 406898530, + "longitude": -749127080 + }, + "name": "231 Rocky Run Road, Glen Gardner, NJ 08826, USA" +}, { + "location": { + "latitude": 407586880, + "longitude": -741670168 + }, + "name": "100 Mount Pleasant Avenue, Newark, NJ 07104, USA" +}, { + "location": { + "latitude": 400106455, + "longitude": -742870190 + }, + "name": "517-521 Huntington Drive, Manchester Township, NJ 08759, USA" +}, { + "location": { + "latitude": 400066188, + "longitude": -746793294 + }, + "name": "" +}, { + "location": { + "latitude": 418803880, + "longitude": -744102673 + }, + "name": "40 Mountain Road, Napanoch, NY 12458, USA" +}, { + "location": { + "latitude": 414204288, + "longitude": -747895140 + }, + "name": "" +}, { + "location": { + "latitude": 414777405, + "longitude": -740615601 + }, + "name": "" +}, { + "location": { + "latitude": 415464475, + "longitude": -747175374 + }, + "name": "48 North Road, Forestburgh, NY 12777, USA" +}, { + "location": { + "latitude": 404062378, + "longitude": -746376177 + }, + "name": "" +}, { + "location": { + "latitude": 405688272, + "longitude": -749285130 + }, + "name": "" +}, { + "location": { + "latitude": 400342070, + "longitude": -748788996 + }, + "name": "" +}, { + "location": { + "latitude": 401809022, + "longitude": -744157964 + }, + "name": "" +}, { + "location": { + "latitude": 404226644, + "longitude": -740517141 + }, + "name": "9 Thompson Avenue, Leonardo, NJ 07737, USA" +}, { + "location": { + "latitude": 410322033, + "longitude": -747871659 + }, + "name": "" +}, { + "location": { + "latitude": 407100674, + "longitude": -747742727 + }, + "name": "" +}, { + "location": { + "latitude": 418811433, + "longitude": -741718005 + }, + "name": "213 Bush Road, Stone Ridge, NY 12484, USA" +}, { + "location": { + "latitude": 415034302, + "longitude": -743850945 + }, + "name": "" +}, { + "location": { + "latitude": 411349992, + "longitude": -743694161 + }, + "name": "" +}, { + "location": { + "latitude": 404839914, + "longitude": -744759616 + }, + "name": "1-17 Bergen Court, New Brunswick, NJ 08901, USA" +}, { + "location": { + "latitude": 414638017, + "longitude": -745957854 + }, + "name": "35 Oakland Valley Road, Cuddebackville, NY 12729, USA" +}, { + "location": { + "latitude": 412127800, + "longitude": -740173578 + }, + "name": "" +}, { + "location": { + "latitude": 401263460, + "longitude": -747964303 + }, + "name": "" +}, { + "location": { + "latitude": 412843391, + "longitude": -749086026 + }, + "name": "" +}, { + "location": { + "latitude": 418512773, + "longitude": -743067823 + }, + "name": "" +}, { + "location": { + "latitude": 404318328, + "longitude": -740835638 + }, + "name": "42-102 Main Street, Belford, NJ 07718, USA" +}, { + "location": { + "latitude": 419020746, + "longitude": -741172328 + }, + "name": "" +}, { + "location": { + "latitude": 404080723, + "longitude": -746119569 + }, + "name": "" +}, { + "location": { + "latitude": 401012643, + "longitude": -744035134 + }, + "name": "" +}, { + "location": { + "latitude": 404306372, + "longitude": -741079661 + }, + "name": "" +}, { + "location": { + "latitude": 403966326, + "longitude": -748519297 + }, + "name": "" +}, { + "location": { + "latitude": 405002031, + "longitude": -748407866 + }, + "name": "" +}, { + "location": { + "latitude": 409532885, + "longitude": -742200683 + }, + "name": "" +}, { + "location": { + "latitude": 416851321, + "longitude": -742674555 + }, + "name": "" +}, { + "location": { + "latitude": 406411633, + "longitude": -741722051 + }, + "name": "3387 Richmond Terrace, Staten Island, NY 10303, USA" +}, { + "location": { + "latitude": 413069058, + "longitude": -744597778 + }, + "name": "261 Van Sickle Road, Goshen, NY 10924, USA" +}, { + "location": { + "latitude": 418465462, + "longitude": -746859398 + }, + "name": "" +}, { + "location": { + "latitude": 411733222, + "longitude": -744228360 + }, + "name": "" +}, { + "location": { + "latitude": 410248224, + "longitude": -747127767 + }, + "name": "3 Hasta Way, Newton, NJ 07860, USA" +}] From ce0cfef3a392660d0b55586d32383c6f3487bc21 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 20 Feb 2015 11:34:47 -0800 Subject: [PATCH 56/91] Added comments to route_guide_client.js --- examples/route_guide_client.js | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/examples/route_guide_client.js b/examples/route_guide_client.js index 549c9b78..10da6d3a 100644 --- a/examples/route_guide_client.js +++ b/examples/route_guide_client.js @@ -36,6 +36,11 @@ var client = new examples.RouteGuide('localhost:50051'); var COORD_FACTOR = 1e7; +/** + * Run the getFeature demo. Calls getFeature with a point known to have a + * feature and a point known not to have a feature. + * @param {function} callback Called when this demo is complete + */ function runGetFeature(callback) { var next = _.after(2, callback); function featureCallback(error, feature) { @@ -65,6 +70,12 @@ function runGetFeature(callback) { client.getFeature(point2, featureCallback); } +/** + * Run the listFeatures demo. Calls listFeatures with a rectangle containing all + * of the features in the pre-generated database. Prints each response as it + * comes in. + * @param {function} callback Called when this demo is complete + */ function runListFeatures(callback) { var rectangle = { lo: { @@ -86,6 +97,12 @@ function runListFeatures(callback) { call.on('end', callback); } +/** + * Run the recordRoute demo. Sends several randomly chosen points from the + * pre-generated feature database with a variable delay in between. Prints the + * statistics when they are sent from the server. + * @param {function} callback Called when this demo is complete + */ function runRecordRoute(callback) { fs.readFile(__dirname + '/route_guide_db.json', function(err, data) { if (err) callback(err); @@ -102,7 +119,18 @@ function runRecordRoute(callback) { console.log('It took', stats.elapsed_time, 'seconds'); callback(); }); + /** + * Constructs a function that asynchronously sends the given point and then + * delays sending its callback + * @param {number} lat The latitude to send + * @param {number} lng The longitude to send + * @return {function(function)} The function that sends the point + */ function pointSender(lat, lng) { + /** + * Sends the point, then calls the callback after a delay + * @param {function} callback Called when complete + */ return function(callback) { console.log('Visiting point ' + lat/COORD_FACTOR + ', ' + lng/COORD_FACTOR); @@ -125,6 +153,11 @@ function runRecordRoute(callback) { }); } +/** + * Run the routeChat demo. Send some chat messages, and print any chat messages + * that are sent from the server. + * @param {function} callback Called when the demo is complete + */ function runRouteChat(callback) { var call = client.routeChat(); call.on('data', function(note) { @@ -168,6 +201,9 @@ function runRouteChat(callback) { call.end(); } +/** + * Run all of the demos in order + */ function main() { async.series([ runGetFeature, From 7ba78a6b276293abb9465e8d8d4aa378555489de Mon Sep 17 00:00:00 2001 From: Yang Gao Date: Fri, 20 Feb 2015 13:04:52 -0800 Subject: [PATCH 57/91] clean up some internal path and names --- interop/empty.proto | 6 ------ 1 file changed, 6 deletions(-) diff --git a/interop/empty.proto b/interop/empty.proto index f66a108c..4295a0a9 100644 --- a/interop/empty.proto +++ b/interop/empty.proto @@ -40,10 +40,4 @@ package grpc.testing; // rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { }; // }; // -// MOE:begin_strip -// The difference between this one and net/rpc/empty-message.proto is that -// 1) The generated message here is in proto2 C++ API. -// 2) The proto2.Empty has minimum dependencies -// (no message_set or net/rpc dependencies) -// MOE:end_strip message Empty {} From fb0f4ddb00c26e682f64762eb3bda7104fe144cd Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 20 Feb 2015 14:58:58 -0800 Subject: [PATCH 58/91] Added pubsub demo client --- examples/pubsub/empty.proto | 44 ++ examples/pubsub/label.proto | 79 ++++ examples/pubsub/pubsub.proto | 734 +++++++++++++++++++++++++++++++++ examples/pubsub/pubsub_demo.js | 273 ++++++++++++ 4 files changed, 1130 insertions(+) create mode 100644 examples/pubsub/empty.proto create mode 100644 examples/pubsub/label.proto create mode 100644 examples/pubsub/pubsub.proto create mode 100644 examples/pubsub/pubsub_demo.js diff --git a/examples/pubsub/empty.proto b/examples/pubsub/empty.proto new file mode 100644 index 00000000..5d6eb108 --- /dev/null +++ b/examples/pubsub/empty.proto @@ -0,0 +1,44 @@ +// This file will be moved to a new location. + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto2"; + +package proto2; + +// An empty message that you can re-use to avoid defining duplicated empty +// messages in your project. A typical example is to use it as argument or the +// return value of a service API. For instance: +// +// service Foo { +// rpc Bar (proto2.Empty) returns (proto2.Empty) { }; +// }; +// +message Empty {} diff --git a/examples/pubsub/label.proto b/examples/pubsub/label.proto new file mode 100644 index 00000000..0af15a25 --- /dev/null +++ b/examples/pubsub/label.proto @@ -0,0 +1,79 @@ +// This file will be moved to a new location. + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Labels provide a way to associate user-defined metadata with various +// objects. Labels may be used to organize objects into non-hierarchical +// groups; think metadata tags attached to mp3s. + +syntax = "proto2"; + +package tech.label; + +// A key-value pair applied to a given object. +message Label { + // The key of a label is a syntactically valid URL (as per RFC 1738) with + // the "scheme" and initial slashes omitted and with the additional + // restrictions noted below. Each key should be globally unique. The + // "host" portion is called the "namespace" and is not necessarily + // resolvable to a network endpoint. Instead, the namespace indicates what + // system or entity defines the semantics of the label. Namespaces do not + // restrict the set of objects to which a label may be associated. + // + // Keys are defined by the following grammar: + // + // key = hostname "/" kpath + // kpath = ksegment *[ "/" ksegment ] + // ksegment = alphadigit | *[ alphadigit | "-" | "_" | "." ] + // + // where "hostname" and "alphadigit" are defined as in RFC 1738. + // + // Example key: + // spanner.google.com/universe + required string key = 1; + + // The value of the label. + oneof value { + // A string value. + string str_value = 2; + // An integer value. + int64 num_value = 3; + } +} + +// A collection of labels, such as the set of all labels attached to an +// object. Each label in the set must have a different key. +// +// Users should prefer to embed "repeated Label" directly when possible. +// This message should only be used in cases where that isn't possible (e.g. +// with oneof). +message Labels { + repeated Label label = 1; +} diff --git a/examples/pubsub/pubsub.proto b/examples/pubsub/pubsub.proto new file mode 100644 index 00000000..ef88981b --- /dev/null +++ b/examples/pubsub/pubsub.proto @@ -0,0 +1,734 @@ +// This file will be moved to a new location. + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// Specification of the Pubsub API. + +syntax = "proto2"; + +import "examples/pubsub/empty.proto"; +import "examples/pubsub/label.proto"; + +package tech.pubsub; + +// ----------------------------------------------------------------------------- +// Overview of the Pubsub API +// ----------------------------------------------------------------------------- + +// This file describes an API for a Pubsub system. This system provides a +// reliable many-to-many communication mechanism between independently written +// publishers and subscribers where the publisher publishes messages to "topics" +// and each subscriber creates a "subscription" and consumes messages from it. +// +// (a) The pubsub system maintains bindings between topics and subscriptions. +// (b) A publisher publishes messages into a topic. +// (c) The pubsub system delivers messages from topics into relevant +// subscriptions. +// (d) A subscriber receives pending messages from its subscription and +// acknowledges or nacks each one to the pubsub system. +// (e) The pubsub system removes acknowledged messages from that subscription. + +// ----------------------------------------------------------------------------- +// Data Model +// ----------------------------------------------------------------------------- + +// The data model consists of the following: +// +// * Topic: A topic is a resource to which messages are published by publishers. +// Topics are named, and the name of the topic is unique within the pubsub +// system. +// +// * Subscription: A subscription records the subscriber's interest in a topic. +// It can optionally include a query to select a subset of interesting +// messages. The pubsub system maintains a logical cursor tracking the +// matching messages which still need to be delivered and acked so that +// they can retried as needed. The set of messages that have not been +// acknowledged is called the subscription backlog. +// +// * Message: A message is a unit of data that flows in the system. It contains +// opaque data from the publisher along with its labels. +// +// * Message Labels (optional): A set of opaque key, value pairs assigned +// by the publisher which the subscriber can use for filtering out messages +// in the topic. For example, a label with key "foo.com/device_type" and +// value "mobile" may be added for messages that are only relevant for a +// mobile subscriber; a subscriber on a phone may decide to create a +// subscription only for messages that have this label. + +// ----------------------------------------------------------------------------- +// Publisher Flow +// ----------------------------------------------------------------------------- + +// A publisher publishes messages to the topic using the Publish request: +// +// PubsubMessage message; +// message.set_data("...."); +// Label label; +// label.set_key("foo.com/key1"); +// label.set_str_value("value1"); +// message.add_label(label); +// PublishRequest request; +// request.set_topic("topicName"); +// request.set_message(message); +// PublisherService.Publish(request); + +// ----------------------------------------------------------------------------- +// Subscriber Flow +// ----------------------------------------------------------------------------- + +// The subscriber part of the API is richer than the publisher part and has a +// number of concepts w.r.t. subscription creation and monitoring: +// +// (1) A subscriber creates a subscription using the CreateSubscription call. +// It may specify an optional "query" to indicate that it wants to receive +// only messages with a certain set of labels using the label query syntax. +// It may also specify an optional truncation policy to indicate when old +// messages from the subcription can be removed. +// +// (2) A subscriber receives messages in one of two ways: via push or pull. +// +// (a) To receive messages via push, the PushConfig field must be specified in +// the Subscription parameter when creating a subscription. The PushConfig +// specifies an endpoint at which the subscriber must expose the +// PushEndpointService. Messages are received via the HandlePubsubEvent +// method. The push subscriber responds to the HandlePubsubEvent method +// with a result code that indicates one of three things: Ack (the message +// has been successfully processed and the Pubsub system may delete it), +// Nack (the message has been rejected, the Pubsub system should resend it +// at a later time), or Push-Back (this is a Nack with the additional +// semantics that the subscriber is overloaded and the pubsub system should +// back off on the rate at which it is invoking HandlePubsubEvent). The +// endpoint may be a load balancer for better scalability. +// +// (b) To receive messages via pull a subscriber calls the Pull method on the +// SubscriberService to get messages from the subscription. For each +// individual message, the subscriber may use the ack_id received in the +// PullResponse to Ack the message, Nack the message, or modify the ack +// deadline with ModifyAckDeadline. See the +// Subscription.ack_deadline_seconds field documentation for details on the +// ack deadline behavior. +// +// Note: Messages may be consumed in parallel by multiple subscribers making +// Pull calls to the same subscription; this will result in the set of +// messages from the subscription being shared and each subscriber +// receiving a subset of the messages. +// +// (4) The subscriber can explicitly truncate the current subscription. +// +// (5) "Truncated" events are delivered when a subscription is +// truncated, whether due to the subscription's truncation policy +// or an explicit request from the subscriber. +// +// Subscription creation: +// +// Subscription subscription; +// subscription.set_topic("topicName"); +// subscription.set_name("subscriptionName"); +// subscription.push_config().set_push_endpoint("machinename:8888"); +// SubscriberService.CreateSubscription(subscription); +// +// Consuming messages via push: +// +// TODO(eschapira): Add HTTP push example. +// +// The port 'machinename:8888' must be bound to a stubby server that implements +// the PushEndpointService with the following method: +// +// int HandlePubsubEvent(PubsubEvent event) { +// if (event.subscription().equals("subscriptionName")) { +// if (event.has_message()) { +// Process(event.message().data()); +// } else if (event.truncated()) { +// ProcessTruncatedEvent(); +// } +// } +// return OK; // This return code implies an acknowledgment +// } +// +// Consuming messages via pull: +// +// The subscription must be created without setting the push_config field. +// +// PullRequest pull_request; +// pull_request.set_subscription("subscriptionName"); +// pull_request.set_return_immediately(false); +// while (true) { +// PullResponse pull_response; +// if (SubscriberService.Pull(pull_request, pull_response) == OK) { +// PubsubEvent event = pull_response.pubsub_event(); +// if (event.has_message()) { +// Process(event.message().data()); +// } else if (event.truncated()) { +// ProcessTruncatedEvent(); +// } +// AcknowledgeRequest ack_request; +// ackRequest.set_subscription("subscriptionName"); +// ackRequest.set_ack_id(pull_response.ack_id()); +// SubscriberService.Acknowledge(ack_request); +// } +// } + +// ----------------------------------------------------------------------------- +// Reliability Semantics +// ----------------------------------------------------------------------------- + +// When a subscriber successfully creates a subscription using +// Subscriber.CreateSubscription, it establishes a "subscription point" with +// respect to that subscription - the subscriber is guaranteed to receive any +// message published after this subscription point that matches the +// subscription's query. Note that messages published before the Subscription +// point may or may not be delivered. +// +// If the system truncates the subscription according to the specified +// truncation policy, the system delivers a subscription status event with the +// "truncated" field set to true. We refer to such events as "truncation +// events". A truncation event: +// +// * Informs the subscriber that part of the subscription messages have been +// discarded. The subscriber may want to recover from the message loss, e.g., +// by resyncing its state with its backend. +// * Establishes a new subscription point, i.e., the subscriber is guaranteed to +// receive all changes published after the trunction event is received (or +// until another truncation event is received). +// +// Note that messages are not delivered in any particular order by the pubsub +// system. Furthermore, the system guarantees at-least-once delivery +// of each message or truncation events until acked. + +// ----------------------------------------------------------------------------- +// Deletion +// ----------------------------------------------------------------------------- + +// Both topics and subscriptions may be deleted. Deletion of a topic implies +// deletion of all attached subscriptions. +// +// When a subscription is deleted directly by calling DeleteSubscription, all +// messages are immediately dropped. If it is a pull subscriber, future pull +// requests will return NOT_FOUND. +// +// When a topic is deleted all corresponding subscriptions are immediately +// deleted, and subscribers experience the same behavior as directly deleting +// the subscription. + +// ----------------------------------------------------------------------------- +// The Publisher service and its protos. +// ----------------------------------------------------------------------------- + +// The service that an application uses to manipulate topics, and to send +// messages to a topic. +service PublisherService { + + // Creates the given topic with the given name. + rpc CreateTopic(Topic) returns (Topic) { + } + + // Adds a message to the topic. Returns NOT_FOUND if the topic does not + // exist. + // (-- For different error code values returned via Stubby, see + // util/task/codes.proto. --) + rpc Publish(PublishRequest) returns (proto2.Empty) { + } + + // Adds one or more messages to the topic. Returns NOT_FOUND if the topic does + // not exist. + rpc PublishBatch(PublishBatchRequest) returns (PublishBatchResponse) { + } + + // Gets the configuration of a topic. Since the topic only has the name + // attribute, this method is only useful to check the existence of a topic. + // If other attributes are added in the future, they will be returned here. + rpc GetTopic(GetTopicRequest) returns (Topic) { + } + + // Lists matching topics. + rpc ListTopics(ListTopicsRequest) returns (ListTopicsResponse) { + } + + // Deletes the topic with the given name. All subscriptions to this topic + // are also deleted. Returns NOT_FOUND if the topic does not exist. + // After a topic is deleted, a new topic may be created with the same name. + rpc DeleteTopic(DeleteTopicRequest) returns (proto2.Empty) { + } +} + +// A topic resource. +message Topic { + // Name of the topic. + optional string name = 1; +} + +// A message data and its labels. +message PubsubMessage { + // The message payload. + optional bytes data = 1; + + // Optional list of labels for this message. Keys in this collection must + // be unique. + //(-- TODO(eschapira): Define how key namespace may be scoped to the topic.--) + repeated tech.label.Label label = 2; + + // ID of this message assigned by the server at publication time. Guaranteed + // to be unique within the topic. This value may be read by a subscriber + // that receives a PubsubMessage via a Pull call or a push delivery. It must + // not be populated by a publisher in a Publish call. + optional string message_id = 3; +} + +// Request for the GetTopic method. +message GetTopicRequest { + // The name of the topic to get. + optional string topic = 1; +} + +// Request for the Publish method. +message PublishRequest { + // The message in the request will be published on this topic. + optional string topic = 1; + + // The message to publish. + optional PubsubMessage message = 2; +} + +// Request for the PublishBatch method. +message PublishBatchRequest { + // The messages in the request will be published on this topic. + optional string topic = 1; + + // The messages to publish. + repeated PubsubMessage messages = 2; +} + +// Response for the PublishBatch method. +message PublishBatchResponse { + // The server-assigned ID of each published message, in the same order as + // the messages in the request. IDs are guaranteed to be unique within + // the topic. + repeated string message_ids = 1; +} + +// Request for the ListTopics method. +message ListTopicsRequest { + // A valid label query expression. + // + optional string query = 1; + + // Maximum number of topics to return. + // (-- If not specified or <= 0, the implementation will select a reasonable + // value. --) + optional int32 max_results = 2; + + // The value obtained in the last ListTopicsResponse + // for continuation. + optional string page_token = 3; + +} + +// Response for the ListTopics method. +message ListTopicsResponse { + // The resulting topics. + repeated Topic topic = 1; + + // If not empty, indicates that there are more topics that match the request, + // and this value should be passed to the next ListTopicsRequest + // to continue. + optional string next_page_token = 2; +} + +// Request for the Delete method. +message DeleteTopicRequest { + // Name of the topic to delete. + optional string topic = 1; +} + +// ----------------------------------------------------------------------------- +// The Subscriber service and its protos. +// ----------------------------------------------------------------------------- + +// The service that an application uses to manipulate subscriptions and to +// consume messages from a subscription via the pull method. +service SubscriberService { + + // Creates a subscription on a given topic for a given subscriber. + // If the subscription already exists, returns ALREADY_EXISTS. + // If the corresponding topic doesn't exist, returns NOT_FOUND. + // + // If the name is not provided in the request, the server will assign a random + // name for this subscription on the same project as the topic. + rpc CreateSubscription(Subscription) returns (Subscription) { + } + + // Gets the configuration details of a subscription. + rpc GetSubscription(GetSubscriptionRequest) returns (Subscription) { + } + + // Lists matching subscriptions. + rpc ListSubscriptions(ListSubscriptionsRequest) + returns (ListSubscriptionsResponse) { + } + + // Deletes an existing subscription. All pending messages in the subscription + // are immediately dropped. Calls to Pull after deletion will return + // NOT_FOUND. + rpc DeleteSubscription(DeleteSubscriptionRequest) returns (proto2.Empty) { + } + + // Removes all the pending messages in the subscription and releases the + // storage associated with them. Results in a truncation event to be sent to + // the subscriber. Messages added after this call returns are stored in the + // subscription as before. + rpc TruncateSubscription(TruncateSubscriptionRequest) returns (proto2.Empty) { + } + + // + // Push subscriber calls. + // + + // Modifies the PushConfig for a specified subscription. + // This method can be used to suspend the flow of messages to an endpoint + // by clearing the PushConfig field in the request. Messages + // will be accumulated for delivery even if no push configuration is + // defined or while the configuration is modified. + rpc ModifyPushConfig(ModifyPushConfigRequest) returns (proto2.Empty) { + } + + // + // Pull Subscriber calls + // + + // Pulls a single message from the server. + // If return_immediately is true, and no messages are available in the + // subscription, this method returns FAILED_PRECONDITION. The system is free + // to return an UNAVAILABLE error if no messages are available in a + // reasonable amount of time (to reduce system load). + rpc Pull(PullRequest) returns (PullResponse) { + } + + // Pulls messages from the server. Returns an empty list if there are no + // messages available in the backlog. The system is free to return UNAVAILABLE + // if there are too many pull requests outstanding for the given subscription. + rpc PullBatch(PullBatchRequest) returns (PullBatchResponse) { + } + + // Modifies the Ack deadline for a message received from a pull request. + rpc ModifyAckDeadline(ModifyAckDeadlineRequest) returns (proto2.Empty) { + } + + // Acknowledges a particular received message: the Pub/Sub system can remove + // the given message from the subscription. Acknowledging a message whose + // Ack deadline has expired may succeed, but the message could have been + // already redelivered. Acknowledging a message more than once will not + // result in an error. This is only used for messages received via pull. + rpc Acknowledge(AcknowledgeRequest) returns (proto2.Empty) { + } + + // Refuses processing a particular received message. The system will + // redeliver this message to some consumer of the subscription at some + // future time. This is only used for messages received via pull. + rpc Nack(NackRequest) returns (proto2.Empty) { + } +} + +// A subscription resource. +message Subscription { + // Name of the subscription. + optional string name = 1; + + // The name of the topic from which this subscription is receiving messages. + optional string topic = 2; + + // If query is non-empty, only messages on the subscriber's + // topic whose labels match the query will be returned. Otherwise all + // messages on the topic will be returned. + // + optional string query = 3; + + // The subscriber may specify requirements for truncating unacknowledged + // subscription entries. The system will honor the + // CreateSubscription request only if it can meet these + // requirements. If this field is not specified, messages are never truncated + // by the system. + optional TruncationPolicy truncation_policy = 4; + + // Specifies which messages can be truncated by the system. + message TruncationPolicy { + oneof policy { + // If max_bytes is specified, the system is allowed to drop + // old messages to keep the combined size of stored messages under + // max_bytes. This is a hint; the system may keep more than + // this many bytes, but will make a best effort to keep the size from + // growing much beyond this parameter. + int64 max_bytes = 1; + + // If max_age_seconds is specified, the system is allowed to + // drop messages that have been stored for at least this many seconds. + // This is a hint; the system may keep these messages, but will make a + // best effort to remove them when their maximum age is reached. + int64 max_age_seconds = 2; + } + } + + // If push delivery is used with this subscription, this field is + // used to configure it. + optional PushConfig push_config = 5; + + // For either push or pull delivery, the value is the maximum time after a + // subscriber receives a message before the subscriber should acknowledge or + // Nack the message. If the Ack deadline for a message passes without an + // Ack or a Nack, the Pub/Sub system will eventually redeliver the message. + // If a subscriber acknowledges after the deadline, the Pub/Sub system may + // accept the Ack, but it is possible that the message has been already + // delivered again. Multiple Acks to the message are allowed and will + // succeed. + // + // For push delivery, this value is used to set the request timeout for + // the call to the push endpoint. + // + // For pull delivery, this value is used as the initial value for the Ack + // deadline. It may be overridden for a specific pull request (message) with + // ModifyAckDeadline. + // While a message is outstanding (i.e. it has been delivered to a pull + // subscriber and the subscriber has not yet Acked or Nacked), the Pub/Sub + // system will not deliver that message to another pull subscriber + // (on a best-effort basis). + optional int32 ack_deadline_seconds = 6; + + // If this parameter is set to n, the system is allowed to (but not required + // to) delete the subscription when at least n seconds have elapsed since the + // client presence was detected. (Presence is detected through any + // interaction using the subscription ID, including Pull(), Get(), or + // acknowledging a message.) + // + // If this parameter is not set, the subscription will stay live until + // explicitly deleted. + // + // Clients can detect such garbage collection when a Get call or a Pull call + // (for pull subscribers only) returns NOT_FOUND. + optional int64 garbage_collect_seconds = 7; +} + +// Configuration for a push delivery endpoint. +message PushConfig { + // A URL locating the endpoint to which messages should be pushed. + // For example, a Webhook endpoint might use "https://example.com/push". + // (-- An Android application might use "gcm:", where is a + // GCM registration id allocated for pushing messages to the application. --) + optional string push_endpoint = 1; +} + +// An event indicating a received message or truncation event. +message PubsubEvent { + // The subscription that received the event. + optional string subscription = 1; + + oneof type { + // A received message. + PubsubMessage message = 2; + + // Indicates that this subscription has been truncated. + bool truncated = 3; + + // Indicates that this subscription has been deleted. (Note that pull + // subscribers will always receive NOT_FOUND in response in their pull + // request on the subscription, rather than seeing this boolean.) + bool deleted = 4; + } +} + +// Request for the GetSubscription method. +message GetSubscriptionRequest { + // The name of the subscription to get. + optional string subscription = 1; +} + +// Request for the ListSubscriptions method. +message ListSubscriptionsRequest { + // A valid label query expression. + // (-- Which labels are required or supported is implementation-specific. + // TODO(eschapira): This method must support to query by topic. We must + // define the key URI for the "topic" label. --) + optional string query = 1; + + // Maximum number of subscriptions to return. + // (-- If not specified or <= 0, the implementation will select a reasonable + // value. --) + optional int32 max_results = 3; + + // The value obtained in the last ListSubscriptionsResponse + // for continuation. + optional string page_token = 4; +} + +// Response for the ListSubscriptions method. +message ListSubscriptionsResponse { + // The subscriptions that match the request. + repeated Subscription subscription = 1; + + // If not empty, indicates that there are more subscriptions that match the + // request and this value should be passed to the next + // ListSubscriptionsRequest to continue. + optional string next_page_token = 2; +} + +// Request for the TruncateSubscription method. +message TruncateSubscriptionRequest { + // The subscription that is being truncated. + optional string subscription = 1; +} + +// Request for the DeleteSubscription method. +message DeleteSubscriptionRequest { + // The subscription to delete. + optional string subscription = 1; +} + +// Request for the ModifyPushConfig method. +message ModifyPushConfigRequest { + // The name of the subscription. + optional string subscription = 1; + + // An empty push_config indicates that the Pub/Sub system should + // pause pushing messages from the given subscription. + optional PushConfig push_config = 2; +} + +// ----------------------------------------------------------------------------- +// The protos used by a pull subscriber. +// ----------------------------------------------------------------------------- + +// Request for the Pull method. +message PullRequest { + // The subscription from which a message should be pulled. + optional string subscription = 1; + + // If this is specified as true the system will respond immediately even if + // it is not able to return a message in the Pull response. Otherwise the + // system is allowed to wait until at least one message is available rather + // than returning FAILED_PRECONDITION. The client may cancel the request if + // it does not wish to wait any longer for the response. + optional bool return_immediately = 2; +} + +// Either a PubsubMessage or a truncation event. One of these two +// must be populated. +message PullResponse { + // This ID must be used to acknowledge the received event or message. + optional string ack_id = 1; + + // A pubsub message or truncation event. + optional PubsubEvent pubsub_event = 2; +} + +// Request for the PullBatch method. +message PullBatchRequest { + // The subscription from which messages should be pulled. + optional string subscription = 1; + + // If this is specified as true the system will respond immediately even if + // it is not able to return a message in the Pull response. Otherwise the + // system is allowed to wait until at least one message is available rather + // than returning no messages. The client may cancel the request if it does + // not wish to wait any longer for the response. + optional bool return_immediately = 2; + + // The maximum number of PubsubEvents returned for this request. The Pub/Sub + // system may return fewer than the number of events specified. + optional int32 max_events = 3; +} + +// Response for the PullBatch method. +message PullBatchResponse { + + // Received Pub/Sub messages or status events. The Pub/Sub system will return + // zero messages if there are no more messages available in the backlog. The + // Pub/Sub system may return fewer than the max_events requested even if + // there are more messages available in the backlog. + repeated PullResponse pull_responses = 2; +} + +// Request for the ModifyAckDeadline method. +message ModifyAckDeadlineRequest { + // The name of the subscription from which messages are being pulled. + optional string subscription = 1; + + // The acknowledgment ID. + optional string ack_id = 2; + + // The new Ack deadline. Must be >= 0. + optional int32 ack_deadline_seconds = 3; +} + +// Request for the Acknowledge method. +message AcknowledgeRequest { + // The subscription whose message is being acknowledged. + optional string subscription = 1; + + // The acknowledgment ID for the message being acknowledged. This was + // returned by the Pub/Sub system in the Pull response. + repeated string ack_id = 2; +} + +// Request for the Nack method. +message NackRequest { + // The subscription whose message is being Nacked. + optional string subscription = 1; + + // The acknowledgment ID for the message being refused. This was returned by + // the Pub/Sub system in the Pull response. + repeated string ack_id = 2; +} + +// ----------------------------------------------------------------------------- +// The service and protos used by a push subscriber. +// ----------------------------------------------------------------------------- + +// The service that a subscriber uses to handle messages sent via push +// delivery. +// This service is not currently exported for HTTP clients. +// TODO(eschapira): Explain HTTP subscribers. +service PushEndpointService { + // Sends a PubsubMessage or a subscription status event to a + // push endpoint. + // The push endpoint responds with an empty message and a code from + // util/task/codes.proto. The following codes have a particular meaning to the + // Pub/Sub system: + // OK - This is interpreted by Pub/Sub as Ack. + // ABORTED - This is intepreted by Pub/Sub as a Nack, without implying + // pushback for congestion control. The Pub/Sub system will + // retry this message at a later time. + // UNAVAILABLE - This is intepreted by Pub/Sub as a Nack, with the additional + // semantics of push-back. The Pub/Sub system will use an AIMD + // congestion control algorithm to backoff the rate of sending + // messages from this subscription. + // Any other code, or a failure to respond, will be interpreted in the same + // way as ABORTED; i.e. the system will retry the message at a later time to + // ensure reliable delivery. + rpc HandlePubsubEvent(PubsubEvent) returns (proto2.Empty); +} diff --git a/examples/pubsub/pubsub_demo.js b/examples/pubsub/pubsub_demo.js new file mode 100644 index 00000000..d61fe2a7 --- /dev/null +++ b/examples/pubsub/pubsub_demo.js @@ -0,0 +1,273 @@ +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +var async = require('async'); +var parseArgs = require('minimist'); +var _ = require('underscore'); +var grpc = require('../..'); +var PROTO_PATH = __dirname + '/pubsub.proto'; +var pubsub = grpc.load(PROTO_PATH).tech.pubsub; + +function PubsubRunner(pub, sub, args) { + this.pub = pub; + this.sub = sub; + this.args = args; +} + +PubsubRunner.prototype.getTestTopicName = function() { + var base_name = '/topics/' + this.args.project_id + '/'; + if (this.args.topic_name) { + return base_name + this.args.topic_name; + } + var now_text = new Date().toLocaleFormat('%Y%m%d%H%M%S%L'); + return base_name + process.env.USER + '-' + now_text; +}; + +PubsubRunner.prototype.getTestSubName = function() { + var base_name = '/subscriptions/' + this.args.project_id + '/'; + if (this.args.sub_name) { + return base_name + this.args.sub_name; + } + var now_text = new Date().toLocaleFormat('%Y%m%d%H%M%S%L'); + return base_name + process.env.USER + '-' + now_text; +}; + +PubsubRunner.prototype.listProjectTopics = function(callback) { + var q = ('cloud.googleapis.com/project in (/projects/' + + this.args.project_id + ')'); + this.pub.listTopics({query: q}, callback); +}; + +PubsubRunner.prototype.topicExists = function(name, callback) { + this.listProjectTopics(function(err, response) { + if (err) { + callback(err); + } else { + callback(null, _.some(response.topic, function(t) { + return t.name === name; + })); + } + }); +}; + +PubsubRunner.prototype.createTopicIfNeeded = function(name, callback) { + this.topicExists(name, function(err, exists) { + if (err) { + callback(err); + } else{ + if (exists) { + callback(null); + } else { + this.pub.createTopic({name: name}, callback); + } + } + }); +}; + +PubsubRunner.prototype.removeTopic = function(callback) { + var name = this.getTestTopicName(); + console.log('... removing Topic', name); + this.pub.deleteTopic({topic: name}, function(err, value) { + if (err) { + console.log('Could not delete a topic: rpc failed with', err); + callback(err); + } else { + console.log('removed Topic', name, 'OK'); + callback(null); + } + }); +}; + +PubsubRunner.prototype.createTopic = function(callback) { + var name = this.getTestTopicName(); + console.log('... creating Topic', name); + this.pub.createTopic({name: name}, function(err, value) { + if (err) { + console.log('Could not create a topic: rpc failed with', err); + callback(err); + } else { + console.log('created Topic', name, 'OK'); + callback(null); + } + }); +}; + +PubsubRunner.prototype.listSomeTopics = function(callback) { + console.log('Listing topics'); + console.log('-------------_'); + this.listProjectTopics(function(err, response) { + if (err) { + console.log('Could not list topic: rpc failed with', err); + callback(err); + } else { + _.each(response.topic, function(t) { + console.log(t.name); + }); + callback(null); + } + }); +}; + +PubsubRunner.prototype.checkExists = function(callback) { + var name = this.getTestTopicName(); + console.log('... checking for topic', name); + this.topicExists(name, function(err, exists) { + if (err) { + console.log('Could not check for a topics: rpc failed with', err); + callback(err); + } else { + if (exists) { + console.log(name, 'is a topic'); + } else { + console.log(name, 'is not a topic'); + } + callback(null); + } + }); +}; + +PubsubRunner.prototype.randomPubSub = function(callback) { + var topic_name = this.getTestTopicName(); + var sub_name = this.getTestSubName(); + var subscription = {name: sub_name, topic: topic_name}; + async.waterfall([ + _.bind(this.createTopicIfNeeded, this, topic_name), + _.bind(this.sub.createSubscription, this, subscription), + function(resp, cb) { + var msg_count = _.random(10, 30); + // Set up msg_count messages to publish + var message_senders = _.times(msg_count, function(n) { + return _.bind(this.pub.publish, this.pub, { + topic: topic_name, + message: {data: 'message ' + n} + }); + }); + async.parallel(message_senders, cb); + }, + function(result, cb) { + console.log('Sent', msg_count, 'messages to', topic_name + ',', + 'checking for them now.'); + var batch_request = { + subscription: sub_name, + max_events: msg_count + }; + this.sub.pull_batch(batch_request, cb); + }, + function(batch, cb) { + var ack_ids = _.pluck(batch.pull_responses, 'ack_id'); + console.log('Got', ack_ids.length, 'messages, acknowledging them...'); + var ack_request = { + subscription: sub_name, + ack_ids: ack_ids + }; + this.sub.acknowledge(ack_request, cb); + }, + function(result, cb) { + console.log( + 'Test messages were acknowledged OK, deleting the subscription'); + this.sub.delete({subscription: sub_name}, cb); + } + ], function (err, result) { + if (err) { + console.log('Could not do random pub sub: rpc failed with', err); + } + callback(err, result); + }); +}; + +function main(callback) { + var argv = parseArgs(process.argv, { + string: [ + 'host', + 'oauth_scope', + 'port', + 'action', + 'project_id', + 'topic_name', + 'sub_name' + ], + default: { + host: 'pubsub-testing.googleapis.com', + oauth_scope: 'https://www.googleapis.com/auth/pubsub', + port: 443, + action: 'all', + project: 'stoked-keyword-656' + } + }); + var valid_actions = [ + 'removeTopic', + 'createTopic', + 'listSomeTopic', + 'checkExists', + 'randomPubSub' + ]; + if (!(argv.action === 'all' || _.some(valid_actions, function(action) { + return action === argv.action; + }))) { + callback(new Error('Action was not valid')); + } + var address = argv.host + ':' + argv.port; + (new GoogleAuth()).getApplicationDefault(function(err, credential) { + if (err) { + callback(err); + return; + } + if (credential.createScopedRequired()) { + credential = credential.createScoped(argv.oauth_scope); + } + var updateMetadata = grpc.getGoogleAuthDelegate(credential); + var ca_path = process.env.SSL_CERT_FILE; + fs.readFile(ca_path, function(err, ca_data) { + if (err) { + callback(err); + return; + } + var ssl_creds = grpc.Credentials.createSsl(ca_data); + var options = {credentials: ssl_creds}; + var pub = new pubsub.PublisherService(address, options, updateMetadata); + var sub = new pubsub.SubscriberService(address, options, updateMetadata); + var runner = new PubsubRunner(pub, sub, argv); + if (argv.action === 'all') { + async.series(_.map(valid_actions, function(name) { + _.bind(runner[name], runner); + }), callback); + } else { + runner[argv.action](callback); + } + }); + }); +} + +if (require.main === module) { + main(function(err) { + if (err) throw err; + }); +} + +module.exports = PubsubRunner; From 054d02e98fb5487c7fd899af87e30ed1d3360ed7 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 20 Feb 2015 15:06:22 -0800 Subject: [PATCH 59/91] Updated client and server to use db from a variable path --- examples/route_guide_client.js | 7 ++++++- examples/route_guide_server.js | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/examples/route_guide_client.js b/examples/route_guide_client.js index 10da6d3a..d4c083a6 100644 --- a/examples/route_guide_client.js +++ b/examples/route_guide_client.js @@ -29,6 +29,8 @@ var async = require('async'); var fs = require('fs'); +var parseArgs = require('minimist'); +var path = require('path'); var _ = require('underscore'); var grpc = require('..'); var examples = grpc.load(__dirname + '/route_guide.proto').examples; @@ -104,7 +106,10 @@ function runListFeatures(callback) { * @param {function} callback Called when this demo is complete */ function runRecordRoute(callback) { - fs.readFile(__dirname + '/route_guide_db.json', function(err, data) { + var argv = parseArgs(process.argv, { + string: 'db_path' + }); + fs.readFile(path.resolve(argv.db_path), function(err, data) { if (err) callback(err); var feature_list = JSON.parse(data); diff --git a/examples/route_guide_server.js b/examples/route_guide_server.js index 89d8d27c..bc9ed251 100644 --- a/examples/route_guide_server.js +++ b/examples/route_guide_server.js @@ -28,6 +28,8 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. var fs = require('fs'); +var parseArgs = require('minimist'); +var path = require('path'); var _ = require('underscore'); var grpc = require('..'); var examples = grpc.load(__dirname + '/route_guide.proto').examples; @@ -234,7 +236,10 @@ if (require.main === module) { // If this is run as a script, start a server on an unused port var routeServer = getServer(); routeServer.bind('0.0.0.0:50051'); - fs.readFile(__dirname + '/route_guide_db.json', function(err, data) { + var argv = parseArgs(process.argv, { + string: 'db_path' + }); + fs.readFile(path.resolve(argv.db_path), function(err, data) { if (err) throw err; feature_list = JSON.parse(data); routeServer.listen(); From a1f3f928de0318c95340d901922836b2dcecc2ef Mon Sep 17 00:00:00 2001 From: nmittler Date: Sun, 22 Feb 2015 15:16:20 -0800 Subject: [PATCH 60/91] Fixing java package for route_guide.proto --- examples/route_guide.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/route_guide.proto b/examples/route_guide.proto index 4c7be175..44211282 100644 --- a/examples/route_guide.proto +++ b/examples/route_guide.proto @@ -29,7 +29,7 @@ syntax = "proto3"; -option java_package = "ex.grpc"; +option java_package = "io.grpc.examples"; package examples; From ddce31ab435ab7847c870ef90c40d4820f93cdaf Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Mon, 23 Feb 2015 10:26:01 -0800 Subject: [PATCH 61/91] Added pubsub demo client --- examples/pubsub/pubsub.proto | 4 +-- examples/pubsub/pubsub_demo.js | 62 ++++++++++++++++++---------------- package.json | 5 +-- 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/examples/pubsub/pubsub.proto b/examples/pubsub/pubsub.proto index ef88981b..41a35477 100644 --- a/examples/pubsub/pubsub.proto +++ b/examples/pubsub/pubsub.proto @@ -34,8 +34,8 @@ syntax = "proto2"; -import "examples/pubsub/empty.proto"; -import "examples/pubsub/label.proto"; +import "empty.proto"; +import "label.proto"; package tech.pubsub; diff --git a/examples/pubsub/pubsub_demo.js b/examples/pubsub/pubsub_demo.js index d61fe2a7..a9b6acbd 100644 --- a/examples/pubsub/pubsub_demo.js +++ b/examples/pubsub/pubsub_demo.js @@ -28,7 +28,10 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. var async = require('async'); +var fs = require('fs'); +var GoogleAuth = require('googleauth'); var parseArgs = require('minimist'); +var strftime = require('strftime'); var _ = require('underscore'); var grpc = require('../..'); var PROTO_PATH = __dirname + '/pubsub.proto'; @@ -45,7 +48,7 @@ PubsubRunner.prototype.getTestTopicName = function() { if (this.args.topic_name) { return base_name + this.args.topic_name; } - var now_text = new Date().toLocaleFormat('%Y%m%d%H%M%S%L'); + var now_text = strftime('%Y%m%d%H%M%S%L'); return base_name + process.env.USER + '-' + now_text; }; @@ -54,7 +57,7 @@ PubsubRunner.prototype.getTestSubName = function() { if (this.args.sub_name) { return base_name + this.args.sub_name; } - var now_text = new Date().toLocaleFormat('%Y%m%d%H%M%S%L'); + var now_text = strftime('%Y%m%d%H%M%S%L'); return base_name + process.env.USER + '-' + now_text; }; @@ -77,6 +80,7 @@ PubsubRunner.prototype.topicExists = function(name, callback) { }; PubsubRunner.prototype.createTopicIfNeeded = function(name, callback) { + var self = this; this.topicExists(name, function(err, exists) { if (err) { callback(err); @@ -84,7 +88,7 @@ PubsubRunner.prototype.createTopicIfNeeded = function(name, callback) { if (exists) { callback(null); } else { - this.pub.createTopic({name: name}, callback); + self.pub.createTopic({name: name}, callback); } } }); @@ -153,45 +157,48 @@ PubsubRunner.prototype.checkExists = function(callback) { }; PubsubRunner.prototype.randomPubSub = function(callback) { + var self = this; var topic_name = this.getTestTopicName(); var sub_name = this.getTestSubName(); var subscription = {name: sub_name, topic: topic_name}; async.waterfall([ _.bind(this.createTopicIfNeeded, this, topic_name), - _.bind(this.sub.createSubscription, this, subscription), + _.bind(this.sub.createSubscription, this.sub, subscription), function(resp, cb) { var msg_count = _.random(10, 30); // Set up msg_count messages to publish var message_senders = _.times(msg_count, function(n) { - return _.bind(this.pub.publish, this.pub, { + return _.bind(self.pub.publish, self.pub, { topic: topic_name, - message: {data: 'message ' + n} + message: {data: new Buffer('message ' + n)} }); }); - async.parallel(message_senders, cb); + async.parallel(message_senders, function(err, result) { + cb(err, result, msg_count); + }); }, - function(result, cb) { + function(result, msg_count, cb) { console.log('Sent', msg_count, 'messages to', topic_name + ',', 'checking for them now.'); var batch_request = { subscription: sub_name, max_events: msg_count }; - this.sub.pull_batch(batch_request, cb); + self.sub.pullBatch(batch_request, cb); }, function(batch, cb) { - var ack_ids = _.pluck(batch.pull_responses, 'ack_id'); - console.log('Got', ack_ids.length, 'messages, acknowledging them...'); + var ack_id = _.pluck(batch.pull_responses, 'ack_id'); + console.log('Got', ack_id.length, 'messages, acknowledging them...'); var ack_request = { subscription: sub_name, - ack_ids: ack_ids + ack_id: ack_id }; - this.sub.acknowledge(ack_request, cb); + self.sub.acknowledge(ack_request, cb); }, function(result, cb) { console.log( 'Test messages were acknowledged OK, deleting the subscription'); - this.sub.delete({subscription: sub_name}, cb); + self.sub.deleteSubscription({subscription: sub_name}, cb); } ], function (err, result) { if (err) { @@ -213,23 +220,23 @@ function main(callback) { 'sub_name' ], default: { - host: 'pubsub-testing.googleapis.com', + host: 'pubsub-staging.googleapis.com', oauth_scope: 'https://www.googleapis.com/auth/pubsub', port: 443, - action: 'all', - project: 'stoked-keyword-656' + action: 'listSomeTopics', + project_id: 'stoked-keyword-656' } }); var valid_actions = [ - 'removeTopic', 'createTopic', - 'listSomeTopic', + 'removeTopic', + 'listSomeTopics', 'checkExists', 'randomPubSub' ]; - if (!(argv.action === 'all' || _.some(valid_actions, function(action) { + if (_.some(valid_actions, function(action) { return action === argv.action; - }))) { + })) { callback(new Error('Action was not valid')); } var address = argv.host + ':' + argv.port; @@ -249,17 +256,14 @@ function main(callback) { return; } var ssl_creds = grpc.Credentials.createSsl(ca_data); - var options = {credentials: ssl_creds}; + var options = { + credentials: ssl_creds, + 'grpc.ssl_target_name_override': argv.host + }; var pub = new pubsub.PublisherService(address, options, updateMetadata); var sub = new pubsub.SubscriberService(address, options, updateMetadata); var runner = new PubsubRunner(pub, sub, argv); - if (argv.action === 'all') { - async.series(_.map(valid_actions, function(name) { - _.bind(runner[name], runner); - }), callback); - } else { - runner[argv.action](callback); - } + runner[argv.action](callback); }); }); } diff --git a/package.json b/package.json index a71577fb..fe51c473 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,10 @@ "underscore.string": "^3.0.0" }, "devDependencies": { - "mocha": "~1.21.0", + "googleauth": "google/google-auth-library-nodejs", "minimist": "^1.1.0", - "googleauth": "google/google-auth-library-nodejs" + "mocha": "~1.21.0", + "strftime": "^0.8.2" }, "files": [ "README.md", From c11fc8d2c54db4515b9dc7734cc981c51f2182f9 Mon Sep 17 00:00:00 2001 From: Yang Gao Date: Mon, 23 Feb 2015 11:30:18 -0800 Subject: [PATCH 62/91] remove stale comment --- examples/route_guide_server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/route_guide_server.js b/examples/route_guide_server.js index bc9ed251..0d3b5851 100644 --- a/examples/route_guide_server.js +++ b/examples/route_guide_server.js @@ -51,7 +51,7 @@ var COORD_FACTOR = 1e7; var feature_list = []; /** - * Get a feature object at the given point, or creates one if it does not exist. + * Get a feature object at the given point. * @param {point} point The point to check * @return {feature} The feature object at the point. Note that an empty name * indicates no feature From d930d42b3cc48ad8b679a45e270db0f7101a6ca8 Mon Sep 17 00:00:00 2001 From: Julien Boeuf Date: Mon, 23 Feb 2015 15:57:14 -0800 Subject: [PATCH 63/91] Verifying the peer name on the X509 Certs correctly. - The SANs take precedence over the CN. - The CN is only checked if there are no SANs. - Fixing the tests as the test cert did not list *.test.google.com in the SANs. Will fix the test cert another time... --- test/interop_sanity_test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/interop_sanity_test.js b/test/interop_sanity_test.js index 8dc933ea..6b3aa3dd 100644 --- a/test/interop_sanity_test.js +++ b/test/interop_sanity_test.js @@ -40,7 +40,7 @@ var server; var port; -var name_override = 'foo.test.google.com'; +var name_override = 'foo.test.google.fr'; describe('Interop tests', function() { before(function(done) { From dcdbbe54229ad741ca1dcd546b50f7069d23fff0 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Mon, 23 Feb 2015 17:40:18 -0800 Subject: [PATCH 64/91] Return error status as actual errors to client callbacks --- src/client.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/client.js b/src/client.js index aaa7be79..54b8dbdc 100644 --- a/src/client.js +++ b/src/client.js @@ -245,7 +245,9 @@ function makeUnaryRequestFunction(method, serialize, deserialize) { return; } if (response.status.code !== grpc.status.OK) { - callback(response.status); + var error = new Error(response.status.details); + error.code = response.status.code; + callback(error); return; } emitter.emit('status', response.status); @@ -314,7 +316,9 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) { return; } if (response.status.code !== grpc.status.OK) { - callback(response.status); + var error = new Error(response.status.details); + error.code = response.status.code; + callback(error); return; } stream.emit('status', response.status); From 5c13ed40a95e435a2cd54f560a3abd7251e045e1 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Mon, 23 Feb 2015 17:44:21 -0800 Subject: [PATCH 65/91] Fixed old lint errors --- examples/pubsub/pubsub_demo.js | 6 +++++- examples/route_guide_client.js | 6 +++++- examples/route_guide_server.js | 10 +++++++--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/examples/pubsub/pubsub_demo.js b/examples/pubsub/pubsub_demo.js index a9b6acbd..94d2002d 100644 --- a/examples/pubsub/pubsub_demo.js +++ b/examples/pubsub/pubsub_demo.js @@ -27,6 +27,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +'use strict'; + var async = require('async'); var fs = require('fs'); var GoogleAuth = require('googleauth'); @@ -270,7 +272,9 @@ function main(callback) { if (require.main === module) { main(function(err) { - if (err) throw err; + if (err) { + throw err; + } }); } diff --git a/examples/route_guide_client.js b/examples/route_guide_client.js index d4c083a6..425a94ee 100644 --- a/examples/route_guide_client.js +++ b/examples/route_guide_client.js @@ -27,6 +27,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +'use strict'; + var async = require('async'); var fs = require('fs'); var parseArgs = require('minimist'); @@ -110,7 +112,9 @@ function runRecordRoute(callback) { string: 'db_path' }); fs.readFile(path.resolve(argv.db_path), function(err, data) { - if (err) callback(err); + if (err) { + callback(err); + } var feature_list = JSON.parse(data); var num_points = 10; diff --git a/examples/route_guide_server.js b/examples/route_guide_server.js index 0d3b5851..8970dd65 100644 --- a/examples/route_guide_server.js +++ b/examples/route_guide_server.js @@ -27,6 +27,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +'use strict'; + var fs = require('fs'); var parseArgs = require('minimist'); var path = require('path'); @@ -163,7 +165,7 @@ function recordRoute(call, callback) { } /* For each point after the first, add the incremental distance from the * previous point to the total distance value */ - if (previous != null) { + if (previous !== null) { distance += getDistance(previous, point); } previous = point; @@ -173,7 +175,7 @@ function recordRoute(call, callback) { point_count: point_count, feature_count: feature_count, // Cast the distance to an integer - distance: distance|0, + distance: Math.floor(distance), // End the timer elapsed_time: process.hrtime(start_time)[0] }); @@ -240,7 +242,9 @@ if (require.main === module) { string: 'db_path' }); fs.readFile(path.resolve(argv.db_path), function(err, data) { - if (err) throw err; + if (err) { + throw err; + } feature_list = JSON.parse(data); routeServer.listen(); }); From a9fac804ca30e5aaf9487e0d179593caf461c76e Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Mon, 23 Feb 2015 17:44:21 -0800 Subject: [PATCH 66/91] Fixed old lint errors --- examples/pubsub/pubsub_demo.js | 6 +++++- examples/route_guide_client.js | 6 +++++- examples/route_guide_server.js | 10 +++++++--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/examples/pubsub/pubsub_demo.js b/examples/pubsub/pubsub_demo.js index a9b6acbd..94d2002d 100644 --- a/examples/pubsub/pubsub_demo.js +++ b/examples/pubsub/pubsub_demo.js @@ -27,6 +27,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +'use strict'; + var async = require('async'); var fs = require('fs'); var GoogleAuth = require('googleauth'); @@ -270,7 +272,9 @@ function main(callback) { if (require.main === module) { main(function(err) { - if (err) throw err; + if (err) { + throw err; + } }); } diff --git a/examples/route_guide_client.js b/examples/route_guide_client.js index d4c083a6..425a94ee 100644 --- a/examples/route_guide_client.js +++ b/examples/route_guide_client.js @@ -27,6 +27,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +'use strict'; + var async = require('async'); var fs = require('fs'); var parseArgs = require('minimist'); @@ -110,7 +112,9 @@ function runRecordRoute(callback) { string: 'db_path' }); fs.readFile(path.resolve(argv.db_path), function(err, data) { - if (err) callback(err); + if (err) { + callback(err); + } var feature_list = JSON.parse(data); var num_points = 10; diff --git a/examples/route_guide_server.js b/examples/route_guide_server.js index 0d3b5851..8970dd65 100644 --- a/examples/route_guide_server.js +++ b/examples/route_guide_server.js @@ -27,6 +27,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +'use strict'; + var fs = require('fs'); var parseArgs = require('minimist'); var path = require('path'); @@ -163,7 +165,7 @@ function recordRoute(call, callback) { } /* For each point after the first, add the incremental distance from the * previous point to the total distance value */ - if (previous != null) { + if (previous !== null) { distance += getDistance(previous, point); } previous = point; @@ -173,7 +175,7 @@ function recordRoute(call, callback) { point_count: point_count, feature_count: feature_count, // Cast the distance to an integer - distance: distance|0, + distance: Math.floor(distance), // End the timer elapsed_time: process.hrtime(start_time)[0] }); @@ -240,7 +242,9 @@ if (require.main === module) { string: 'db_path' }); fs.readFile(path.resolve(argv.db_path), function(err, data) { - if (err) throw err; + if (err) { + throw err; + } feature_list = JSON.parse(data); routeServer.listen(); }); From c38a6a53623ed2bc3a7a20194ddbe6c2799c6b72 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Tue, 24 Feb 2015 09:24:02 -0800 Subject: [PATCH 67/91] Fixed copyright format in some example files --- examples/pubsub/pubsub_demo.js | 60 ++++++++++++++++++---------------- examples/route_guide_client.js | 60 ++++++++++++++++++---------------- examples/route_guide_server.js | 60 ++++++++++++++++++---------------- 3 files changed, 96 insertions(+), 84 deletions(-) diff --git a/examples/pubsub/pubsub_demo.js b/examples/pubsub/pubsub_demo.js index 94d2002d..4f7a9a92 100644 --- a/examples/pubsub/pubsub_demo.js +++ b/examples/pubsub/pubsub_demo.js @@ -1,31 +1,35 @@ -// Copyright 2015, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ 'use strict'; diff --git a/examples/route_guide_client.js b/examples/route_guide_client.js index 425a94ee..0b3e9c58 100644 --- a/examples/route_guide_client.js +++ b/examples/route_guide_client.js @@ -1,31 +1,35 @@ -// Copyright 2015, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ 'use strict'; diff --git a/examples/route_guide_server.js b/examples/route_guide_server.js index 8970dd65..95553684 100644 --- a/examples/route_guide_server.js +++ b/examples/route_guide_server.js @@ -1,31 +1,35 @@ -// Copyright 2015, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ 'use strict'; From 583e64e8380ac484b3d7f66046a0b84dfaf76307 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Tue, 24 Feb 2015 11:04:35 -0800 Subject: [PATCH 68/91] Updated interop proto for compatibility with proto3 servers --- interop/messages.proto | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interop/messages.proto b/interop/messages.proto index 65a81404..de0b1a23 100644 --- a/interop/messages.proto +++ b/interop/messages.proto @@ -49,7 +49,7 @@ enum PayloadType { // A block of data, to simply increase gRPC message size. message Payload { // The type of data in body. - optional PayloadType type = 1; + optional PayloadType type = 1 [default = COMPRESSABLE]; // Primary contents of payload. optional bytes body = 2; } @@ -58,7 +58,7 @@ message Payload { message SimpleRequest { // Desired payload type in the response from the server. // If response_type is RANDOM, server randomly chooses one from other formats. - optional PayloadType response_type = 1; + optional PayloadType response_type = 1 [default = COMPRESSABLE]; // Desired payload size in the response from the server. // If response_type is COMPRESSABLE, this denotes the size before compression. @@ -116,7 +116,7 @@ message StreamingOutputCallRequest { // If response_type is RANDOM, the payload from each response in the stream // might be of different types. This is to simulate a mixed type of payload // stream. - optional PayloadType response_type = 1; + optional PayloadType response_type = 1 [default = COMPRESSABLE]; // Configuration for each expected response message. repeated ResponseParameters response_parameters = 2; From 06c26162839c0527f6d347ee8a36622926ef8d74 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Tue, 24 Feb 2015 15:33:26 -0800 Subject: [PATCH 69/91] Fixed reference to grpc_default_credentials_create --- ext/credentials.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/credentials.cc b/ext/credentials.cc index 4b95c72b..3f65d59c 100644 --- a/ext/credentials.cc +++ b/ext/credentials.cc @@ -130,7 +130,7 @@ NAN_METHOD(Credentials::New) { NAN_METHOD(Credentials::CreateDefault) { NanScope(); - NanReturnValue(WrapStruct(grpc_default_credentials_create())); + NanReturnValue(WrapStruct(grpc_google_default_credentials_create())); } NAN_METHOD(Credentials::CreateSsl) { From 00588c6bb5ebf98d91181be8cd902272e81ca153 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Tue, 24 Feb 2015 17:02:09 -0800 Subject: [PATCH 70/91] Fixed import of google-auth-library --- examples/pubsub/pubsub_demo.js | 2 +- index.js | 2 +- interop/interop_client.js | 2 +- package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/pubsub/pubsub_demo.js b/examples/pubsub/pubsub_demo.js index 4f7a9a92..26301515 100644 --- a/examples/pubsub/pubsub_demo.js +++ b/examples/pubsub/pubsub_demo.js @@ -35,7 +35,7 @@ var async = require('async'); var fs = require('fs'); -var GoogleAuth = require('googleauth'); +var GoogleAuth = require('google-auth-library'); var parseArgs = require('minimist'); var strftime = require('strftime'); var _ = require('underscore'); diff --git a/index.js b/index.js index 4b5302e4..ad3dd96a 100644 --- a/index.js +++ b/index.js @@ -78,7 +78,7 @@ function load(filename) { /** * Get a function that a client can use to update metadata with authentication * information from a Google Auth credential object, which comes from the - * googleauth library. + * google-auth-library. * @param {Object} credential The credential object to use * @return {function(Object, callback)} Metadata updater function */ diff --git a/interop/interop_client.js b/interop/interop_client.js index eaf254bc..8060baf8 100644 --- a/interop/interop_client.js +++ b/interop/interop_client.js @@ -37,7 +37,7 @@ var fs = require('fs'); var path = require('path'); var grpc = require('..'); var testProto = grpc.load(__dirname + '/test.proto').grpc.testing; -var GoogleAuth = require('googleauth'); +var GoogleAuth = require('google-auth-library'); var assert = require('assert'); diff --git a/package.json b/package.json index e6ac5505..1c44b106 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ }, "devDependencies": { "async": "^0.9.0", - "googleauth": "google/google-auth-library-nodejs", + "google-auth-library": "^0.9.2", "minimist": "^1.1.0", "mocha": "~1.21.0", "strftime": "^0.8.2" From 1fc959e5c43b43140eabaad65b1762532ddc20a2 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 25 Feb 2015 10:38:34 -0800 Subject: [PATCH 71/91] Fixed TLS host resolution problems --- ext/channel.cc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ext/channel.cc b/ext/channel.cc index 6c7a89e5..bc9461d7 100644 --- a/ext/channel.cc +++ b/ext/channel.cc @@ -103,11 +103,15 @@ NAN_METHOD(Channel::New) { grpc_channel *wrapped_channel; // Owned by the Channel object NanUtf8String *host = new NanUtf8String(args[0]); + NanUtf8String *host_override = NULL; if (args[1]->IsUndefined()) { wrapped_channel = grpc_channel_create(**host, NULL); } else if (args[1]->IsObject()) { grpc_credentials *creds = NULL; Handle args_hash(args[1]->ToObject()->Clone()); + if (args_hash->HasOwnProperty(NanNew(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG))) { + host_override = new NanUtf8String(args_hash->Get(NanNew(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG))); + } if (args_hash->HasOwnProperty(NanNew("credentials"))) { Handle creds_value = args_hash->Get(NanNew("credentials")); if (!Credentials::HasInstance(creds_value)) { @@ -155,7 +159,12 @@ NAN_METHOD(Channel::New) { } else { return NanThrowTypeError("Channel expects a string and an object"); } - Channel *channel = new Channel(wrapped_channel, host); + Channel *channel; + if (host_override == NULL) { + channel = new Channel(wrapped_channel, host); + } else { + channel = new Channel(wrapped_channel, host_override); + } channel->Wrap(args.This()); NanReturnValue(args.This()); } else { From fdf82db60474010e276a6c570c63f30489d8e684 Mon Sep 17 00:00:00 2001 From: Dan Ciruli Date: Wed, 25 Feb 2015 12:24:39 -0800 Subject: [PATCH 72/91] Update binding.gyp --- binding.gyp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binding.gyp b/binding.gyp index fb4c779f..5c34be24 100644 --- a/binding.gyp +++ b/binding.gyp @@ -7,7 +7,7 @@ "targets" : [ { 'include_dirs': [ - " Date: Wed, 25 Feb 2015 12:30:26 -0800 Subject: [PATCH 73/91] Changing to use node instead of nodejs --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 1c44b106..24b4a6ea 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "version": "0.2.0", "description": "gRPC Library for Node", "scripts": { - "lint": "nodejs ./node_modules/jshint/bin/jshint src test examples interop index.js", - "test": "nodejs ./node_modules/mocha/bin/mocha && npm run-script lint" + "lint": "node ./node_modules/jshint/bin/jshint src test examples interop index.js", + "test": "node ./node_modules/mocha/bin/mocha && npm run-script lint" }, "dependencies": { "bindings": "^1.2.1", From 8ea34970339c42f56b2ef89dc4f160e7d8ec60ee Mon Sep 17 00:00:00 2001 From: Michael Lumish Date: Wed, 25 Feb 2015 13:24:30 -0800 Subject: [PATCH 74/91] Add Debian nodejs-legacy instructions --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 8880213e..5b3de6b4 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ Alpha : Ready for early adopters +## Prerequisites + +This requires `node` to be installed. If you instead have the `nodejs` executable on Debian, you should install the [`nodejs-legacy`](https://packages.debian.org/sid/nodejs-legacy) package. + ## Installation First, clone this repository (NPM package coming soon). Then follow the instructions in the `INSTALL` file in the root of the repository to install the C core library that this package depends on. From 805a5dcd9012e4417a4a961ca87a03f99e52a13f Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 25 Feb 2015 13:28:22 -0800 Subject: [PATCH 75/91] Bumped node version to 0.5.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1c44b106..57434a61 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "grpc", - "version": "0.2.0", + "version": "0.5.0", "description": "gRPC Library for Node", "scripts": { "lint": "nodejs ./node_modules/jshint/bin/jshint src test examples interop index.js", From 9ed34d53e8baaba9b2c013dfc0df1bd397b014b8 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 25 Feb 2015 16:29:54 -0800 Subject: [PATCH 76/91] Added important Node package information and LICENSE file for inclusion in Node package --- LICENSE | 28 ++++++++++++++++++++++++++++ package.json | 11 ++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..0209b570 --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +Copyright 2015, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/package.json b/package.json index e9995e7f..8e0a7bdb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,14 @@ { "name": "grpc", "version": "0.5.0", + "author": "Google Inc.", "description": "gRPC Library for Node", + "contributors": [ + { + "name": "Michael Lumish", + "email": "mlumish@google.com" + } + ], "scripts": { "lint": "node ./node_modules/jshint/bin/jshint src test examples interop index.js", "test": "node ./node_modules/mocha/bin/mocha && npm run-script lint" @@ -22,6 +29,7 @@ "strftime": "^0.8.2" }, "files": [ + "LICENSE", "README.md", "index.js", "binding.gyp", @@ -31,5 +39,6 @@ "src", "test" ], - "main": "index.js" + "main": "index.js", + "license": "BSD-3-Clause" } From 380f929a70325bb6afd5df7c0bc42aa3cbdd5cdb Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 26 Feb 2015 14:52:51 -0800 Subject: [PATCH 77/91] Changed c++ version flag in binding.gyp --- binding.gyp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binding.gyp b/binding.gyp index 5c34be24..10afaf69 100644 --- a/binding.gyp +++ b/binding.gyp @@ -10,7 +10,7 @@ " Date: Thu, 26 Feb 2015 18:21:48 -0800 Subject: [PATCH 78/91] Added useful information and links to Node's package.json --- package.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 8e0a7bdb..0ef1c990 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,24 @@ { "name": "grpc", - "version": "0.5.0", + "version": "0.5.1", "author": "Google Inc.", "description": "gRPC Library for Node", + "homepage": "http://www.grpc.io/", + "repository": { + "type": "git", + "url": "https://github.com/grpc/grpc.git" + }, + "bugs": "https://github.com/grpc/grpc/issues", "contributors": [ { "name": "Michael Lumish", "email": "mlumish@google.com" } ], + "directories": { + "lib": "src", + "example": "examples" + }, "scripts": { "lint": "node ./node_modules/jshint/bin/jshint src test examples interop index.js", "test": "node ./node_modules/mocha/bin/mocha && npm run-script lint" From 0777e281e58422232ca0be46420f66423126afb6 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Mon, 2 Mar 2015 17:28:02 -0800 Subject: [PATCH 79/91] Updated Node library to new secure server API --- ext/server.cc | 32 ++++++++++++-------------------- interop/interop_server.js | 10 +++++----- src/server.js | 19 ++++++++++--------- 3 files changed, 27 insertions(+), 34 deletions(-) diff --git a/ext/server.cc b/ext/server.cc index ab45da8d..a87f9194 100644 --- a/ext/server.cc +++ b/ext/server.cc @@ -164,19 +164,7 @@ NAN_METHOD(Server::New) { if (args[0]->IsUndefined()) { wrapped_server = grpc_server_create(queue, NULL); } else if (args[0]->IsObject()) { - grpc_server_credentials *creds = NULL; - Handle args_hash(args[0]->ToObject()->Clone()); - if (args_hash->HasOwnProperty(NanNew("credentials"))) { - Handle creds_value = args_hash->Get(NanNew("credentials")); - if (!ServerCredentials::HasInstance(creds_value)) { - return NanThrowTypeError( - "credentials arg must be a ServerCredentials object"); - } - ServerCredentials *creds_object = - ObjectWrap::Unwrap(creds_value->ToObject()); - creds = creds_object->GetWrappedServerCredentials(); - args_hash->Delete(NanNew("credentials")); - } + Handle args_hash(args[0]->ToObject()); Handle keys(args_hash->GetOwnPropertyNames()); grpc_channel_args channel_args; channel_args.num_args = keys->Length(); @@ -203,11 +191,7 @@ NAN_METHOD(Server::New) { return NanThrowTypeError("Arg values must be strings"); } } - if (creds == NULL) { - wrapped_server = grpc_server_create(queue, &channel_args); - } else { - wrapped_server = grpc_secure_server_create(creds, queue, &channel_args); - } + wrapped_server = grpc_server_create(queue, &channel_args); free(channel_args.args); } else { return NanThrowTypeError("Server expects an object"); @@ -258,11 +242,19 @@ NAN_METHOD(Server::AddSecureHttp2Port) { "addSecureHttp2Port can only be called on a Server"); } if (!args[0]->IsString()) { - return NanThrowTypeError("addSecureHttp2Port's argument must be a String"); + return NanThrowTypeError( + "addSecureHttp2Port's first argument must be a String"); + } + if (!ServerCredentials::HasInstance(args[1])) { + return NanThrowTypeError( + "addSecureHttp2Port's second argument must be ServerCredentials"); } Server *server = ObjectWrap::Unwrap(args.This()); + ServerCredentials *creds = ObjectWrap::Unwrap( + args[1]->ToObject()); NanReturnValue(NanNew(grpc_server_add_secure_http2_port( - server->wrapped_server, *NanUtf8String(args[0])))); + server->wrapped_server, *NanUtf8String(args[0]), + creds->GetWrappedServerCredentials()))); } NAN_METHOD(Server::Start) { diff --git a/interop/interop_server.js b/interop/interop_server.js index 125ede17..8e5c0366 100644 --- a/interop/interop_server.js +++ b/interop/interop_server.js @@ -165,16 +165,16 @@ function handleHalfDuplex(call) { function getServer(port, tls) { // TODO(mlumish): enable TLS functionality var options = {}; + var server_creds = null; if (tls) { var key_path = path.join(__dirname, '../test/data/server1.key'); var pem_path = path.join(__dirname, '../test/data/server1.pem'); var key_data = fs.readFileSync(key_path); var pem_data = fs.readFileSync(pem_path); - var server_creds = grpc.ServerCredentials.createSsl(null, - key_data, - pem_data); - options.credentials = server_creds; + server_creds = grpc.ServerCredentials.createSsl(null, + key_data, + pem_data); } var server = new Server({ 'grpc.testing.TestService' : { @@ -186,7 +186,7 @@ function getServer(port, tls) { halfDuplexCall: handleHalfDuplex } }, null, options); - var port_num = server.bind('0.0.0.0:' + port, tls); + var port_num = server.bind('0.0.0.0:' + port, server_creds); return {server: server, port: port_num}; } diff --git a/src/server.js b/src/server.js index 91dde022..b72d1106 100644 --- a/src/server.js +++ b/src/server.js @@ -517,14 +517,15 @@ Server.prototype.register = function(name, handler, serialize, deserialize, }; /** - * Binds the server to the given port, with SSL enabled if secure is specified + * Binds the server to the given port, with SSL enabled if creds is given * @param {string} port The port that the server should bind on, in the format * "address:port" - * @param {boolean=} secure Whether the server should open a secure port + * @param {boolean=} creds Server credential object to be used for SSL. Pass + * nothing for an insecure port */ -Server.prototype.bind = function(port, secure) { - if (secure) { - return this._server.addSecureHttp2Port(port); +Server.prototype.bind = function(port, creds) { + if (creds) { + return this._server.addSecureHttp2Port(port, creds); } else { return this._server.addHttp2Port(port); } @@ -604,14 +605,14 @@ function makeServerConstructor(services) { } /** - * Binds the server to the given port, with SSL enabled if secure is specified + * Binds the server to the given port, with SSL enabled if creds is supplied * @param {string} port The port that the server should bind on, in the format * "address:port" - * @param {boolean=} secure Whether the server should open a secure port + * @param {boolean=} creds Credentials to use for SSL * @return {SurfaceServer} this */ - SurfaceServer.prototype.bind = function(port, secure) { - return this.inner_server.bind(port, secure); + SurfaceServer.prototype.bind = function(port, creds) { + return this.inner_server.bind(port, creds); }; /** From e3eab63868589a690b43a797947e87e196670cb4 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 4 Mar 2015 11:28:06 -0800 Subject: [PATCH 80/91] Loosened some dependencies, specified compatible engines --- package.json | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 0ef1c990..13606956 100644 --- a/package.json +++ b/package.json @@ -24,20 +24,23 @@ "test": "node ./node_modules/mocha/bin/mocha && npm run-script lint" }, "dependencies": { - "bindings": "^1.2.1", - "jshint": "^2.5.5", - "nan": "~1.3.0", + "bindings": "^1.2.0", + "nan": "^1.5.0", "protobufjs": "murgatroid99/ProtoBuf.js", - "underscore": "^1.7.0", + "underscore": "^1.6.0", "underscore.string": "^3.0.0" }, "devDependencies": { "async": "^0.9.0", "google-auth-library": "^0.9.2", + "jshint": "^2.5.0", "minimist": "^1.1.0", "mocha": "~1.21.0", "strftime": "^0.8.2" }, + "engines": { + "node": ">=0.10.13 <0.11" + }, "files": [ "LICENSE", "README.md", From 820ff875c7cd20b759f9d669a25c0ab42ba5780f Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 4 Mar 2015 11:28:37 -0800 Subject: [PATCH 81/91] Updated some c++ files for 0.11/0.12 compatibility --- ext/byte_buffer.cc | 17 +++++++++-------- ext/call.cc | 47 +++++++++++++++++++++++----------------------- ext/call.h | 2 +- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/ext/byte_buffer.cc b/ext/byte_buffer.cc index c165d26e..5235c8e0 100644 --- a/ext/byte_buffer.cc +++ b/ext/byte_buffer.cc @@ -44,7 +44,6 @@ namespace grpc { namespace node { -using ::node::Buffer; using v8::Context; using v8::Function; using v8::Handle; @@ -54,8 +53,8 @@ using v8::Value; grpc_byte_buffer *BufferToByteBuffer(Handle buffer) { NanScope(); - int length = Buffer::Length(buffer); - char *data = Buffer::Data(buffer); + int length = ::node::Buffer::Length(buffer); + char *data = ::node::Buffer::Data(buffer); gpr_slice slice = gpr_slice_malloc(length); memcpy(GPR_SLICE_START_PTR(slice), data, length); grpc_byte_buffer *byte_buffer(grpc_byte_buffer_create(&slice, 1)); @@ -66,7 +65,7 @@ grpc_byte_buffer *BufferToByteBuffer(Handle buffer) { Handle ByteBufferToBuffer(grpc_byte_buffer *buffer) { NanEscapableScope(); if (buffer == NULL) { - NanReturnNull(); + return NanNull(); } size_t length = grpc_byte_buffer_length(buffer); char *result = reinterpret_cast(calloc(length, sizeof(char))); @@ -82,12 +81,14 @@ Handle ByteBufferToBuffer(grpc_byte_buffer *buffer) { Handle MakeFastBuffer(Handle slowBuffer) { NanEscapableScope(); - Handle globalObj = Context::GetCurrent()->Global(); + Handle globalObj = NanGetCurrentContext()->Global(); Handle bufferConstructor = Handle::Cast( globalObj->Get(NanNew("Buffer"))); - Handle consArgs[3] = { slowBuffer, - NanNew(Buffer::Length(slowBuffer)), - NanNew(0) }; + Handle consArgs[3] = { + slowBuffer, + NanNew(::node::Buffer::Length(slowBuffer)), + NanNew(0) + }; Handle fastBuffer = bufferConstructor->NewInstance(3, consArgs); return NanEscapeScope(fastBuffer); } diff --git a/ext/call.cc b/ext/call.cc index 9ed389f3..1d85abb1 100644 --- a/ext/call.cc +++ b/ext/call.cc @@ -54,8 +54,6 @@ using std::vector; namespace grpc { namespace node { -using ::node::Buffer; -using v8::Arguments; using v8::Array; using v8::Boolean; using v8::Exception; @@ -74,7 +72,7 @@ using v8::Uint32; using v8::String; using v8::Value; -Persistent Call::constructor; +NanCallback *Call::constructor; Persistent Call::fun_tpl; @@ -101,9 +99,9 @@ bool CreateMetadataArray(Handle metadata, grpc_metadata_array *array, Handle 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); + if (::node::Buffer::HasInstance(value)) { + current->value = ::node::Buffer::Data(value); + current->value_length = ::node::Buffer::Length(value); Persistent handle; NanAssignPersistent(handle, value); resources->handles.push_back(unique_ptr( @@ -140,7 +138,7 @@ Handle ParseMetadata(const grpc_metadata_array *metadata_array) { Handle metadata_object = NanNew(); for (unsigned int i = 0; i < length; i++) { grpc_metadata* elem = &metadata_elements[i]; - Handle key_string = String::New(elem->key); + Handle key_string = NanNew(elem->key); Handle array; if (metadata_object->Has(key_string)) { array = Handle::Cast(metadata_object->Get(key_string)); @@ -194,7 +192,7 @@ class SendMessageOp : public Op { } bool ParseOp(Handle value, grpc_op *out, shared_ptr resources) { - if (!Buffer::HasInstance(value)) { + if (!::node::Buffer::HasInstance(value)) { return false; } out->data.send_message = BufferToByteBuffer(value); @@ -357,7 +355,7 @@ class ClientStatusOp : public Op { Handle status_obj = NanNew(); status_obj->Set(NanNew("code"), NanNew(status)); if (status_details != NULL) { - status_obj->Set(NanNew("details"), String::New(status_details)); + status_obj->Set(NanNew("details"), NanNew(status_details)); } status_obj->Set(NanNew("metadata"), ParseMetadata(&metadata_array)); return NanEscapeScope(status_obj); @@ -436,20 +434,21 @@ Call::~Call() { void Call::Init(Handle exports) { NanScope(); - Local tpl = FunctionTemplate::New(New); + Local tpl = NanNew(New); tpl->SetClassName(NanNew("Call")); tpl->InstanceTemplate()->SetInternalFieldCount(1); NanSetPrototypeTemplate(tpl, "startBatch", - FunctionTemplate::New(StartBatch)->GetFunction()); + NanNew(StartBatch)->GetFunction()); NanSetPrototypeTemplate(tpl, "cancel", - FunctionTemplate::New(Cancel)->GetFunction()); + NanNew(Cancel)->GetFunction()); NanAssignPersistent(fun_tpl, tpl); - NanAssignPersistent(constructor, tpl->GetFunction()); - constructor->Set(NanNew("WRITE_BUFFER_HINT"), - NanNew(GRPC_WRITE_BUFFER_HINT)); - constructor->Set(NanNew("WRITE_NO_COMPRESS"), - NanNew(GRPC_WRITE_NO_COMPRESS)); - exports->Set(String::NewSymbol("Call"), constructor); + Handle ctr = tpl->GetFunction(); + ctr->Set(NanNew("WRITE_BUFFER_HINT"), + NanNew(GRPC_WRITE_BUFFER_HINT)); + ctr->Set(NanNew("WRITE_NO_COMPRESS"), + NanNew(GRPC_WRITE_NO_COMPRESS)); + exports->Set(NanNew("Call"), ctr); + constructor = new NanCallback(ctr); } bool Call::HasInstance(Handle val) { @@ -463,8 +462,8 @@ Handle Call::WrapStruct(grpc_call *call) { return NanEscapeScope(NanNull()); } const int argc = 1; - Handle argv[argc] = {External::New(reinterpret_cast(call))}; - return NanEscapeScope(constructor->NewInstance(argc, argv)); + Handle argv[argc] = {NanNew(reinterpret_cast(call))}; + return NanEscapeScope(constructor->GetFunction()->NewInstance(argc, argv)); } NAN_METHOD(Call::New) { @@ -473,9 +472,10 @@ NAN_METHOD(Call::New) { if (args.IsConstructCall()) { Call *call; if (args[0]->IsExternal()) { + Handle ext = args[0].As(); // This option is used for wrapping an existing call grpc_call *call_value = - reinterpret_cast(External::Unwrap(args[0])); + reinterpret_cast(ext->Value()); call = new Call(call_value); } else { if (!Channel::HasInstance(args[0])) { @@ -500,15 +500,14 @@ NAN_METHOD(Call::New) { wrapped_channel, CompletionQueueAsyncWorker::GetQueue(), *method, channel->GetHost(), MillisecondsToTimespec(deadline)); call = new Call(wrapped_call); - args.This()->SetHiddenValue(String::NewSymbol("channel_"), - channel_object); + args.This()->SetHiddenValue(NanNew("channel_"), channel_object); } call->Wrap(args.This()); NanReturnValue(args.This()); } else { const int argc = 4; Local argv[argc] = {args[0], args[1], args[2], args[3]}; - NanReturnValue(constructor->NewInstance(argc, argv)); + NanReturnValue(constructor->GetFunction()->NewInstance(argc, argv)); } } diff --git a/ext/call.h b/ext/call.h index 933541ce..2e9e1c5b 100644 --- a/ext/call.h +++ b/ext/call.h @@ -118,7 +118,7 @@ class Call : public ::node::ObjectWrap { static NAN_METHOD(New); static NAN_METHOD(StartBatch); static NAN_METHOD(Cancel); - static v8::Persistent constructor; + static NanCallback *constructor; // Used for typechecking instances of this javascript class static v8::Persistent fun_tpl; From 1008c5a7302cc785f946ac6f793d0bf35bf845fc Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 4 Mar 2015 14:42:31 -0800 Subject: [PATCH 82/91] The library now compiles with Node 0.11+ and all versions of io.js --- ext/call.cc | 8 +++--- ext/call.h | 8 +++--- ext/channel.cc | 14 +++++----- ext/channel.h | 2 +- ext/credentials.cc | 56 +++++++++++++++++++-------------------- ext/credentials.h | 2 +- ext/node_grpc.cc | 6 ++--- ext/server.cc | 27 ++++++++++--------- ext/server.h | 2 +- ext/server_credentials.cc | 40 ++++++++++++++-------------- ext/server_credentials.h | 2 +- 11 files changed, 85 insertions(+), 82 deletions(-) diff --git a/ext/call.cc b/ext/call.cc index 1d85abb1..afb65417 100644 --- a/ext/call.cc +++ b/ext/call.cc @@ -102,8 +102,8 @@ bool CreateMetadataArray(Handle metadata, grpc_metadata_array *array, if (::node::Buffer::HasInstance(value)) { current->value = ::node::Buffer::Data(value); current->value_length = ::node::Buffer::Length(value); - Persistent handle; - NanAssignPersistent(handle, value); + Persistent *handle = new Persistent(); + NanAssignPersistent(*handle, value); resources->handles.push_back(unique_ptr( new PersistentHolder(handle))); } else if (value->IsString()) { @@ -196,8 +196,8 @@ class SendMessageOp : public Op { return false; } out->data.send_message = BufferToByteBuffer(value); - Persistent handle; - NanAssignPersistent(handle, value); + Persistent *handle = new Persistent(); + NanAssignPersistent(*handle, value); resources->handles.push_back(unique_ptr( new PersistentHolder(handle))); return true; diff --git a/ext/call.h b/ext/call.h index 2e9e1c5b..43142c70 100644 --- a/ext/call.h +++ b/ext/call.h @@ -40,6 +40,7 @@ #include #include #include "grpc/grpc.h" +#include "grpc/support/log.h" #include "channel.h" @@ -54,16 +55,17 @@ v8::Handle ParseMetadata(const grpc_metadata_array *metadata_array); class PersistentHolder { public: - explicit PersistentHolder(v8::Persistent persist) : + explicit PersistentHolder(v8::Persistent *persist) : persist(persist) { } ~PersistentHolder() { - NanDisposePersistent(persist); + NanDisposePersistent(*persist); + delete persist; } private: - v8::Persistent persist; + v8::Persistent *persist; }; struct Resources { diff --git a/ext/channel.cc b/ext/channel.cc index bc9461d7..787e2749 100644 --- a/ext/channel.cc +++ b/ext/channel.cc @@ -45,7 +45,6 @@ namespace grpc { namespace node { -using v8::Arguments; using v8::Array; using v8::Exception; using v8::Function; @@ -59,7 +58,7 @@ using v8::Persistent; using v8::String; using v8::Value; -Persistent Channel::constructor; +NanCallback *Channel::constructor; Persistent Channel::fun_tpl; Channel::Channel(grpc_channel *channel, NanUtf8String *host) @@ -74,14 +73,15 @@ Channel::~Channel() { void Channel::Init(Handle exports) { NanScope(); - Local tpl = FunctionTemplate::New(New); + Local tpl = NanNew(New); tpl->SetClassName(NanNew("Channel")); tpl->InstanceTemplate()->SetInternalFieldCount(1); NanSetPrototypeTemplate(tpl, "close", - FunctionTemplate::New(Close)->GetFunction()); + NanNew(Close)->GetFunction()); NanAssignPersistent(fun_tpl, tpl); - NanAssignPersistent(constructor, tpl->GetFunction()); - exports->Set(NanNew("Channel"), constructor); + Handle ctr = tpl->GetFunction(); + constructor = new NanCallback(ctr); + exports->Set(NanNew("Channel"), ctr); } bool Channel::HasInstance(Handle val) { @@ -170,7 +170,7 @@ NAN_METHOD(Channel::New) { } else { const int argc = 2; Local argv[argc] = {args[0], args[1]}; - NanReturnValue(constructor->NewInstance(argc, argv)); + NanReturnValue(constructor->GetFunction()->NewInstance(argc, argv)); } } diff --git a/ext/channel.h b/ext/channel.h index bf793194..b3aa0f70 100644 --- a/ext/channel.h +++ b/ext/channel.h @@ -66,7 +66,7 @@ class Channel : public ::node::ObjectWrap { static NAN_METHOD(New); static NAN_METHOD(Close); - static v8::Persistent constructor; + static NanCallback *constructor; static v8::Persistent fun_tpl; grpc_channel *wrapped_channel; diff --git a/ext/credentials.cc b/ext/credentials.cc index 3f65d59c..34872017 100644 --- a/ext/credentials.cc +++ b/ext/credentials.cc @@ -41,8 +41,6 @@ namespace grpc { namespace node { -using ::node::Buffer; -using v8::Arguments; using v8::Exception; using v8::External; using v8::Function; @@ -56,7 +54,7 @@ using v8::ObjectTemplate; using v8::Persistent; using v8::Value; -Persistent Credentials::constructor; +NanCallback *Credentials::constructor; Persistent Credentials::fun_tpl; Credentials::Credentials(grpc_credentials *credentials) @@ -68,24 +66,25 @@ Credentials::~Credentials() { void Credentials::Init(Handle exports) { NanScope(); - Local tpl = FunctionTemplate::New(New); + Local tpl = NanNew(New); tpl->SetClassName(NanNew("Credentials")); tpl->InstanceTemplate()->SetInternalFieldCount(1); NanAssignPersistent(fun_tpl, tpl); - NanAssignPersistent(constructor, tpl->GetFunction()); - constructor->Set(NanNew("createDefault"), - FunctionTemplate::New(CreateDefault)->GetFunction()); - constructor->Set(NanNew("createSsl"), - FunctionTemplate::New(CreateSsl)->GetFunction()); - constructor->Set(NanNew("createComposite"), - FunctionTemplate::New(CreateComposite)->GetFunction()); - constructor->Set(NanNew("createGce"), - FunctionTemplate::New(CreateGce)->GetFunction()); - constructor->Set(NanNew("createFake"), - FunctionTemplate::New(CreateFake)->GetFunction()); - constructor->Set(NanNew("createIam"), - FunctionTemplate::New(CreateIam)->GetFunction()); - exports->Set(NanNew("Credentials"), constructor); + Handle ctr = tpl->GetFunction(); + ctr->Set(NanNew("createDefault"), + NanNew(CreateDefault)->GetFunction()); + ctr->Set(NanNew("createSsl"), + NanNew(CreateSsl)->GetFunction()); + ctr->Set(NanNew("createComposite"), + NanNew(CreateComposite)->GetFunction()); + ctr->Set(NanNew("createGce"), + NanNew(CreateGce)->GetFunction()); + ctr->Set(NanNew("createFake"), + NanNew(CreateFake)->GetFunction()); + ctr->Set(NanNew("createIam"), + NanNew(CreateIam)->GetFunction()); + constructor = new NanCallback(ctr); + exports->Set(NanNew("Credentials"), ctr); } bool Credentials::HasInstance(Handle val) { @@ -100,8 +99,8 @@ Handle Credentials::WrapStruct(grpc_credentials *credentials) { } const int argc = 1; Handle argv[argc] = { - External::New(reinterpret_cast(credentials))}; - return NanEscapeScope(constructor->NewInstance(argc, argv)); + NanNew(reinterpret_cast(credentials))}; + return NanEscapeScope(constructor->GetFunction()->NewInstance(argc, argv)); } grpc_credentials *Credentials::GetWrappedCredentials() { @@ -116,15 +115,16 @@ NAN_METHOD(Credentials::New) { return NanThrowTypeError( "Credentials can only be created with the provided functions"); } + Handle ext = args[0].As(); grpc_credentials *creds_value = - reinterpret_cast(External::Unwrap(args[0])); + reinterpret_cast(ext->Value()); Credentials *credentials = new Credentials(creds_value); credentials->Wrap(args.This()); NanReturnValue(args.This()); } else { const int argc = 1; Local argv[argc] = {args[0]}; - NanReturnValue(constructor->NewInstance(argc, argv)); + NanReturnValue(constructor->GetFunction()->NewInstance(argc, argv)); } } @@ -137,19 +137,19 @@ NAN_METHOD(Credentials::CreateSsl) { NanScope(); char *root_certs = NULL; grpc_ssl_pem_key_cert_pair key_cert_pair = {NULL, NULL}; - if (Buffer::HasInstance(args[0])) { - root_certs = Buffer::Data(args[0]); + if (::node::Buffer::HasInstance(args[0])) { + root_certs = ::node::Buffer::Data(args[0]); } else if (!(args[0]->IsNull() || args[0]->IsUndefined())) { return NanThrowTypeError("createSsl's first argument must be a Buffer"); } - if (Buffer::HasInstance(args[1])) { - key_cert_pair.private_key = Buffer::Data(args[1]); + if (::node::Buffer::HasInstance(args[1])) { + key_cert_pair.private_key = ::node::Buffer::Data(args[1]); } else if (!(args[1]->IsNull() || args[1]->IsUndefined())) { return NanThrowTypeError( "createSSl's second argument must be a Buffer if provided"); } - if (Buffer::HasInstance(args[2])) { - key_cert_pair.cert_chain = Buffer::Data(args[2]); + if (::node::Buffer::HasInstance(args[2])) { + key_cert_pair.cert_chain = ::node::Buffer::Data(args[2]); } else if (!(args[2]->IsNull() || args[2]->IsUndefined())) { return NanThrowTypeError( "createSSl's third argument must be a Buffer if provided"); diff --git a/ext/credentials.h b/ext/credentials.h index e60be3d4..794736fe 100644 --- a/ext/credentials.h +++ b/ext/credentials.h @@ -68,7 +68,7 @@ class Credentials : public ::node::ObjectWrap { static NAN_METHOD(CreateGce); static NAN_METHOD(CreateFake); static NAN_METHOD(CreateIam); - static v8::Persistent constructor; + static NanCallback *constructor; // Used for typechecking instances of this javascript class static v8::Persistent fun_tpl; diff --git a/ext/node_grpc.cc b/ext/node_grpc.cc index 9f509583..4e31cbaa 100644 --- a/ext/node_grpc.cc +++ b/ext/node_grpc.cc @@ -51,7 +51,7 @@ using v8::String; void InitStatusConstants(Handle exports) { NanScope(); - Handle status = Object::New(); + Handle status = NanNew(); exports->Set(NanNew("status"), status); Handle OK(NanNew(GRPC_STATUS_OK)); status->Set(NanNew("OK"), OK); @@ -100,7 +100,7 @@ void InitStatusConstants(Handle exports) { void InitCallErrorConstants(Handle exports) { NanScope(); - Handle call_error = Object::New(); + Handle call_error = NanNew(); exports->Set(NanNew("callError"), call_error); Handle OK(NanNew(GRPC_CALL_OK)); call_error->Set(NanNew("OK"), OK); @@ -131,7 +131,7 @@ void InitCallErrorConstants(Handle exports) { void InitOpTypeConstants(Handle exports) { NanScope(); - Handle op_type = Object::New(); + Handle op_type = NanNew(); exports->Set(NanNew("opType"), op_type); Handle SEND_INITIAL_METADATA( NanNew(GRPC_OP_SEND_INITIAL_METADATA)); diff --git a/ext/server.cc b/ext/server.cc index ab45da8d..5050042d 100644 --- a/ext/server.cc +++ b/ext/server.cc @@ -53,7 +53,6 @@ namespace grpc { namespace node { using std::unique_ptr; -using v8::Arguments; using v8::Array; using v8::Boolean; using v8::Date; @@ -69,7 +68,7 @@ using v8::Persistent; using v8::String; using v8::Value; -Persistent Server::constructor; +NanCallback *Server::constructor; Persistent Server::fun_tpl; class NewCallOp : public Op { @@ -121,28 +120,30 @@ Server::~Server() { grpc_server_destroy(wrapped_server); } void Server::Init(Handle exports) { NanScope(); - Local tpl = FunctionTemplate::New(New); - tpl->SetClassName(String::NewSymbol("Server")); + Local tpl = NanNew(New); + tpl->SetClassName(NanNew("Server")); tpl->InstanceTemplate()->SetInternalFieldCount(1); NanSetPrototypeTemplate(tpl, "requestCall", - FunctionTemplate::New(RequestCall)->GetFunction()); + NanNew(RequestCall)->GetFunction()); - NanSetPrototypeTemplate(tpl, "addHttp2Port", - FunctionTemplate::New(AddHttp2Port)->GetFunction()); + NanSetPrototypeTemplate( + tpl, "addHttp2Port", + NanNew(AddHttp2Port)->GetFunction()); NanSetPrototypeTemplate( tpl, "addSecureHttp2Port", - FunctionTemplate::New(AddSecureHttp2Port)->GetFunction()); + NanNew(AddSecureHttp2Port)->GetFunction()); NanSetPrototypeTemplate(tpl, "start", - FunctionTemplate::New(Start)->GetFunction()); + NanNew(Start)->GetFunction()); NanSetPrototypeTemplate(tpl, "shutdown", - FunctionTemplate::New(Shutdown)->GetFunction()); + NanNew(Shutdown)->GetFunction()); NanAssignPersistent(fun_tpl, tpl); - NanAssignPersistent(constructor, tpl->GetFunction()); - exports->Set(String::NewSymbol("Server"), constructor); + Handle ctr = tpl->GetFunction(); + constructor = new NanCallback(ctr); + exports->Set(NanNew("Server"), ctr); } bool Server::HasInstance(Handle val) { @@ -157,7 +158,7 @@ NAN_METHOD(Server::New) { if (!args.IsConstructCall()) { const int argc = 1; Local argv[argc] = {args[0]}; - NanReturnValue(constructor->NewInstance(argc, argv)); + NanReturnValue(constructor->GetFunction()->NewInstance(argc, argv)); } grpc_server *wrapped_server; grpc_completion_queue *queue = CompletionQueueAsyncWorker::GetQueue(); diff --git a/ext/server.h b/ext/server.h index 2056fe7d..641d5ccb 100644 --- a/ext/server.h +++ b/ext/server.h @@ -67,7 +67,7 @@ class Server : public ::node::ObjectWrap { static NAN_METHOD(AddSecureHttp2Port); static NAN_METHOD(Start); static NAN_METHOD(Shutdown); - static v8::Persistent constructor; + static NanCallback *constructor; static v8::Persistent fun_tpl; grpc_server *wrapped_server; diff --git a/ext/server_credentials.cc b/ext/server_credentials.cc index f75a2bf7..d2b63cdc 100644 --- a/ext/server_credentials.cc +++ b/ext/server_credentials.cc @@ -41,8 +41,6 @@ namespace grpc { namespace node { -using ::node::Buffer; -using v8::Arguments; using v8::Exception; using v8::External; using v8::Function; @@ -56,7 +54,7 @@ using v8::ObjectTemplate; using v8::Persistent; using v8::Value; -Persistent ServerCredentials::constructor; +NanCallback *ServerCredentials::constructor; Persistent ServerCredentials::fun_tpl; ServerCredentials::ServerCredentials(grpc_server_credentials *credentials) @@ -68,16 +66,17 @@ ServerCredentials::~ServerCredentials() { void ServerCredentials::Init(Handle exports) { NanScope(); - Local tpl = FunctionTemplate::New(New); + Local tpl = NanNew(New); tpl->SetClassName(NanNew("ServerCredentials")); tpl->InstanceTemplate()->SetInternalFieldCount(1); NanAssignPersistent(fun_tpl, tpl); - NanAssignPersistent(constructor, tpl->GetFunction()); - constructor->Set(NanNew("createSsl"), - FunctionTemplate::New(CreateSsl)->GetFunction()); - constructor->Set(NanNew("createFake"), - FunctionTemplate::New(CreateFake)->GetFunction()); - exports->Set(NanNew("ServerCredentials"), constructor); + Handle ctr = tpl->GetFunction(); + ctr->Set(NanNew("createSsl"), + NanNew(CreateSsl)->GetFunction()); + ctr->Set(NanNew("createFake"), + NanNew(CreateFake)->GetFunction()); + constructor = new NanCallback(ctr); + exports->Set(NanNew("ServerCredentials"), ctr); } bool ServerCredentials::HasInstance(Handle val) { @@ -93,8 +92,8 @@ Handle ServerCredentials::WrapStruct( } const int argc = 1; Handle argv[argc] = { - External::New(reinterpret_cast(credentials))}; - return NanEscapeScope(constructor->NewInstance(argc, argv)); + NanNew(reinterpret_cast(credentials))}; + return NanEscapeScope(constructor->GetFunction()->NewInstance(argc, argv)); } grpc_server_credentials *ServerCredentials::GetWrappedServerCredentials() { @@ -109,15 +108,16 @@ NAN_METHOD(ServerCredentials::New) { return NanThrowTypeError( "ServerCredentials can only be created with the provide functions"); } + Handle ext = args[0].As(); grpc_server_credentials *creds_value = - reinterpret_cast(External::Unwrap(args[0])); + reinterpret_cast(ext->Value()); ServerCredentials *credentials = new ServerCredentials(creds_value); credentials->Wrap(args.This()); NanReturnValue(args.This()); } else { const int argc = 1; Local argv[argc] = {args[0]}; - NanReturnValue(constructor->NewInstance(argc, argv)); + NanReturnValue(constructor->GetFunction()->NewInstance(argc, argv)); } } @@ -126,20 +126,20 @@ NAN_METHOD(ServerCredentials::CreateSsl) { NanScope(); char *root_certs = NULL; grpc_ssl_pem_key_cert_pair key_cert_pair; - if (Buffer::HasInstance(args[0])) { - root_certs = Buffer::Data(args[0]); + if (::node::Buffer::HasInstance(args[0])) { + root_certs = ::node::Buffer::Data(args[0]); } else if (!(args[0]->IsNull() || args[0]->IsUndefined())) { return NanThrowTypeError( "createSSl's first argument must be a Buffer if provided"); } - if (!Buffer::HasInstance(args[1])) { + if (!::node::Buffer::HasInstance(args[1])) { return NanThrowTypeError("createSsl's second argument must be a Buffer"); } - key_cert_pair.private_key = Buffer::Data(args[1]); - if (!Buffer::HasInstance(args[2])) { + key_cert_pair.private_key = ::node::Buffer::Data(args[1]); + if (!::node::Buffer::HasInstance(args[2])) { return NanThrowTypeError("createSsl's third argument must be a Buffer"); } - key_cert_pair.cert_chain = Buffer::Data(args[2]); + key_cert_pair.cert_chain = ::node::Buffer::Data(args[2]); NanReturnValue(WrapStruct( grpc_ssl_server_credentials_create(root_certs, &key_cert_pair, 1))); } diff --git a/ext/server_credentials.h b/ext/server_credentials.h index f0990242..aaa7ef29 100644 --- a/ext/server_credentials.h +++ b/ext/server_credentials.h @@ -64,7 +64,7 @@ class ServerCredentials : public ::node::ObjectWrap { static NAN_METHOD(New); static NAN_METHOD(CreateSsl); static NAN_METHOD(CreateFake); - static v8::Persistent constructor; + static NanCallback *constructor; // Used for typechecking instances of this javascript class static v8::Persistent fun_tpl; From 59e0c87eb97b7e4cb34572c69aa7689d42fe430b Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 4 Mar 2015 14:54:32 -0800 Subject: [PATCH 83/91] Removes engine restriction from package.json, bumps version --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 13606956..20eb21fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "grpc", - "version": "0.5.1", + "version": "0.5.2", "author": "Google Inc.", "description": "gRPC Library for Node", "homepage": "http://www.grpc.io/", @@ -39,7 +39,7 @@ "strftime": "^0.8.2" }, "engines": { - "node": ">=0.10.13 <0.11" + "node": ">=0.10.13" }, "files": [ "LICENSE", From ccdb716ca9993a210e8e164f90439ae173b6965d Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 4 Mar 2015 17:29:32 -0800 Subject: [PATCH 84/91] Cleaned out some cruft from binding.gyp --- binding.gyp | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/binding.gyp b/binding.gyp index 10afaf69..6f7620d9 100644 --- a/binding.gyp +++ b/binding.gyp @@ -24,7 +24,9 @@ 'link_settings': { 'libraries': [ '-lrt', - '-lpthread' + '-lpthread', + '-lgrpc', + '-lgpr' ], }, "target_name": "grpc", @@ -38,27 +40,6 @@ "ext/server.cc", "ext/server_credentials.cc", "ext/timeval.cc" - ], - 'conditions' : [ - ['no_install=="yes"', { - 'include_dirs': [ - "<(grpc_root)/include" - ], - 'link_settings': { - 'libraries': [ - '<(grpc_root)/<(grpc_lib_subdir)/libgrpc.a', - '<(grpc_root)/<(grpc_lib_subdir)/libgpr.a' - ] - } - }], - ['no_install!="yes"', { - 'link_settings': { - 'libraries': [ - '-lgrpc', - '-lgpr' - ] - } - }] ] } ] From 41de97cc27f537a812000ae1911222c69bc9a522 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Wed, 4 Mar 2015 17:33:00 -0800 Subject: [PATCH 85/91] Removed extra variables --- binding.gyp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/binding.gyp b/binding.gyp index 6f7620d9..7ef3bdf4 100644 --- a/binding.gyp +++ b/binding.gyp @@ -1,9 +1,4 @@ { - "variables" : { - 'no_install': " Date: Mon, 9 Mar 2015 11:16:56 -0700 Subject: [PATCH 86/91] Added more tests --- test/end_to_end_test.js | 69 +++++++++++++++++++++++++++++ test/math_client_test.js | 2 +- test/server_test.js | 94 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 test/server_test.js diff --git a/test/end_to_end_test.js b/test/end_to_end_test.js index 1cc19286..c39364d4 100644 --- a/test/end_to_end_test.js +++ b/test/end_to_end_test.js @@ -235,4 +235,73 @@ describe('end-to-end', function() { }); }); }); + it('should send multiple messages', function(complete) { + var done = multiDone(complete, 2); + var requests = ['req1', 'req2']; + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 3); + var status_text = 'xyz'; + var call = new grpc.Call(channel, + 'dummy_method', + Infinity); + var client_batch = {}; + client_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; + client_batch[grpc.opType.SEND_MESSAGE] = new Buffer(requests[0]); + client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + call.startBatch(client_batch, function(err, response) { + assert.ifError(err); + assert.deepEqual(response, { + 'send metadata': true, + 'send message': true, + 'metadata': {} + }); + var req2_batch = {}; + req2_batch[grpc.opType.SEND_MESSAGE] = new Buffer(requests[1]); + req2_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + req2_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(req2_batch, function(err, resp) { + assert.ifError(err); + assert.deepEqual(resp, { + 'send message': true, + 'client close': true, + 'status': { + 'code': grpc.status.OK, + 'details': status_text, + 'metadata': {} + } + }); + done(); + }); + }); + + server.requestCall(function(err, call_details) { + var new_call = call_details['new call']; + assert.notEqual(new_call, null); + var server_call = new_call.call; + assert.notEqual(server_call, null); + var server_batch = {}; + server_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; + server_batch[grpc.opType.RECV_MESSAGE] = true; + server_call.startBatch(server_batch, function(err, response) { + assert.ifError(err); + assert(response['send metadata']); + assert.strictEqual(response.read.toString(), requests[0]); + var end_batch = {}; + end_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; + end_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { + 'metadata': {}, + 'code': grpc.status.OK, + 'details': status_text + }; + end_batch[grpc.opType.RECV_MESSAGE] = true; + server_call.startBatch(end_batch, function(err, response) { + assert.ifError(err); + assert(response['send status']); + assert(!response.cancelled); + assert.strictEqual(response.read.toString(), requests[1]); + done(); + }); + }); + }); + }); }); diff --git a/test/math_client_test.js b/test/math_client_test.js index d83f6411..b9155fdf 100644 --- a/test/math_client_test.js +++ b/test/math_client_test.js @@ -81,7 +81,7 @@ describe('Math client', function() { done(); }); }); - it('should handle a client streaming request', function(done) { + it.only('should handle a client streaming request', function(done) { var call = math_client.sum(function handleSumResult(err, value) { assert.ifError(err); assert.equal(value.num, 21); diff --git a/test/server_test.js b/test/server_test.js new file mode 100644 index 00000000..7cb34fa0 --- /dev/null +++ b/test/server_test.js @@ -0,0 +1,94 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +'use strict'; + +var assert = require('assert'); +var grpc = require('bindings')('grpc.node'); + +describe('server', function() { + describe('constructor', function() { + it('should work with no arguments', function() { + assert.doesNotThrow(function() { + new grpc.Server(); + }); + }); + it('should work with an empty list argument', function() { + assert.doesNotThrow(function() { + new grpc.Server([]); + }); + }); + }); + describe('addHttp2Port', function() { + var server; + before(function() { + server = new grpc.Server(); + }); + it('should bind to an unused port', function() { + var port; + assert.doesNotThrow(function() { + port = server.addHttp2Port('0.0.0.0:0'); + }); + assert(port > 0); + }); + }); + describe('addSecureHttp2Port', function() { + var server; + before(function() { + server = new grpc.Server(); + }); + it('should bind to an unused port with fake credentials', function() { + var port; + var creds = grpc.ServerCredentials.createFake(); + assert.doesNotThrow(function() { + port = server.addSecureHttp2Port('0.0.0.0:0', creds); + }); + assert(port > 0); + }); + }); + describe('listen', function() { + var server; + before(function() { + server = new grpc.Server(); + server.addHttp2Port('0.0.0.0:0'); + }); + after(function() { + server.shutdown(); + }); + it('should listen without error', function() { + assert.doesNotThrow(function() { + server.start(); + }); + }); + }); +}); From b0910a21f5209ec1126f3e725f4d314c4fdd3e76 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Mon, 9 Mar 2015 16:09:55 -0700 Subject: [PATCH 87/91] Fixed segfault by fixing scope issue --- ext/byte_buffer.cc | 2 +- ext/completion_queue_async_worker.cc | 1 - test/math_client_test.js | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ext/byte_buffer.cc b/ext/byte_buffer.cc index 5235c8e0..82b54b51 100644 --- a/ext/byte_buffer.cc +++ b/ext/byte_buffer.cc @@ -65,7 +65,7 @@ grpc_byte_buffer *BufferToByteBuffer(Handle buffer) { Handle ByteBufferToBuffer(grpc_byte_buffer *buffer) { NanEscapableScope(); if (buffer == NULL) { - return NanNull(); + return NanEscapeScope(NanNull()); } size_t length = grpc_byte_buffer_length(buffer); char *result = reinterpret_cast(calloc(length, sizeof(char))); diff --git a/ext/completion_queue_async_worker.cc b/ext/completion_queue_async_worker.cc index ca22527e..cd7acd1d 100644 --- a/ext/completion_queue_async_worker.cc +++ b/ext/completion_queue_async_worker.cc @@ -80,7 +80,6 @@ void CompletionQueueAsyncWorker::HandleOKCallback() { NanScope(); NanCallback *callback = GetTagCallback(result->tag); Handle argv[] = {NanNull(), GetTagNodeValue(result->tag)}; - callback->Call(2, argv); DestroyTag(result->tag); diff --git a/test/math_client_test.js b/test/math_client_test.js index b9155fdf..d83f6411 100644 --- a/test/math_client_test.js +++ b/test/math_client_test.js @@ -81,7 +81,7 @@ describe('Math client', function() { done(); }); }); - it.only('should handle a client streaming request', function(done) { + it('should handle a client streaming request', function(done) { var call = math_client.sum(function handleSumResult(err, value) { assert.ifError(err); assert.equal(value.num, 21); From 9807dca39f74dea8534c8b55106419fbdcb85028 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Tue, 10 Mar 2015 09:54:58 -0700 Subject: [PATCH 88/91] Updated Node package version to 0.5.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 20eb21fc..f8c3ab07 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "grpc", - "version": "0.5.2", + "version": "0.5.3", "author": "Google Inc.", "description": "gRPC Library for Node", "homepage": "http://www.grpc.io/", From 2a2bedf55b65ad538e78e883706269e8e5697d83 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 12 Mar 2015 12:53:10 -0700 Subject: [PATCH 89/91] Improved node install instructions and bumped version --- README.md | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5b3de6b4..b1d2310e 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,9 @@ This requires `node` to be installed. If you instead have the `nodejs` executabl ## Installation -First, clone this repository (NPM package coming soon). Then follow the instructions in the `INSTALL` file in the root of the repository to install the C core library that this package depends on. - -Then, simply run `npm install` in or referencing this directory. + 1. Clone [the grpc repository](https://github.com/grpc/grpc). + 2. Follow the instructions in the `INSTALL` file in the root of that repository to install the C core library that this package depends on. + 3. Run `npm install`. ## Tests diff --git a/package.json b/package.json index f8c3ab07..29cbab97 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "grpc", - "version": "0.5.3", + "version": "0.5.4", "author": "Google Inc.", "description": "gRPC Library for Node", "homepage": "http://www.grpc.io/", From a0d811af39dee02bd16482040062c17f8ad54c86 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Thu, 12 Mar 2015 18:25:17 -0700 Subject: [PATCH 90/91] Switched protobufjs dependency to npm package instead of GitHub --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 29cbab97..744b1aa7 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "dependencies": { "bindings": "^1.2.0", "nan": "^1.5.0", - "protobufjs": "murgatroid99/ProtoBuf.js", + "protobufjs": "^4.0.0-b2", "underscore": "^1.6.0", "underscore.string": "^3.0.0" }, From 61b75e081ac593e17b408dad3f460889fdb65cb5 Mon Sep 17 00:00:00 2001 From: murgatroid99 Date: Fri, 13 Mar 2015 11:19:58 -0700 Subject: [PATCH 91/91] Node package version bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 744b1aa7..1d0aa0e6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "grpc", - "version": "0.5.4", + "version": "0.5.5", "author": "Google Inc.", "description": "gRPC Library for Node", "homepage": "http://www.grpc.io/",