Merge github.com:google/grpc into gcov

pull/8/head
Craig Tiller 10 years ago
commit ee988bc497
  1. 4
      .gitmodules
  2. 5
      INSTALL
  3. 4
      Makefile
  4. 12
      src/node/README.md
  5. 46
      src/node/binding.gyp
  6. 79
      src/node/byte_buffer.cc
  7. 56
      src/node/byte_buffer.h
  8. 392
      src/node/call.cc
  9. 82
      src/node/call.h
  10. 182
      src/node/channel.cc
  11. 79
      src/node/channel.h
  12. 209
      src/node/client.js
  13. 62
      src/node/common.js
  14. 89
      src/node/completion_queue_async_worker.cc
  15. 79
      src/node/completion_queue_async_worker.h
  16. 209
      src/node/credentials.cc
  17. 81
      src/node/credentials.h
  18. 164
      src/node/event.cc
  19. 48
      src/node/event.h
  20. 25
      src/node/examples/math.proto
  21. 201
      src/node/examples/math_server.js
  22. 182
      src/node/node_grpc.cc
  23. 18
      src/node/package.json
  24. 52
      src/node/port_picker.js
  25. 236
      src/node/server.cc
  26. 79
      src/node/server.h
  27. 261
      src/node/server.js
  28. 161
      src/node/server_credentials.cc
  29. 77
      src/node/server_credentials.h
  30. 339
      src/node/surface_client.js
  31. 358
      src/node/surface_server.js
  32. 101
      src/node/tag.cc
  33. 59
      src/node/tag.h
  34. 202
      src/node/test/call_test.js
  35. 88
      src/node/test/channel_test.js
  36. 183
      src/node/test/client_server_test.js
  37. 130
      src/node/test/constant_test.js
  38. 1
      src/node/test/data/README
  39. 15
      src/node/test/data/ca.pem
  40. 16
      src/node/test/data/server1.key
  41. 16
      src/node/test/data/server1.pem
  42. 201
      src/node/test/end_to_end_test.js
  43. 209
      src/node/test/math_client_test.js
  44. 121
      src/node/test/server_test.js
  45. 66
      src/node/timeval.cc
  46. 48
      src/node/timeval.h
  47. 3
      src/php/ext/grpc/config.m4
  48. 4
      templates/Makefile.template
  49. 1
      third_party/libevent
  50. 19
      tools/gce_setup/grpc_docker.sh

4
.gitmodules vendored

@ -9,10 +9,6 @@
path = third_party/protobuf
url = https://github.com/google/protobuf.git
branch = v3.0.0-alpha-1
[submodule "third_party/libevent"]
path = third_party/libevent
url = https://github.com/libevent/libevent.git
branch = patches-2.0
[submodule "third_party/gflags"]
path = third_party/gflags
url = https://code.google.com/p/gflags

@ -12,8 +12,7 @@ Note that the Makefile makes it much easier for you to compile from sources
if you were to clone recursively our git repository.
grpc core currently depends on zlib and OpenSSL 1.0.2beta3, and also requires
libevent2 for the Linux port.
grpc core currently depends on zlib and OpenSSL 1.0.2beta3.
grpc++'s tests depends on protobuf 3.0.0, gtests and gflags.
@ -46,7 +45,7 @@ and let the Makefile build them itself.
You may also install the dependencies yourself, from the sources, or from
your distribution's package manager.
The development packages needed for grpc are libevent2 under Linux, and zlib.
The only development package needed for grpc is zlib.
The development packages needed for grpc++'s tests are gtests, and gflags.
To the best of our knowledge, no distribution has an OpenSSL package that

@ -496,9 +496,9 @@ shared_cxx: dep_cxx libs/$(CONFIG)/libgrpc++.$(SHARED_EXT)
privatelibs: privatelibs_c privatelibs_cxx
privatelibs_c: dep_c libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libend2end_fixture_chttp2_fake_security.a libs/$(CONFIG)/libend2end_fixture_chttp2_fullstack.a libs/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_fullstack.a libs/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack.a libs/$(CONFIG)/libend2end_fixture_chttp2_socket_pair.a libs/$(CONFIG)/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/$(CONFIG)/libend2end_test_cancel_after_accept.a libs/$(CONFIG)/libend2end_test_cancel_after_accept_and_writes_closed.a libs/$(CONFIG)/libend2end_test_cancel_after_invoke.a libs/$(CONFIG)/libend2end_test_cancel_before_invoke.a libs/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a libs/$(CONFIG)/libend2end_test_census_simple_request.a libs/$(CONFIG)/libend2end_test_disappearing_server.a libs/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_inflight_calls.a libs/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_tags.a libs/$(CONFIG)/libend2end_test_invoke_large_request.a libs/$(CONFIG)/libend2end_test_max_concurrent_streams.a libs/$(CONFIG)/libend2end_test_no_op.a libs/$(CONFIG)/libend2end_test_ping_pong_streaming.a libs/$(CONFIG)/libend2end_test_request_response_with_binary_metadata_and_payload.a libs/$(CONFIG)/libend2end_test_request_response_with_metadata_and_payload.a libs/$(CONFIG)/libend2end_test_request_response_with_payload.a libs/$(CONFIG)/libend2end_test_request_response_with_trailing_metadata_and_payload.a libs/$(CONFIG)/libend2end_test_simple_delayed_request.a libs/$(CONFIG)/libend2end_test_simple_request.a libs/$(CONFIG)/libend2end_test_thread_stress.a libs/$(CONFIG)/libend2end_test_writes_done_hangs_with_pending_read.a libs/$(CONFIG)/libend2end_certs.a
privatelibs_c: dep_c libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libend2end_fixture_chttp2_fake_security.a libs/$(CONFIG)/libend2end_fixture_chttp2_fullstack.a libs/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_fullstack.a libs/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack.a libs/$(CONFIG)/libend2end_fixture_chttp2_socket_pair.a libs/$(CONFIG)/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/$(CONFIG)/libend2end_test_cancel_after_accept.a libs/$(CONFIG)/libend2end_test_cancel_after_accept_and_writes_closed.a libs/$(CONFIG)/libend2end_test_cancel_after_invoke.a libs/$(CONFIG)/libend2end_test_cancel_before_invoke.a libs/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a libs/$(CONFIG)/libend2end_test_census_simple_request.a libs/$(CONFIG)/libend2end_test_disappearing_server.a libs/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_inflight_calls.a libs/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_tags.a libs/$(CONFIG)/libend2end_test_invoke_large_request.a libs/$(CONFIG)/libend2end_test_max_concurrent_streams.a libs/$(CONFIG)/libend2end_test_no_op.a libs/$(CONFIG)/libend2end_test_ping_pong_streaming.a libs/$(CONFIG)/libend2end_test_request_response_with_binary_metadata_and_payload.a libs/$(CONFIG)/libend2end_test_request_response_with_metadata_and_payload.a libs/$(CONFIG)/libend2end_test_request_response_with_payload.a libs/$(CONFIG)/libend2end_test_request_response_with_trailing_metadata_and_payload.a libs/$(CONFIG)/libend2end_test_simple_delayed_request.a libs/$(CONFIG)/libend2end_test_simple_request.a libs/$(CONFIG)/libend2end_test_thread_stress.a libs/$(CONFIG)/libend2end_test_writes_done_hangs_with_pending_read.a libs/$(CONFIG)/libend2end_certs.a
privatelibs_cxx: dep_cxx libs/$(CONFIG)/libgpr_test_util.a libs/$(CONFIG)/libgrpc_test_util.a libs/$(CONFIG)/libgrpc++_test_util.a libs/$(CONFIG)/libend2end_fixture_chttp2_fake_security.a libs/$(CONFIG)/libend2end_fixture_chttp2_fullstack.a libs/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_fullstack.a libs/$(CONFIG)/libend2end_fixture_chttp2_simple_ssl_with_oauth2_fullstack.a libs/$(CONFIG)/libend2end_fixture_chttp2_socket_pair.a libs/$(CONFIG)/libend2end_fixture_chttp2_socket_pair_one_byte_at_a_time.a libs/$(CONFIG)/libend2end_test_cancel_after_accept.a libs/$(CONFIG)/libend2end_test_cancel_after_accept_and_writes_closed.a libs/$(CONFIG)/libend2end_test_cancel_after_invoke.a libs/$(CONFIG)/libend2end_test_cancel_before_invoke.a libs/$(CONFIG)/libend2end_test_cancel_in_a_vacuum.a libs/$(CONFIG)/libend2end_test_census_simple_request.a libs/$(CONFIG)/libend2end_test_disappearing_server.a libs/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_inflight_calls.a libs/$(CONFIG)/libend2end_test_early_server_shutdown_finishes_tags.a libs/$(CONFIG)/libend2end_test_invoke_large_request.a libs/$(CONFIG)/libend2end_test_max_concurrent_streams.a libs/$(CONFIG)/libend2end_test_no_op.a libs/$(CONFIG)/libend2end_test_ping_pong_streaming.a libs/$(CONFIG)/libend2end_test_request_response_with_binary_metadata_and_payload.a libs/$(CONFIG)/libend2end_test_request_response_with_metadata_and_payload.a libs/$(CONFIG)/libend2end_test_request_response_with_payload.a libs/$(CONFIG)/libend2end_test_request_response_with_trailing_metadata_and_payload.a libs/$(CONFIG)/libend2end_test_simple_delayed_request.a libs/$(CONFIG)/libend2end_test_simple_request.a libs/$(CONFIG)/libend2end_test_thread_stress.a libs/$(CONFIG)/libend2end_test_writes_done_hangs_with_pending_read.a libs/$(CONFIG)/libend2end_certs.a
privatelibs_cxx: dep_cxx libs/$(CONFIG)/libgrpc++_test_util.a
buildtests: buildtests_c buildtests_cxx

@ -0,0 +1,12 @@
# Node.js GRPC extension
The package is built with
node-gyp configure
node-gyp build
or, for brevity
node-gyp configure build
The tests can be run with `npm test` on a dev install.

@ -0,0 +1,46 @@
{
"targets" : [
{
'include_dirs': [
"<!(node -e \"require('nan')\")"
],
'cxxflags': [
'-Wall',
'-pthread',
'-pedantic',
'-g',
'-zdefs'
'-Werror',
],
'ldflags': [
'-g',
'-L/usr/local/google/home/mlumish/grpc_dev/lib'
],
'link_settings': {
'libraries': [
'-lgrpc',
'-levent',
'-levent_pthreads',
'-levent_core',
'-lrt',
'-lgpr',
'-lpthread'
],
},
"target_name": "grpc",
"sources": [
"byte_buffer.cc",
"call.cc",
"channel.cc",
"completion_queue_async_worker.cc",
"credentials.cc",
"event.cc",
"node_grpc.cc",
"server.cc",
"server_credentials.cc",
"tag.cc",
"timeval.cc"
]
}
]
}

@ -0,0 +1,79 @@
/*
*
* 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 <string.h>
#include <malloc.h>
#include <node.h>
#include <nan.h>
#include "grpc/grpc.h"
#include "grpc/support/slice.h"
namespace grpc {
namespace node {
#include "byte_buffer.h"
using ::node::Buffer;
using v8::Handle;
using v8::Value;
grpc_byte_buffer *BufferToByteBuffer(Handle<Value> buffer) {
NanScope();
int length = Buffer::Length(buffer);
char *data = 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));
gpr_slice_unref(slice);
return byte_buffer;
}
Handle<Value> ByteBufferToBuffer(grpc_byte_buffer *buffer) {
NanEscapableScope();
if (buffer == NULL) {
NanReturnNull();
}
size_t length = grpc_byte_buffer_length(buffer);
char *result = reinterpret_cast<char *>(calloc(length, sizeof(char)));
size_t offset = 0;
grpc_byte_buffer_reader *reader = grpc_byte_buffer_reader_create(buffer);
gpr_slice next;
while (grpc_byte_buffer_reader_next(reader, &next) != 0) {
memcpy(result + offset, GPR_SLICE_START_PTR(next), GPR_SLICE_LENGTH(next));
offset += GPR_SLICE_LENGTH(next);
}
return NanEscapeScope(NanNewBufferHandle(result, length));
}
} // namespace node
} // namespace grpc

@ -0,0 +1,56 @@
/*
*
* 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_BYTE_BUFFER_H_
#define NET_GRPC_NODE_BYTE_BUFFER_H_
#include <string.h>
#include <node.h>
#include <nan.h>
#include "grpc/grpc.h"
namespace grpc {
namespace node {
/* Convert a Node.js Buffer to grpc_byte_buffer. Requires that
::node::Buffer::HasInstance(buffer) */
grpc_byte_buffer *BufferToByteBuffer(v8::Handle<v8::Value> buffer);
/* Convert a grpc_byte_buffer to a Node.js Buffer */
v8::Handle<v8::Value> ByteBufferToBuffer(grpc_byte_buffer *buffer);
} // namespace node
} // namespace grpc
#endif // NET_GRPC_NODE_BYTE_BUFFER_H_

@ -0,0 +1,392 @@
/*
*
* 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 <node.h>
#include "grpc/grpc.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 v8::Arguments;
using v8::Array;
using v8::Exception;
using v8::External;
using v8::Function;
using v8::FunctionTemplate;
using v8::Handle;
using v8::HandleScope;
using v8::Integer;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::ObjectTemplate;
using v8::Persistent;
using v8::Uint32;
using v8::String;
using v8::Value;
Persistent<Function> Call::constructor;
Persistent<FunctionTemplate> Call::fun_tpl;
Call::Call(grpc_call *call) : wrapped_call(call) {}
Call::~Call() { grpc_call_destroy(wrapped_call); }
void Call::Init(Handle<Object> exports) {
NanScope();
Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
tpl->SetClassName(NanNew("Call"));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
NanSetPrototypeTemplate(tpl, "addMetadata",
FunctionTemplate::New(AddMetadata)->GetFunction());
NanSetPrototypeTemplate(tpl, "startInvoke",
FunctionTemplate::New(StartInvoke)->GetFunction());
NanSetPrototypeTemplate(tpl, "serverAccept",
FunctionTemplate::New(ServerAccept)->GetFunction());
NanSetPrototypeTemplate(
tpl, "serverEndInitialMetadata",
FunctionTemplate::New(ServerEndInitialMetadata)->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"),
NanNew<Uint32, uint32_t>(GRPC_WRITE_BUFFER_HINT));
constructor->Set(NanNew("WRITE_NO_COMPRESS"),
NanNew<Uint32, uint32_t>(GRPC_WRITE_NO_COMPRESS));
exports->Set(String::NewSymbol("Call"), constructor);
}
bool Call::HasInstance(Handle<Value> val) {
NanScope();
return NanHasInstance(fun_tpl, val);
}
Handle<Value> Call::WrapStruct(grpc_call *call) {
NanEscapableScope();
if (call == NULL) {
return NanEscapeScope(NanNull());
}
const int argc = 1;
Handle<Value> argv[argc] = {External::New(reinterpret_cast<void *>(call))};
return NanEscapeScope(constructor->NewInstance(argc, argv));
}
NAN_METHOD(Call::New) {
NanScope();
if (args.IsConstructCall()) {
Call *call;
if (args[0]->IsExternal()) {
// This option is used for wrapping an existing call
grpc_call *call_value =
reinterpret_cast<grpc_call *>(External::Unwrap(args[0]));
call = new Call(call_value);
} else {
if (!Channel::HasInstance(args[0])) {
return NanThrowTypeError("Call's first argument must be a Channel");
}
if (!args[1]->IsString()) {
return NanThrowTypeError("Call's second argument must be a string");
}
if (!(args[2]->IsNumber() || args[2]->IsDate())) {
return NanThrowTypeError(
"Call's third argument must be a date or a number");
}
Handle<Object> channel_object = args[0]->ToObject();
Channel *channel = ObjectWrap::Unwrap<Channel>(channel_object);
if (channel->GetWrappedChannel() == NULL) {
return NanThrowError("Call cannot be created from a closed channel");
}
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));
call = new Call(wrapped_call);
args.This()->SetHiddenValue(String::NewSymbol("channel_"),
channel_object);
}
call->Wrap(args.This());
NanReturnValue(args.This());
} else {
const int argc = 4;
Local<Value> argv[argc] = {args[0], args[1], args[2], args[3]};
NanReturnValue(constructor->NewInstance(argc, argv));
}
}
NAN_METHOD(Call::AddMetadata) {
NanScope();
if (!HasInstance(args.This())) {
return NanThrowTypeError("addMetadata can only be called on Call objects");
}
Call *call = ObjectWrap::Unwrap<Call>(args.This());
for (int i = 0; !args[i]->IsUndefined(); i++) {
if (!args[i]->IsObject()) {
return NanThrowTypeError(
"addMetadata arguments must be objects with key and value");
}
Handle<Object> item = args[i]->ToObject();
Handle<Value> key = item->Get(NanNew("key"));
if (!key->IsString()) {
return NanThrowTypeError(
"objects passed to addMetadata must have key->string");
}
Handle<Value> value = item->Get(NanNew("value"));
if (!Buffer::HasInstance(value)) {
return NanThrowTypeError(
"objects passed to addMetadata must have value->Buffer");
}
grpc_metadata metadata;
NanUtf8String utf8_key(key);
metadata.key = *utf8_key;
metadata.value = Buffer::Data(value);
metadata.value_length = Buffer::Length(value);
grpc_call_error error =
grpc_call_add_metadata(call->wrapped_call, &metadata, 0);
if (error != GRPC_CALL_OK) {
return NanThrowError("addMetadata failed", error);
}
}
NanReturnUndefined();
}
NAN_METHOD(Call::StartInvoke) {
NanScope();
if (!HasInstance(args.This())) {
return NanThrowTypeError("startInvoke can only be called on Call objects");
}
if (!args[0]->IsFunction()) {
return NanThrowTypeError("StartInvoke's first argument must be a function");
}
if (!args[1]->IsFunction()) {
return NanThrowTypeError(
"StartInvoke's second argument must be a function");
}
if (!args[2]->IsFunction()) {
return NanThrowTypeError("StartInvoke's third argument must be a function");
}
if (!args[3]->IsUint32()) {
return NanThrowTypeError(
"StartInvoke's fourth argument must be integer flags");
}
Call *call = ObjectWrap::Unwrap<Call>(args.This());
unsigned int flags = args[3]->Uint32Value();
grpc_call_error error = grpc_call_start_invoke(
call->wrapped_call, CompletionQueueAsyncWorker::GetQueue(),
CreateTag(args[0], args.This()), CreateTag(args[1], args.This()),
CreateTag(args[2], args.This()), flags);
if (error == GRPC_CALL_OK) {
CompletionQueueAsyncWorker::Next();
CompletionQueueAsyncWorker::Next();
CompletionQueueAsyncWorker::Next();
} else {
return NanThrowError("startInvoke 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<Call>(args.This());
grpc_call_error error = grpc_call_server_accept(
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<Call>(args.This());
unsigned int flags = args[1]->Uint32Value();
grpc_call_error error =
grpc_call_server_end_initial_metadata(call->wrapped_call, flags);
if (error != GRPC_CALL_OK) {
return NanThrowError("serverEndInitialMetadata failed", error);
}
NanReturnUndefined();
}
NAN_METHOD(Call::Cancel) {
NanScope();
if (!HasInstance(args.This())) {
return NanThrowTypeError("startInvoke can only be called on Call objects");
}
Call *call = ObjectWrap::Unwrap<Call>(args.This());
grpc_call_error error = grpc_call_cancel(call->wrapped_call);
if (error != GRPC_CALL_OK) {
return NanThrowError("cancel failed", error);
}
NanReturnUndefined();
}
NAN_METHOD(Call::StartWrite) {
NanScope();
if (!HasInstance(args.This())) {
return NanThrowTypeError("startWrite can only be called on Call objects");
}
if (!Buffer::HasInstance(args[0])) {
return NanThrowTypeError("startWrite's first argument must be a Buffer");
}
if (!args[1]->IsFunction()) {
return NanThrowTypeError("startWrite's second argument must be a function");
}
if (!args[2]->IsUint32()) {
return NanThrowTypeError(
"startWrite's third argument must be integer flags");
}
Call *call = ObjectWrap::Unwrap<Call>(args.This());
grpc_byte_buffer *buffer = BufferToByteBuffer(args[0]);
unsigned int flags = args[2]->Uint32Value();
grpc_call_error error = grpc_call_start_write(
call->wrapped_call, buffer, CreateTag(args[1], args.This()), flags);
if (error == GRPC_CALL_OK) {
CompletionQueueAsyncWorker::Next();
} else {
return NanThrowError("startWrite failed", error);
}
NanReturnUndefined();
}
NAN_METHOD(Call::StartWriteStatus) {
NanScope();
if (!HasInstance(args.This())) {
return NanThrowTypeError(
"startWriteStatus can only be called on Call objects");
}
if (!args[0]->IsUint32()) {
return NanThrowTypeError(
"startWriteStatus's first argument must be a status code");
}
if (!args[1]->IsString()) {
return NanThrowTypeError(
"startWriteStatus's second argument must be a string");
}
if (!args[2]->IsFunction()) {
return NanThrowTypeError(
"startWriteStatus's third argument must be a function");
}
Call *call = ObjectWrap::Unwrap<Call>(args.This());
NanUtf8String details(args[1]);
grpc_call_error error = grpc_call_start_write_status(
call->wrapped_call, (grpc_status_code)args[0]->Uint32Value(), *details,
CreateTag(args[2], args.This()));
if (error == GRPC_CALL_OK) {
CompletionQueueAsyncWorker::Next();
} else {
return NanThrowError("startWriteStatus failed", error);
}
NanReturnUndefined();
}
NAN_METHOD(Call::WritesDone) {
NanScope();
if (!HasInstance(args.This())) {
return NanThrowTypeError("writesDone can only be called on Call objects");
}
if (!args[0]->IsFunction()) {
return NanThrowTypeError("writesDone's first argument must be a function");
}
Call *call = ObjectWrap::Unwrap<Call>(args.This());
grpc_call_error error = grpc_call_writes_done(
call->wrapped_call, CreateTag(args[0], args.This()));
if (error == GRPC_CALL_OK) {
CompletionQueueAsyncWorker::Next();
} else {
return NanThrowError("writesDone failed", error);
}
NanReturnUndefined();
}
NAN_METHOD(Call::StartRead) {
NanScope();
if (!HasInstance(args.This())) {
return NanThrowTypeError("startRead can only be called on Call objects");
}
if (!args[0]->IsFunction()) {
return NanThrowTypeError("startRead's first argument must be a function");
}
Call *call = ObjectWrap::Unwrap<Call>(args.This());
grpc_call_error error =
grpc_call_start_read(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

@ -0,0 +1,82 @@
/*
*
* 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_CALL_H_
#define NET_GRPC_NODE_CALL_H_
#include <node.h>
#include <nan.h>
#include "grpc/grpc.h"
#include "channel.h"
namespace grpc {
namespace node {
/* Wrapper class for grpc_call structs. */
class Call : public ::node::ObjectWrap {
public:
static void Init(v8::Handle<v8::Object> exports);
static bool HasInstance(v8::Handle<v8::Value> val);
/* Wrap a grpc_call struct in a javascript object */
static v8::Handle<v8::Value> WrapStruct(grpc_call *call);
private:
explicit Call(grpc_call *call);
~Call();
// Prevent copying
Call(const Call &);
Call &operator=(const Call &);
static NAN_METHOD(New);
static NAN_METHOD(AddMetadata);
static NAN_METHOD(StartInvoke);
static NAN_METHOD(ServerAccept);
static NAN_METHOD(ServerEndInitialMetadata);
static NAN_METHOD(Cancel);
static NAN_METHOD(StartWrite);
static NAN_METHOD(StartWriteStatus);
static NAN_METHOD(WritesDone);
static NAN_METHOD(StartRead);
static v8::Persistent<v8::Function> constructor;
// Used for typechecking instances of this javascript class
static v8::Persistent<v8::FunctionTemplate> fun_tpl;
grpc_call *wrapped_call;
};
} // namespace node
} // namespace grpc
#endif // NET_GRPC_NODE_CALL_H_

@ -0,0 +1,182 @@
/*
*
* 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 <malloc.h>
#include <vector>
#include <node.h>
#include <nan.h>
#include "grpc/grpc.h"
#include "grpc/grpc_security.h"
#include "channel.h"
#include "credentials.h"
namespace grpc {
namespace node {
using v8::Arguments;
using v8::Array;
using v8::Exception;
using v8::Function;
using v8::FunctionTemplate;
using v8::Handle;
using v8::HandleScope;
using v8::Integer;
using v8::Local;
using v8::Object;
using v8::Persistent;
using v8::String;
using v8::Value;
Persistent<Function> Channel::constructor;
Persistent<FunctionTemplate> Channel::fun_tpl;
Channel::Channel(grpc_channel *channel, NanUtf8String *host)
: wrapped_channel(channel), host(host) {}
Channel::~Channel() {
if (wrapped_channel != NULL) {
grpc_channel_destroy(wrapped_channel);
}
delete host;
}
void Channel::Init(Handle<Object> exports) {
NanScope();
Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
tpl->SetClassName(NanNew("Channel"));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
NanSetPrototypeTemplate(tpl, "close",
FunctionTemplate::New(Close)->GetFunction());
NanAssignPersistent(fun_tpl, tpl);
NanAssignPersistent(constructor, tpl->GetFunction());
exports->Set(NanNew("Channel"), constructor);
}
bool Channel::HasInstance(Handle<Value> val) {
NanScope();
return NanHasInstance(fun_tpl, val);
}
grpc_channel *Channel::GetWrappedChannel() { return this->wrapped_channel; }
char *Channel::GetHost() { return **this->host; }
NAN_METHOD(Channel::New) {
NanScope();
if (args.IsConstructCall()) {
if (!args[0]->IsString()) {
return NanThrowTypeError("Channel expects a string and an object");
}
grpc_channel *wrapped_channel;
// Owned by the Channel object
NanUtf8String *host = new NanUtf8String(args[0]);
if (args[1]->IsUndefined()) {
wrapped_channel = grpc_channel_create(**host, NULL);
} else if (args[1]->IsObject()) {
grpc_credentials *creds = NULL;
Handle<Object> args_hash(args[1]->ToObject()->Clone());
if (args_hash->HasOwnProperty(NanNew("credentials"))) {
Handle<Value> creds_value = args_hash->Get(NanNew("credentials"));
if (!Credentials::HasInstance(creds_value)) {
return NanThrowTypeError(
"credentials arg must be a Credentials object");
}
Credentials *creds_object =
ObjectWrap::Unwrap<Credentials>(creds_value->ToObject());
creds = creds_object->GetWrappedCredentials();
args_hash->Delete(NanNew("credentials"));
}
Handle<Array> keys(args_hash->GetOwnPropertyNames());
grpc_channel_args channel_args;
channel_args.num_args = keys->Length();
channel_args.args = reinterpret_cast<grpc_arg *>(
calloc(channel_args.num_args, sizeof(grpc_arg)));
/* These are used to keep all strings until then end of the block, then
destroy them */
std::vector<NanUtf8String *> key_strings(keys->Length());
std::vector<NanUtf8String *> value_strings(keys->Length());
for (unsigned int i = 0; i < channel_args.num_args; i++) {
Handle<String> current_key(keys->Get(i)->ToString());
Handle<Value> current_value(args_hash->Get(current_key));
key_strings[i] = new NanUtf8String(current_key);
channel_args.args[i].key = **key_strings[i];
if (current_value->IsInt32()) {
channel_args.args[i].type = GRPC_ARG_INTEGER;
channel_args.args[i].value.integer = current_value->Int32Value();
} else if (current_value->IsString()) {
channel_args.args[i].type = GRPC_ARG_STRING;
value_strings[i] = new NanUtf8String(current_value);
channel_args.args[i].value.string = **value_strings[i];
} else {
free(channel_args.args);
return NanThrowTypeError("Arg values must be strings");
}
}
if (creds == NULL) {
wrapped_channel = grpc_channel_create(**host, &channel_args);
} else {
wrapped_channel =
grpc_secure_channel_create(creds, **host, &channel_args);
}
free(channel_args.args);
} else {
return NanThrowTypeError("Channel expects a string and an object");
}
Channel *channel = new Channel(wrapped_channel, host);
channel->Wrap(args.This());
NanReturnValue(args.This());
} else {
const int argc = 2;
Local<Value> argv[argc] = {args[0], args[1]};
NanReturnValue(constructor->NewInstance(argc, argv));
}
}
NAN_METHOD(Channel::Close) {
NanScope();
if (!HasInstance(args.This())) {
return NanThrowTypeError("close can only be called on Channel objects");
}
Channel *channel = ObjectWrap::Unwrap<Channel>(args.This());
if (channel->wrapped_channel != NULL) {
grpc_channel_destroy(channel->wrapped_channel);
channel->wrapped_channel = NULL;
}
NanReturnUndefined();
}
} // namespace node
} // namespace grpc

@ -0,0 +1,79 @@
/*
*
* 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_CHANNEL_H_
#define NET_GRPC_NODE_CHANNEL_H_
#include <node.h>
#include <nan.h>
#include "grpc/grpc.h"
namespace grpc {
namespace node {
/* Wrapper class for grpc_channel structs */
class Channel : public ::node::ObjectWrap {
public:
static void Init(v8::Handle<v8::Object> exports);
static bool HasInstance(v8::Handle<v8::Value> val);
/* This is used to typecheck javascript objects before converting them to
this type */
static v8::Persistent<v8::Value> prototype;
/* Returns the grpc_channel struct that this object wraps */
grpc_channel *GetWrappedChannel();
/* Return the hostname that this channel connects to */
char *GetHost();
private:
explicit Channel(grpc_channel *channel, NanUtf8String *host);
~Channel();
// Prevent copying
Channel(const Channel &);
Channel &operator=(const Channel &);
static NAN_METHOD(New);
static NAN_METHOD(Close);
static v8::Persistent<v8::Function> constructor;
static v8::Persistent<v8::FunctionTemplate> fun_tpl;
grpc_channel *wrapped_channel;
NanUtf8String *host;
};
} // namespace node
} // namespace grpc
#endif // NET_GRPC_NODE_CHANNEL_H_

@ -0,0 +1,209 @@
/*
*
* 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('bindings')('grpc.node');
var common = require('./common');
var Duplex = require('stream').Duplex;
var util = require('util');
util.inherits(GrpcClientStream, Duplex);
/**
* Class for representing a gRPC client side stream as a Node stream. Extends
* from stream.Duplex.
* @constructor
* @param {grpc.Call} call Call object to proxy
* @param {object} options Stream options
*/
function GrpcClientStream(call, options) {
Duplex.call(this, options);
var self = this;
// Indicates that we can start reading and have not received a null read
var can_read = false;
// Indicates that a read is currently pending
var reading = false;
// Indicates that we can call startWrite
var can_write = false;
// Indicates that a write is currently pending
var writing = false;
this._call = call;
/**
* Callback to handle receiving a READ event. Pushes the data from that event
* onto the read queue and starts reading again if applicable.
* @param {grpc.Event} event The READ event object
*/
function readCallback(event) {
var data = event.data;
if (self.push(data)) {
if (data == null) {
// Disable starting to read after null read was received
can_read = false;
reading = false;
} else {
call.startRead(readCallback);
}
} else {
// Indicate that reading can be resumed by calling startReading
reading = false;
}
};
/**
* Initiate a read, which continues until self.push returns false (indicating
* that reading should be paused) or data is null (indicating that there is no
* more data to read).
*/
function startReading() {
call.startRead(readCallback);
}
// TODO(mlumish): possibly change queue implementation due to shift slowness
var write_queue = [];
/**
* Write the next chunk of data in the write queue if there is one. Otherwise
* indicate that there is no pending write. When the write succeeds, this
* function is called again.
*/
function writeNext() {
if (write_queue.length > 0) {
writing = true;
var next = write_queue.shift();
var writeCallback = function(event) {
next.callback();
writeNext();
};
call.startWrite(next.chunk, writeCallback, 0);
} else {
writing = false;
}
}
call.startInvoke(function(event) {
can_read = true;
can_write = true;
startReading();
writeNext();
}, function(event) {
self.emit('metadata', event.data);
}, function(event) {
self.emit('status', event.data);
}, 0);
this.on('finish', function() {
call.writesDone(function() {});
});
/**
* Indicate that reads should start, and start them if the INVOKE_ACCEPTED
* event has been received.
*/
this._enableRead = function() {
if (!reading) {
reading = true;
if (can_read) {
startReading();
}
}
};
/**
* Push the chunk onto the write queue, and write from the write queue if
* there is not a pending write
* @param {Buffer} chunk The chunk of data to write
* @param {function(Error=)} callback The callback to call when the write
* completes
*/
this._tryWrite = function(chunk, callback) {
write_queue.push({chunk: chunk, callback: callback});
if (can_write && !writing) {
writeNext();
}
};
}
/**
* Start reading. This is an implementation of a method needed for implementing
* stream.Readable.
* @param {number} size Ignored
*/
GrpcClientStream.prototype._read = function(size) {
this._enableRead();
};
/**
* 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) {
this._tryWrite(chunk, callback);
};
/**
* 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 {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,
metadata,
deadline) {
if (deadline === undefined) {
deadline = Infinity;
}
var call = new grpc.Call(channel, method, deadline);
if (metadata) {
call.addMetadata(metadata);
}
return new GrpcClientStream(call);
}
/**
* 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
*/
exports.status = grpc.status;
/**
* Call error name to code number mapping
*/
exports.callError = grpc.callError;

@ -0,0 +1,62 @@
/*
*
* 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('highland');
/**
* When the given stream finishes without error, call the callback once. This
* will not be called until something begins to consume the stream.
* @param {function} callback The callback to call at stream end
* @param {stream} source The stream to watch
* @return {stream} The stream with the callback attached
*/
function onSuccessfulStreamEnd(callback, source) {
var error = false;
return source.consume(function(err, x, push, next) {
if (x === _.nil) {
if (!error) {
callback();
}
push(null, x);
} else if (err) {
error = true;
push(err);
next();
} else {
push(err, x);
next();
}
});
}
exports.onSuccessfulStreamEnd = onSuccessfulStreamEnd;

@ -0,0 +1,89 @@
/*
*
* 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 <node.h>
#include <nan.h>
#include "grpc/grpc.h"
#include "grpc/support/time.h"
#include "completion_queue_async_worker.h"
#include "event.h"
#include "tag.h"
namespace grpc {
namespace node {
using v8::Function;
using v8::Handle;
using v8::Object;
using v8::Persistent;
using v8::Value;
grpc_completion_queue *CompletionQueueAsyncWorker::queue;
CompletionQueueAsyncWorker::CompletionQueueAsyncWorker()
: NanAsyncWorker(NULL) {}
CompletionQueueAsyncWorker::~CompletionQueueAsyncWorker() {}
void CompletionQueueAsyncWorker::Execute() {
result = grpc_completion_queue_next(queue, gpr_inf_future);
}
grpc_completion_queue *CompletionQueueAsyncWorker::GetQueue() { return queue; }
void CompletionQueueAsyncWorker::Next() {
NanScope();
CompletionQueueAsyncWorker *worker = new CompletionQueueAsyncWorker();
NanAsyncQueueWorker(worker);
}
void CompletionQueueAsyncWorker::Init(Handle<Object> exports) {
NanScope();
queue = grpc_completion_queue_create();
}
void CompletionQueueAsyncWorker::HandleOKCallback() {
NanScope();
NanCallback event_callback(GetTagHandle(result->tag).As<Function>());
Handle<Value> argv[] = {CreateEventObject(result)};
DestroyTag(result->tag);
grpc_event_finish(result);
result = NULL;
event_callback.Call(1, argv);
}
} // namespace node
} // namespace grpc

@ -0,0 +1,79 @@
/*
*
* 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_COMPLETION_QUEUE_ASYNC_WORKER_H_
#define NET_GRPC_NODE_COMPLETION_QUEUE_ASYNC_WORKER_H_
#include <nan.h>
#include "grpc/grpc.h"
namespace grpc {
namespace node {
/* A worker that asynchronously calls completion_queue_next, and queues onto the
node event loop a call to the function stored in the event's tag. */
class CompletionQueueAsyncWorker : public NanAsyncWorker {
public:
CompletionQueueAsyncWorker();
~CompletionQueueAsyncWorker();
/* Calls completion_queue_next with the provided deadline, and stores the
event if there was one or sets an error message if there was not */
void Execute();
/* Returns the completion queue attached to this class */
static grpc_completion_queue *GetQueue();
/* Convenience function to create a worker with the given arguments and queue
it to run asynchronously */
static void Next();
/* Initialize the CompletionQueueAsyncWorker class */
static void Init(v8::Handle<v8::Object> exports);
protected:
/* Called when Execute has succeeded (completed without setting an error
message). Calls the saved callback with the event that came from
completion_queue_next */
void HandleOKCallback();
private:
grpc_event *result;
static grpc_completion_queue *queue;
};
} // namespace node
} // namespace grpc
#endif // NET_GRPC_NODE_COMPLETION_QUEUE_ASYNC_WORKER_H_

@ -0,0 +1,209 @@
/*
*
* 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 <node.h>
#include "grpc/grpc.h"
#include "grpc/grpc_security.h"
#include "grpc/support/log.h"
#include "credentials.h"
namespace grpc {
namespace node {
using ::node::Buffer;
using v8::Arguments;
using v8::Exception;
using v8::External;
using v8::Function;
using v8::FunctionTemplate;
using v8::Handle;
using v8::HandleScope;
using v8::Integer;
using v8::Local;
using v8::Object;
using v8::ObjectTemplate;
using v8::Persistent;
using v8::Value;
Persistent<Function> Credentials::constructor;
Persistent<FunctionTemplate> Credentials::fun_tpl;
Credentials::Credentials(grpc_credentials *credentials)
: wrapped_credentials(credentials) {}
Credentials::~Credentials() {
gpr_log(GPR_DEBUG, "Destroying credentials object");
grpc_credentials_release(wrapped_credentials);
}
void Credentials::Init(Handle<Object> exports) {
NanScope();
Local<FunctionTemplate> tpl = FunctionTemplate::New(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);
}
bool Credentials::HasInstance(Handle<Value> val) {
NanScope();
return NanHasInstance(fun_tpl, val);
}
Handle<Value> Credentials::WrapStruct(grpc_credentials *credentials) {
NanEscapableScope();
if (credentials == NULL) {
return NanEscapeScope(NanNull());
}
const int argc = 1;
Handle<Value> argv[argc] = {
External::New(reinterpret_cast<void *>(credentials))};
return NanEscapeScope(constructor->NewInstance(argc, argv));
}
grpc_credentials *Credentials::GetWrappedCredentials() {
return wrapped_credentials;
}
NAN_METHOD(Credentials::New) {
NanScope();
if (args.IsConstructCall()) {
if (!args[0]->IsExternal()) {
return NanThrowTypeError(
"Credentials can only be created with the provided functions");
}
grpc_credentials *creds_value =
reinterpret_cast<grpc_credentials *>(External::Unwrap(args[0]));
Credentials *credentials = new Credentials(creds_value);
credentials->Wrap(args.This());
NanReturnValue(args.This());
} else {
const int argc = 1;
Local<Value> argv[argc] = {args[0]};
NanReturnValue(constructor->NewInstance(argc, argv));
}
}
NAN_METHOD(Credentials::CreateDefault) {
NanScope();
NanReturnValue(WrapStruct(grpc_default_credentials_create()));
}
NAN_METHOD(Credentials::CreateSsl) {
NanScope();
char *root_certs;
char *private_key = NULL;
char *cert_chain = NULL;
int root_certs_length, private_key_length = 0, cert_chain_length = 0;
if (!Buffer::HasInstance(args[0])) {
return NanThrowTypeError("createSsl's first argument must be a Buffer");
}
root_certs = Buffer::Data(args[0]);
root_certs_length = Buffer::Length(args[0]);
if (Buffer::HasInstance(args[1])) {
private_key = Buffer::Data(args[1]);
private_key_length = Buffer::Length(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])) {
cert_chain = Buffer::Data(args[2]);
cert_chain_length = Buffer::Length(args[2]);
} else if (!(args[2]->IsNull() || args[2]->IsUndefined())) {
return NanThrowTypeError(
"createSSl's third argument must be a Buffer if provided");
}
NanReturnValue(WrapStruct(grpc_ssl_credentials_create(
reinterpret_cast<unsigned char *>(root_certs), root_certs_length,
reinterpret_cast<unsigned char *>(private_key), private_key_length,
reinterpret_cast<unsigned char *>(cert_chain), cert_chain_length)));
}
NAN_METHOD(Credentials::CreateComposite) {
NanScope();
if (!HasInstance(args[0])) {
return NanThrowTypeError(
"createComposite's first argument must be a Credentials object");
}
if (!HasInstance(args[1])) {
return NanThrowTypeError(
"createComposite's second argument must be a Credentials object");
}
Credentials *creds1 = ObjectWrap::Unwrap<Credentials>(args[0]->ToObject());
Credentials *creds2 = ObjectWrap::Unwrap<Credentials>(args[1]->ToObject());
NanReturnValue(WrapStruct(grpc_composite_credentials_create(
creds1->wrapped_credentials, creds2->wrapped_credentials)));
}
NAN_METHOD(Credentials::CreateGce) {
NanScope();
NanReturnValue(WrapStruct(grpc_compute_engine_credentials_create()));
}
NAN_METHOD(Credentials::CreateFake) {
NanScope();
NanReturnValue(WrapStruct(grpc_fake_transport_security_credentials_create()));
}
NAN_METHOD(Credentials::CreateIam) {
NanScope();
if (!args[0]->IsString()) {
return NanThrowTypeError("createIam's first argument must be a string");
}
if (!args[1]->IsString()) {
return NanThrowTypeError("createIam's second argument must be a string");
}
NanUtf8String auth_token(args[0]);
NanUtf8String auth_selector(args[1]);
NanReturnValue(
WrapStruct(grpc_iam_credentials_create(*auth_token, *auth_selector)));
}
} // namespace node
} // namespace grpc

@ -0,0 +1,81 @@
/*
*
* 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_CREDENTIALS_H_
#define NET_GRPC_NODE_CREDENTIALS_H_
#include <node.h>
#include <nan.h>
#include "grpc/grpc.h"
#include "grpc/grpc_security.h"
namespace grpc {
namespace node {
/* Wrapper class for grpc_credentials structs */
class Credentials : public ::node::ObjectWrap {
public:
static void Init(v8::Handle<v8::Object> exports);
static bool HasInstance(v8::Handle<v8::Value> val);
/* Wrap a grpc_credentials struct in a javascript object */
static v8::Handle<v8::Value> WrapStruct(grpc_credentials *credentials);
/* Returns the grpc_credentials struct that this object wraps */
grpc_credentials *GetWrappedCredentials();
private:
explicit Credentials(grpc_credentials *credentials);
~Credentials();
// Prevent copying
Credentials(const Credentials &);
Credentials &operator=(const Credentials &);
static NAN_METHOD(New);
static NAN_METHOD(CreateDefault);
static NAN_METHOD(CreateSsl);
static NAN_METHOD(CreateComposite);
static NAN_METHOD(CreateGce);
static NAN_METHOD(CreateFake);
static NAN_METHOD(CreateIam);
static v8::Persistent<v8::Function> constructor;
// Used for typechecking instances of this javascript class
static v8::Persistent<v8::FunctionTemplate> fun_tpl;
grpc_credentials *wrapped_credentials;
};
} // namespace node
} // namespace grpc
#endif // NET_GRPC_NODE_CREDENTIALS_H_

@ -0,0 +1,164 @@
/*
*
* 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 <node.h>
#include <nan.h>
#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 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<Value> GetEventData(grpc_event *event) {
NanEscapableScope();
size_t count;
grpc_metadata *items;
Handle<Array> metadata;
Handle<Object> status;
Handle<Object> rpc_new;
switch (event->type) {
case GRPC_READ:
return NanEscapeScope(ByteBufferToBuffer(event->data.read));
case GRPC_INVOKE_ACCEPTED:
return NanEscapeScope(NanNew<Number>(event->data.invoke_accepted));
case GRPC_WRITE_ACCEPTED:
return NanEscapeScope(NanNew<Number>(event->data.write_accepted));
case GRPC_FINISH_ACCEPTED:
return NanEscapeScope(NanNew<Number>(event->data.finish_accepted));
case GRPC_CLIENT_METADATA_READ:
count = event->data.client_metadata_read.count;
items = event->data.client_metadata_read.elements;
metadata = NanNew<Array>(static_cast<int>(count));
for (unsigned int i = 0; i < count; i++) {
Handle<Object> item_obj = NanNew<Object>();
item_obj->Set(NanNew<String, const char *>("key"),
NanNew<String, char *>(items[i].key));
item_obj->Set(
NanNew<String, const char *>("value"),
NanNew<String, char *>(items[i].value,
static_cast<int>(items[i].value_length)));
metadata->Set(i, item_obj);
}
return NanEscapeScope(metadata);
case GRPC_FINISHED:
status = NanNew<Object>();
status->Set(NanNew("code"), NanNew<Number>(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;
metadata = NanNew<Array>(static_cast<int>(count));
for (unsigned int i = 0; i < count; i++) {
Handle<Object> item_obj = NanNew<Object>();
item_obj->Set(NanNew<String, const char *>("key"),
NanNew<String, char *>(items[i].key));
item_obj->Set(
NanNew<String, const char *>("value"),
NanNew<String, char *>(items[i].value,
static_cast<int>(items[i].value_length)));
metadata->Set(i, item_obj);
}
status->Set(NanNew("metadata"), metadata);
return NanEscapeScope(status);
case GRPC_SERVER_RPC_NEW:
rpc_new = NanNew<Object>();
if (event->data.server_rpc_new.method == NULL) {
return NanEscapeScope(NanNull());
}
rpc_new->Set(
NanNew<String, const char *>("method"),
NanNew<String, const char *>(event->data.server_rpc_new.method));
rpc_new->Set(
NanNew<String, const char *>("host"),
NanNew<String, const char *>(event->data.server_rpc_new.host));
rpc_new->Set(NanNew<String, const char *>("absolute_deadline"),
NanNew<Date>(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<Array>(static_cast<int>(count));
for (unsigned int i = 0; i < count; i++) {
Handle<Object> item_obj = Object::New();
item_obj->Set(NanNew<String, const char *>("key"),
NanNew<String, char *>(items[i].key));
item_obj->Set(
NanNew<String, const char *>("value"),
NanNew<String, char *>(items[i].value,
static_cast<int>(items[i].value_length)));
metadata->Set(i, item_obj);
}
rpc_new->Set(NanNew<String, const char *>("metadata"), metadata);
return NanEscapeScope(rpc_new);
default:
return NanEscapeScope(NanNull());
}
}
Handle<Value> CreateEventObject(grpc_event *event) {
NanEscapableScope();
if (event == NULL) {
return NanEscapeScope(NanNull());
}
Handle<Object> event_obj = NanNew<Object>();
Handle<Value> call;
if (TagHasCall(event->tag)) {
call = TagGetCall(event->tag);
} else {
call = Call::WrapStruct(event->call);
}
event_obj->Set(NanNew<String, const char *>("call"), call);
event_obj->Set(NanNew<String, const char *>("type"),
NanNew<Number>(event->type));
event_obj->Set(NanNew<String, const char *>("data"), GetEventData(event));
return NanEscapeScope(event_obj);
}
} // namespace node
} // namespace grpc

@ -0,0 +1,48 @@
/*
*
* 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 <node.h>
#include "grpc/grpc.h"
namespace grpc {
namespace node {
v8::Handle<v8::Value> CreateEventObject(grpc_event *event);
} // namespace node
} // namespace grpc
#endif // NET_GRPC_NODE_EVENT_H_

@ -0,0 +1,25 @@
syntax = "proto2";
package math;
message DivArgs {
required int64 dividend = 1;
required int64 divisor = 2;
}
message DivReply {
required int64 quotient = 1;
required int64 remainder = 2;
}
message FibArgs {
optional int64 limit = 1;
}
message Num {
required int64 num = 1;
}
message FibReply {
required int64 count = 1;
}

@ -0,0 +1,201 @@
/*
*
* 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 ProtoBuf = require('protobufjs');
var fs = require('fs');
var util = require('util');
var Transform = require('stream').Transform;
var builder = ProtoBuf.loadProtoFile(__dirname + '/math.proto');
var math = builder.build('math');
var makeConstructor = require('../surface_server.js').makeServerConstructor;
/**
* Get a function that deserializes a specific type of protobuf.
* @param {function()} cls The constructor of the message type to deserialize
* @return {function(Buffer):cls} The deserialization function
*/
function deserializeCls(cls) {
/**
* Deserialize a buffer to a message object
* @param {Buffer} arg_buf The buffer to deserialize
* @return {cls} The resulting object
*/
return function deserialize(arg_buf) {
return cls.decode(arg_buf);
};
}
/**
* Get a function that serializes objects to a buffer by protobuf class.
* @param {function()} Cls The constructor of the message type to serialize
* @return {function(Cls):Buffer} The serialization function
*/
function serializeCls(Cls) {
/**
* Serialize an object to a Buffer
* @param {Object} arg The object to serialize
* @return {Buffer} The serialized object
*/
return function serialize(arg) {
return new Buffer(new Cls(arg).encode().toBuffer());
};
}
/* This function call creates a server constructor for servers that that expose
* the four specified methods. This specifies how to serialize messages that the
* server sends and deserialize messages that the client sends, and whether the
* client or the server will send a stream of messages, for each method. This
* also specifies a prefix that will be added to method names when sending them
* on the wire. This function call and all of the preceding code in this file
* are intended to approximate what the generated code will look like for the
* math service */
var Server = makeConstructor({
Div: {
serialize: serializeCls(math.DivReply),
deserialize: deserializeCls(math.DivArgs),
client_stream: false,
server_stream: false
},
Fib: {
serialize: serializeCls(math.Num),
deserialize: deserializeCls(math.FibArgs),
client_stream: false,
server_stream: true
},
Sum: {
serialize: serializeCls(math.Num),
deserialize: deserializeCls(math.Num),
client_stream: true,
server_stream: false
},
DivMany: {
serialize: serializeCls(math.DivReply),
deserialize: deserializeCls(math.DivArgs),
client_stream: true,
server_stream: true
}
}, '/Math/');
/**
* Server function for division. Provides the /Math/DivMany and /Math/Div
* functions (Div is just DivMany with only one stream element). For each
* DivArgs parameter, responds with a DivReply with the results of the division
* @param {Object} call The object containing request and cancellation info
* @param {function(Error, *)} cb Response callback
*/
function mathDiv(call, cb) {
var req = call.request;
if (req.divisor == 0) {
cb(new Error('cannot divide by zero'));
}
cb(null, {
quotient: req.dividend / req.divisor,
remainder: req.dividend % req.divisor
});
}
/**
* Server function for Fibonacci numbers. Provides the /Math/Fib function. Reads
* a single parameter that indicates the number of responses, and then responds
* with a stream of that many Fibonacci numbers.
* @param {stream} stream The stream for sending responses.
*/
function mathFib(stream) {
// Here, call is a standard writable Node object Stream
var previous = 0, current = 1;
for (var i = 0; i < stream.request.limit; i++) {
stream.write({num: current});
var temp = current;
current += previous;
previous = temp;
}
stream.end();
}
/**
* Server function for summation. Provides the /Math/Sum function. Reads a
* stream of number parameters, then responds with their sum.
* @param {stream} call The stream of arguments.
* @param {function(Error, *)} cb Response callback
*/
function mathSum(call, cb) {
// Here, call is a standard readable Node object Stream
var sum = 0;
call.on('data', function(data) {
sum += data.num | 0;
});
call.on('end', function() {
cb(null, {num: sum});
});
}
function mathDivMany(stream) {
// Here, call is a standard duplex Node object Stream
util.inherits(DivTransform, Transform);
function DivTransform() {
var options = {objectMode: true};
Transform.call(this, options);
}
DivTransform.prototype._transform = function(div_args, encoding, callback) {
if (div_args.divisor == 0) {
callback(new Error('cannot divide by zero'));
}
callback(null, {
quotient: div_args.dividend / div_args.divisor,
remainder: div_args.dividend % div_args.divisor
});
};
var transform = new DivTransform();
stream.pipe(transform);
transform.pipe(stream);
}
var server = new Server({
Div: mathDiv,
Fib: mathFib,
Sum: mathSum,
DivMany: mathDivMany
});
if (require.main === module) {
server.bind('localhost:7070').listen();
}
/**
* See docs for server
*/
module.exports = server;

@ -0,0 +1,182 @@
/*
*
* 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 <node.h>
#include <nan.h>
#include <v8.h>
#include "grpc/grpc.h"
#include "call.h"
#include "channel.h"
#include "event.h"
#include "server.h"
#include "completion_queue_async_worker.h"
#include "credentials.h"
#include "server_credentials.h"
using v8::Handle;
using v8::Value;
using v8::Object;
using v8::Uint32;
using v8::String;
void InitStatusConstants(Handle<Object> exports) {
NanScope();
Handle<Object> status = Object::New();
exports->Set(NanNew("status"), status);
Handle<Value> OK(NanNew<Uint32, uint32_t>(GRPC_STATUS_OK));
status->Set(NanNew("OK"), OK);
Handle<Value> CANCELLED(NanNew<Uint32, uint32_t>(GRPC_STATUS_CANCELLED));
status->Set(NanNew("CANCELLED"), CANCELLED);
Handle<Value> UNKNOWN(NanNew<Uint32, uint32_t>(GRPC_STATUS_UNKNOWN));
status->Set(NanNew("UNKNOWN"), UNKNOWN);
Handle<Value> INVALID_ARGUMENT(
NanNew<Uint32, uint32_t>(GRPC_STATUS_INVALID_ARGUMENT));
status->Set(NanNew("INVALID_ARGUMENT"), INVALID_ARGUMENT);
Handle<Value> DEADLINE_EXCEEDED(
NanNew<Uint32, uint32_t>(GRPC_STATUS_DEADLINE_EXCEEDED));
status->Set(NanNew("DEADLINE_EXCEEDED"), DEADLINE_EXCEEDED);
Handle<Value> NOT_FOUND(NanNew<Uint32, uint32_t>(GRPC_STATUS_NOT_FOUND));
status->Set(NanNew("NOT_FOUND"), NOT_FOUND);
Handle<Value> ALREADY_EXISTS(
NanNew<Uint32, uint32_t>(GRPC_STATUS_ALREADY_EXISTS));
status->Set(NanNew("ALREADY_EXISTS"), ALREADY_EXISTS);
Handle<Value> PERMISSION_DENIED(
NanNew<Uint32, uint32_t>(GRPC_STATUS_PERMISSION_DENIED));
status->Set(NanNew("PERMISSION_DENIED"), PERMISSION_DENIED);
Handle<Value> UNAUTHENTICATED(
NanNew<Uint32, uint32_t>(GRPC_STATUS_UNAUTHENTICATED));
status->Set(NanNew("UNAUTHENTICATED"), UNAUTHENTICATED);
Handle<Value> RESOURCE_EXHAUSTED(
NanNew<Uint32, uint32_t>(GRPC_STATUS_RESOURCE_EXHAUSTED));
status->Set(NanNew("RESOURCE_EXHAUSTED"), RESOURCE_EXHAUSTED);
Handle<Value> FAILED_PRECONDITION(
NanNew<Uint32, uint32_t>(GRPC_STATUS_FAILED_PRECONDITION));
status->Set(NanNew("FAILED_PRECONDITION"), FAILED_PRECONDITION);
Handle<Value> ABORTED(NanNew<Uint32, uint32_t>(GRPC_STATUS_ABORTED));
status->Set(NanNew("ABORTED"), ABORTED);
Handle<Value> OUT_OF_RANGE(
NanNew<Uint32, uint32_t>(GRPC_STATUS_OUT_OF_RANGE));
status->Set(NanNew("OUT_OF_RANGE"), OUT_OF_RANGE);
Handle<Value> UNIMPLEMENTED(
NanNew<Uint32, uint32_t>(GRPC_STATUS_UNIMPLEMENTED));
status->Set(NanNew("UNIMPLEMENTED"), UNIMPLEMENTED);
Handle<Value> INTERNAL(NanNew<Uint32, uint32_t>(GRPC_STATUS_INTERNAL));
status->Set(NanNew("INTERNAL"), INTERNAL);
Handle<Value> UNAVAILABLE(NanNew<Uint32, uint32_t>(GRPC_STATUS_UNAVAILABLE));
status->Set(NanNew("UNAVAILABLE"), UNAVAILABLE);
Handle<Value> DATA_LOSS(NanNew<Uint32, uint32_t>(GRPC_STATUS_DATA_LOSS));
status->Set(NanNew("DATA_LOSS"), DATA_LOSS);
}
void InitCallErrorConstants(Handle<Object> exports) {
NanScope();
Handle<Object> call_error = Object::New();
exports->Set(NanNew("callError"), call_error);
Handle<Value> OK(NanNew<Uint32, uint32_t>(GRPC_CALL_OK));
call_error->Set(NanNew("OK"), OK);
Handle<Value> ERROR(NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR));
call_error->Set(NanNew("ERROR"), ERROR);
Handle<Value> NOT_ON_SERVER(
NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_ON_SERVER));
call_error->Set(NanNew("NOT_ON_SERVER"), NOT_ON_SERVER);
Handle<Value> NOT_ON_CLIENT(
NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_ON_CLIENT));
call_error->Set(NanNew("NOT_ON_CLIENT"), NOT_ON_CLIENT);
Handle<Value> ALREADY_INVOKED(
NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_ALREADY_INVOKED));
call_error->Set(NanNew("ALREADY_INVOKED"), ALREADY_INVOKED);
Handle<Value> NOT_INVOKED(
NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_NOT_INVOKED));
call_error->Set(NanNew("NOT_INVOKED"), NOT_INVOKED);
Handle<Value> ALREADY_FINISHED(
NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_ALREADY_FINISHED));
call_error->Set(NanNew("ALREADY_FINISHED"), ALREADY_FINISHED);
Handle<Value> TOO_MANY_OPERATIONS(
NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS));
call_error->Set(NanNew("TOO_MANY_OPERATIONS"), TOO_MANY_OPERATIONS);
Handle<Value> INVALID_FLAGS(
NanNew<Uint32, uint32_t>(GRPC_CALL_ERROR_INVALID_FLAGS));
call_error->Set(NanNew("INVALID_FLAGS"), INVALID_FLAGS);
}
void InitOpErrorConstants(Handle<Object> exports) {
NanScope();
Handle<Object> op_error = Object::New();
exports->Set(NanNew("opError"), op_error);
Handle<Value> OK(NanNew<Uint32, uint32_t>(GRPC_OP_OK));
op_error->Set(NanNew("OK"), OK);
Handle<Value> ERROR(NanNew<Uint32, uint32_t>(GRPC_OP_ERROR));
op_error->Set(NanNew("ERROR"), ERROR);
}
void InitCompletionTypeConstants(Handle<Object> exports) {
NanScope();
Handle<Object> completion_type = Object::New();
exports->Set(NanNew("completionType"), completion_type);
Handle<Value> QUEUE_SHUTDOWN(NanNew<Uint32, uint32_t>(GRPC_QUEUE_SHUTDOWN));
completion_type->Set(NanNew("QUEUE_SHUTDOWN"), QUEUE_SHUTDOWN);
Handle<Value> READ(NanNew<Uint32, uint32_t>(GRPC_READ));
completion_type->Set(NanNew("READ"), READ);
Handle<Value> INVOKE_ACCEPTED(NanNew<Uint32, uint32_t>(GRPC_INVOKE_ACCEPTED));
completion_type->Set(NanNew("INVOKE_ACCEPTED"), INVOKE_ACCEPTED);
Handle<Value> WRITE_ACCEPTED(NanNew<Uint32, uint32_t>(GRPC_WRITE_ACCEPTED));
completion_type->Set(NanNew("WRITE_ACCEPTED"), WRITE_ACCEPTED);
Handle<Value> FINISH_ACCEPTED(NanNew<Uint32, uint32_t>(GRPC_FINISH_ACCEPTED));
completion_type->Set(NanNew("FINISH_ACCEPTED"), FINISH_ACCEPTED);
Handle<Value> CLIENT_METADATA_READ(
NanNew<Uint32, uint32_t>(GRPC_CLIENT_METADATA_READ));
completion_type->Set(NanNew("CLIENT_METADATA_READ"), CLIENT_METADATA_READ);
Handle<Value> FINISHED(NanNew<Uint32, uint32_t>(GRPC_FINISHED));
completion_type->Set(NanNew("FINISHED"), FINISHED);
Handle<Value> SERVER_RPC_NEW(NanNew<Uint32, uint32_t>(GRPC_SERVER_RPC_NEW));
completion_type->Set(NanNew("SERVER_RPC_NEW"), SERVER_RPC_NEW);
}
void init(Handle<Object> exports) {
NanScope();
grpc_init();
InitStatusConstants(exports);
InitCallErrorConstants(exports);
InitOpErrorConstants(exports);
InitCompletionTypeConstants(exports);
grpc::node::Call::Init(exports);
grpc::node::Channel::Init(exports);
grpc::node::Server::Init(exports);
grpc::node::CompletionQueueAsyncWorker::Init(exports);
grpc::node::Credentials::Init(exports);
grpc::node::ServerCredentials::Init(exports);
}
NODE_MODULE(grpc, init)

@ -0,0 +1,18 @@
{
"name": "grpc",
"version": "0.1.0",
"description": "gRPC Library for Node",
"scripts": {
"test": "./node_modules/mocha/bin/mocha"
},
"dependencies": {
"bindings": "^1.2.1",
"nan": "~1.3.0",
"underscore": "^1.7.0"
},
"devDependencies": {
"mocha": "~1.21.0",
"highland": "~2.0.0",
"protobufjs": "~3.8.0"
}
}

@ -0,0 +1,52 @@
/*
*
* 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 net = require('net');
/**
* Finds a free port that a server can bind to, in the format
* "address:port"
* @param {function(string)} cb The callback that should execute when the port
* is available
*/
function nextAvailablePort(cb) {
var server = net.createServer();
server.listen(function() {
var address = server.address();
server.close(function() {
cb(address.address + ':' + address.port.toString());
});
});
}
exports.nextAvailablePort = nextAvailablePort;

@ -0,0 +1,236 @@
/*
*
* 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 "server.h"
#include <node.h>
#include <nan.h>
#include <malloc.h>
#include <vector>
#include "grpc/grpc.h"
#include "grpc/grpc_security.h"
#include "call.h"
#include "completion_queue_async_worker.h"
#include "tag.h"
#include "server_credentials.h"
namespace grpc {
namespace node {
using v8::Arguments;
using v8::Array;
using v8::Boolean;
using v8::Exception;
using v8::Function;
using v8::FunctionTemplate;
using v8::Handle;
using v8::HandleScope;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::Persistent;
using v8::String;
using v8::Value;
Persistent<Function> Server::constructor;
Persistent<FunctionTemplate> Server::fun_tpl;
Server::Server(grpc_server *server) : wrapped_server(server) {}
Server::~Server() { grpc_server_destroy(wrapped_server); }
void Server::Init(Handle<Object> exports) {
NanScope();
Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
tpl->SetClassName(String::NewSymbol("Server"));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
NanSetPrototypeTemplate(tpl, "requestCall",
FunctionTemplate::New(RequestCall)->GetFunction());
NanSetPrototypeTemplate(tpl, "addHttp2Port",
FunctionTemplate::New(AddHttp2Port)->GetFunction());
NanSetPrototypeTemplate(
tpl, "addSecureHttp2Port",
FunctionTemplate::New(AddSecureHttp2Port)->GetFunction());
NanSetPrototypeTemplate(tpl, "start",
FunctionTemplate::New(Start)->GetFunction());
NanSetPrototypeTemplate(tpl, "shutdown",
FunctionTemplate::New(Shutdown)->GetFunction());
NanAssignPersistent(fun_tpl, tpl);
NanAssignPersistent(constructor, tpl->GetFunction());
exports->Set(String::NewSymbol("Server"), constructor);
}
bool Server::HasInstance(Handle<Value> val) {
return NanHasInstance(fun_tpl, val);
}
NAN_METHOD(Server::New) {
NanScope();
/* If this is not a constructor call, make a constructor call and return
the result */
if (!args.IsConstructCall()) {
const int argc = 1;
Local<Value> argv[argc] = {args[0]};
NanReturnValue(constructor->NewInstance(argc, argv));
}
grpc_server *wrapped_server;
grpc_completion_queue *queue = CompletionQueueAsyncWorker::GetQueue();
if (args[0]->IsUndefined()) {
wrapped_server = grpc_server_create(queue, NULL);
} else if (args[0]->IsObject()) {
grpc_server_credentials *creds = NULL;
Handle<Object> args_hash(args[0]->ToObject()->Clone());
if (args_hash->HasOwnProperty(NanNew("credentials"))) {
Handle<Value> 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<ServerCredentials>(creds_value->ToObject());
creds = creds_object->GetWrappedServerCredentials();
args_hash->Delete(NanNew("credentials"));
}
Handle<Array> keys(args_hash->GetOwnPropertyNames());
grpc_channel_args channel_args;
channel_args.num_args = keys->Length();
channel_args.args = reinterpret_cast<grpc_arg *>(
calloc(channel_args.num_args, sizeof(grpc_arg)));
/* These are used to keep all strings until then end of the block, then
destroy them */
std::vector<NanUtf8String *> key_strings(keys->Length());
std::vector<NanUtf8String *> value_strings(keys->Length());
for (unsigned int i = 0; i < channel_args.num_args; i++) {
Handle<String> current_key(keys->Get(i)->ToString());
Handle<Value> current_value(args_hash->Get(current_key));
key_strings[i] = new NanUtf8String(current_key);
channel_args.args[i].key = **key_strings[i];
if (current_value->IsInt32()) {
channel_args.args[i].type = GRPC_ARG_INTEGER;
channel_args.args[i].value.integer = current_value->Int32Value();
} else if (current_value->IsString()) {
channel_args.args[i].type = GRPC_ARG_STRING;
value_strings[i] = new NanUtf8String(current_value);
channel_args.args[i].value.string = **value_strings[i];
} else {
free(channel_args.args);
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);
}
free(channel_args.args);
} else {
return NanThrowTypeError("Server expects an object");
}
Server *server = new Server(wrapped_server);
server->Wrap(args.This());
NanReturnValue(args.This());
}
NAN_METHOD(Server::RequestCall) {
NanScope();
if (!HasInstance(args.This())) {
return NanThrowTypeError("requestCall can only be called on a Server");
}
Server *server = ObjectWrap::Unwrap<Server>(args.This());
grpc_call_error error = grpc_server_request_call(
server->wrapped_server, CreateTag(args[0], NanNull()));
if (error == GRPC_CALL_OK) {
CompletionQueueAsyncWorker::Next();
} else {
return NanThrowError("requestCall failed", error);
}
NanReturnUndefined();
}
NAN_METHOD(Server::AddHttp2Port) {
NanScope();
if (!HasInstance(args.This())) {
return NanThrowTypeError("addHttp2Port can only be called on a Server");
}
if (!args[0]->IsString()) {
return NanThrowTypeError("addHttp2Port's argument must be a String");
}
Server *server = ObjectWrap::Unwrap<Server>(args.This());
NanReturnValue(NanNew<Boolean>(grpc_server_add_http2_port(
server->wrapped_server, *NanUtf8String(args[0]))));
}
NAN_METHOD(Server::AddSecureHttp2Port) {
NanScope();
if (!HasInstance(args.This())) {
return NanThrowTypeError(
"addSecureHttp2Port can only be called on a Server");
}
if (!args[0]->IsString()) {
return NanThrowTypeError("addSecureHttp2Port's argument must be a String");
}
Server *server = ObjectWrap::Unwrap<Server>(args.This());
NanReturnValue(NanNew<Boolean>(grpc_server_add_secure_http2_port(
server->wrapped_server, *NanUtf8String(args[0]))));
}
NAN_METHOD(Server::Start) {
NanScope();
if (!HasInstance(args.This())) {
return NanThrowTypeError("start can only be called on a Server");
}
Server *server = ObjectWrap::Unwrap<Server>(args.This());
grpc_server_start(server->wrapped_server);
NanReturnUndefined();
}
NAN_METHOD(Server::Shutdown) {
NanScope();
if (!HasInstance(args.This())) {
return NanThrowTypeError("shutdown can only be called on a Server");
}
Server *server = ObjectWrap::Unwrap<Server>(args.This());
grpc_server_shutdown(server->wrapped_server);
NanReturnUndefined();
}
} // namespace node
} // namespace grpc

@ -0,0 +1,79 @@
/*
*
* 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_SERVER_H_
#define NET_GRPC_NODE_SERVER_H_
#include <node.h>
#include <nan.h>
#include "grpc/grpc.h"
namespace grpc {
namespace node {
/* Wraps grpc_server as a JavaScript object. Provides a constructor
and wrapper methods for grpc_server_create, grpc_server_request_call,
grpc_server_add_http2_port, and grpc_server_start. */
class Server : public ::node::ObjectWrap {
public:
/* Initializes the Server class and exposes the constructor and
wrapper methods to JavaScript */
static void Init(v8::Handle<v8::Object> exports);
/* Tests whether the given value was constructed by this class's
JavaScript constructor */
static bool HasInstance(v8::Handle<v8::Value> val);
private:
explicit Server(grpc_server *server);
~Server();
// Prevent copying
Server(const Server &);
Server &operator=(const Server &);
static NAN_METHOD(New);
static NAN_METHOD(RequestCall);
static NAN_METHOD(AddHttp2Port);
static NAN_METHOD(AddSecureHttp2Port);
static NAN_METHOD(Start);
static NAN_METHOD(Shutdown);
static v8::Persistent<v8::Function> constructor;
static v8::Persistent<v8::FunctionTemplate> fun_tpl;
grpc_server *wrapped_server;
};
} // namespace node
} // namespace grpc
#endif // NET_GRPC_NODE_SERVER_H_

@ -0,0 +1,261 @@
/*
*
* 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('bindings')('grpc.node');
var common = require('./common');
var Duplex = require('stream').Duplex;
var util = require('util');
util.inherits(GrpcServerStream, Duplex);
/**
* 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 {object} options Stream options
*/
function GrpcServerStream(call, options) {
Duplex.call(this, options);
this._call = call;
// Indicate that a status has been sent
var finished = false;
var self = this;
var status = {
'code' : grpc.status.OK,
'details' : 'OK'
};
/**
* Send the pending status
*/
function sendStatus() {
call.startWriteStatus(status.code, status.details, function() {
});
finished = true;
}
this.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
* and the details will be set to 'Unknown Error'.
* @param {Error} err The error object
*/
function setStatus(err) {
var code = grpc.status.INTERNAL;
var details = 'Unknown Error';
if (err.hasOwnProperty('code')) {
code = err.code;
if (err.hasOwnProperty('details')) {
details = err.details;
}
}
status = {'code': code, 'details': details};
}
/**
* Terminate the call. This includes indicating that reads are done, draining
* all pending writes, and sending the given error as a status
* @param {Error} err The error object
* @this GrpcServerStream
*/
function terminateCall(err) {
// Drain readable data
this.on('data', function() {});
setStatus(err);
this.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(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);
}
}
};
}
/**
* 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();
};
/**
* Start writing a chunk of data. This is an implementation of a method required
* for implementing stream.Writable.
* @param {Buffer} chunk The chunk of data to write
* @param {string} encoding Ignored
* @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(chunk, function(event) {
callback();
}, 0);
};
/**
* Constructs a server object that stores request handlers and delegates
* incoming requests to those handlers
* @constructor
* @param {Array} options Options that should be passed to the internal server
* implementation
*/
function Server(options) {
this.handlers = {};
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
*/
this.start = function() {
if (this.started) {
throw 'Server is already running';
}
server.start();
/**
* Handles the SERVER_RPC_NEW event. If there is a handler associated with
* the requested method, use that handler to respond to the request. Then
* 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) {
return;
}
server.requestCall(handleNewCall);
var handler = undefined;
var deadline = data.absolute_deadline;
var cancelled = false;
if (handlers.hasOwnProperty(data.method)) {
handler = handlers[data.method];
}
call.serverAccept(function(event) {
if (event.data.code === grpc.status.CANCELLED) {
cancelled = true;
}
}, 0);
call.serverEndInitialMetadata(0);
var stream = new GrpcServerStream(call);
Object.defineProperty(stream, 'cancelled', {
get: function() { return cancelled;}
});
try {
handler(stream, data.metadata);
} catch (e) {
stream.emit('error', e);
}
}
server.requestCall(handleNewCall);
};
/** Shuts down the server.
*/
this.shutdown = function() {
server.shutdown();
};
}
/**
* Registers a handler to handle the named method. Fails if there already is
* a handler for the given method. Returns true on success
* @param {string} name The name of the method that the provided function should
* handle/respond to.
* @param {function} handler Function that takes a stream of request values and
* returns a stream of response values
* @return {boolean} True if the handler was set. False if a handler was already
* set for that name.
*/
Server.prototype.register = function(name, handler) {
if (this.handlers.hasOwnProperty(name)) {
return false;
}
this.handlers[name] = handler;
return true;
};
/**
* 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
*/
Server.prototype.bind = function(port, secure) {
if (secure) {
this._server.addSecureHttp2Port(port);
} else {
this._server.addHttp2Port(port);
}
};
/**
* See documentation for Server
*/
module.exports = Server;

@ -0,0 +1,161 @@
/*
*
* 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 <node.h>
#include "grpc/grpc.h"
#include "grpc/grpc_security.h"
#include "grpc/support/log.h"
#include "server_credentials.h"
namespace grpc {
namespace node {
using ::node::Buffer;
using v8::Arguments;
using v8::Exception;
using v8::External;
using v8::Function;
using v8::FunctionTemplate;
using v8::Handle;
using v8::HandleScope;
using v8::Integer;
using v8::Local;
using v8::Object;
using v8::ObjectTemplate;
using v8::Persistent;
using v8::Value;
Persistent<Function> ServerCredentials::constructor;
Persistent<FunctionTemplate> ServerCredentials::fun_tpl;
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);
}
void ServerCredentials::Init(Handle<Object> exports) {
NanScope();
Local<FunctionTemplate> tpl = FunctionTemplate::New(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);
}
bool ServerCredentials::HasInstance(Handle<Value> val) {
NanScope();
return NanHasInstance(fun_tpl, val);
}
Handle<Value> ServerCredentials::WrapStruct(
grpc_server_credentials *credentials) {
NanEscapableScope();
if (credentials == NULL) {
return NanEscapeScope(NanNull());
}
const int argc = 1;
Handle<Value> argv[argc] = {
External::New(reinterpret_cast<void *>(credentials))};
return NanEscapeScope(constructor->NewInstance(argc, argv));
}
grpc_server_credentials *ServerCredentials::GetWrappedServerCredentials() {
return wrapped_credentials;
}
NAN_METHOD(ServerCredentials::New) {
NanScope();
if (args.IsConstructCall()) {
if (!args[0]->IsExternal()) {
return NanThrowTypeError(
"ServerCredentials can only be created with the provide functions");
}
grpc_server_credentials *creds_value =
reinterpret_cast<grpc_server_credentials *>(External::Unwrap(args[0]));
ServerCredentials *credentials = new ServerCredentials(creds_value);
credentials->Wrap(args.This());
NanReturnValue(args.This());
} else {
const int argc = 1;
Local<Value> argv[argc] = {args[0]};
NanReturnValue(constructor->NewInstance(argc, argv));
}
}
NAN_METHOD(ServerCredentials::CreateSsl) {
NanScope();
char *root_certs = NULL;
char *private_key;
char *cert_chain;
int root_certs_length = 0, private_key_length, cert_chain_length;
if (Buffer::HasInstance(args[0])) {
root_certs = Buffer::Data(args[0]);
root_certs_length = Buffer::Length(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])) {
return NanThrowTypeError("createSsl's second argument must be a Buffer");
}
private_key = Buffer::Data(args[1]);
private_key_length = Buffer::Length(args[1]);
if (!Buffer::HasInstance(args[2])) {
return NanThrowTypeError("createSsl's third argument must be a Buffer");
}
cert_chain = Buffer::Data(args[2]);
cert_chain_length = Buffer::Length(args[2]);
NanReturnValue(WrapStruct(grpc_ssl_server_credentials_create(
reinterpret_cast<unsigned char *>(root_certs), root_certs_length,
reinterpret_cast<unsigned char *>(private_key), private_key_length,
reinterpret_cast<unsigned char *>(cert_chain), cert_chain_length)));
}
NAN_METHOD(ServerCredentials::CreateFake) {
NanScope();
NanReturnValue(
WrapStruct(grpc_fake_transport_security_server_credentials_create()));
}
} // namespace node
} // namespace grpc

@ -0,0 +1,77 @@
/*
*
* 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_SERVER_CREDENTIALS_H_
#define NET_GRPC_NODE_SERVER_CREDENTIALS_H_
#include <node.h>
#include <nan.h>
#include "grpc/grpc.h"
#include "grpc/grpc_security.h"
namespace grpc {
namespace node {
/* Wrapper class for grpc_server_credentials structs */
class ServerCredentials : public ::node::ObjectWrap {
public:
static void Init(v8::Handle<v8::Object> exports);
static bool HasInstance(v8::Handle<v8::Value> val);
/* Wrap a grpc_server_credentials struct in a javascript object */
static v8::Handle<v8::Value> WrapStruct(grpc_server_credentials *credentials);
/* Returns the grpc_server_credentials struct that this object wraps */
grpc_server_credentials *GetWrappedServerCredentials();
private:
explicit ServerCredentials(grpc_server_credentials *credentials);
~ServerCredentials();
// Prevent copying
ServerCredentials(const ServerCredentials &);
ServerCredentials &operator=(const ServerCredentials &);
static NAN_METHOD(New);
static NAN_METHOD(CreateSsl);
static NAN_METHOD(CreateFake);
static v8::Persistent<v8::Function> constructor;
// Used for typechecking instances of this javascript class
static v8::Persistent<v8::FunctionTemplate> fun_tpl;
grpc_server_credentials *wrapped_credentials;
};
} // namespace node
} // namespace grpc
#endif // NET_GRPC_NODE_SERVER_CREDENTIALS_H_

@ -0,0 +1,339 @@
/*
*
* 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 client = require('./client.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
* @param {function(Buffer)} deserialize Function for deserializing binary data
* @param {object} options Stream options
*/
function ClientReadableObjectStream(stream, deserialize, options) {
options = _.extend(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(deserialize(chunk))) {
self._stream.pause();
}
});
this._stream.pause();
}
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
* @param {function(*):Buffer} serialize Function for serializing objects
* @param {object} options Stream options
*/
function ClientWritableObjectStream(stream, serialize, options) {
options = _.extend(options, {objectMode: true});
Writable.call(this, options);
this._stream = stream;
this._serialize = serialize;
forwardEvent(stream, this, 'status');
forwardEvent(stream, this, 'metadata');
this.on('finish', function() {
this._stream.end();
});
}
util.inherits(ClientBidiObjectStream, Duplex);
/**
* Class for representing a gRPC bidi streaming call as a Node stream on the
* client side. Extends from stream.Duplex.
* @constructor
* @param {stream} stream Underlying binary Duplex stream for the call
* @param {function(*):Buffer} serialize Function for serializing objects
* @param {function(Buffer)} deserialize Function for deserializing binary data
* @param {object} options Stream options
*/
function ClientBidiObjectStream(stream, serialize, deserialize, options) {
options = _.extend(options, {objectMode: true});
Duplex.call(this, options);
this._stream = stream;
this._serialize = serialize;
var self = this;
forwardEvent(stream, this, 'status');
forwardEvent(stream, this, 'metadata');
this._stream.on('data', function forwardData(chunk) {
if (!self.push(deserialize(chunk))) {
self._stream.pause();
}
});
this._stream.pause();
this.on('finish', function() {
this._stream.end();
});
}
/**
* _read implementation for both types of streams that allow reading.
* @this {ClientReadableObjectStream|ClientBidiObjectStream}
* @param {number} size Ignored
*/
function _read(size) {
this._stream.resume();
}
/**
* See docs for _read
*/
ClientReadableObjectStream.prototype._read = _read;
/**
* See docs for _read
*/
ClientBidiObjectStream.prototype._read = _read;
/**
* _write implementation for both types of streams that allow writing
* @this {ClientWritableObjectStream|ClientBidiObjectStream}
* @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(this._serialize(chunk), encoding, callback);
}
/**
* See docs for _write
*/
ClientWritableObjectStream.prototype._write = _write;
/**
* See docs for _write
*/
ClientBidiObjectStream.prototype._write = _write;
/**
* 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.
* @param {client.Channel} channel The channel on which to make the request
* @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(channel, argument, callback, metadata, deadline) {
var stream = client.makeRequest(channel, method, metadata, deadline);
var emitter = new EventEmitter();
forwardEvent(stream, emitter, 'status');
forwardEvent(stream, emitter, 'metadata');
stream.write(serialize(argument));
stream.end();
stream.on('data', function forwardData(chunk) {
try {
callback(null, deserialize(chunk));
} catch (e) {
callback(e);
}
});
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.
* @param {client.Channel} channel The channel on which to make the request
* @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(channel, callback, metadata, deadline) {
var stream = client.makeRequest(channel, method, metadata, deadline);
var obj_stream = new ClientWritableObjectStream(stream, serialize, {});
stream.on('data', function forwardData(chunk) {
try {
callback(null, deserialize(chunk));
} catch (e) {
callback(e);
}
});
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.
* @param {client.Channel} channel The channel on which to make the request
* @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(channel, argument, metadata, deadline) {
var stream = client.makeRequest(channel, method, metadata, deadline);
var obj_stream = new ClientReadableObjectStream(stream, deserialize, {});
stream.write(serialize(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.
* @param {client.Channel} channel The channel on which to make the request
* @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(channel, metadata, deadline) {
var stream = client.makeRequest(channel, method, metadata, deadline);
var obj_stream = new ClientBidiObjectStream(stream,
serialize,
deserialize,
{});
return obj_stream;
}
return makeBidiStreamRequest;
}
/**
* See docs for makeUnaryRequestFunction
*/
exports.makeUnaryRequestFunction = makeUnaryRequestFunction;
/**
* See docs for makeClientStreamRequestFunction
*/
exports.makeClientStreamRequestFunction = makeClientStreamRequestFunction;
/**
* See docs for makeServerStreamRequestFunction
*/
exports.makeServerStreamRequestFunction = makeServerStreamRequestFunction;
/**
* See docs for makeBidiStreamRequestFunction
*/
exports.makeBidiStreamRequestFunction = makeBidiStreamRequestFunction;
/**
* See docs for client.Channel
*/
exports.Channel = client.Channel;
/**
* See docs for client.status
*/
exports.status = client.status;
/**
* See docs for client.callError
*/
exports.callError = client.callError;

@ -0,0 +1,358 @@
/*
*
* 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 Server = require('./server.js');
var stream = require('stream');
var Readable = stream.Readable;
var Writable = stream.Writable;
var Duplex = stream.Duplex;
var util = require('util');
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
* @param {function(Buffer)} deserialize Function for deserializing binary data
* @param {object} options Stream options
*/
function ServerReadableObjectStream(stream, deserialize, options) {
options = _.extend(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('data', function forwardData(chunk) {
if (!self.push(deserialize(chunk))) {
self._stream.pause();
}
});
this._stream.on('end', function forwardEnd() {
self.push(null);
});
this._stream.pause();
}
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
* @param {function(*):Buffer} serialize Function for serializing objects
* @param {object} options Stream options
*/
function ServerWritableObjectStream(stream, serialize, options) {
options = _.extend(options, {objectMode: true});
Writable.call(this, options);
this._stream = stream;
this._serialize = serialize;
this.on('finish', function() {
this._stream.end();
});
}
util.inherits(ServerBidiObjectStream, Duplex);
/**
* Class for representing a gRPC bidi streaming call as a Node stream on the
* server side. Extends from stream.Duplex.
* @constructor
* @param {stream} stream Underlying binary Duplex stream for the call
* @param {function(*):Buffer} serialize Function for serializing objects
* @param {function(Buffer)} deserialize Function for deserializing binary data
* @param {object} options Stream options
*/
function ServerBidiObjectStream(stream, serialize, deserialize, options) {
options = _.extend(options, {objectMode: true});
Duplex.call(this, options);
this._stream = stream;
this._serialize = serialize;
var self = this;
this._stream.on('data', function forwardData(chunk) {
if (!self.push(deserialize(chunk))) {
self._stream.pause();
}
});
this._stream.on('end', function forwardEnd() {
self.push(null);
});
this._stream.pause();
this.on('finish', function() {
this._stream.end();
});
}
/**
* _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;
/**
* See docs for _read
*/
ServerBidiObjectStream.prototype._read = _read;
/**
* _write implementation for both types of streams that allow writing
* @this {ServerWritableObjectStream|ServerBidiObjectStream}
* @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(this._serialize(chunk), encoding, callback);
}
/**
* See docs for _write
*/
ServerWritableObjectStream.prototype._write = _write;
/**
* See docs for _write
*/
ServerBidiObjectStream.prototype._write = _write;
/**
* Creates a binary stream handler function from a unary handler function
* @param {function(Object, function(Error, *))} handler Unary call handler
* @param {function(*):Buffer} serialize Serialization function
* @param {function(Buffer):*} deserialize Deserialization function
* @return {function(stream)} Binary stream handler
*/
function makeUnaryHandler(handler, serialize, deserialize) {
/**
* 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
*/
return function handleUnaryCall(stream) {
stream.on('data', function handleUnaryData(value) {
var call = {request: deserialize(value)};
Object.defineProperty(call, 'cancelled', {
get: function() { return stream.cancelled;}
});
handler(call, function sendUnaryData(err, value) {
if (err) {
stream.emit('error', err);
} else {
stream.write(serialize(value));
stream.end();
}
});
});
};
}
/**
* Creates a binary stream handler function from a client stream handler
* function
* @param {function(Readable, function(Error, *))} handler Client stream call
* handler
* @param {function(*):Buffer} serialize Serialization function
* @param {function(Buffer):*} deserialize Deserialization function
* @return {function(stream)} Binary stream handler
*/
function makeClientStreamHandler(handler, serialize, deserialize) {
/**
* 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
*/
return function handleClientStreamCall(stream) {
var object_stream = new ServerReadableObjectStream(stream, deserialize, {});
handler(object_stream, function sendClientStreamData(err, value) {
if (err) {
stream.emit('error', err);
} else {
stream.write(serialize(value));
stream.end();
}
});
};
}
/**
* Creates a binary stream handler function from a server stream handler
* function
* @param {function(Writable)} handler Server stream call handler
* @param {function(*):Buffer} serialize Serialization function
* @param {function(Buffer):*} deserialize Deserialization function
* @return {function(stream)} Binary stream handler
*/
function makeServerStreamHandler(handler, serialize, deserialize) {
/**
* Handles a stream by attaching it to a serializing stream, and passing it to
* the handler.
* @param {stream} stream Binary data stream
*/
return function handleServerStreamCall(stream) {
stream.on('data', function handleClientData(value) {
var object_stream = new ServerWritableObjectStream(stream,
serialize,
{});
object_stream.request = deserialize(value);
handler(object_stream);
});
};
}
/**
* Creates a binary stream handler function from a bidi stream handler function
* @param {function(Duplex)} handler Unary call handler
* @param {function(*):Buffer} serialize Serialization function
* @param {function(Buffer):*} deserialize Deserialization function
* @return {function(stream)} Binary stream handler
*/
function makeBidiStreamHandler(handler, serialize, deserialize) {
/**
* Handles a stream by wrapping it in a serializing and deserializing object
* stream, and passing it to the handler.
* @param {stream} stream Binary data stream
*/
return function handleBidiStreamCall(stream) {
var object_stream = new ServerBidiObjectStream(stream,
serialize,
deserialize,
{});
handler(object_stream);
};
}
/**
* 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(methods, prefix) {
/**
* Create a server with the given handlers for all of the methods.
* @constructor
* @param {Object} handlers Map from method names to method handlers.
* @param {Object} options Options to pass to the underlying server
*/
function SurfaceServer(handlers, options) {
var server = new Server(options);
this.inner_server = server;
_.each(handlers, function(handler, name) {
var method = methods[name];
var method_type;
if (method.client_stream) {
if (method.server_stream) {
method_type = 'bidi';
} else {
method_type = 'client_stream';
}
} else {
if (method.server_stream) {
method_type = 'server_stream';
} else {
method_type = 'unary';
}
}
var binary_handler = handler_makers[method_type](handler,
method.serialize,
method.deserialize);
server.register('' + prefix + name, binary_handler);
}, 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) {
this.inner_server.bind(port, secure);
return this;
};
/**
* 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;

@ -0,0 +1,101 @@
/*
*
* 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 <stdlib.h>
#include <node.h>
#include <nan.h>
#include "tag.h"
namespace grpc {
namespace node {
using v8::Handle;
using v8::HandleScope;
using v8::Persistent;
using v8::Value;
struct tag {
tag(Persistent<Value> *tag, Persistent<Value> *call)
: persist_tag(tag), persist_call(call) {}
~tag() {
persist_tag->Dispose();
if (persist_call != NULL) {
persist_call->Dispose();
}
}
Persistent<Value> *persist_tag;
Persistent<Value> *persist_call;
};
void *CreateTag(Handle<Value> tag, Handle<Value> call) {
NanScope();
Persistent<Value> *persist_tag = new Persistent<Value>();
NanAssignPersistent(*persist_tag, tag);
Persistent<Value> *persist_call;
if (call->IsNull() || call->IsUndefined()) {
persist_call = NULL;
} else {
persist_call = new Persistent<Value>();
NanAssignPersistent(*persist_call, call);
}
struct tag *tag_struct = new struct tag(persist_tag, persist_call);
return reinterpret_cast<void *>(tag_struct);
}
Handle<Value> GetTagHandle(void *tag) {
NanEscapableScope();
struct tag *tag_struct = reinterpret_cast<struct tag *>(tag);
Handle<Value> tag_value = NanNew<Value>(*tag_struct->persist_tag);
return NanEscapeScope(tag_value);
}
bool TagHasCall(void *tag) {
struct tag *tag_struct = reinterpret_cast<struct tag *>(tag);
return tag_struct->persist_call != NULL;
}
Handle<Value> TagGetCall(void *tag) {
NanEscapableScope();
struct tag *tag_struct = reinterpret_cast<struct tag *>(tag);
if (tag_struct->persist_call == NULL) {
return NanEscapeScope(NanNull());
}
Handle<Value> call_value = NanNew<Value>(*tag_struct->persist_call);
return NanEscapeScope(call_value);
}
void DestroyTag(void *tag) { delete reinterpret_cast<struct tag *>(tag); }
} // namespace node
} // namespace grpc

@ -0,0 +1,59 @@
/*
*
* 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 <node.h>
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<v8::Value> tag, v8::Handle<v8::Value> call);
/* Return the javascript value stored in the tag */
v8::Handle<v8::Value> 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<v8::Value> TagGetCall(void *call);
/* 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_

@ -0,0 +1,202 @@
/*
*
* 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 channel = new grpc.Channel('localhost:7070');
/**
* 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;
}
describe('call', function() {
describe('constructor', function() {
it('should reject anything less than 3 arguments', function() {
assert.throws(function() {
new grpc.Call();
}, TypeError);
assert.throws(function() {
new grpc.Call(channel);
}, TypeError);
assert.throws(function() {
new grpc.Call(channel, 'method');
}, TypeError);
});
it('should succeed with a Channel, a string, and a date or number',
function() {
assert.doesNotThrow(function() {
new grpc.Call(channel, 'method', new Date());
});
assert.doesNotThrow(function() {
new grpc.Call(channel, 'method', 0);
});
});
it('should fail with a closed channel', function() {
var local_channel = new grpc.Channel('hostname');
local_channel.close();
assert.throws(function() {
new grpc.Call(channel, 'method');
});
});
it('should fail with other types', function() {
assert.throws(function() {
new grpc.Call({}, 'method', 0);
}, TypeError);
assert.throws(function() {
new grpc.Call(channel, null, 0);
}, TypeError);
assert.throws(function() {
new grpc.Call(channel, 'method', 'now');
}, TypeError);
});
});
describe('addMetadata', function() {
it('should succeed with objects containing keys and values', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
assert.doesNotThrow(function() {
call.addMetadata();
});
assert.doesNotThrow(function() {
call.addMetadata({'key' : 'key',
'value' : new Buffer('value')});
});
assert.doesNotThrow(function() {
call.addMetadata({'key' : 'key1',
'value' : new Buffer('value1')},
{'key' : 'key2',
'value' : new Buffer('value2')});
});
});
it('should fail with other parameter types', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
assert.throws(function() {
call.addMetadata(null);
}, TypeError);
assert.throws(function() {
call.addMetadata('value');
}, TypeError);
assert.throws(function() {
call.addMetadata(5);
}, TypeError);
});
it('should fail if startInvoke was already called', function(done) {
var call = new grpc.Call(channel, 'method', getDeadline(1));
call.startInvoke(function() {},
function() {},
function() {done();},
0);
assert.throws(function() {
call.addMetadata({'key' : 'key', 'value' : new Buffer('value') });
}, function(err) {
return err.code === grpc.callError.ALREADY_INVOKED;
});
// Cancel to speed up the test
call.cancel();
});
});
describe('startInvoke', function() {
it('should fail with fewer than 4 arguments', function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
assert.throws(function() {
call.startInvoke();
}, TypeError);
assert.throws(function() {
call.startInvoke(function() {});
}, TypeError);
assert.throws(function() {
call.startInvoke(function() {},
function() {});
}, TypeError);
assert.throws(function() {
call.startInvoke(function() {},
function() {},
function() {});
}, TypeError);
});
it('should work with 3 args and an int', function(done) {
assert.doesNotThrow(function() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
call.startInvoke(function() {},
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.startInvoke(0, 0, 0, 0);
}, TypeError);
assert.throws(function() {
call.startInvoke(function() {},
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() {
var call = new grpc.Call(channel, 'method', getDeadline(1));
assert.doesNotThrow(function() {
call.cancel();
});
});
});
});

@ -0,0 +1,88 @@
/*
*
* 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');
describe('channel', function() {
describe('constructor', function() {
it('should require a string for the first argument', function() {
assert.doesNotThrow(function() {
new grpc.Channel('hostname');
});
assert.throws(function() {
new grpc.Channel();
}, TypeError);
assert.throws(function() {
new grpc.Channel(5);
});
});
it('should accept an object for the second parameter', function() {
assert.doesNotThrow(function() {
new grpc.Channel('hostname', {});
});
assert.throws(function() {
new grpc.Channel('hostname', 5);
});
});
it('should only accept objects with string or int values', function() {
assert.doesNotThrow(function() {
new grpc.Channel('hostname', {'key' : 'value'});
});
assert.doesNotThrow(function() {
new grpc.Channel('hostname', {'key' : 5});
});
assert.throws(function() {
new grpc.Channel('hostname', {'key' : null});
});
assert.throws(function() {
new grpc.Channel('hostname', {'key' : new Date()});
});
});
});
describe('close', function() {
it('should succeed silently', function() {
var channel = new grpc.Channel('hostname', {});
assert.doesNotThrow(function() {
channel.close();
});
});
it('should be idempotent', function() {
var channel = new grpc.Channel('hostname', {});
assert.doesNotThrow(function() {
channel.close();
channel.close();
});
});
});
});

@ -0,0 +1,183 @@
/*
*
* 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('../server');
var client = require('../client');
var port_picker = require('../port_picker');
var common = require('../common');
var _ = require('highland');
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'
};
}
describe('echo client', function() {
it('should receive echo responses', function(done) {
port_picker.nextAvailablePort(function(port) {
var server = new Server();
server.bind(port);
server.register('echo', echoHandler);
server.start();
var messages = ['echo1', 'echo2', 'echo3', 'echo4'];
var channel = new grpc.Channel(port);
var stream = client.makeRequest(
channel,
'echo');
_(messages).map(function(val) {
return new Buffer(val);
}).pipe(stream);
var index = 0;
stream.on('data', function(chunk) {
assert.equal(messages[index], chunk.toString());
index += 1;
});
stream.on('end', function() {
server.shutdown();
done();
});
});
});
it('should get an error status that the server throws', function(done) {
port_picker.nextAvailablePort(function(port) {
var server = new Server();
server.bind(port);
server.register('error', errorHandler);
server.start();
var channel = new grpc.Channel(port);
var stream = client.makeRequest(
channel,
'error',
null,
getDeadline(1));
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');
server.shutdown();
done();
});
});
});
});
/* TODO(mlumish): explore options for reducing duplication between this test
* and the insecure echo client test */
describe('secure echo client', function() {
it('should recieve echo responses', function(done) {
port_picker.nextAvailablePort(function(port) {
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);
var server = new Server({'credentials' : server_creds});
server.bind(port, true);
server.register('echo', echoHandler);
server.start();
var messages = ['echo1', 'echo2', 'echo3', 'echo4'];
var channel = new grpc.Channel(port, {
'grpc.ssl_target_name_override' : 'foo.test.google.com',
'credentials' : creds
});
var stream = client.makeRequest(
channel,
'echo');
_(messages).map(function(val) {
return new Buffer(val);
}).pipe(stream);
var index = 0;
stream.on('data', function(chunk) {
assert.equal(messages[index], chunk.toString());
index += 1;
});
stream.on('end', function() {
server.shutdown();
done();
});
});
});
});
});
});
});

@ -0,0 +1,130 @@
/*
*
* 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');
/**
* List of all status names
* @const
* @type {Array.<string>}
*/
var statusNames = [
'OK',
'CANCELLED',
'UNKNOWN',
'INVALID_ARGUMENT',
'DEADLINE_EXCEEDED',
'NOT_FOUND',
'ALREADY_EXISTS',
'PERMISSION_DENIED',
'UNAUTHENTICATED',
'RESOURCE_EXHAUSTED',
'FAILED_PRECONDITION',
'ABORTED',
'OUT_OF_RANGE',
'UNIMPLEMENTED',
'INTERNAL',
'UNAVAILABLE',
'DATA_LOSS'
];
/**
* List of all call error names
* @const
* @type {Array.<string>}
*/
var callErrorNames = [
'OK',
'ERROR',
'NOT_ON_SERVER',
'NOT_ON_CLIENT',
'ALREADY_INVOKED',
'NOT_INVOKED',
'ALREADY_FINISHED',
'TOO_MANY_OPERATIONS',
'INVALID_FLAGS'
];
/**
* List of all op error names
* @const
* @type {Array.<string>}
*/
var opErrorNames = [
'OK',
'ERROR'
];
/**
* List of all completion type names
* @const
* @type {Array.<string>}
*/
var completionTypeNames = [
'QUEUE_SHUTDOWN',
'READ',
'INVOKE_ACCEPTED',
'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++) {
assert(grpc.status.hasOwnProperty(statusNames[i]),
'status missing: ' + statusNames[i]);
}
});
it('should have all of the call errors', function() {
for (var i = 0; i < callErrorNames.length; i++) {
assert(grpc.callError.hasOwnProperty(callErrorNames[i]),
'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]);
}
});
});

@ -0,0 +1 @@
CONFIRMEDTESTKEY

@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla
Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT
BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7
+L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu
g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd
Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau
sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m
oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG
Dfcog5wrJytaQ6UA0wE=
-----END CERTIFICATE-----

@ -0,0 +1,16 @@
-----BEGIN PRIVATE KEY-----
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD
M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf
3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY
AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm
V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY
tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p
dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q
K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR
81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff
DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd
aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2
ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3
XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe
F98XJ7tIFfJq
-----END PRIVATE KEY-----

@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE-----
MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET
MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5
MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl
c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs
JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO
RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30
3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL
BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6
b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ
KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS
wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e
aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s=
-----END CERTIFICATE-----

@ -0,0 +1,201 @@
/*
*
* 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 port_picker = require('../port_picker');
/**
* 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();
}
};
}
describe('end-to-end', function() {
it('should start and end a request without error', function(complete) {
port_picker.nextAvailablePort(function(port) {
var server = new grpc.Server();
var done = multiDone(function() {
complete();
server.shutdown();
}, 2);
server.addHttp2Port(port);
var channel = new grpc.Channel(port);
var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 3);
var status_text = 'xyz';
var call = new grpc.Call(channel,
'dummy_method',
deadline);
call.startInvoke(function(event) {
assert.strictEqual(event.type,
grpc.completionType.INVOKE_ACCEPTED);
call.writesDone(function(event) {
assert.strictEqual(event.type,
grpc.completionType.FINISH_ACCEPTED);
assert.strictEqual(event.data, grpc.opError.OK);
});
},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);
done();
}, 0);
server.start();
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();
});
});
});
});
it('should send and receive data without error', function(complete) {
port_picker.nextAvailablePort(function(port) {
var req_text = 'client_request';
var reply_text = 'server_response';
var server = new grpc.Server();
var done = multiDone(function() {
complete();
server.shutdown();
}, 6);
server.addHttp2Port(port);
var channel = new grpc.Channel(port);
var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 3);
var status_text = 'success';
var call = new grpc.Call(channel,
'dummy_method',
deadline);
call.startInvoke(function(event) {
assert.strictEqual(event.type,
grpc.completionType.INVOKE_ACCEPTED);
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);
done();
});
},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);
server.start();
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);
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);
});
});
});
});
});

@ -0,0 +1,209 @@
/*
*
* 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 client = require('../surface_client.js');
var ProtoBuf = require('protobufjs');
var port_picker = require('../port_picker');
var builder = ProtoBuf.loadProtoFile(__dirname + '/../examples/math.proto');
var math = builder.build('math');
/**
* Get a function that deserializes a specific type of protobuf.
* @param {function()} cls The constructor of the message type to deserialize
* @return {function(Buffer):cls} The deserialization function
*/
function deserializeCls(cls) {
/**
* Deserialize a buffer to a message object
* @param {Buffer} arg_buf The buffer to deserialize
* @return {cls} The resulting object
*/
return function deserialize(arg_buf) {
return cls.decode(arg_buf);
};
}
/**
* Serialize an object to a buffer
* @param {*} arg The object to serialize
* @return {Buffer} The serialized object
*/
function serialize(arg) {
return new Buffer(arg.encode().toBuffer());
}
/**
* Sends a Div request on the channel.
* @param {client.Channel} channel The channel on which to make the request
* @param {DivArg} argument The argument to the call. Should be serializable
* with serialize
* @param {function(?Error, value=)} The callback to for when the response is
* received
* @param {array=} 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
*/
var div = client.makeUnaryRequestFunction(
'/Math/Div',
serialize,
deserializeCls(math.DivReply));
/**
* Sends a Fib request on the channel.
* @param {client.Channel} channel The channel on which to make the request
* @param {*} argument The argument to the call. Should be serializable with
* serialize
* @param {array=} 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
*/
var fib = client.makeServerStreamRequestFunction(
'/Math/Fib',
serialize,
deserializeCls(math.Num));
/**
* Sends a Sum request on the channel.
* @param {client.Channel} channel The channel on which to make the request
* @param {function(?Error, value=)} The callback to for when the response is
* received
* @param {array=} 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
*/
var sum = client.makeClientStreamRequestFunction(
'/Math/Sum',
serialize,
deserializeCls(math.Num));
/**
* Sends a DivMany request on the channel.
* @param {client.Channel} channel The channel on which to make the request
* @param {array=} 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
*/
var divMany = client.makeBidiStreamRequestFunction(
'/Math/DivMany',
serialize,
deserializeCls(math.DivReply));
/**
* Channel to use to make requests to a running server.
*/
var channel;
/**
* Server to test against
*/
var server = require('../examples/math_server.js');
describe('Math client', function() {
before(function(done) {
port_picker.nextAvailablePort(function(port) {
server.bind(port).listen();
channel = new client.Channel(port);
done();
});
});
after(function() {
server.shutdown();
});
it('should handle a single request', function(done) {
var arg = new math.DivArgs({dividend: 7, divisor: 4});
var call = div(channel, arg, function handleDivResult(err, value) {
assert.ifError(err);
assert.equal(value.get('quotient'), 1);
assert.equal(value.get('remainder'), 3);
});
call.on('status', function checkStatus(status) {
assert.strictEqual(status.code, client.status.OK);
done();
});
});
it('should handle a server streaming request', function(done) {
var arg = new math.FibArgs({limit: 7});
var call = fib(channel, arg);
var expected_results = [1, 1, 2, 3, 5, 8, 13];
var next_expected = 0;
call.on('data', function checkResponse(value) {
assert.equal(value.get('num'), expected_results[next_expected]);
next_expected += 1;
});
call.on('status', function checkStatus(status) {
assert.strictEqual(status.code, client.status.OK);
done();
});
});
it('should handle a client streaming request', function(done) {
var call = sum(channel, function handleSumResult(err, value) {
assert.ifError(err);
assert.equal(value.get('num'), 21);
});
for (var i = 0; i < 7; i++) {
call.write(new math.Num({'num': i}));
}
call.end();
call.on('status', function checkStatus(status) {
assert.strictEqual(status.code, client.status.OK);
done();
});
});
it('should handle a bidirectional streaming request', function(done) {
function checkResponse(index, value) {
assert.equal(value.get('quotient'), index);
assert.equal(value.get('remainder'), 1);
}
var call = divMany(channel);
var response_index = 0;
call.on('data', function(value) {
checkResponse(response_index, value);
response_index += 1;
});
for (var i = 0; i < 7; i++) {
call.write(new math.DivArgs({dividend: 2 * i + 1, divisor: 2}));
}
call.end();
call.on('status', function checkStatus(status) {
assert.strictEqual(status.code, client.status.OK);
done();
});
});
});

@ -0,0 +1,121 @@
/*
*
* 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('../server');
var port_picker = require('../port_picker');
/**
* 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() {
it('should echo inputs as responses', function(done) {
done = multiDone(done, 4);
port_picker.nextAvailablePort(function(port) {
var server = new Server();
server.bind(port);
server.register('echo', echoHandler);
server.start();
var req_text = 'echo test string';
var status_text = 'OK';
var channel = new grpc.Channel(port);
var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 3);
var call = new grpc.Call(channel,
'echo',
deadline);
call.startInvoke(function(event) {
assert.strictEqual(event.type,
grpc.completionType.INVOKE_ACCEPTED);
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();
});
},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);
server.shutdown();
done();
}, 0);
});
});
});

@ -0,0 +1,66 @@
/*
*
* 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 <limits>
#include "grpc/grpc.h"
#include "grpc/support/time.h"
#include "timeval.h"
namespace grpc {
namespace node {
gpr_timespec MillisecondsToTimespec(double millis) {
if (millis == std::numeric_limits<double>::infinity()) {
return gpr_inf_future;
} else if (millis == -std::numeric_limits<double>::infinity()) {
return gpr_inf_past;
} else {
return gpr_time_from_micros(static_cast<int64_t>(millis * 1000));
}
}
double TimespecToMilliseconds(gpr_timespec timespec) {
if (gpr_time_cmp(timespec, gpr_inf_future) == 0) {
return std::numeric_limits<double>::infinity();
} else if (gpr_time_cmp(timespec, gpr_inf_past) == 0) {
return -std::numeric_limits<double>::infinity();
} else {
struct timeval time = gpr_timeval_from_timespec(timespec);
return (static_cast<double>(time.tv_sec) * 1000 +
static_cast<double>(time.tv_usec) / 1000);
}
}
} // namespace node
} // namespace grpc

@ -0,0 +1,48 @@
/*
*
* 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_TIMEVAL_H_
#define NET_GRPC_NODE_TIMEVAL_H_
#include "grpc/support/time.h"
namespace grpc {
namespace node {
double TimespecToMilliseconds(gpr_timespec time);
gpr_timespec MillisecondsToTimespec(double millis);
} // namespace node
} // namespace grpc
#endif // NET_GRPC_NODE_TIMEVAL_H_

@ -32,6 +32,9 @@ if test "$PHP_GRPC" != "no"; then
GRPC_SHARED_LIBADD="-lpthread $GRPC_SHARED_LIBADD"
PHP_ADD_LIBRARY(pthread)
PHP_ADD_LIBRARY(dl,,GRPC_SHARED_LIBADD)
PHP_ADD_LIBRARY(dl)
PHP_ADD_LIBRARY(rt,,GRPC_SHARED_LIBADD)
PHP_ADD_LIBRARY(rt)

@ -338,7 +338,7 @@ privatelibs: privatelibs_c privatelibs_cxx
privatelibs_c: dep_c\
% for lib in libs:
% if lib.build == 'private':
% if lib.build == 'private' and not lib.get('c++', False):
libs/$(CONFIG)/lib${lib.name}.a\
% endif
% endfor
@ -346,7 +346,7 @@ privatelibs_c: dep_c\
privatelibs_cxx: dep_cxx\
% for lib in libs:
% if lib.build == 'private':
% if lib.build == 'private' and lib.get('c++', False):
libs/$(CONFIG)/lib${lib.name}.a\
% endif
% endfor

@ -1 +0,0 @@
Subproject commit f7d92c63928a1460f3d99b9bc418bd3b686a0dca

@ -18,6 +18,20 @@
# * on startup, some of the docker images will be regenerated automatically
# - used grpc_update_image to update images via that instance
# Creates the ssh key file expect by 'gcloud compute ssh' if it does not exist.
#
# Allows gcloud ssh commands to run on freshly started docker instances.
_grpc_ensure_gcloud_ssh() {
local default_key_file="$HOME/.ssh/google_compute_engine"
[ -f $default_key_file ] || {
ssh-keygen -f $default_key_file -N '' > /dev/null || {
echo "could not precreate $default_key_file" 1>&2
return 1
}
}
}
# Pushes a dockerfile dir to cloud storage.
#
# dockerfile is expected to the parent directory to a nunber of directoies each
@ -50,6 +64,7 @@ grpc_push_dockerfiles() {
# Adds the user to docker group on a GCE instance, and restarts the docker
# daemon
grpc_add_docker_user() {
_grpc_ensure_gcloud_ssh || return 1;
local host=$1
[[ -n $host ]] || {
echo "$FUNCNAME: missing arg: host" 1>&2
@ -81,6 +96,7 @@ grpc_add_docker_user() {
# grpc_update_image gs://bucket/path/to/dockerfile parent \.
# image_label path/to/docker_dir docker_gce_instance [project] [zone]
grpc_update_image() {
_grpc_ensure_gcloud_ssh || return 1;
local gs_root_uri=$1
[[ -n $gs_root_uri ]] || {
echo "$FUNCNAME: missing arg: gs_root_uri" 1>&2
@ -350,6 +366,8 @@ grpc_update_docker_images_args() {
#
# Updates the GCE docker instance <server_name>
grpc_update_docker_images() {
_grpc_ensure_gcloud_ssh || return 1;
# declare vars local so that they don't pollute the shell environment
# where they this func is used.
local grpc_zone grpc_project dry_run # set by grpc_set_project_and_zone
@ -469,6 +487,7 @@ grpc_launch_server() {
#
# --server_host=<svr_addr> --server_port=<svr_port> --test_case=<...>
grpc_interop_test() {
_grpc_ensure_gcloud_ssh || return 1;
# declare vars local so that they don't pollute the shell environment
# where they this func is used.

Loading…
Cancel
Save