Merge branch 'master' of github.com:grpc/grpc into iomgr_executor

pull/3726/head
David Garcia Quintas 9 years ago
commit ad8dee1545
  1. 0
      .istanbul.yml
  2. 95
      binding.gyp
  3. 2729
      build.json
  4. 2
      examples/node/greeter_client.js
  5. 2
      examples/node/greeter_server.js
  6. 10
      examples/node/package.json
  7. 4
      examples/node/route_guide/route_guide_client.js
  8. 4
      examples/node/route_guide/route_guide_server.js
  9. 55
      examples/python/README.md
  10. 202
      examples/python/helloworld/helloworld_pb2.py
  11. 12370
      grpc.gyp
  12. 37
      package.json
  13. 2
      src/core/channel/client_channel.c
  14. 47
      src/core/client_config/lb_policies/pick_first.c
  15. 24
      src/core/iomgr/closure.c
  16. 3
      src/core/iomgr/closure.h
  17. 2
      src/core/iomgr/tcp_server_windows.c
  18. 2
      src/core/iomgr/udp_server.c
  19. 3
      src/core/iomgr/udp_server.h
  20. 1
      src/core/surface/call.c
  21. 28
      src/node/LICENSE
  22. 36
      src/node/README.md
  23. 16
      src/node/bin/README.md
  24. 2
      src/node/bin/service_packager
  25. 100
      src/node/binding.gyp
  26. 142
      src/node/cli/service_packager.js
  27. 36
      src/node/cli/service_packager/index.js
  28. 17
      src/node/cli/service_packager/package.json.template
  29. 62
      src/node/examples/stock.proto
  30. 47
      src/node/examples/stock_client.js
  31. 87
      src/node/examples/stock_server.js
  32. 27
      src/node/ext/call.cc
  33. 5
      src/node/ext/call.h
  34. 259
      src/node/ext/call_credentials.cc
  35. 100
      src/node/ext/call_credentials.h
  36. 8
      src/node/ext/channel.cc
  37. 106
      src/node/ext/channel_credentials.cc
  38. 23
      src/node/ext/channel_credentials.h
  39. 8
      src/node/ext/node_grpc.cc
  40. 31
      src/node/index.js
  41. 43
      src/node/interop/empty.proto
  42. 192
      src/node/interop/interop_client.js
  43. 52
      src/node/interop/interop_server.js
  44. 132
      src/node/interop/messages.proto
  45. 72
      src/node/interop/test.proto
  46. 2
      src/node/performance/perf_test.js
  47. 2
      src/node/performance/qps_test.js
  48. 54
      src/node/src/client.js
  49. 163
      src/node/src/credentials.js
  50. 5
      src/node/src/metadata.js
  51. 2
      src/node/src/server.js
  52. 4
      src/node/test/async_test.js
  53. 4
      src/node/test/call_test.js
  54. 7
      src/node/test/channel_test.js
  55. 2
      src/node/test/constant_test.js
  56. 243
      src/node/test/credentials_test.js
  57. 4
      src/node/test/end_to_end_test.js
  58. 2
      src/node/test/health_test.js
  59. 4
      src/node/test/interop_sanity_test.js
  60. 0
      src/node/test/math/math.proto
  61. 2
      src/node/test/math/math_server.js
  62. 6
      src/node/test/math_client_test.js
  63. 2
      src/node/test/server_test.js
  64. 32
      src/node/test/surface_test.js
  65. 111
      src/php/ext/grpc/php_grpc.c
  66. 29
      templates/grpc.gyp.template
  67. 3
      test/core/iomgr/udp_server_test.c
  68. 7
      tools/jenkins/build_docker_and_run_tests.sh
  69. 4
      tools/jenkins/docker_run_tests.sh
  70. 5
      tools/jenkins/grpc_jenkins_slave/Dockerfile
  71. 5
      tools/run_tests/build_node.sh
  72. 49
      tools/run_tests/build_python.sh
  73. 19
      tools/run_tests/port_server.py
  74. 12
      tools/run_tests/run_node.sh
  75. 2
      tools/run_tests/run_python.sh
  76. 69
      tools/run_tests/run_tests.py

@ -0,0 +1,95 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
"variables" : {
'config': '<!(echo $CONFIG)'
},
"targets" : [
{
'include_dirs': [
"<!(node -e \"require('nan')\")"
],
'cflags': [
'-std=c++0x',
'-Wall',
'-pthread',
'-g',
'-zdefs',
'-Werror',
'-Wno-error=deprecated-declarations'
],
'ldflags': [
'-g'
],
"conditions": [
['OS != "win"', {
'conditions': [
['config=="gcov"', {
'cflags': [
'-ftest-coverage',
'-fprofile-arcs',
'-O0'
],
'ldflags': [
'-ftest-coverage',
'-fprofile-arcs'
]
}
]
]
}],
['OS == "mac"', {
'xcode_settings': {
'MACOSX_DEPLOYMENT_TARGET': '10.9',
'OTHER_CFLAGS': [
'-std=c++11',
'-stdlib=libc++'
]
}
}]
],
"target_name": "grpc_node",
"sources": [
"src/node/ext/byte_buffer.cc",
"src/node/ext/call.cc",
"src/node/ext/call_credentials.cc",
"src/node/ext/channel.cc",
"src/node/ext/channel_credentials.cc",
"src/node/ext/completion_queue_async_worker.cc",
"src/node/ext/node_grpc.cc",
"src/node/ext/server.cc",
"src/node/ext/server_credentials.cc",
"src/node/ext/timeval.cc"
],
"dependencies": [
"grpc.gyp:grpc"
]
}
]
}

File diff suppressed because it is too large Load Diff

@ -33,7 +33,7 @@
var PROTO_PATH = __dirname + '/helloworld.proto'; var PROTO_PATH = __dirname + '/helloworld.proto';
var grpc = require('grpc'); var grpc = require('../../');
var hello_proto = grpc.load(PROTO_PATH).helloworld; var hello_proto = grpc.load(PROTO_PATH).helloworld;
function main() { function main() {

@ -33,7 +33,7 @@
var PROTO_PATH = __dirname + '/helloworld.proto'; var PROTO_PATH = __dirname + '/helloworld.proto';
var grpc = require('grpc'); var grpc = require('../../');
var hello_proto = grpc.load(PROTO_PATH).helloworld; var hello_proto = grpc.load(PROTO_PATH).helloworld;
/** /**

@ -1,10 +0,0 @@
{
"name": "grpc-demo",
"version": "0.11.0",
"dependencies": {
"async": "^0.9.0",
"grpc": "~0.11.0",
"minimist": "^1.1.0",
"underscore": "^1.8.2"
}
}

@ -31,8 +31,8 @@ var async = require('async');
var fs = require('fs'); var fs = require('fs');
var parseArgs = require('minimist'); var parseArgs = require('minimist');
var path = require('path'); var path = require('path');
var _ = require('underscore'); var _ = require('lodash');
var grpc = require('grpc'); var grpc = require('../../../');
var routeguide = grpc.load(__dirname + '/route_guide.proto').routeguide; var routeguide = grpc.load(__dirname + '/route_guide.proto').routeguide;
var client = new routeguide.RouteGuide('localhost:50051', var client = new routeguide.RouteGuide('localhost:50051',
grpc.Credentials.createInsecure()); grpc.Credentials.createInsecure());

@ -30,8 +30,8 @@
var fs = require('fs'); var fs = require('fs');
var parseArgs = require('minimist'); var parseArgs = require('minimist');
var path = require('path'); var path = require('path');
var _ = require('underscore'); var _ = require('lodash');
var grpc = require('grpc'); var grpc = require('../../../');
var routeguide = grpc.load(__dirname + '/route_guide.proto').routeguide; var routeguide = grpc.load(__dirname + '/route_guide.proto').routeguide;
var COORD_FACTOR = 1e7; var COORD_FACTOR = 1e7;

@ -0,0 +1,55 @@
gRPC in 3 minutes (Python)
========================
Background
-------------
For this sample, we've already generated the server and client stubs from
[helloworld.proto][] and we'll be using a specific reference platform.
Prerequisites
-------------
- Debian 8.2 "Jessie" platform with `root` access
- `git`
- `python2.7`
- `pip`
- Python development headers
Set-up
-------
```sh
$ # install the gRPC Core:
$ sudo apt-get install libgrpc-dev
$ # install gRPC Python:
$ sudo pip install -U grpcio==0.11.0b1
$ # Since this "hello, world" example uses protocol buffers:
$ sudo pip install -U protobuf==3.0.0a3
$ # Clone the repository to get the example code:
$ git clone https://github.com/grpc/grpc
$ # Navigate to the "hello, world" Python example:
$ cd grpc/examples/python/helloworld
```
Try it!
-------
- Run the server
```sh
$ python2.7 greeter_server.py &
```
- Run the client
```sh
$ python2.7 greeter_client.py
```
Tutorial
--------
You can find a more detailed tutorial in [gRPC Basics: Python][]
[helloworld.proto]:../protos/helloworld.proto
[Install gRPC Python]:../../src/python#installation
[gRPC Basics: Python]:http://www.grpc.io/docs/tutorials/basic/python.html

@ -0,0 +1,202 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: helloworld.proto
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
from google.protobuf import descriptor_pb2
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name='helloworld.proto',
package='helloworld',
syntax='proto3',
serialized_pb=b'\n\x10helloworld.proto\x12\nhelloworld\"\x1c\n\x0cHelloRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"\x1d\n\nHelloReply\x12\x0f\n\x07message\x18\x01 \x01(\t2I\n\x07Greeter\x12>\n\x08SayHello\x12\x18.helloworld.HelloRequest\x1a\x16.helloworld.HelloReply\"\x00\x42\x18\n\x10io.grpc.examples\xa2\x02\x03HLWb\x06proto3'
)
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
_HELLOREQUEST = _descriptor.Descriptor(
name='HelloRequest',
full_name='helloworld.HelloRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='name', full_name='helloworld.HelloRequest.name', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=32,
serialized_end=60,
)
_HELLOREPLY = _descriptor.Descriptor(
name='HelloReply',
full_name='helloworld.HelloReply',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='message', full_name='helloworld.HelloReply.message', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=62,
serialized_end=91,
)
DESCRIPTOR.message_types_by_name['HelloRequest'] = _HELLOREQUEST
DESCRIPTOR.message_types_by_name['HelloReply'] = _HELLOREPLY
HelloRequest = _reflection.GeneratedProtocolMessageType('HelloRequest', (_message.Message,), dict(
DESCRIPTOR = _HELLOREQUEST,
__module__ = 'helloworld_pb2'
# @@protoc_insertion_point(class_scope:helloworld.HelloRequest)
))
_sym_db.RegisterMessage(HelloRequest)
HelloReply = _reflection.GeneratedProtocolMessageType('HelloReply', (_message.Message,), dict(
DESCRIPTOR = _HELLOREPLY,
__module__ = 'helloworld_pb2'
# @@protoc_insertion_point(class_scope:helloworld.HelloReply)
))
_sym_db.RegisterMessage(HelloReply)
DESCRIPTOR.has_options = True
DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), b'\n\020io.grpc.examples\242\002\003HLW')
import abc
from grpc.beta import implementations as beta_implementations
from grpc.early_adopter import implementations as early_adopter_implementations
from grpc.framework.alpha import utilities as alpha_utilities
from grpc.framework.common import cardinality
from grpc.framework.interfaces.face import utilities as face_utilities
class EarlyAdopterGreeterServicer(object):
"""<fill me in later!>"""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def SayHello(self, request, context):
raise NotImplementedError()
class EarlyAdopterGreeterServer(object):
"""<fill me in later!>"""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def start(self):
raise NotImplementedError()
@abc.abstractmethod
def stop(self):
raise NotImplementedError()
class EarlyAdopterGreeterStub(object):
"""<fill me in later!>"""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def SayHello(self, request):
raise NotImplementedError()
SayHello.async = None
def early_adopter_create_Greeter_server(servicer, port, private_key=None, certificate_chain=None):
import helloworld_pb2
import helloworld_pb2
method_service_descriptions = {
"SayHello": alpha_utilities.unary_unary_service_description(
servicer.SayHello,
helloworld_pb2.HelloRequest.FromString,
helloworld_pb2.HelloReply.SerializeToString,
),
}
return early_adopter_implementations.server("helloworld.Greeter", method_service_descriptions, port, private_key=private_key, certificate_chain=certificate_chain)
def early_adopter_create_Greeter_stub(host, port, metadata_transformer=None, secure=False, root_certificates=None, private_key=None, certificate_chain=None, server_host_override=None):
import helloworld_pb2
import helloworld_pb2
method_invocation_descriptions = {
"SayHello": alpha_utilities.unary_unary_invocation_description(
helloworld_pb2.HelloRequest.SerializeToString,
helloworld_pb2.HelloReply.FromString,
),
}
return early_adopter_implementations.stub("helloworld.Greeter", method_invocation_descriptions, host, port, metadata_transformer=metadata_transformer, secure=secure, root_certificates=root_certificates, private_key=private_key, certificate_chain=certificate_chain, server_host_override=server_host_override)
class BetaGreeterServicer(object):
"""<fill me in later!>"""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def SayHello(self, request, context):
raise NotImplementedError()
class BetaGreeterStub(object):
"""The interface to which stubs will conform."""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def SayHello(self, request, timeout):
raise NotImplementedError()
SayHello.future = None
def beta_create_Greeter_server(servicer, pool=None, pool_size=None, default_timeout=None, maximum_timeout=None):
import helloworld_pb2
import helloworld_pb2
request_deserializers = {
('helloworld.Greeter', 'SayHello'): helloworld_pb2.HelloRequest.FromString,
}
response_serializers = {
('helloworld.Greeter', 'SayHello'): helloworld_pb2.HelloReply.SerializeToString,
}
method_implementations = {
('helloworld.Greeter', 'SayHello'): face_utilities.unary_unary_inline(servicer.SayHello),
}
server_options = beta_implementations.server_options(request_deserializers=request_deserializers, response_serializers=response_serializers, thread_pool=pool, thread_pool_size=pool_size, default_timeout=default_timeout, maximum_timeout=maximum_timeout)
return beta_implementations.server(method_implementations, options=server_options)
def beta_create_Greeter_stub(channel, host=None, metadata_transformer=None, pool=None, pool_size=None):
import helloworld_pb2
import helloworld_pb2
request_serializers = {
('helloworld.Greeter', 'SayHello'): helloworld_pb2.HelloRequest.SerializeToString,
}
response_deserializers = {
('helloworld.Greeter', 'SayHello'): helloworld_pb2.HelloReply.FromString,
}
cardinalities = {
'SayHello': cardinality.Cardinality.UNARY_UNARY,
}
stub_options = beta_implementations.stub_options(host=host, metadata_transformer=metadata_transformer, request_serializers=request_serializers, response_deserializers=response_deserializers, thread_pool=pool, thread_pool_size=pool_size)
return beta_implementations.dynamic_stub(channel, 'helloworld.Greeter', cardinalities, options=stub_options)
# @@protoc_insertion_point(module_scope)

12370
grpc.gyp

File diff suppressed because it is too large Load Diff

@ -16,14 +16,13 @@
} }
], ],
"directories": { "directories": {
"lib": "src", "lib": "src/node/src"
"example": "examples"
}, },
"scripts": { "scripts": {
"lint": "node ./node_modules/jshint/bin/jshint src test examples interop index.js", "lint": "node ./node_modules/jshint/bin/jshint src/node/src src/node/test src/node/examples src/node/interop src/node/index.js",
"test": "./node_modules/.bin/mocha && npm run-script lint", "test": "./node_modules/.bin/mocha src/node/test && npm run-script lint",
"gen_docs": "./node_modules/.bin/jsdoc -c jsdoc_conf.json", "gen_docs": "./node_modules/.bin/jsdoc -c src/node/jsdoc_conf.json",
"coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha" "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha src/node/test"
}, },
"dependencies": { "dependencies": {
"bindings": "^1.2.0", "bindings": "^1.2.0",
@ -39,26 +38,24 @@
"jshint": "^2.5.0", "jshint": "^2.5.0",
"minimist": "^1.1.0", "minimist": "^1.1.0",
"mocha": "~1.21.0", "mocha": "~1.21.0",
"mustache": "^2.0.0", "mustache": "^2.0.0"
"strftime": "^0.8.2"
}, },
"engines": { "engines": {
"node": ">=0.10.13" "node": ">=0.10.13"
}, },
"files": [ "files": [
"LICENSE", "LICENSE",
"README.md", "src/node/README.md",
"index.js", "src/node/index.js",
"binding.gyp", "src/node/ext",
"bin", "src/node/health_check",
"cli", "src/node/src",
"examples", "src/core",
"ext", "test/proto",
"health_check", "include",
"interop", "grpc.gyp",
"src", "binding.gyp"
"test"
], ],
"main": "index.js", "main": "src/node/index.js",
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
} }

@ -51,7 +51,7 @@
typedef struct call_data call_data; typedef struct call_data call_data;
typedef struct { typedef struct client_channel_channel_data {
/** metadata context for this channel */ /** metadata context for this channel */
grpc_mdctx *mdctx; grpc_mdctx *mdctx;
/** resolver for this channel */ /** resolver for this channel */

@ -101,6 +101,9 @@ void pf_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) {
for (i = 0; i < p->num_subchannels; i++) { for (i = 0; i < p->num_subchannels; i++) {
GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[i], "pick_first"); GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[i], "pick_first");
} }
if (p->selected) {
GRPC_SUBCHANNEL_UNREF(exec_ctx, p->selected, "picked_first");
}
grpc_connectivity_state_destroy(exec_ctx, &p->state_tracker); grpc_connectivity_state_destroy(exec_ctx, &p->state_tracker);
gpr_free(p->subchannels); gpr_free(p->subchannels);
gpr_mu_destroy(&p->mu); gpr_mu_destroy(&p->mu);
@ -172,6 +175,35 @@ void pf_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
} }
} }
static void destroy_subchannels(grpc_exec_ctx *exec_ctx, void *arg,
int iomgr_success) {
pick_first_lb_policy *p = arg;
size_t i;
grpc_transport_op op;
size_t num_subchannels = p->num_subchannels;
grpc_subchannel **subchannels;
grpc_subchannel *exclude_subchannel;
gpr_mu_lock(&p->mu);
subchannels = p->subchannels;
p->num_subchannels = 0;
p->subchannels = NULL;
exclude_subchannel = p->selected;
gpr_mu_unlock(&p->mu);
GRPC_LB_POLICY_UNREF(exec_ctx, &p->base, "destroy_subchannels");
for (i = 0; i < num_subchannels; i++) {
if (subchannels[i] != exclude_subchannel) {
memset(&op, 0, sizeof(op));
op.disconnect = 1;
grpc_subchannel_process_transport_op(exec_ctx, subchannels[i], &op);
}
GRPC_SUBCHANNEL_UNREF(exec_ctx, subchannels[i], "pick_first");
}
gpr_free(subchannels);
}
static void pf_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg, static void pf_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg,
int iomgr_success) { int iomgr_success) {
pick_first_lb_policy *p = arg; pick_first_lb_policy *p = arg;
@ -200,6 +232,11 @@ static void pf_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg,
grpc_connectivity_state_set(exec_ctx, &p->state_tracker, grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
GRPC_CHANNEL_READY, "connecting_ready"); GRPC_CHANNEL_READY, "connecting_ready");
p->selected = p->subchannels[p->checking_subchannel]; p->selected = p->subchannels[p->checking_subchannel];
GRPC_SUBCHANNEL_REF(p->selected, "picked_first");
/* drop the pick list: we are connected now */
GRPC_LB_POLICY_REF(&p->base, "destroy_subchannels");
grpc_exec_ctx_enqueue(exec_ctx, grpc_closure_create(destroy_subchannels, p), 1);
/* update any calls that were waiting for a pick */
while ((pp = p->pending_picks)) { while ((pp = p->pending_picks)) {
p->pending_picks = pp->next; p->pending_picks = pp->next;
*pp->target = p->selected; *pp->target = p->selected;
@ -279,10 +316,15 @@ static void pf_broadcast(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
size_t i; size_t i;
size_t n; size_t n;
grpc_subchannel **subchannels; grpc_subchannel **subchannels;
grpc_subchannel *selected;
gpr_mu_lock(&p->mu); gpr_mu_lock(&p->mu);
n = p->num_subchannels; n = p->num_subchannels;
subchannels = gpr_malloc(n * sizeof(*subchannels)); subchannels = gpr_malloc(n * sizeof(*subchannels));
selected = p->selected;
if (selected) {
GRPC_SUBCHANNEL_REF(selected, "pf_broadcast_to_selected");
}
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
subchannels[i] = p->subchannels[i]; subchannels[i] = p->subchannels[i];
GRPC_SUBCHANNEL_REF(subchannels[i], "pf_broadcast"); GRPC_SUBCHANNEL_REF(subchannels[i], "pf_broadcast");
@ -290,9 +332,14 @@ static void pf_broadcast(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol,
gpr_mu_unlock(&p->mu); gpr_mu_unlock(&p->mu);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
if (selected == subchannels[i]) continue;
grpc_subchannel_process_transport_op(exec_ctx, subchannels[i], op); grpc_subchannel_process_transport_op(exec_ctx, subchannels[i], op);
GRPC_SUBCHANNEL_UNREF(exec_ctx, subchannels[i], "pf_broadcast"); GRPC_SUBCHANNEL_UNREF(exec_ctx, subchannels[i], "pf_broadcast");
} }
if (p->selected) {
grpc_subchannel_process_transport_op(exec_ctx, selected, op);
GRPC_SUBCHANNEL_UNREF(exec_ctx, selected, "pf_broadcast_to_selected");
}
gpr_free(subchannels); gpr_free(subchannels);
} }

@ -33,6 +33,8 @@
#include "src/core/iomgr/closure.h" #include "src/core/iomgr/closure.h"
#include <grpc/support/alloc.h>
void grpc_closure_init(grpc_closure *closure, grpc_iomgr_cb_func cb, void grpc_closure_init(grpc_closure *closure, grpc_iomgr_cb_func cb,
void *cb_arg) { void *cb_arg) {
closure->cb = cb; closure->cb = cb;
@ -79,3 +81,25 @@ grpc_closure *grpc_closure_list_pop(grpc_closure_list *list) {
list->head = list->head->next; list->head = list->head->next;
return head; return head;
} }
typedef struct {
grpc_iomgr_cb_func cb;
void *cb_arg;
grpc_closure wrapper;
} wrapped_closure;
static void closure_wrapper(grpc_exec_ctx *exec_ctx, void *arg, int success) {
wrapped_closure *wc = arg;
grpc_iomgr_cb_func cb = wc->cb;
void *cb_arg = wc->cb_arg;
gpr_free(wc);
cb(exec_ctx, cb_arg, success);
}
grpc_closure *grpc_closure_create(grpc_iomgr_cb_func cb, void *cb_arg) {
wrapped_closure *wc = gpr_malloc(sizeof(*wc));
wc->cb = cb;
wc->cb_arg = cb_arg;
grpc_closure_init(&wc->wrapper, closure_wrapper, wc);
return &wc->wrapper;
}

@ -77,6 +77,9 @@ struct grpc_closure {
void grpc_closure_init(grpc_closure *closure, grpc_iomgr_cb_func cb, void grpc_closure_init(grpc_closure *closure, grpc_iomgr_cb_func cb,
void *cb_arg); void *cb_arg);
/* Create a heap allocated closure: try to avoid except for very rare events */
grpc_closure *grpc_closure_create(grpc_iomgr_cb_func cb, void *cb_arg);
#define GRPC_CLOSURE_LIST_INIT \ #define GRPC_CLOSURE_LIST_INIT \
{ NULL, NULL } { NULL, NULL }

@ -336,6 +336,8 @@ static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, int from_iocp) {
peer_name_string); peer_name_string);
gpr_free(fd_name); gpr_free(fd_name);
gpr_free(peer_name_string); gpr_free(peer_name_string);
} else {
closesocket(sock);
} }
} }

@ -278,7 +278,7 @@ static void on_read(grpc_exec_ctx *exec_ctx, void *arg, int success) {
/* Tell the registered callback that data is available to read. */ /* Tell the registered callback that data is available to read. */
GPR_ASSERT(sp->read_cb); GPR_ASSERT(sp->read_cb);
sp->read_cb(sp->emfd, sp->server->grpc_server); sp->read_cb(exec_ctx, sp->emfd, sp->server->grpc_server);
/* Re-arm the notification event so we get another chance to read. */ /* Re-arm the notification event so we get another chance to read. */
grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure); grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure);

@ -43,7 +43,8 @@ typedef struct grpc_server grpc_server;
typedef struct grpc_udp_server grpc_udp_server; typedef struct grpc_udp_server grpc_udp_server;
/* Called when data is available to read from the socket. */ /* Called when data is available to read from the socket. */
typedef void (*grpc_udp_server_read_cb)(grpc_fd *emfd, grpc_server *server); typedef void (*grpc_udp_server_read_cb)(grpc_exec_ctx *exec_ctx, grpc_fd *emfd,
grpc_server *server);
/* Create a server, initially not bound to any ports */ /* Create a server, initially not bound to any ports */
grpc_udp_server *grpc_udp_server_create(void); grpc_udp_server *grpc_udp_server_create(void);

@ -936,6 +936,7 @@ static int add_slice_to_message(grpc_call *call, gpr_slice slice) {
} }
/* we have to be reading a message to know what to do here */ /* we have to be reading a message to know what to do here */
if (!call->reading_message) { if (!call->reading_message) {
gpr_slice_unref(slice);
cancel_with_status(call, GRPC_STATUS_INVALID_ARGUMENT, cancel_with_status(call, GRPC_STATUS_INVALID_ARGUMENT,
"Received payload data while not reading a message"); "Received payload data while not reading a message");
return 0; return 0;

@ -1,28 +0,0 @@
Copyright 2015, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -5,51 +5,19 @@ Beta
## PREREQUISITES ## PREREQUISITES
- `node`: This requires `node` to be installed. If you instead have the `nodejs` executable on Debian, you should install the [`nodejs-legacy`](https://packages.debian.org/sid/nodejs-legacy) package. - `node`: This requires `node` to be installed. If you instead have the `nodejs` executable on Debian, you should install the [`nodejs-legacy`](https://packages.debian.org/sid/nodejs-legacy) package.
- [homebrew][] on Mac OS X. These simplify the installation of the gRPC C core.
## INSTALLATION ## INSTALLATION
**Linux (Debian):**
Add [Debian jessie-backports][] to your `sources.list` file. Example:
```sh
echo "deb http://http.debian.net/debian jessie-backports main" | \
sudo tee -a /etc/apt/sources.list
```
Install the gRPC Debian package
```sh
sudo apt-get update
sudo apt-get install libgrpc-dev
```
Install the gRPC NPM package Install the gRPC NPM package
```sh ```sh
npm install grpc npm install grpc
``` ```
**Mac OS X**
Install [homebrew][]. Run the following command to install gRPC Node.js.
```sh
$ curl -fsSL https://goo.gl/getgrpc | bash -s nodejs
```
This will download and run the [gRPC install script][], then install the latest version of gRPC Nodejs npm package.
## BUILD FROM SOURCE ## BUILD FROM SOURCE
1. Clone [the grpc Git Repository](https://github.com/grpc/grpc). 1. Clone [the grpc Git Repository](https://github.com/grpc/grpc).
2. Follow the instructions in the `INSTALL` file in the root of that repository to install the C core library that this package depends on.
3. Run `npm install`. 3. Run `npm install`.
If you install the gRPC C core library in a custom location, then you need to set some environment variables to install this library. The command will look like this:
```sh
CXXFLAGS=-I<custom location>/include LDFLAGS=-L<custom location>/lib npm install [grpc]
```
## TESTING ## TESTING
To run the test suite, simply run `npm test` in the install location. To run the test suite, simply run `npm test` in the install location.
@ -110,7 +78,3 @@ ServerCredentials
``` ```
An object with factory methods for creating credential objects for servers. An object with factory methods for creating credential objects for servers.
[homebrew]:http://brew.sh
[gRPC install script]:https://raw.githubusercontent.com/grpc/homebrew-grpc/master/scripts/install
[Debian jessie-backports]:http://backports.debian.org/Instructions/

@ -1,16 +0,0 @@
# Command Line Tools
# Service Packager
The command line tool `bin/service_packager`, when called with the following command line:
```bash
service_packager proto_file -o output_path -n name -v version [-i input_path...]
```
Populates `output_path` with a node package consisting of a `package.json` populated with `name` and `version`, an `index.js`, a `LICENSE` file copied from gRPC, and a `service.json`, which is compiled from `proto_file` and the given `input_path`s. `require('output_path')` returns an object that is equivalent to
```js
{ client: require('grpc').load('service.json'),
auth: require('google-auth-library') }
```

@ -1,2 +0,0 @@
#!/usr/bin/env node
require(__dirname+'/../cli/service_packager.js').main(process.argv.slice(2));

@ -1,100 +0,0 @@
{
"variables" : {
'config': '<!(echo $CONFIG)'
},
"targets" : [
{
'include_dirs': [
"<!(node -e \"require('nan')\")"
],
'cflags': [
'-std=c++0x',
'-Wall',
'-pthread',
'-g',
'-zdefs',
'-Werror',
'-Wno-error=deprecated-declarations'
],
'ldflags': [
'-g'
],
"conditions": [
['OS != "win"', {
'variables': {
'pkg_config_grpc': '<!(pkg-config --exists grpc >/dev/null 2>&1 && echo true || echo false)'
},
'conditions': [
['config=="gcov"', {
'cflags': [
'-ftest-coverage',
'-fprofile-arcs',
'-O0'
],
'ldflags': [
'-ftest-coverage',
'-fprofile-arcs'
]
}
],
['pkg_config_grpc == "true"', {
'link_settings': {
'libraries': [
'<!@(pkg-config --libs-only-l --static grpc)'
]
},
'cflags': [
'<!@(pkg-config --cflags grpc)'
],
'libraries': [
'<!@(pkg-config --libs-only-L --static grpc)'
],
'ldflags': [
'<!@(pkg-config --libs-only-other --static grpc)'
]
}, {
'link_settings': {
'libraries': [
'-lpthread',
'-lgrpc',
'-lgpr'
],
},
'conditions':[
['OS != "mac"', {
'link_settings': {
'libraries': [
'-lrt'
]
}
}]
]
}
]
]
}],
['OS == "mac"', {
'xcode_settings': {
'MACOSX_DEPLOYMENT_TARGET': '10.9',
'OTHER_CFLAGS': [
'-std=c++11',
'-stdlib=libc++'
]
}
}]
],
"target_name": "grpc",
"sources": [
"ext/byte_buffer.cc",
"ext/call.cc",
"ext/channel.cc",
"ext/completion_queue_async_worker.cc",
"ext/credentials.cc",
"ext/node_grpc.cc",
"ext/server.cc",
"ext/server_credentials.cc",
"ext/timeval.cc"
]
}
]
}

@ -1,142 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
'use strict';
var fs = require('fs');
var path = require('path');
var _ = require('lodash');
var async = require('async');
var pbjs = require('protobufjs/cli/pbjs');
var parseArgs = require('minimist');
var Mustache = require('mustache');
var package_json = require('../package.json');
var template_path = path.resolve(__dirname, 'service_packager');
var package_tpl_path = path.join(template_path, 'package.json.template');
var arg_format = {
string: ['include', 'out', 'name', 'version'],
alias: {
include: 'i',
out: 'o',
name: 'n',
version: 'v'
}
};
// TODO(mlumish): autogenerate README.md from proto file
/**
* Render package.json file from template using provided parameters.
* @param {Object} params Map of parameter names to values
* @param {function(Error, string)} callback Callback to pass rendered template
* text to
*/
function generatePackage(params, callback) {
fs.readFile(package_tpl_path, {encoding: 'utf-8'}, function(err, template) {
if (err) {
callback(err);
} else {
var rendered = Mustache.render(template, params);
callback(null, rendered);
}
});
}
/**
* Copy a file
* @param {string} src_path The filepath to copy from
* @param {string} dest_path The filepath to copy to
*/
function copyFile(src_path, dest_path) {
fs.createReadStream(src_path).pipe(fs.createWriteStream(dest_path));
}
/**
* Run the script. Copies the index.js and LICENSE files to the output path,
* renders the package.json template to the output path, and generates a
* service.json file from the input proto files using pbjs. The arguments are
* taken directly from the command line, and handled as follows:
* -i (--include) : An include path for pbjs (can be dpulicated)
* -o (--output): The output path
* -n (--name): The name of the package
* -v (--version): The package version
* @param {Array} argv The argument vector
*/
function main(argv) {
var args = parseArgs(argv, arg_format);
var out_path = path.resolve(args.out);
var include_dirs = [];
if (args.include) {
include_dirs = _.map(_.flatten([args.include]), function(p) {
return path.resolve(p);
});
}
args.grpc_version = package_json.version;
generatePackage(args, function(err, rendered) {
if (err) throw err;
fs.writeFile(path.join(out_path, 'package.json'), rendered, function(err) {
if (err) throw err;
});
});
copyFile(path.join(template_path, 'index.js'),
path.join(out_path, 'index.js'));
copyFile(path.join(__dirname, '..', 'LICENSE'),
path.join(out_path, 'LICENSE'));
var service_stream = fs.createWriteStream(path.join(out_path,
'service.json'));
var pbjs_args = _.flatten(['node', 'pbjs',
args._[0],
'-legacy',
_.map(include_dirs, function(dir) {
return "-path=" + dir;
})]);
var old_stdout = process.stdout;
process.__defineGetter__('stdout', function() {
return service_stream;
});
var pbjs_status = pbjs.main(pbjs_args);
process.__defineGetter__('stdout', function() {
return old_stdout;
});
if (pbjs_status !== pbjs.STATUS_OK) {
throw new Error('pbjs failed with status code ' + pbjs_status);
}
}
exports.main = main;

@ -1,36 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
var grpc = require('grpc');
exports.client = grpc.load(__dirname + '/service.json', 'json');
exports.auth = require('google-auth-library');

@ -1,17 +0,0 @@
{
"name": "{{{name}}}",
"version": "{{{version}}}",
"author": "Google Inc.",
"description": "Client library for {{{name}}} built on gRPC",
"license": "Apache-2.0",
"dependencies": {
"grpc": "{{{grpc_version}}}",
"google-auth-library": "^0.9.2"
},
"main": "index.js",
"files": [
"LICENSE",
"index.js",
"service.json"
]
}

@ -1,62 +0,0 @@
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package examples;
// Protocol type definitions
message StockRequest {
string symbol = 1;
int32 num_trades_to_watch = 2;
}
message StockReply {
float price = 1;
string symbol = 2;
}
// Interface exported by the server
service Stock {
// Simple blocking RPC
rpc GetLastTradePrice(StockRequest) returns (StockReply) {
}
// Bidirectional streaming RPC
rpc GetLastTradePriceMultiple(stream StockRequest) returns
(stream StockReply) {
}
// Unidirectional server-to-client streaming RPC
rpc WatchFutureTrades(StockRequest) returns (stream StockReply) {
}
// Unidirectional client-to-server streaming RPC
rpc GetHighestTradePrice(stream StockRequest) returns (StockReply) {
}
}

@ -1,47 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
var grpc = require('..');
var examples = grpc.load(__dirname + '/stock.proto').examples;
/**
* This exports a client constructor for the Stock service. The usage looks like
*
* var StockClient = require('stock_client.js');
* var stockClient = new StockClient(server_address,
* grpc.Credentials.createInsecure());
* stockClient.getLastTradePrice({symbol: 'GOOG'}, function(error, response) {
* console.log(error || response);
* });
*/
module.exports = examples.Stock;

@ -1,87 +0,0 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
'use strict';
var _ = require('lodash');
var grpc = require('..');
var examples = grpc.load(__dirname + '/stock.proto').examples;
function getLastTradePrice(call, callback) {
callback(null, {symbol: call.request.symbol, price: 88});
}
function watchFutureTrades(call) {
for (var i = 0; i < call.request.num_trades_to_watch; i++) {
call.write({price: 88.00 + i * 10.00});
}
call.end();
}
function getHighestTradePrice(call, callback) {
var trades = [];
call.on('data', function(data) {
trades.push({symbol: data.symbol, price: _.random(0, 100)});
});
call.on('end', function() {
if(_.isEmpty(trades)) {
callback(null, {});
} else {
callback(null, _.max(trades, function(trade){return trade.price;}));
}
});
}
function getLastTradePriceMultiple(call) {
call.on('data', function(data) {
call.write({price: 88});
});
call.on('end', function() {
call.end();
});
}
var stockServer = new grpc.Server();
stockServer.addProtoService(examples.Stock.service, {
getLastTradePrice: getLastTradePrice,
getLastTradePriceMultiple: getLastTradePriceMultiple,
watchFutureTrades: watchFutureTrades,
getHighestTradePrice: getHighestTradePrice
});
if (require.main === module) {
stockServer.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure());
stockServer.start();
}
module.exports = stockServer;

@ -39,12 +39,14 @@
#include "grpc/support/log.h" #include "grpc/support/log.h"
#include "grpc/grpc.h" #include "grpc/grpc.h"
#include "grpc/grpc_security.h"
#include "grpc/support/alloc.h" #include "grpc/support/alloc.h"
#include "grpc/support/time.h" #include "grpc/support/time.h"
#include "byte_buffer.h" #include "byte_buffer.h"
#include "call.h" #include "call.h"
#include "channel.h" #include "channel.h"
#include "completion_queue_async_worker.h" #include "completion_queue_async_worker.h"
#include "call_credentials.h"
#include "timeval.h" #include "timeval.h"
using std::unique_ptr; using std::unique_ptr;
@ -168,8 +170,9 @@ Local<Value> ParseMetadata(const grpc_metadata_array *metadata_array) {
} }
if (EndsWith(elem->key, "-bin")) { if (EndsWith(elem->key, "-bin")) {
Nan::Set(array, index_map[elem->key], Nan::Set(array, index_map[elem->key],
MakeFastBuffer(
Nan::CopyBuffer(elem->value, Nan::CopyBuffer(elem->value,
elem->value_length).ToLocalChecked()); elem->value_length).ToLocalChecked()));
} else { } else {
Nan::Set(array, index_map[elem->key], Nan::Set(array, index_map[elem->key],
Nan::New(elem->value).ToLocalChecked()); Nan::New(elem->value).ToLocalChecked());
@ -501,6 +504,7 @@ void Call::Init(Local<Object> exports) {
Nan::SetPrototypeMethod(tpl, "cancel", Cancel); Nan::SetPrototypeMethod(tpl, "cancel", Cancel);
Nan::SetPrototypeMethod(tpl, "cancelWithStatus", CancelWithStatus); Nan::SetPrototypeMethod(tpl, "cancelWithStatus", CancelWithStatus);
Nan::SetPrototypeMethod(tpl, "getPeer", GetPeer); Nan::SetPrototypeMethod(tpl, "getPeer", GetPeer);
Nan::SetPrototypeMethod(tpl, "setCredentials", SetCredentials);
fun_tpl.Reset(tpl); fun_tpl.Reset(tpl);
Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked(); Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(exports, Nan::New("Call").ToLocalChecked(), ctr); Nan::Set(exports, Nan::New("Call").ToLocalChecked(), ctr);
@ -724,5 +728,26 @@ NAN_METHOD(Call::GetPeer) {
info.GetReturnValue().Set(peer_value); info.GetReturnValue().Set(peer_value);
} }
NAN_METHOD(Call::SetCredentials) {
Nan::HandleScope scope;
if (!HasInstance(info.This())) {
return Nan::ThrowTypeError(
"setCredentials can only be called on Call objects");
}
if (!CallCredentials::HasInstance(info[0])) {
return Nan::ThrowTypeError(
"setCredentials' first argument must be a CallCredentials");
}
Call *call = ObjectWrap::Unwrap<Call>(info.This());
CallCredentials *creds_object = ObjectWrap::Unwrap<CallCredentials>(
Nan::To<Object>(info[0]).ToLocalChecked());
grpc_credentials *creds = creds_object->GetWrappedCredentials();
grpc_call_error error = GRPC_CALL_ERROR;
if (creds) {
error = grpc_call_set_credentials(call->wrapped_call, creds);
}
info.GetReturnValue().Set(Nan::New<Uint32>(error));
}
} // namespace node } // namespace node
} // namespace grpc } // namespace grpc

@ -73,6 +73,10 @@ struct Resources {
std::vector<unique_ptr<PersistentValue> > handles; std::vector<unique_ptr<PersistentValue> > handles;
}; };
bool CreateMetadataArray(v8::Local<v8::Object> metadata,
grpc_metadata_array *array,
shared_ptr<Resources> resources);
class Op { class Op {
public: public:
virtual v8::Local<v8::Value> GetNodeValue() const = 0; virtual v8::Local<v8::Value> GetNodeValue() const = 0;
@ -122,6 +126,7 @@ class Call : public Nan::ObjectWrap {
static NAN_METHOD(Cancel); static NAN_METHOD(Cancel);
static NAN_METHOD(CancelWithStatus); static NAN_METHOD(CancelWithStatus);
static NAN_METHOD(GetPeer); static NAN_METHOD(GetPeer);
static NAN_METHOD(SetCredentials);
static Nan::Callback *constructor; static Nan::Callback *constructor;
// Used for typechecking instances of this javascript class // Used for typechecking instances of this javascript class
static Nan::Persistent<v8::FunctionTemplate> fun_tpl; static Nan::Persistent<v8::FunctionTemplate> fun_tpl;

@ -0,0 +1,259 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <node.h>
#include "grpc/grpc.h"
#include "grpc/grpc_security.h"
#include "grpc/support/log.h"
#include "call_credentials.h"
#include "call.h"
namespace grpc {
namespace node {
using Nan::Callback;
using Nan::EscapableHandleScope;
using Nan::HandleScope;
using Nan::Maybe;
using Nan::MaybeLocal;
using Nan::ObjectWrap;
using Nan::Persistent;
using Nan::Utf8String;
using v8::Exception;
using v8::External;
using v8::Function;
using v8::FunctionTemplate;
using v8::Integer;
using v8::Local;
using v8::Object;
using v8::ObjectTemplate;
using v8::Value;
Nan::Callback *CallCredentials::constructor;
Persistent<FunctionTemplate> CallCredentials::fun_tpl;
CallCredentials::CallCredentials(grpc_credentials *credentials)
: wrapped_credentials(credentials) {}
CallCredentials::~CallCredentials() {
grpc_credentials_release(wrapped_credentials);
}
void CallCredentials::Init(Local<Object> exports) {
HandleScope scope;
Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
tpl->SetClassName(Nan::New("CallCredentials").ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1);
Nan::SetPrototypeMethod(tpl, "compose", Compose);
fun_tpl.Reset(tpl);
Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(ctr, Nan::New("createFromPlugin").ToLocalChecked(),
Nan::GetFunction(
Nan::New<FunctionTemplate>(CreateFromPlugin)).ToLocalChecked());
Nan::Set(exports, Nan::New("CallCredentials").ToLocalChecked(), ctr);
constructor = new Nan::Callback(ctr);
}
bool CallCredentials::HasInstance(Local<Value> val) {
HandleScope scope;
return Nan::New(fun_tpl)->HasInstance(val);
}
Local<Value> CallCredentials::WrapStruct(grpc_credentials *credentials) {
EscapableHandleScope scope;
const int argc = 1;
if (credentials == NULL) {
return scope.Escape(Nan::Null());
}
Local<Value> argv[argc] = {
Nan::New<External>(reinterpret_cast<void *>(credentials))};
MaybeLocal<Object> maybe_instance = Nan::NewInstance(
constructor->GetFunction(), argc, argv);
if (maybe_instance.IsEmpty()) {
return scope.Escape(Nan::Null());
} else {
return scope.Escape(maybe_instance.ToLocalChecked());
}
}
grpc_credentials *CallCredentials::GetWrappedCredentials() {
return wrapped_credentials;
}
NAN_METHOD(CallCredentials::New) {
if (info.IsConstructCall()) {
if (!info[0]->IsExternal()) {
return Nan::ThrowTypeError(
"CallCredentials can only be created with the provided functions");
}
Local<External> ext = info[0].As<External>();
grpc_credentials *creds_value =
reinterpret_cast<grpc_credentials *>(ext->Value());
CallCredentials *credentials = new CallCredentials(creds_value);
credentials->Wrap(info.This());
info.GetReturnValue().Set(info.This());
return;
} else {
const int argc = 1;
Local<Value> argv[argc] = {info[0]};
MaybeLocal<Object> maybe_instance = constructor->GetFunction()->NewInstance(
argc, argv);
if (maybe_instance.IsEmpty()) {
// There's probably a pending exception
return;
} else {
info.GetReturnValue().Set(maybe_instance.ToLocalChecked());
}
}
}
NAN_METHOD(CallCredentials::Compose) {
if (!CallCredentials::HasInstance(info.This())) {
return Nan::ThrowTypeError(
"compose can only be called on CallCredentials objects");
}
if (!CallCredentials::HasInstance(info[0])) {
return Nan::ThrowTypeError(
"compose's first argument must be a CallCredentials object");
}
CallCredentials *self = ObjectWrap::Unwrap<CallCredentials>(info.This());
CallCredentials *other = ObjectWrap::Unwrap<CallCredentials>(
Nan::To<Object>(info[0]).ToLocalChecked());
grpc_credentials *creds = grpc_composite_credentials_create(
self->wrapped_credentials, other->wrapped_credentials, NULL);
info.GetReturnValue().Set(WrapStruct(creds));
}
NAN_METHOD(CallCredentials::CreateFromPlugin) {
if (!info[0]->IsFunction()) {
return Nan::ThrowTypeError(
"createFromPlugin's argument must be a function");
}
grpc_metadata_credentials_plugin plugin;
plugin_state *state = new plugin_state;
state->callback = new Nan::Callback(info[0].As<Function>());
plugin.get_metadata = plugin_get_metadata;
plugin.destroy = plugin_destroy_state;
plugin.state = reinterpret_cast<void*>(state);
grpc_credentials *creds = grpc_metadata_credentials_create_from_plugin(plugin,
NULL);
info.GetReturnValue().Set(WrapStruct(creds));
}
NAN_METHOD(PluginCallback) {
// Arguments: status code, error details, metadata
if (!info[0]->IsUint32()) {
return Nan::ThrowTypeError(
"The callback's first argument must be a status code");
}
if (!info[1]->IsString()) {
return Nan::ThrowTypeError(
"The callback's second argument must be a string");
}
if (!info[2]->IsObject()) {
return Nan::ThrowTypeError(
"The callback's third argument must be an object");
}
shared_ptr<Resources> resources(new Resources);
grpc_status_code code = static_cast<grpc_status_code>(
Nan::To<uint32_t>(info[0]).FromJust());
char *details = *Utf8String(info[1]);
grpc_metadata_array array;
if (!CreateMetadataArray(Nan::To<Object>(info[2]).ToLocalChecked(),
&array, resources)){
return Nan::ThrowError("Failed to parse metadata");
}
grpc_credentials_plugin_metadata_cb cb =
reinterpret_cast<grpc_credentials_plugin_metadata_cb>(
Nan::Get(info.Callee(),
Nan::New("cb").ToLocalChecked()
).ToLocalChecked().As<External>()->Value());
void *user_data =
Nan::Get(info.Callee(),
Nan::New("user_data").ToLocalChecked()
).ToLocalChecked().As<External>()->Value();
cb(user_data, array.metadata, array.count, code, details);
}
NAUV_WORK_CB(SendPluginCallback) {
Nan::HandleScope scope;
plugin_callback_data *data = reinterpret_cast<plugin_callback_data*>(
async->data);
// Attach cb and user_data to plugin_callback so that it can access them later
v8::Local<v8::Function> plugin_callback = Nan::GetFunction(
Nan::New<v8::FunctionTemplate>(PluginCallback)).ToLocalChecked();
Nan::Set(plugin_callback, Nan::New("cb").ToLocalChecked(),
Nan::New<v8::External>(reinterpret_cast<void*>(data->cb)));
Nan::Set(plugin_callback, Nan::New("user_data").ToLocalChecked(),
Nan::New<v8::External>(data->user_data));
const int argc = 2;
v8::Local<v8::Value> argv[argc] = {
Nan::New(data->service_url).ToLocalChecked(),
plugin_callback
};
Nan::Callback *callback = data->state->callback;
callback->Call(argc, argv);
delete data;
uv_unref((uv_handle_t *)async);
uv_close((uv_handle_t *)async, (uv_close_cb)free);
}
void plugin_get_metadata(void *state, const char *service_url,
grpc_credentials_plugin_metadata_cb cb,
void *user_data) {
uv_async_t *async = static_cast<uv_async_t*>(malloc(sizeof(uv_async_t)));
uv_async_init(uv_default_loop(),
async,
SendPluginCallback);
plugin_callback_data *data = new plugin_callback_data;
data->state = reinterpret_cast<plugin_state*>(state);
data->service_url = service_url;
data->cb = cb;
data->user_data = user_data;
async->data = data;
/* libuv says that it will coalesce calls to uv_async_send. If there is ever a
* problem with a callback not getting called, that is probably the reason */
uv_async_send(async);
}
void plugin_destroy_state(void *ptr) {
plugin_state *state = reinterpret_cast<plugin_state *>(ptr);
delete state->callback;
}
} // namespace node
} // namespace grpc

@ -0,0 +1,100 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPC_NODE_CALL_CREDENTIALS_H_
#define GRPC_NODE_CALL_CREDENTIALS_H_
#include <node.h>
#include <nan.h>
#include "grpc/grpc_security.h"
namespace grpc {
namespace node {
class CallCredentials : public Nan::ObjectWrap {
public:
static void Init(v8::Local<v8::Object> exports);
static bool HasInstance(v8::Local<v8::Value> val);
/* Wrap a grpc_credentials struct in a javascript object */
static v8::Local<v8::Value> WrapStruct(grpc_credentials *credentials);
/* Returns the grpc_credentials struct that this object wraps */
grpc_credentials *GetWrappedCredentials();
private:
explicit CallCredentials(grpc_credentials *credentials);
~CallCredentials();
// Prevent copying
CallCredentials(const CallCredentials &);
CallCredentials &operator=(const CallCredentials &);
static NAN_METHOD(New);
static NAN_METHOD(CreateSsl);
static NAN_METHOD(CreateFromPlugin);
static NAN_METHOD(Compose);
static Nan::Callback *constructor;
// Used for typechecking instances of this javascript class
static Nan::Persistent<v8::FunctionTemplate> fun_tpl;
grpc_credentials *wrapped_credentials;
};
/* Auth metadata plugin functionality */
typedef struct plugin_state {
Nan::Callback *callback;
} plugin_state;
typedef struct plugin_callback_data {
plugin_state *state;
const char *service_url;
grpc_credentials_plugin_metadata_cb cb;
void *user_data;
} plugin_callback_data;
void plugin_get_metadata(void *state, const char *service_url,
grpc_credentials_plugin_metadata_cb cb,
void *user_data);
void plugin_destroy_state(void *state);
NAN_METHOD(PluginCallback);
NAUV_WORK_CB(SendPluginCallback);
} // namespace node
} // namepsace grpc
#endif // GRPC_NODE_CALL_CREDENTIALS_H_

@ -42,7 +42,7 @@
#include "call.h" #include "call.h"
#include "channel.h" #include "channel.h"
#include "completion_queue_async_worker.h" #include "completion_queue_async_worker.h"
#include "credentials.h" #include "channel_credentials.h"
#include "timeval.h" #include "timeval.h"
namespace grpc { namespace grpc {
@ -112,11 +112,11 @@ NAN_METHOD(Channel::New) {
// Owned by the Channel object // Owned by the Channel object
Utf8String host(info[0]); Utf8String host(info[0]);
grpc_credentials *creds; grpc_credentials *creds;
if (!Credentials::HasInstance(info[1])) { if (!ChannelCredentials::HasInstance(info[1])) {
return Nan::ThrowTypeError( return Nan::ThrowTypeError(
"Channel's second argument must be a credential"); "Channel's second argument must be a ChannelCredentials");
} }
Credentials *creds_object = ObjectWrap::Unwrap<Credentials>( ChannelCredentials *creds_object = ObjectWrap::Unwrap<ChannelCredentials>(
Nan::To<Object>(info[1]).ToLocalChecked()); Nan::To<Object>(info[1]).ToLocalChecked());
creds = creds_object->GetWrappedCredentials(); creds = creds_object->GetWrappedCredentials();
grpc_channel_args *channel_args_ptr; grpc_channel_args *channel_args_ptr;

@ -36,7 +36,9 @@
#include "grpc/grpc.h" #include "grpc/grpc.h"
#include "grpc/grpc_security.h" #include "grpc/grpc_security.h"
#include "grpc/support/log.h" #include "grpc/support/log.h"
#include "credentials.h" #include "channel_credentials.h"
#include "call_credentials.h"
#include "call.h"
namespace grpc { namespace grpc {
namespace node { namespace node {
@ -60,51 +62,40 @@ using v8::Object;
using v8::ObjectTemplate; using v8::ObjectTemplate;
using v8::Value; using v8::Value;
Nan::Callback *Credentials::constructor; Nan::Callback *ChannelCredentials::constructor;
Persistent<FunctionTemplate> Credentials::fun_tpl; Persistent<FunctionTemplate> ChannelCredentials::fun_tpl;
Credentials::Credentials(grpc_credentials *credentials) ChannelCredentials::ChannelCredentials(grpc_credentials *credentials)
: wrapped_credentials(credentials) {} : wrapped_credentials(credentials) {}
Credentials::~Credentials() { ChannelCredentials::~ChannelCredentials() {
grpc_credentials_release(wrapped_credentials); grpc_credentials_release(wrapped_credentials);
} }
void Credentials::Init(Local<Object> exports) { void ChannelCredentials::Init(Local<Object> exports) {
HandleScope scope; HandleScope scope;
Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New); Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
tpl->SetClassName(Nan::New("Credentials").ToLocalChecked()); tpl->SetClassName(Nan::New("ChannelCredentials").ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1); tpl->InstanceTemplate()->SetInternalFieldCount(1);
Nan::SetPrototypeMethod(tpl, "compose", Compose);
fun_tpl.Reset(tpl); fun_tpl.Reset(tpl);
Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked(); Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(ctr, Nan::New("createDefault").ToLocalChecked(),
Nan::GetFunction(
Nan::New<FunctionTemplate>(CreateDefault)).ToLocalChecked());
Nan::Set(ctr, Nan::New("createSsl").ToLocalChecked(), Nan::Set(ctr, Nan::New("createSsl").ToLocalChecked(),
Nan::GetFunction( Nan::GetFunction(
Nan::New<FunctionTemplate>(CreateSsl)).ToLocalChecked()); Nan::New<FunctionTemplate>(CreateSsl)).ToLocalChecked());
Nan::Set(ctr, Nan::New("createComposite").ToLocalChecked(),
Nan::GetFunction(
Nan::New<FunctionTemplate>(CreateComposite)).ToLocalChecked());
Nan::Set(ctr, Nan::New("createGce").ToLocalChecked(),
Nan::GetFunction(
Nan::New<FunctionTemplate>(CreateGce)).ToLocalChecked());
Nan::Set(ctr, Nan::New("createIam").ToLocalChecked(),
Nan::GetFunction(
Nan::New<FunctionTemplate>(CreateIam)).ToLocalChecked());
Nan::Set(ctr, Nan::New("createInsecure").ToLocalChecked(), Nan::Set(ctr, Nan::New("createInsecure").ToLocalChecked(),
Nan::GetFunction( Nan::GetFunction(
Nan::New<FunctionTemplate>(CreateInsecure)).ToLocalChecked()); Nan::New<FunctionTemplate>(CreateInsecure)).ToLocalChecked());
Nan::Set(exports, Nan::New("Credentials").ToLocalChecked(), ctr); Nan::Set(exports, Nan::New("ChannelCredentials").ToLocalChecked(), ctr);
constructor = new Nan::Callback(ctr); constructor = new Nan::Callback(ctr);
} }
bool Credentials::HasInstance(Local<Value> val) { bool ChannelCredentials::HasInstance(Local<Value> val) {
HandleScope scope; HandleScope scope;
return Nan::New(fun_tpl)->HasInstance(val); return Nan::New(fun_tpl)->HasInstance(val);
} }
Local<Value> Credentials::WrapStruct(grpc_credentials *credentials) { Local<Value> ChannelCredentials::WrapStruct(grpc_credentials *credentials) {
EscapableHandleScope scope; EscapableHandleScope scope;
const int argc = 1; const int argc = 1;
Local<Value> argv[argc] = { Local<Value> argv[argc] = {
@ -118,20 +109,20 @@ Local<Value> Credentials::WrapStruct(grpc_credentials *credentials) {
} }
} }
grpc_credentials *Credentials::GetWrappedCredentials() { grpc_credentials *ChannelCredentials::GetWrappedCredentials() {
return wrapped_credentials; return wrapped_credentials;
} }
NAN_METHOD(Credentials::New) { NAN_METHOD(ChannelCredentials::New) {
if (info.IsConstructCall()) { if (info.IsConstructCall()) {
if (!info[0]->IsExternal()) { if (!info[0]->IsExternal()) {
return Nan::ThrowTypeError( return Nan::ThrowTypeError(
"Credentials can only be created with the provided functions"); "ChannelCredentials can only be created with the provided functions");
} }
Local<External> ext = info[0].As<External>(); Local<External> ext = info[0].As<External>();
grpc_credentials *creds_value = grpc_credentials *creds_value =
reinterpret_cast<grpc_credentials *>(ext->Value()); reinterpret_cast<grpc_credentials *>(ext->Value());
Credentials *credentials = new Credentials(creds_value); ChannelCredentials *credentials = new ChannelCredentials(creds_value);
credentials->Wrap(info.This()); credentials->Wrap(info.This());
info.GetReturnValue().Set(info.This()); info.GetReturnValue().Set(info.This());
return; return;
@ -149,16 +140,7 @@ NAN_METHOD(Credentials::New) {
} }
} }
NAN_METHOD(Credentials::CreateDefault) { NAN_METHOD(ChannelCredentials::CreateSsl) {
grpc_credentials *creds = grpc_google_default_credentials_create();
if (creds == NULL) {
info.GetReturnValue().SetNull();
} else {
info.GetReturnValue().Set(WrapStruct(creds));
}
}
NAN_METHOD(Credentials::CreateSsl) {
char *root_certs = NULL; char *root_certs = NULL;
grpc_ssl_pem_key_cert_pair key_cert_pair = {NULL, NULL}; grpc_ssl_pem_key_cert_pair key_cert_pair = {NULL, NULL};
if (::node::Buffer::HasInstance(info[0])) { if (::node::Buffer::HasInstance(info[0])) {
@ -188,49 +170,25 @@ NAN_METHOD(Credentials::CreateSsl) {
} }
} }
NAN_METHOD(Credentials::CreateComposite) { NAN_METHOD(ChannelCredentials::Compose) {
if (!HasInstance(info[0])) { if (!ChannelCredentials::HasInstance(info.This())) {
return Nan::ThrowTypeError( return Nan::ThrowTypeError(
"createComposite's first argument must be a Credentials object"); "compose can only be called on ChannelCredentials objects");
} }
if (!HasInstance(info[1])) { if (!CallCredentials::HasInstance(info[0])) {
return Nan::ThrowTypeError( return Nan::ThrowTypeError(
"createComposite's second argument must be a Credentials object"); "compose's first argument must be a CallCredentials object");
} }
Credentials *creds1 = ObjectWrap::Unwrap<Credentials>( ChannelCredentials *self = ObjectWrap::Unwrap<ChannelCredentials>(
info.This());
if (self->wrapped_credentials == NULL) {
return Nan::ThrowTypeError(
"Cannot compose insecure credential");
}
CallCredentials *other = ObjectWrap::Unwrap<CallCredentials>(
Nan::To<Object>(info[0]).ToLocalChecked()); Nan::To<Object>(info[0]).ToLocalChecked());
Credentials *creds2 = ObjectWrap::Unwrap<Credentials>(
Nan::To<Object>(info[1]).ToLocalChecked());
grpc_credentials *creds = grpc_composite_credentials_create( grpc_credentials *creds = grpc_composite_credentials_create(
creds1->wrapped_credentials, creds2->wrapped_credentials, NULL); self->wrapped_credentials, other->GetWrappedCredentials(), NULL);
if (creds == NULL) {
info.GetReturnValue().SetNull();
} else {
info.GetReturnValue().Set(WrapStruct(creds));
}
}
NAN_METHOD(Credentials::CreateGce) {
Nan::HandleScope scope;
grpc_credentials *creds = grpc_google_compute_engine_credentials_create(NULL);
if (creds == NULL) {
info.GetReturnValue().SetNull();
} else {
info.GetReturnValue().Set(WrapStruct(creds));
}
}
NAN_METHOD(Credentials::CreateIam) {
if (!info[0]->IsString()) {
return Nan::ThrowTypeError("createIam's first argument must be a string");
}
if (!info[1]->IsString()) {
return Nan::ThrowTypeError("createIam's second argument must be a string");
}
Utf8String auth_token(info[0]);
Utf8String auth_selector(info[1]);
grpc_credentials *creds =
grpc_google_iam_credentials_create(*auth_token, *auth_selector, NULL);
if (creds == NULL) { if (creds == NULL) {
info.GetReturnValue().SetNull(); info.GetReturnValue().SetNull();
} else { } else {
@ -238,7 +196,7 @@ NAN_METHOD(Credentials::CreateIam) {
} }
} }
NAN_METHOD(Credentials::CreateInsecure) { NAN_METHOD(ChannelCredentials::CreateInsecure) {
info.GetReturnValue().Set(WrapStruct(NULL)); info.GetReturnValue().Set(WrapStruct(NULL));
} }

@ -31,8 +31,8 @@
* *
*/ */
#ifndef NET_GRPC_NODE_CREDENTIALS_H_ #ifndef NET_GRPC_NODE_CHANNEL_CREDENTIALS_H_
#define NET_GRPC_NODE_CREDENTIALS_H_ #define NET_GRPC_NODE_CHANNEL_CREDENTIALS_H_
#include <node.h> #include <node.h>
#include <nan.h> #include <nan.h>
@ -43,7 +43,7 @@ namespace grpc {
namespace node { namespace node {
/* Wrapper class for grpc_credentials structs */ /* Wrapper class for grpc_credentials structs */
class Credentials : public Nan::ObjectWrap { class ChannelCredentials : public Nan::ObjectWrap {
public: public:
static void Init(v8::Local<v8::Object> exports); static void Init(v8::Local<v8::Object> exports);
static bool HasInstance(v8::Local<v8::Value> val); static bool HasInstance(v8::Local<v8::Value> val);
@ -54,21 +54,18 @@ class Credentials : public Nan::ObjectWrap {
grpc_credentials *GetWrappedCredentials(); grpc_credentials *GetWrappedCredentials();
private: private:
explicit Credentials(grpc_credentials *credentials); explicit ChannelCredentials(grpc_credentials *credentials);
~Credentials(); ~ChannelCredentials();
// Prevent copying // Prevent copying
Credentials(const Credentials &); ChannelCredentials(const ChannelCredentials &);
Credentials &operator=(const Credentials &); ChannelCredentials &operator=(const ChannelCredentials &);
static NAN_METHOD(New); static NAN_METHOD(New);
static NAN_METHOD(CreateDefault);
static NAN_METHOD(CreateSsl); static NAN_METHOD(CreateSsl);
static NAN_METHOD(CreateComposite);
static NAN_METHOD(CreateGce);
static NAN_METHOD(CreateFake);
static NAN_METHOD(CreateIam);
static NAN_METHOD(CreateInsecure); static NAN_METHOD(CreateInsecure);
static NAN_METHOD(Compose);
static Nan::Callback *constructor; static Nan::Callback *constructor;
// Used for typechecking instances of this javascript class // Used for typechecking instances of this javascript class
static Nan::Persistent<v8::FunctionTemplate> fun_tpl; static Nan::Persistent<v8::FunctionTemplate> fun_tpl;
@ -79,4 +76,4 @@ class Credentials : public Nan::ObjectWrap {
} // namespace node } // namespace node
} // namespace grpc } // namespace grpc
#endif // NET_GRPC_NODE_CREDENTIALS_H_ #endif // NET_GRPC_NODE_CHANNEL_CREDENTIALS_H_

@ -37,10 +37,11 @@
#include "grpc/grpc.h" #include "grpc/grpc.h"
#include "call.h" #include "call.h"
#include "call_credentials.h"
#include "channel.h" #include "channel.h"
#include "channel_credentials.h"
#include "server.h" #include "server.h"
#include "completion_queue_async_worker.h" #include "completion_queue_async_worker.h"
#include "credentials.h"
#include "server_credentials.h" #include "server_credentials.h"
using v8::Local; using v8::Local;
@ -240,11 +241,12 @@ void init(Local<Object> exports) {
InitWriteFlags(exports); InitWriteFlags(exports);
grpc::node::Call::Init(exports); grpc::node::Call::Init(exports);
grpc::node::CallCredentials::Init(exports);
grpc::node::Channel::Init(exports); grpc::node::Channel::Init(exports);
grpc::node::ChannelCredentials::Init(exports);
grpc::node::Server::Init(exports); grpc::node::Server::Init(exports);
grpc::node::CompletionQueueAsyncWorker::Init(exports); grpc::node::CompletionQueueAsyncWorker::Init(exports);
grpc::node::Credentials::Init(exports);
grpc::node::ServerCredentials::Init(exports); grpc::node::ServerCredentials::Init(exports);
} }
NODE_MODULE(grpc, init) NODE_MODULE(grpc_node, init)

@ -43,7 +43,7 @@ var server = require('./src/server.js');
var Metadata = require('./src/metadata.js'); var Metadata = require('./src/metadata.js');
var grpc = require('bindings')('grpc'); var grpc = require('bindings')('grpc_node');
/** /**
* Load a gRPC object from an existing ProtoBuf.Reflect object. * Load a gRPC object from an existing ProtoBuf.Reflect object.
@ -90,36 +90,9 @@ exports.load = function load(filename, format) {
default: default:
throw new Error('Unrecognized format "' + format + '"'); throw new Error('Unrecognized format "' + format + '"');
} }
return loadObject(builder.ns); return loadObject(builder.ns);
}; };
/**
* Get a function that a client can use to update metadata with authentication
* information from a Google Auth credential object, which comes from the
* google-auth-library.
* @param {Object} credential The credential object to use
* @return {function(Object, callback)} Metadata updater function
*/
exports.getGoogleAuthDelegate = function getGoogleAuthDelegate(credential) {
/**
* Update a metadata object with authentication information.
* @param {string} authURI The uri to authenticate to
* @param {Object} metadata Metadata object
* @param {function(Error, Object)} callback
*/
return function updateMetadata(authURI, metadata, callback) {
credential.getRequestMetadata(authURI, function(err, header) {
if (err) {
callback(err);
return;
}
metadata.add('authorization', header.Authorization);
callback(null, metadata);
});
};
};
/** /**
* @see module:src/server.Server * @see module:src/server.Server
*/ */
@ -153,7 +126,7 @@ exports.writeFlags = grpc.writeFlags;
/** /**
* Credentials factories * Credentials factories
*/ */
exports.Credentials = grpc.Credentials; exports.credentials = require('./src/credentials.js');
/** /**
* ServerCredentials factories * ServerCredentials factories

@ -1,43 +0,0 @@
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package grpc.testing;
// An empty message that you can re-use to avoid defining duplicated empty
// messages in your project. A typical example is to use it as argument or the
// return value of a service API. For instance:
//
// service Foo {
// rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { };
// };
//
message Empty {}

@ -37,7 +37,9 @@ var fs = require('fs');
var path = require('path'); var path = require('path');
var _ = require('lodash'); var _ = require('lodash');
var grpc = require('..'); var grpc = require('..');
var testProto = grpc.load(__dirname + '/test.proto').grpc.testing; var testProto = grpc.load({
root: __dirname + '/../../..',
file: 'test/proto/test.proto'}).grpc.testing;
var GoogleAuth = require('google-auth-library'); var GoogleAuth = require('google-auth-library');
var assert = require('assert'); var assert = require('assert');
@ -49,6 +51,9 @@ var AUTH_USER = ('155450119199-vefjjaekcc6cmsd5914v6lqufunmh9ue' +
var COMPUTE_ENGINE_USER = ('155450119199-r5aaqa2vqoa9g5mv2m6s3m1l293rlmel' + var COMPUTE_ENGINE_USER = ('155450119199-r5aaqa2vqoa9g5mv2m6s3m1l293rlmel' +
'@developer.gserviceaccount.com'); '@developer.gserviceaccount.com');
var ECHO_INITIAL_KEY = 'x-grpc-test-echo-initial';
var ECHO_TRAILING_KEY = 'x-grpc-test-echo-trailing-bin';
/** /**
* Create a buffer filled with size zeroes * Create a buffer filled with size zeroes
* @param {number} size The length of the buffer * @param {number} size The length of the buffer
@ -60,6 +65,27 @@ function zeroBuffer(size) {
return zeros; return zeros;
} }
/**
* 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();
}
};
}
/** /**
* Run the empty_unary test * Run the empty_unary test
* @param {Client} client The client to test against * @param {Client} client The client to test against
@ -271,6 +297,54 @@ function timeoutOnSleepingServer(client, done) {
}); });
} }
function customMetadata(client, done) {
done = multiDone(done, 5);
var metadata = new grpc.Metadata();
metadata.set(ECHO_INITIAL_KEY, 'test_initial_metadata_value');
metadata.set(ECHO_TRAILING_KEY, new Buffer('ababab', 'hex'));
var arg = {
response_type: 'COMPRESSABLE',
response_size: 314159,
payload: {
body: zeroBuffer(271828)
}
};
var streaming_arg = {
payload: {
body: zeroBuffer(271828)
}
};
var unary = client.unaryCall(arg, function(err, resp) {
assert.ifError(err);
done();
}, metadata);
unary.on('metadata', function(metadata) {
assert.deepEqual(metadata.get(ECHO_INITIAL_KEY),
['test_initial_metadata_value']);
done();
});
unary.on('status', function(status) {
var echo_trailer = status.metadata.get(ECHO_TRAILING_KEY);
assert(echo_trailer.length > 0);
assert.strictEqual(echo_trailer[0].toString('hex'), 'ababab');
done();
});
var stream = client.fullDuplexCall(metadata);
stream.on('metadata', function(metadata) {
assert.deepEqual(metadata.get(ECHO_INITIAL_KEY),
['test_initial_metadata_value']);
done();
});
stream.on('status', function(status) {
var echo_trailer = status.metadata.get(ECHO_TRAILING_KEY);
assert(echo_trailer.length > 0);
assert.strictEqual(echo_trailer[0].toString('hex'), 'ababab');
done();
});
stream.write(streaming_arg);
stream.end();
}
/** /**
* Run one of the authentication tests. * Run one of the authentication tests.
* @param {string} expected_user The expected username in the response * @param {string} expected_user The expected username in the response
@ -280,12 +354,6 @@ function timeoutOnSleepingServer(client, done) {
* primarily for use with mocha * primarily for use with mocha
*/ */
function authTest(expected_user, scope, client, done) { function authTest(expected_user, scope, client, done) {
(new GoogleAuth()).getApplicationDefault(function(err, credential) {
assert.ifError(err);
if (credential.createScopedRequired() && scope) {
credential = credential.createScoped(scope);
}
client.$updateMetadata = grpc.getGoogleAuthDelegate(credential);
var arg = { var arg = {
response_type: 'COMPRESSABLE', response_type: 'COMPRESSABLE',
response_size: 314159, response_size: 314159,
@ -307,7 +375,6 @@ function authTest(expected_user, scope, client, done) {
done(); done();
} }
}); });
});
} }
function oauth2Test(expected_user, scope, per_rpc, client, done) { function oauth2Test(expected_user, scope, per_rpc, client, done) {
@ -345,24 +412,86 @@ function oauth2Test(expected_user, scope, per_rpc, client, done) {
}); });
} }
function perRpcAuthTest(expected_user, scope, per_rpc, client, done) {
(new GoogleAuth()).getApplicationDefault(function(err, credential) {
assert.ifError(err);
var arg = {
fill_username: true,
fill_oauth_scope: true
};
if (credential.createScopedRequired() && scope) {
credential = credential.createScoped(scope);
}
var creds = grpc.credentials.createFromGoogleCredential(credential);
client.unaryCall(arg, function(err, resp) {
assert.ifError(err);
assert.strictEqual(resp.username, expected_user);
assert.strictEqual(resp.oauth_scope, AUTH_SCOPE_RESPONSE);
if (done) {
done();
}
}, null, {credentials: creds});
});
}
function getApplicationCreds(scope, callback) {
(new GoogleAuth()).getApplicationDefault(function(err, credential) {
if (err) {
callback(err);
return;
}
if (credential.createScopedRequired() && scope) {
credential = credential.createScoped(scope);
}
callback(null, grpc.credentials.createFromGoogleCredential(credential));
});
}
function getOauth2Creds(scope, callback) {
(new GoogleAuth()).getApplicationDefault(function(err, credential) {
if (err) {
callback(err);
return;
}
credential = credential.createScoped(scope);
credential.getAccessToken(function(err, token) {
if (err) {
callback(err);
return;
}
var updateMd = function(service_url, callback) {
var metadata = new grpc.Metadata();
metadata.add('authorization', 'Bearer ' + token);
callback(null, metadata);
};
callback(null, grpc.credentials.createFromMetadataGenerator(updateMd));
});
});
}
/** /**
* Map from test case names to test functions * Map from test case names to test functions
*/ */
var test_cases = { var test_cases = {
empty_unary: emptyUnary, empty_unary: {run: emptyUnary},
large_unary: largeUnary, large_unary: {run: largeUnary},
client_streaming: clientStreaming, client_streaming: {run: clientStreaming},
server_streaming: serverStreaming, server_streaming: {run: serverStreaming},
ping_pong: pingPong, ping_pong: {run: pingPong},
empty_stream: emptyStream, empty_stream: {run: emptyStream},
cancel_after_begin: cancelAfterBegin, cancel_after_begin: {run: cancelAfterBegin},
cancel_after_first_response: cancelAfterFirstResponse, cancel_after_first_response: {run: cancelAfterFirstResponse},
timeout_on_sleeping_server: timeoutOnSleepingServer, timeout_on_sleeping_server: {run: timeoutOnSleepingServer},
compute_engine_creds: _.partial(authTest, COMPUTE_ENGINE_USER, null), custom_metadata: {run: customMetadata},
service_account_creds: _.partial(authTest, AUTH_USER, AUTH_SCOPE), compute_engine_creds: {run: _.partial(authTest, COMPUTE_ENGINE_USER, null),
jwt_token_creds: _.partial(authTest, AUTH_USER, null), getCreds: _.partial(getApplicationCreds, null)},
oauth2_auth_token: _.partial(oauth2Test, AUTH_USER, AUTH_SCOPE, false), service_account_creds: {run: _.partial(authTest, AUTH_USER, AUTH_SCOPE),
per_rpc_creds: _.partial(oauth2Test, AUTH_USER, AUTH_SCOPE, true) getCreds: _.partial(getApplicationCreds, AUTH_SCOPE)},
jwt_token_creds: {run: _.partial(authTest, AUTH_USER, null),
getCreds: _.partial(getApplicationCreds, null)},
oauth2_auth_token: {run: _.partial(oauth2Test, AUTH_USER, AUTH_SCOPE, false),
getCreds: _.partial(getOauth2Creds, AUTH_SCOPE)},
per_rpc_creds: {run: _.partial(perRpcAuthTest, AUTH_USER, AUTH_SCOPE, true)}
}; };
/** /**
@ -388,17 +517,30 @@ function runTest(address, host_override, test_case, tls, test_ca, done) {
ca_path = process.env.SSL_CERT_FILE; ca_path = process.env.SSL_CERT_FILE;
} }
var ca_data = fs.readFileSync(ca_path); var ca_data = fs.readFileSync(ca_path);
creds = grpc.Credentials.createSsl(ca_data); creds = grpc.credentials.createSsl(ca_data);
if (host_override) { if (host_override) {
options['grpc.ssl_target_name_override'] = host_override; options['grpc.ssl_target_name_override'] = host_override;
options['grpc.default_authority'] = host_override; options['grpc.default_authority'] = host_override;
} }
} else { } else {
creds = grpc.Credentials.createInsecure(); creds = grpc.credentials.createInsecure();
} }
var test = test_cases[test_case];
var execute = function(err, creds) {
assert.ifError(err);
var client = new testProto.TestService(address, creds, options); var client = new testProto.TestService(address, creds, options);
test.run(client, done);
};
test_cases[test_case](client, done); if (test.getCreds) {
test.getCreds(function(err, new_creds) {
execute(err, grpc.credentials.combineChannelCredentials(
creds, new_creds));
});
} else {
execute(null, creds);
}
} }
if (require.main === module) { if (require.main === module) {

@ -37,7 +37,12 @@ var fs = require('fs');
var path = require('path'); var path = require('path');
var _ = require('lodash'); var _ = require('lodash');
var grpc = require('..'); var grpc = require('..');
var testProto = grpc.load(__dirname + '/test.proto').grpc.testing; var testProto = grpc.load({
root: __dirname + '/../../..',
file: 'test/proto/test.proto'}).grpc.testing;
var ECHO_INITIAL_KEY = 'x-grpc-test-echo-initial';
var ECHO_TRAILING_KEY = 'x-grpc-test-echo-trailing-bin';
/** /**
* Create a buffer filled with size zeroes * Create a buffer filled with size zeroes
@ -50,6 +55,34 @@ function zeroBuffer(size) {
return zeros; return zeros;
} }
/**
* Echos a header metadata item as specified in the interop spec.
* @param {Call} call The call to echo metadata on
*/
function echoHeader(call) {
var echo_initial = call.metadata.get(ECHO_INITIAL_KEY);
if (echo_initial.length > 0) {
var response_metadata = new grpc.Metadata();
response_metadata.set(ECHO_INITIAL_KEY, echo_initial[0]);
call.sendMetadata(response_metadata);
}
}
/**
* Gets the trailer metadata that should be echoed when the call is done,
* as specified in the interop spec.
* @param {Call} call The call to get metadata from
* @return {grpc.Metadata} The metadata to send as a trailer
*/
function getEchoTrailer(call) {
var echo_trailer = call.metadata.get(ECHO_TRAILING_KEY);
var response_trailer = new grpc.Metadata();
if (echo_trailer.length > 0) {
response_trailer.set(ECHO_TRAILING_KEY, echo_trailer[0]);
}
return response_trailer;
}
/** /**
* Respond to an empty parameter with an empty response. * Respond to an empty parameter with an empty response.
* NOTE: this currently does not work due to issue #137 * NOTE: this currently does not work due to issue #137
@ -58,7 +91,8 @@ function zeroBuffer(size) {
* or error * or error
*/ */
function handleEmpty(call, callback) { function handleEmpty(call, callback) {
callback(null, {}); echoHeader(call);
callback(null, {}, getEchoTrailer(call));
} }
/** /**
@ -68,6 +102,7 @@ function handleEmpty(call, callback) {
* error * error
*/ */
function handleUnary(call, callback) { function handleUnary(call, callback) {
echoHeader(call);
var req = call.request; var req = call.request;
var zeros = zeroBuffer(req.response_size); var zeros = zeroBuffer(req.response_size);
var payload_type = req.response_type; var payload_type = req.response_type;
@ -75,7 +110,8 @@ function handleUnary(call, callback) {
payload_type = ['COMPRESSABLE', payload_type = ['COMPRESSABLE',
'UNCOMPRESSABLE'][Math.random() < 0.5 ? 0 : 1]; 'UNCOMPRESSABLE'][Math.random() < 0.5 ? 0 : 1];
} }
callback(null, {payload: {type: payload_type, body: zeros}}); callback(null, {payload: {type: payload_type, body: zeros}},
getEchoTrailer(call));
} }
/** /**
@ -85,12 +121,14 @@ function handleUnary(call, callback) {
* error * error
*/ */
function handleStreamingInput(call, callback) { function handleStreamingInput(call, callback) {
echoHeader(call);
var aggregate_size = 0; var aggregate_size = 0;
call.on('data', function(value) { call.on('data', function(value) {
aggregate_size += value.payload.body.length; aggregate_size += value.payload.body.length;
}); });
call.on('end', function() { call.on('end', function() {
callback(null, {aggregated_payload_size: aggregate_size}); callback(null, {aggregated_payload_size: aggregate_size},
getEchoTrailer(call));
}); });
} }
@ -99,6 +137,7 @@ function handleStreamingInput(call, callback) {
* @param {Call} call Call to handle * @param {Call} call Call to handle
*/ */
function handleStreamingOutput(call) { function handleStreamingOutput(call) {
echoHeader(call);
var req = call.request; var req = call.request;
var payload_type = req.response_type; var payload_type = req.response_type;
if (payload_type === 'RANDOM') { if (payload_type === 'RANDOM') {
@ -113,7 +152,7 @@ function handleStreamingOutput(call) {
} }
}); });
}); });
call.end(); call.end(getEchoTrailer(call));
} }
/** /**
@ -122,6 +161,7 @@ function handleStreamingOutput(call) {
* @param {Call} call Call to handle * @param {Call} call Call to handle
*/ */
function handleFullDuplex(call) { function handleFullDuplex(call) {
echoHeader(call);
call.on('data', function(value) { call.on('data', function(value) {
var payload_type = value.response_type; var payload_type = value.response_type;
if (payload_type === 'RANDOM') { if (payload_type === 'RANDOM') {
@ -138,7 +178,7 @@ function handleFullDuplex(call) {
}); });
}); });
call.on('end', function() { call.on('end', function() {
call.end(); call.end(getEchoTrailer(call));
}); });
} }

@ -1,132 +0,0 @@
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Message definitions to be used by integration test service definitions.
syntax = "proto3";
package grpc.testing;
// The type of payload that should be returned.
enum PayloadType {
// Compressable text format.
COMPRESSABLE = 0;
// Uncompressable binary format.
UNCOMPRESSABLE = 1;
// Randomly chosen from all other formats defined in this enum.
RANDOM = 2;
}
// A block of data, to simply increase gRPC message size.
message Payload {
// The type of data in body.
PayloadType type = 1;
// Primary contents of payload.
bytes body = 2;
}
// Unary request.
message SimpleRequest {
// Desired payload type in the response from the server.
// If response_type is RANDOM, server randomly chooses one from other formats.
PayloadType response_type = 1;
// Desired payload size in the response from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
int32 response_size = 2;
// Optional input payload sent along with the request.
Payload payload = 3;
// Whether SimpleResponse should include username.
bool fill_username = 4;
// Whether SimpleResponse should include OAuth scope.
bool fill_oauth_scope = 5;
}
// Unary response, as configured by the request.
message SimpleResponse {
// Payload to increase message size.
Payload payload = 1;
// The user the request came from, for verifying authentication was
// successful when the client expected it.
string username = 2;
// OAuth scope.
string oauth_scope = 3;
}
// Client-streaming request.
message StreamingInputCallRequest {
// Optional input payload sent along with the request.
Payload payload = 1;
// Not expecting any payload from the response.
}
// Client-streaming response.
message StreamingInputCallResponse {
// Aggregated size of payloads received from the client.
int32 aggregated_payload_size = 1;
}
// Configuration for a particular response.
message ResponseParameters {
// Desired payload sizes in responses from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
int32 size = 1;
// Desired interval between consecutive responses in the response stream in
// microseconds.
int32 interval_us = 2;
}
// Server-streaming request.
message StreamingOutputCallRequest {
// Desired payload type in the response from the server.
// If response_type is RANDOM, the payload from each response in the stream
// might be of different types. This is to simulate a mixed type of payload
// stream.
PayloadType response_type = 1;
// Configuration for each expected response message.
repeated ResponseParameters response_parameters = 2;
// Optional input payload sent along with the request.
Payload payload = 3;
}
// Server-streaming response, as configured by the request and parameters.
message StreamingOutputCallResponse {
// Payload to increase response size.
Payload payload = 1;
}

@ -1,72 +0,0 @@
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// An integration test service that covers all the method signature permutations
// of unary/streaming requests/responses.
syntax = "proto3";
import "empty.proto";
import "messages.proto";
package grpc.testing;
// A simple service to test the various types of RPCs and experiment with
// performance with various types of payload.
service TestService {
// One empty request followed by one empty response.
rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty);
// One request followed by one response.
rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
// One request followed by a sequence of responses (streamed download).
// The server returns the payload with client desired type and sizes.
rpc StreamingOutputCall(StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
// A sequence of requests followed by one response (streamed upload).
// The server returns the aggregated size of client payload as the result.
rpc StreamingInputCall(stream StreamingInputCallRequest)
returns (StreamingInputCallResponse);
// A sequence of requests with each request served by the server immediately.
// As one request could lead to multiple responses, this interface
// demonstrates the idea of full duplexing.
rpc FullDuplexCall(stream StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
// A sequence of requests followed by a sequence of responses.
// The server buffers all the client requests and then serves them in order. A
// stream of responses are returned to the client when the server starts with
// first request.
rpc HalfDuplexCall(stream StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
}

@ -42,7 +42,7 @@ function runTest(iterations, callback) {
var testServer = interop_server.getServer(0, false); var testServer = interop_server.getServer(0, false);
testServer.server.start(); testServer.server.start();
var client = new testProto.TestService('localhost:' + testServer.port, var client = new testProto.TestService('localhost:' + testServer.port,
grpc.Credentials.createInsecure()); grpc.credentials.createInsecure());
function runIterations(finish) { function runIterations(finish) {
var start = process.hrtime(); var start = process.hrtime();

@ -62,7 +62,7 @@ function runTest(concurrent_calls, seconds, callback) {
var testServer = interop_server.getServer(0, false); var testServer = interop_server.getServer(0, false);
testServer.server.start(); testServer.server.start();
var client = new testProto.TestService('localhost:' + testServer.port, var client = new testProto.TestService('localhost:' + testServer.port,
grpc.Credentials.createInsecure()); grpc.credentials.createInsecure());
var warmup_num = 100; var warmup_num = 100;

@ -40,7 +40,7 @@
var _ = require('lodash'); var _ = require('lodash');
var grpc = require('bindings')('grpc.node'); var grpc = require('bindings')('grpc_node');
var common = require('./common'); var common = require('./common');
@ -54,7 +54,7 @@ var Readable = stream.Readable;
var Writable = stream.Writable; var Writable = stream.Writable;
var Duplex = stream.Duplex; var Duplex = stream.Duplex;
var util = require('util'); var util = require('util');
var version = require('../package.json').version; var version = require('../../../package.json').version;
util.inherits(ClientWritableStream, Writable); util.inherits(ClientWritableStream, Writable);
@ -233,17 +233,23 @@ function getCall(channel, method, options) {
var host; var host;
var parent; var parent;
var propagate_flags; var propagate_flags;
var credentials;
if (options) { if (options) {
deadline = options.deadline; deadline = options.deadline;
host = options.host; host = options.host;
parent = _.get(options, 'parent.call'); parent = _.get(options, 'parent.call');
propagate_flags = options.propagate_flags; propagate_flags = options.propagate_flags;
credentials = options.credentials;
} }
if (deadline === undefined) { if (deadline === undefined) {
deadline = Infinity; deadline = Infinity;
} }
return new grpc.Call(channel, method, deadline, host, var call = new grpc.Call(channel, method, deadline, host,
parent, propagate_flags); parent, propagate_flags);
if (credentials) {
call.setCredentials(credentials);
}
return call;
} }
/** /**
@ -282,12 +288,6 @@ function makeUnaryRequestFunction(method, serialize, deserialize) {
emitter.getPeer = function getPeer() { emitter.getPeer = function getPeer() {
return call.getPeer(); return call.getPeer();
}; };
this.$updateMetadata(this.$auth_uri, metadata, function(error, metadata) {
if (error) {
call.cancel();
callback(error);
return;
}
var client_batch = {}; var client_batch = {};
var message = serialize(argument); var message = serialize(argument);
if (options) { if (options) {
@ -336,7 +336,6 @@ function makeUnaryRequestFunction(method, serialize, deserialize) {
emitter.emit('metadata', Metadata._fromCoreRepresentation( emitter.emit('metadata', Metadata._fromCoreRepresentation(
response.metadata)); response.metadata));
}); });
});
return emitter; return emitter;
} }
return makeUnaryRequest; return makeUnaryRequest;
@ -371,12 +370,6 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) {
metadata = metadata.clone(); metadata = metadata.clone();
} }
var stream = new ClientWritableStream(call, serialize); var stream = new ClientWritableStream(call, serialize);
this.$updateMetadata(this.$auth_uri, metadata, function(error, metadata) {
if (error) {
call.cancel();
callback(error);
return;
}
var metadata_batch = {}; var metadata_batch = {};
metadata_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata_batch[grpc.opType.SEND_INITIAL_METADATA] =
metadata._getCoreRepresentation(); metadata._getCoreRepresentation();
@ -427,7 +420,6 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) {
} }
stream.emit('status', status); stream.emit('status', status);
}); });
});
return stream; return stream;
} }
return makeClientStreamRequest; return makeClientStreamRequest;
@ -462,12 +454,6 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) {
metadata = metadata.clone(); metadata = metadata.clone();
} }
var stream = new ClientReadableStream(call, deserialize); var stream = new ClientReadableStream(call, deserialize);
this.$updateMetadata(this.$auth_uri, metadata, function(error, metadata) {
if (error) {
call.cancel();
stream.emit('error', error);
return;
}
var start_batch = {}; var start_batch = {};
var message = serialize(argument); var message = serialize(argument);
if (options) { if (options) {
@ -507,7 +493,6 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) {
} }
} }
}); });
});
return stream; return stream;
} }
return makeServerStreamRequest; return makeServerStreamRequest;
@ -540,12 +525,6 @@ function makeBidiStreamRequestFunction(method, serialize, deserialize) {
metadata = metadata.clone(); metadata = metadata.clone();
} }
var stream = new ClientDuplexStream(call, serialize, deserialize); var stream = new ClientDuplexStream(call, serialize, deserialize);
this.$updateMetadata(this.$auth_uri, metadata, function(error, metadata) {
if (error) {
call.cancel();
stream.emit('error', error);
return;
}
var start_batch = {}; var start_batch = {};
start_batch[grpc.opType.SEND_INITIAL_METADATA] = start_batch[grpc.opType.SEND_INITIAL_METADATA] =
metadata._getCoreRepresentation(); metadata._getCoreRepresentation();
@ -579,7 +558,6 @@ function makeBidiStreamRequestFunction(method, serialize, deserialize) {
} }
} }
}); });
});
return stream; return stream;
} }
return makeBidiStreamRequest; return makeBidiStreamRequest;
@ -618,15 +596,8 @@ exports.makeClientConstructor = function(methods, serviceName) {
* @param {grpc.Credentials} credentials Credentials to use to connect * @param {grpc.Credentials} credentials Credentials to use to connect
* to the server * to the server
* @param {Object} options Options to pass to the underlying channel * @param {Object} options Options to pass to the underlying channel
* @param {function(string, Object, function)=} updateMetadata function to
* update the metadata for each request
*/ */
function Client(address, credentials, options, updateMetadata) { function Client(address, credentials, options) {
if (!updateMetadata) {
updateMetadata = function(uri, metadata, callback) {
callback(null, metadata);
};
}
if (!options) { if (!options) {
options = {}; options = {};
} }
@ -634,11 +605,6 @@ exports.makeClientConstructor = function(methods, serviceName) {
/* Private fields use $ as a prefix instead of _ because it is an invalid /* Private fields use $ as a prefix instead of _ because it is an invalid
* prefix of a method name */ * prefix of a method name */
this.$channel = new grpc.Channel(address, credentials, options); this.$channel = new grpc.Channel(address, credentials, options);
// Remove the optional DNS scheme, trailing port, and trailing backslash
address = address.replace(/^(dns:\/{3})?([^:\/]+)(:\d+)?\/?$/, '$2');
this.$server_address = address;
this.$auth_uri = 'https://' + this.$server_address + '/' + serviceName;
this.$updateMetadata = updateMetadata;
} }
_.each(methods, function(attrs, name) { _.each(methods, function(attrs, name) {

@ -0,0 +1,163 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* Credentials module
*
* This module contains factory methods for two different credential types:
* CallCredentials and ChannelCredentials. ChannelCredentials are things like
* SSL credentials that can be used to secure a connection, and are used to
* construct a Client object. CallCredentials genrally modify metadata, so they
* can be attached to an individual method call.
*
* CallCredentials can be composed with other CallCredentials to create
* CallCredentials. ChannelCredentials can be composed with CallCredentials
* to create ChannelCredentials. No combined credential can have more than
* one ChannelCredentials.
*
* For example, to create a client secured with SSL that uses Google
* default application credentials to authenticate:
*
* var channel_creds = credentials.createSsl(root_certs);
* (new GoogleAuth()).getApplicationDefault(function(err, credential) {
* var call_creds = credentials.createFromGoogleCredential(credential);
* var combined_creds = credentials.combineChannelCredentials(
* channel_creds, call_creds);
* var client = new Client(address, combined_creds);
* });
*
* @module
*/
'use strict';
var grpc = require('bindings')('grpc_node.node');
var CallCredentials = grpc.CallCredentials;
var ChannelCredentials = grpc.ChannelCredentials;
var Metadata = require('./metadata.js');
/**
* Create an SSL Credentials object. If using a client-side certificate, both
* the second and third arguments must be passed.
* @param {Buffer} root_certs The root certificate data
* @param {Buffer=} private_key The client certificate private key, if
* applicable
* @param {Buffer=} cert_chain The client certificate cert chain, if applicable
* @return {ChannelCredentials} The SSL Credentials object
*/
exports.createSsl = ChannelCredentials.createSsl;
/**
* Create a gRPC credentials object from a metadata generation function. This
* function gets the service URL and a callback as parameters. The error
* passed to the callback can optionally have a 'code' value attached to it,
* which corresponds to a status code that this library uses.
* @param {function(String, function(Error, Metadata))} metadata_generator The
* function that generates metadata
* @return {CallCredentials} The credentials object
*/
exports.createFromMetadataGenerator = function(metadata_generator) {
return CallCredentials.createFromPlugin(function(service_url, callback) {
metadata_generator(service_url, function(error, metadata) {
var code = grpc.status.OK;
var message = '';
if (error) {
message = error.message;
if (error.hasOwnProperty('code')) {
code = error.code;
}
}
callback(code, message, metadata._getCoreRepresentation());
});
});
};
/**
* Create a gRPC credential from a Google credential object.
* @param {Object} google_credential The Google credential object to use
* @return {CallCredentials} The resulting credentials object
*/
exports.createFromGoogleCredential = function(google_credential) {
return exports.createFromMetadataGenerator(function(service_url, callback) {
google_credential.getRequestMetadata(service_url, function(err, header) {
if (err) {
callback(err);
return;
}
var metadata = new Metadata();
metadata.add('authorization', header.Authorization);
callback(null, metadata);
});
});
};
/**
* Combine a ChannelCredentials with any number of CallCredentials into a single
* ChannelCredentials object.
* @param {ChannelCredentials} channel_credential The ChannelCredentials to
* start with
* @param {...CallCredentials} credentials The CallCredentials to compose
* @return ChannelCredentials A credentials object that combines all of the
* input credentials
*/
exports.combineChannelCredentials = function(channel_credential) {
var current = channel_credential;
for (var i = 1; i < arguments.length; i++) {
current = current.compose(arguments[i]);
}
return current;
};
/**
* Combine any number of CallCredentials into a single CallCredentials object
* @param {...CallCredentials} credentials the CallCredentials to compose
* @return CallCredentials A credentials object that combines all of the input
* credentials
*/
exports.combineCallCredentials = function() {
var current = arguments[0];
for (var i = 1; i < arguments.length; i++) {
current = current.compose(arguments[i]);
}
return current;
}
/**
* Create an insecure credentials object. This is used to create a channel that
* does not use SSL. This cannot be composed with anything.
* @return {ChannelCredentials} The insecure credentials object
*/
exports.createInsecure = ChannelCredentials.createInsecure;

@ -59,6 +59,7 @@ function normalizeKey(key) {
function validate(key, value) { function validate(key, value) {
if (_.endsWith(key, '-bin')) { if (_.endsWith(key, '-bin')) {
if (!(value instanceof Buffer)) { if (!(value instanceof Buffer)) {
console.log(value.constructor.toString());
throw new Error('keys that end with \'-bin\' must have Buffer values'); throw new Error('keys that end with \'-bin\' must have Buffer values');
} }
} else { } else {
@ -173,7 +174,9 @@ Metadata.prototype._getCoreRepresentation = function() {
Metadata._fromCoreRepresentation = function(metadata) { Metadata._fromCoreRepresentation = function(metadata) {
var newMetadata = new Metadata(); var newMetadata = new Metadata();
if (metadata) { if (metadata) {
newMetadata._internal_repr = _.cloneDeep(metadata); _.forOwn(metadata, function(value, key) {
newMetadata._internal_repr[key] = _.clone(value);
});
} }
return newMetadata; return newMetadata;
}; };

@ -40,7 +40,7 @@
var _ = require('lodash'); var _ = require('lodash');
var grpc = require('bindings')('grpc.node'); var grpc = require('bindings')('grpc_node');
var common = require('./common'); var common = require('./common');

@ -36,7 +36,7 @@
var assert = require('assert'); var assert = require('assert');
var grpc = require('..'); var grpc = require('..');
var math = grpc.load(__dirname + '/../examples/math.proto').math; var math = grpc.load(__dirname + '/math/math.proto').math;
/** /**
@ -47,7 +47,7 @@ var math_client;
/** /**
* Server to test against * Server to test against
*/ */
var getServer = require('../examples/math_server.js'); var getServer = require('./math/math_server.js');
var server = getServer(); var server = getServer();

@ -34,7 +34,7 @@
'use strict'; 'use strict';
var assert = require('assert'); var assert = require('assert');
var grpc = require('bindings')('grpc.node'); var grpc = require('bindings')('grpc_node');
/** /**
* Helper function to return an absolute deadline given a relative timeout in * Helper function to return an absolute deadline given a relative timeout in
@ -48,7 +48,7 @@ function getDeadline(timeout_secs) {
return deadline; return deadline;
} }
var insecureCreds = grpc.Credentials.createInsecure(); var insecureCreds = grpc.ChannelCredentials.createInsecure();
describe('call', function() { describe('call', function() {
var channel; var channel;

@ -34,7 +34,7 @@
'use strict'; 'use strict';
var assert = require('assert'); var assert = require('assert');
var grpc = require('bindings')('grpc.node'); var grpc = require('bindings')('grpc_node');
/** /**
* This is used for testing functions with multiple asynchronous calls that * This is used for testing functions with multiple asynchronous calls that
@ -56,7 +56,7 @@ function multiDone(done, count) {
} }
}; };
} }
var insecureCreds = grpc.Credentials.createInsecure(); var insecureCreds = grpc.ChannelCredentials.createInsecure();
describe('channel', function() { describe('channel', function() {
describe('constructor', function() { describe('constructor', function() {
@ -149,12 +149,13 @@ describe('channel', function() {
afterEach(function() { afterEach(function() {
channel.close(); channel.close();
}); });
it('should time out if called alone', function(done) { it.only('should time out if called alone', function(done) {
var old_state = channel.getConnectivityState(); var old_state = channel.getConnectivityState();
var deadline = new Date(); var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 1); deadline.setSeconds(deadline.getSeconds() + 1);
channel.watchConnectivityState(old_state, deadline, function(err, value) { channel.watchConnectivityState(old_state, deadline, function(err, value) {
assert(err); assert(err);
console.log('Callback from watchConnectivityState');
done(); done();
}); });
}); });

@ -34,7 +34,7 @@
'use strict'; 'use strict';
var assert = require('assert'); var assert = require('assert');
var grpc = require('bindings')('grpc.node'); var grpc = require('bindings')('grpc_node');
/** /**
* List of all status names * List of all status names

@ -0,0 +1,243 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
'use strict';
var assert = require('assert');
var fs = require('fs');
var path = require('path');
var grpc = require('..');
/**
* 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('client credentials', function() {
var Client;
var server;
var port;
var client_ssl_creds;
var client_options = {};
before(function() {
var proto = grpc.load(__dirname + '/test_service.proto');
server = new grpc.Server();
server.addProtoService(proto.TestService.service, {
unary: function(call, cb) {
call.sendMetadata(call.metadata);
cb(null, {});
},
clientStream: function(stream, cb){
stream.on('data', function(data) {});
stream.on('end', function() {
stream.sendMetadata(stream.metadata);
cb(null, {});
});
},
serverStream: function(stream) {
stream.sendMetadata(stream.metadata);
stream.end();
},
bidiStream: function(stream) {
stream.on('data', function(data) {});
stream.on('end', function() {
stream.sendMetadata(stream.metadata);
stream.end();
});
}
});
var key_path = path.join(__dirname, './data/server1.key');
var pem_path = path.join(__dirname, './data/server1.pem');
var key_data = fs.readFileSync(key_path);
var pem_data = fs.readFileSync(pem_path);
var creds = grpc.ServerCredentials.createSsl(null,
[{private_key: key_data,
cert_chain: pem_data}]);
//creds = grpc.ServerCredentials.createInsecure();
port = server.bind('localhost:0', creds);
server.start();
Client = proto.TestService;
var ca_path = path.join(__dirname, '../test/data/ca.pem');
var ca_data = fs.readFileSync(ca_path);
client_ssl_creds = grpc.credentials.createSsl(ca_data);
var host_override = 'foo.test.google.fr';
client_options['grpc.ssl_target_name_override'] = host_override;
client_options['grpc.default_authority'] = host_override;
});
after(function() {
server.forceShutdown();
});
it('Should accept SSL creds for a client', function(done) {
var client = new Client('localhost:' + port, client_ssl_creds,
client_options);
client.unary({}, function(err, data) {
assert.ifError(err);
done();
});
});
it('Should update metadata with SSL creds', function(done) {
var metadataUpdater = function(service_url, callback) {
var metadata = new grpc.Metadata();
metadata.set('plugin_key', 'plugin_value');
callback(null, metadata);
};
var creds = grpc.credentials.createFromMetadataGenerator(metadataUpdater);
var combined_creds = grpc.credentials.combineCredentials(client_ssl_creds,
creds);
var client = new Client('localhost:' + port, combined_creds,
client_options);
var call = client.unary({}, function(err, data) {
assert.ifError(err);
});
call.on('metadata', function(metadata) {
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
done();
});
});
it('Should update metadata for two simultaneous calls', function(done) {
done = multiDone(done, 2);
var metadataUpdater = function(service_url, callback) {
var metadata = new grpc.Metadata();
metadata.set('plugin_key', 'plugin_value');
callback(null, metadata);
};
var creds = grpc.credentials.createFromMetadataGenerator(metadataUpdater);
var combined_creds = grpc.credentials.combineCredentials(client_ssl_creds,
creds);
var client = new Client('localhost:' + port, combined_creds,
client_options);
var call = client.unary({}, function(err, data) {
assert.ifError(err);
});
call.on('metadata', function(metadata) {
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
done();
});
var call2 = client.unary({}, function(err, data) {
assert.ifError(err);
});
call2.on('metadata', function(metadata) {
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
done();
});
});
describe('Per-rpc creds', function() {
var client;
var updater_creds;
before(function() {
client = new Client('localhost:' + port, client_ssl_creds,
client_options);
var metadataUpdater = function(service_url, callback) {
var metadata = new grpc.Metadata();
metadata.set('plugin_key', 'plugin_value');
callback(null, metadata);
};
updater_creds = grpc.credentials.createFromMetadataGenerator(
metadataUpdater);
});
it('Should update metadata on a unary call', function(done) {
var call = client.unary({}, function(err, data) {
assert.ifError(err);
}, null, {credentials: updater_creds});
call.on('metadata', function(metadata) {
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
done();
});
});
it('should update metadata on a client streaming call', function(done) {
var call = client.clientStream(function(err, data) {
assert.ifError(err);
}, null, {credentials: updater_creds});
call.on('metadata', function(metadata) {
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
done();
});
call.end();
});
it('should update metadata on a server streaming call', function(done) {
var call = client.serverStream({}, null, {credentials: updater_creds});
call.on('data', function() {});
call.on('metadata', function(metadata) {
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
done();
});
});
it('should update metadata on a bidi streaming call', function(done) {
var call = client.bidiStream(null, {credentials: updater_creds});
call.on('data', function() {});
call.on('metadata', function(metadata) {
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
done();
});
call.end();
});
it('should be able to use multiple plugin credentials', function(done) {
var altMetadataUpdater = function(service_url, callback) {
var metadata = new grpc.Metadata();
metadata.set('other_plugin_key', 'other_plugin_value');
callback(null, metadata);
};
var alt_updater_creds = grpc.credentials.createFromMetadataGenerator(
altMetadataUpdater);
var combined_updater = grpc.credentials.combineCallCredentials(
updater_creds, alt_updater_creds);
var call = client.unary({}, function(err, data) {
assert.ifError(err);
}, null, {credentials: updater_creds});
call.on('metadata', function(metadata) {
assert.deepEqual(metadata.get('plugin_key'), ['plugin_value']);
assert.deepEqual(metadata.get('other_plugin_key'),
['other_plugin_value']);
done();
});
});
});
});

@ -34,7 +34,7 @@
'use strict'; 'use strict';
var assert = require('assert'); var assert = require('assert');
var grpc = require('bindings')('grpc.node'); var grpc = require('bindings')('grpc_node');
/** /**
* This is used for testing functions with multiple asynchronous calls that * This is used for testing functions with multiple asynchronous calls that
@ -57,7 +57,7 @@ function multiDone(done, count) {
}; };
} }
var insecureCreds = grpc.Credentials.createInsecure(); var insecureCreds = grpc.ChannelCredentials.createInsecure();
describe('end-to-end', function() { describe('end-to-end', function() {
var server; var server;

@ -54,7 +54,7 @@ describe('Health Checking', function() {
grpc.ServerCredentials.createInsecure()); grpc.ServerCredentials.createInsecure());
healthServer.start(); healthServer.start();
healthClient = new health.Client('localhost:' + port_num, healthClient = new health.Client('localhost:' + port_num,
grpc.Credentials.createInsecure()); grpc.credentials.createInsecure());
}); });
after(function() { after(function() {
healthServer.forceShutdown(); healthServer.forceShutdown();

@ -90,4 +90,8 @@ describe('Interop tests', function() {
interop_client.runTest(port, name_override, 'timeout_on_sleeping_server', interop_client.runTest(port, name_override, 'timeout_on_sleeping_server',
true, true, done); true, true, done);
}); });
it('should pass custom_metadata', function(done) {
interop_client.runTest(port, name_override, 'custom_metadata',
true, true, done);
});
}); });

@ -33,7 +33,7 @@
'use strict'; 'use strict';
var grpc = require('..'); var grpc = require('../..');
var math = grpc.load(__dirname + '/math.proto').math; var math = grpc.load(__dirname + '/math.proto').math;
/** /**

@ -36,7 +36,7 @@
var assert = require('assert'); var assert = require('assert');
var grpc = require('..'); var grpc = require('..');
var math = grpc.load(__dirname + '/../examples/math.proto').math; var math = grpc.load(__dirname + '/math/math.proto').math;
/** /**
* Client to use to make requests to a running server. * Client to use to make requests to a running server.
@ -46,7 +46,7 @@ var math_client;
/** /**
* Server to test against * Server to test against
*/ */
var getServer = require('../examples/math_server.js'); var getServer = require('./math/math_server.js');
var server = getServer(); var server = getServer();
@ -56,7 +56,7 @@ describe('Math client', function() {
grpc.ServerCredentials.createInsecure()); grpc.ServerCredentials.createInsecure());
server.start(); server.start();
math_client = new math.Math('localhost:' + port_num, math_client = new math.Math('localhost:' + port_num,
grpc.Credentials.createInsecure()); grpc.credentials.createInsecure());
done(); done();
}); });
after(function() { after(function() {

@ -36,7 +36,7 @@
var assert = require('assert'); var assert = require('assert');
var fs = require('fs'); var fs = require('fs');
var path = require('path'); var path = require('path');
var grpc = require('bindings')('grpc.node'); var grpc = require('bindings')('grpc_node');
describe('server', function() { describe('server', function() {
describe('constructor', function() { describe('constructor', function() {

@ -41,7 +41,7 @@ var ProtoBuf = require('protobufjs');
var grpc = require('..'); var grpc = require('..');
var math_proto = ProtoBuf.loadProtoFile(__dirname + '/../examples/math.proto'); var math_proto = ProtoBuf.loadProtoFile(__dirname + '/math/math.proto');
var mathService = math_proto.lookup('math.Math'); var mathService = math_proto.lookup('math.Math');
@ -163,7 +163,7 @@ describe('waitForClientReady', function() {
Client = surface_client.makeProtobufClientConstructor(mathService); Client = surface_client.makeProtobufClientConstructor(mathService);
}); });
beforeEach(function() { beforeEach(function() {
client = new Client('localhost:' + port, grpc.Credentials.createInsecure()); client = new Client('localhost:' + port, grpc.credentials.createInsecure());
}); });
after(function() { after(function() {
server.forceShutdown(); server.forceShutdown();
@ -217,7 +217,7 @@ describe('Echo service', function() {
}); });
var port = server.bind('localhost:0', server_insecure_creds); var port = server.bind('localhost:0', server_insecure_creds);
var Client = surface_client.makeProtobufClientConstructor(echo_service); var Client = surface_client.makeProtobufClientConstructor(echo_service);
client = new Client('localhost:' + port, grpc.Credentials.createInsecure()); client = new Client('localhost:' + port, grpc.credentials.createInsecure());
server.start(); server.start();
}); });
after(function() { after(function() {
@ -263,7 +263,7 @@ describe('Generic client and server', function() {
server.start(); server.start();
var Client = grpc.makeGenericClientConstructor(string_service_attrs); var Client = grpc.makeGenericClientConstructor(string_service_attrs);
client = new Client('localhost:' + port, client = new Client('localhost:' + port,
grpc.Credentials.createInsecure()); grpc.credentials.createInsecure());
}); });
after(function() { after(function() {
server.forceShutdown(); server.forceShutdown();
@ -311,7 +311,7 @@ describe('Echo metadata', function() {
}); });
var port = server.bind('localhost:0', server_insecure_creds); var port = server.bind('localhost:0', server_insecure_creds);
var Client = surface_client.makeProtobufClientConstructor(test_service); var Client = surface_client.makeProtobufClientConstructor(test_service);
client = new Client('localhost:' + port, grpc.Credentials.createInsecure()); client = new Client('localhost:' + port, grpc.credentials.createInsecure());
server.start(); server.start();
metadata = new grpc.Metadata(); metadata = new grpc.Metadata();
metadata.set('key', 'value'); metadata.set('key', 'value');
@ -356,7 +356,7 @@ describe('Echo metadata', function() {
call.end(); call.end();
}); });
it('shows the correct user-agent string', function(done) { it('shows the correct user-agent string', function(done) {
var version = require('../package.json').version; var version = require('../../../package.json').version;
var call = client.unary({}, function(err, data) { assert.ifError(err); }, var call = client.unary({}, function(err, data) { assert.ifError(err); },
metadata); metadata);
call.on('metadata', function(metadata) { call.on('metadata', function(metadata) {
@ -437,7 +437,7 @@ describe('Other conditions', function() {
}); });
port = server.bind('localhost:0', server_insecure_creds); port = server.bind('localhost:0', server_insecure_creds);
Client = surface_client.makeProtobufClientConstructor(test_service); Client = surface_client.makeProtobufClientConstructor(test_service);
client = new Client('localhost:' + port, grpc.Credentials.createInsecure()); client = new Client('localhost:' + port, grpc.credentials.createInsecure());
server.start(); server.start();
}); });
after(function() { after(function() {
@ -484,7 +484,7 @@ describe('Other conditions', function() {
var Client = surface_client.makeClientConstructor(test_service_attrs, var Client = surface_client.makeClientConstructor(test_service_attrs,
'TestService'); 'TestService');
misbehavingClient = new Client('localhost:' + port, misbehavingClient = new Client('localhost:' + port,
grpc.Credentials.createInsecure()); grpc.credentials.createInsecure());
}); });
it('should respond correctly to a unary call', function(done) { it('should respond correctly to a unary call', function(done) {
misbehavingClient.unary(badArg, function(err, data) { misbehavingClient.unary(badArg, function(err, data) {
@ -711,7 +711,7 @@ describe('Call propagation', function() {
}); });
var port = server.bind('localhost:0', server_insecure_creds); var port = server.bind('localhost:0', server_insecure_creds);
Client = surface_client.makeProtobufClientConstructor(test_service); Client = surface_client.makeProtobufClientConstructor(test_service);
client = new Client('localhost:' + port, grpc.Credentials.createInsecure()); client = new Client('localhost:' + port, grpc.credentials.createInsecure());
server.start(); server.start();
}); });
after(function() { after(function() {
@ -748,7 +748,7 @@ describe('Call propagation', function() {
var proxy_port = proxy.bind('localhost:0', server_insecure_creds); var proxy_port = proxy.bind('localhost:0', server_insecure_creds);
proxy.start(); proxy.start();
var proxy_client = new Client('localhost:' + proxy_port, var proxy_client = new Client('localhost:' + proxy_port,
grpc.Credentials.createInsecure()); grpc.credentials.createInsecure());
var call = proxy_client.unary({}, function(err, value) { var call = proxy_client.unary({}, function(err, value) {
done(); done();
}); });
@ -771,7 +771,7 @@ describe('Call propagation', function() {
var proxy_port = proxy.bind('localhost:0', server_insecure_creds); var proxy_port = proxy.bind('localhost:0', server_insecure_creds);
proxy.start(); proxy.start();
var proxy_client = new Client('localhost:' + proxy_port, var proxy_client = new Client('localhost:' + proxy_port,
grpc.Credentials.createInsecure()); grpc.credentials.createInsecure());
var call = proxy_client.clientStream(function(err, value) { var call = proxy_client.clientStream(function(err, value) {
done(); done();
}); });
@ -792,7 +792,7 @@ describe('Call propagation', function() {
var proxy_port = proxy.bind('localhost:0', server_insecure_creds); var proxy_port = proxy.bind('localhost:0', server_insecure_creds);
proxy.start(); proxy.start();
var proxy_client = new Client('localhost:' + proxy_port, var proxy_client = new Client('localhost:' + proxy_port,
grpc.Credentials.createInsecure()); grpc.credentials.createInsecure());
var call = proxy_client.serverStream({}); var call = proxy_client.serverStream({});
call.on('error', function(err) { call.on('error', function(err) {
done(); done();
@ -813,7 +813,7 @@ describe('Call propagation', function() {
var proxy_port = proxy.bind('localhost:0', server_insecure_creds); var proxy_port = proxy.bind('localhost:0', server_insecure_creds);
proxy.start(); proxy.start();
var proxy_client = new Client('localhost:' + proxy_port, var proxy_client = new Client('localhost:' + proxy_port,
grpc.Credentials.createInsecure()); grpc.credentials.createInsecure());
var call = proxy_client.bidiStream(); var call = proxy_client.bidiStream();
call.on('error', function(err) { call.on('error', function(err) {
done(); done();
@ -842,7 +842,7 @@ describe('Call propagation', function() {
var proxy_port = proxy.bind('localhost:0', server_insecure_creds); var proxy_port = proxy.bind('localhost:0', server_insecure_creds);
proxy.start(); proxy.start();
var proxy_client = new Client('localhost:' + proxy_port, var proxy_client = new Client('localhost:' + proxy_port,
grpc.Credentials.createInsecure()); grpc.credentials.createInsecure());
var deadline = new Date(); var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 1); deadline.setSeconds(deadline.getSeconds() + 1);
proxy_client.clientStream(function(err, value) { proxy_client.clientStream(function(err, value) {
@ -865,7 +865,7 @@ describe('Call propagation', function() {
var proxy_port = proxy.bind('localhost:0', server_insecure_creds); var proxy_port = proxy.bind('localhost:0', server_insecure_creds);
proxy.start(); proxy.start();
var proxy_client = new Client('localhost:' + proxy_port, var proxy_client = new Client('localhost:' + proxy_port,
grpc.Credentials.createInsecure()); grpc.credentials.createInsecure());
var deadline = new Date(); var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 1); deadline.setSeconds(deadline.getSeconds() + 1);
var call = proxy_client.bidiStream(null, {deadline: deadline}); var call = proxy_client.bidiStream(null, {deadline: deadline});
@ -888,7 +888,7 @@ describe('Cancelling surface client', function() {
}); });
var port = server.bind('localhost:0', server_insecure_creds); var port = server.bind('localhost:0', server_insecure_creds);
var Client = surface_client.makeProtobufClientConstructor(mathService); var Client = surface_client.makeProtobufClientConstructor(mathService);
client = new Client('localhost:' + port, grpc.Credentials.createInsecure()); client = new Client('localhost:' + port, grpc.credentials.createInsecure());
server.start(); server.start();
}); });
after(function() { after(function() {

@ -109,91 +109,120 @@ PHP_MINIT_FUNCTION(grpc) {
*/ */
/* Register call error constants */ /* Register call error constants */
grpc_init(); grpc_init();
REGISTER_LONG_CONSTANT("Grpc\\CALL_OK", GRPC_CALL_OK, CONST_CS); REGISTER_LONG_CONSTANT("Grpc\\CALL_OK", GRPC_CALL_OK,
REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR", GRPC_CALL_ERROR, CONST_CS); CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR", GRPC_CALL_ERROR,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_ON_SERVER", REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_ON_SERVER",
GRPC_CALL_ERROR_NOT_ON_SERVER, CONST_CS); GRPC_CALL_ERROR_NOT_ON_SERVER,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_ON_CLIENT", REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_ON_CLIENT",
GRPC_CALL_ERROR_NOT_ON_CLIENT, CONST_CS); GRPC_CALL_ERROR_NOT_ON_CLIENT,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_ALREADY_INVOKED", REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_ALREADY_INVOKED",
GRPC_CALL_ERROR_ALREADY_INVOKED, CONST_CS); GRPC_CALL_ERROR_ALREADY_INVOKED,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_INVOKED", REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_INVOKED",
GRPC_CALL_ERROR_NOT_INVOKED, CONST_CS); GRPC_CALL_ERROR_NOT_INVOKED,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_ALREADY_FINISHED", REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_ALREADY_FINISHED",
GRPC_CALL_ERROR_ALREADY_FINISHED, CONST_CS); GRPC_CALL_ERROR_ALREADY_FINISHED,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_TOO_MANY_OPERATIONS", REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_TOO_MANY_OPERATIONS",
GRPC_CALL_ERROR_TOO_MANY_OPERATIONS, CONST_CS); GRPC_CALL_ERROR_TOO_MANY_OPERATIONS,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_INVALID_FLAGS", REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_INVALID_FLAGS",
GRPC_CALL_ERROR_INVALID_FLAGS, CONST_CS); GRPC_CALL_ERROR_INVALID_FLAGS,
CONST_CS | CONST_PERSISTENT);
/* Register flag constants */ /* Register flag constants */
REGISTER_LONG_CONSTANT("Grpc\\WRITE_BUFFER_HINT", GRPC_WRITE_BUFFER_HINT, REGISTER_LONG_CONSTANT("Grpc\\WRITE_BUFFER_HINT", GRPC_WRITE_BUFFER_HINT,
CONST_CS); CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\WRITE_NO_COMPRESS", GRPC_WRITE_NO_COMPRESS, REGISTER_LONG_CONSTANT("Grpc\\WRITE_NO_COMPRESS", GRPC_WRITE_NO_COMPRESS,
CONST_CS); CONST_CS | CONST_PERSISTENT);
/* Register status constants */ /* Register status constants */
REGISTER_LONG_CONSTANT("Grpc\\STATUS_OK", GRPC_STATUS_OK, CONST_CS); REGISTER_LONG_CONSTANT("Grpc\\STATUS_OK", GRPC_STATUS_OK,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_CANCELLED", GRPC_STATUS_CANCELLED, REGISTER_LONG_CONSTANT("Grpc\\STATUS_CANCELLED", GRPC_STATUS_CANCELLED,
CONST_CS); CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNKNOWN", GRPC_STATUS_UNKNOWN, CONST_CS); REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNKNOWN", GRPC_STATUS_UNKNOWN,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_INVALID_ARGUMENT", REGISTER_LONG_CONSTANT("Grpc\\STATUS_INVALID_ARGUMENT",
GRPC_STATUS_INVALID_ARGUMENT, CONST_CS); GRPC_STATUS_INVALID_ARGUMENT,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_DEADLINE_EXCEEDED", REGISTER_LONG_CONSTANT("Grpc\\STATUS_DEADLINE_EXCEEDED",
GRPC_STATUS_DEADLINE_EXCEEDED, CONST_CS); GRPC_STATUS_DEADLINE_EXCEEDED,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_NOT_FOUND", GRPC_STATUS_NOT_FOUND, REGISTER_LONG_CONSTANT("Grpc\\STATUS_NOT_FOUND", GRPC_STATUS_NOT_FOUND,
CONST_CS); CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_ALREADY_EXISTS", REGISTER_LONG_CONSTANT("Grpc\\STATUS_ALREADY_EXISTS",
GRPC_STATUS_ALREADY_EXISTS, CONST_CS); GRPC_STATUS_ALREADY_EXISTS,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_PERMISSION_DENIED", REGISTER_LONG_CONSTANT("Grpc\\STATUS_PERMISSION_DENIED",
GRPC_STATUS_PERMISSION_DENIED, CONST_CS); GRPC_STATUS_PERMISSION_DENIED,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNAUTHENTICATED", REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNAUTHENTICATED",
GRPC_STATUS_UNAUTHENTICATED, CONST_CS); GRPC_STATUS_UNAUTHENTICATED,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_RESOURCE_EXHAUSTED", REGISTER_LONG_CONSTANT("Grpc\\STATUS_RESOURCE_EXHAUSTED",
GRPC_STATUS_RESOURCE_EXHAUSTED, CONST_CS); GRPC_STATUS_RESOURCE_EXHAUSTED,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_FAILED_PRECONDITION", REGISTER_LONG_CONSTANT("Grpc\\STATUS_FAILED_PRECONDITION",
GRPC_STATUS_FAILED_PRECONDITION, CONST_CS); GRPC_STATUS_FAILED_PRECONDITION,
REGISTER_LONG_CONSTANT("Grpc\\STATUS_ABORTED", GRPC_STATUS_ABORTED, CONST_CS); CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_ABORTED", GRPC_STATUS_ABORTED,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_OUT_OF_RANGE", GRPC_STATUS_OUT_OF_RANGE, REGISTER_LONG_CONSTANT("Grpc\\STATUS_OUT_OF_RANGE", GRPC_STATUS_OUT_OF_RANGE,
CONST_CS); CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNIMPLEMENTED", REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNIMPLEMENTED",
GRPC_STATUS_UNIMPLEMENTED, CONST_CS); GRPC_STATUS_UNIMPLEMENTED,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_INTERNAL", GRPC_STATUS_INTERNAL, REGISTER_LONG_CONSTANT("Grpc\\STATUS_INTERNAL", GRPC_STATUS_INTERNAL,
CONST_CS); CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNAVAILABLE", GRPC_STATUS_UNAVAILABLE, REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNAVAILABLE", GRPC_STATUS_UNAVAILABLE,
CONST_CS); CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\STATUS_DATA_LOSS", GRPC_STATUS_DATA_LOSS, REGISTER_LONG_CONSTANT("Grpc\\STATUS_DATA_LOSS", GRPC_STATUS_DATA_LOSS,
CONST_CS); CONST_CS | CONST_PERSISTENT);
/* Register op type constants */ /* Register op type constants */
REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_INITIAL_METADATA", REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_INITIAL_METADATA",
GRPC_OP_SEND_INITIAL_METADATA, CONST_CS); GRPC_OP_SEND_INITIAL_METADATA,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_MESSAGE", REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_MESSAGE",
GRPC_OP_SEND_MESSAGE, CONST_CS); GRPC_OP_SEND_MESSAGE,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_CLOSE_FROM_CLIENT", REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_CLOSE_FROM_CLIENT",
GRPC_OP_SEND_CLOSE_FROM_CLIENT, CONST_CS); GRPC_OP_SEND_CLOSE_FROM_CLIENT,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_STATUS_FROM_SERVER", REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_STATUS_FROM_SERVER",
GRPC_OP_SEND_STATUS_FROM_SERVER, CONST_CS); GRPC_OP_SEND_STATUS_FROM_SERVER,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_INITIAL_METADATA", REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_INITIAL_METADATA",
GRPC_OP_RECV_INITIAL_METADATA, CONST_CS); GRPC_OP_RECV_INITIAL_METADATA,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_MESSAGE", REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_MESSAGE",
GRPC_OP_RECV_MESSAGE, CONST_CS); GRPC_OP_RECV_MESSAGE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_STATUS_ON_CLIENT", REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_STATUS_ON_CLIENT",
GRPC_OP_RECV_STATUS_ON_CLIENT, CONST_CS); GRPC_OP_RECV_STATUS_ON_CLIENT,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_CLOSE_ON_SERVER", REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_CLOSE_ON_SERVER",
GRPC_OP_RECV_CLOSE_ON_SERVER, CONST_CS); GRPC_OP_RECV_CLOSE_ON_SERVER,
CONST_CS | CONST_PERSISTENT);
/* Register connectivity state constants */ /* Register connectivity state constants */
REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_IDLE", REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_IDLE",
GRPC_CHANNEL_IDLE, CONST_CS); GRPC_CHANNEL_IDLE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_CONNECTING", REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_CONNECTING",
GRPC_CHANNEL_CONNECTING, CONST_CS); GRPC_CHANNEL_CONNECTING, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_READY", REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_READY",
GRPC_CHANNEL_READY, CONST_CS); GRPC_CHANNEL_READY, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_TRANSIENT_FAILURE", REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_TRANSIENT_FAILURE",
GRPC_CHANNEL_TRANSIENT_FAILURE, CONST_CS); GRPC_CHANNEL_TRANSIENT_FAILURE,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_FATAL_FAILURE", REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_FATAL_FAILURE",
GRPC_CHANNEL_FATAL_FAILURE, CONST_CS); GRPC_CHANNEL_FATAL_FAILURE,
CONST_CS | CONST_PERSISTENT);
grpc_init_call(TSRMLS_C); grpc_init_call(TSRMLS_C);
grpc_init_channel(TSRMLS_C); grpc_init_channel(TSRMLS_C);

@ -80,12 +80,17 @@
'-ldl', '-ldl',
'-lpthread', '-lpthread',
'-lz' '-lz'
] ],
'direct_dependent_settings': {
'include_dirs': [
'.',
'include'
],
}
}, },
'targets': [ 'targets': [
% for lib in libs: % for lib in libs:
# TODO: Add C++ targets % if lib.name == 'gpr' or lib.name == 'grpc':
% if lib.language == 'c':
{ {
'target_name': '${lib.name}', 'target_name': '${lib.name}',
'product_prefix': 'lib', 'product_prefix': 'lib',
@ -103,23 +108,5 @@
}, },
% endif % endif
% endfor % endfor
% for tgt in targets:
% if tgt.language == 'c':
{
'target_name': '${tgt.name}',
'type': 'executable',
'dependencies': [
% for dep in getattr(tgt, 'deps', []):
'${dep}',
% endfor
],
'sources': [
% for source in tgt.src:
'${source}',
% endfor
]
},
% endif
% endfor
] ]
} }

@ -49,7 +49,8 @@ static grpc_pollset g_pollset;
static int g_number_of_reads = 0; static int g_number_of_reads = 0;
static int g_number_of_bytes_read = 0; static int g_number_of_bytes_read = 0;
static void on_read(grpc_fd *emfd, grpc_server *server) { static void on_read(grpc_exec_ctx *exec_ctx, grpc_fd *emfd,
grpc_server *server) {
char read_buffer[512]; char read_buffer[512];
ssize_t byte_count; ssize_t byte_count;

@ -37,8 +37,13 @@ cd `dirname $0`/../..
git_root=`pwd` git_root=`pwd`
cd - cd -
# Ensure existence of ccache directory
mkdir -p /tmp/ccache mkdir -p /tmp/ccache
# Ensure existence of the home directory for XDG caches (e.g. what pip uses for
# its cache location now that --download-cache is deprecated).
mkdir -p /tmp/xdg-cache-home
# Create a local branch so the child Docker script won't complain # Create a local branch so the child Docker script won't complain
git branch -f jenkins-docker git branch -f jenkins-docker
@ -57,9 +62,11 @@ docker run \
-e "config=$config" \ -e "config=$config" \
-e "arch=$arch" \ -e "arch=$arch" \
-e CCACHE_DIR=/tmp/ccache \ -e CCACHE_DIR=/tmp/ccache \
-e XDG_CACHE_HOME=/tmp/xdg-cache-home \
-i $TTY_FLAG \ -i $TTY_FLAG \
-v "$git_root:/var/local/jenkins/grpc" \ -v "$git_root:/var/local/jenkins/grpc" \
-v /tmp/ccache:/tmp/ccache \ -v /tmp/ccache:/tmp/ccache \
-v /tmp/xdg-cache-home:/tmp/xdg-cache-home \
-v /var/run/docker.sock:/var/run/docker.sock \ -v /var/run/docker.sock:/var/run/docker.sock \
-v $(which docker):/bin/docker \ -v $(which docker):/bin/docker \
-w /var/local/git/grpc \ -w /var/local/git/grpc \

@ -36,6 +36,10 @@ set -e
export CONFIG=$config export CONFIG=$config
export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-3.5 export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-3.5
# Ensure that programs depending on current-user-ownership of cache directories
# are satisfied (it's being mounted from outside the image).
chown `whoami` $XDG_CACHE_HOME
mkdir -p /var/local/git mkdir -p /var/local/git
git clone --recursive /var/local/jenkins/grpc /var/local/git/grpc git clone --recursive /var/local/jenkins/grpc /var/local/git/grpc

@ -126,10 +126,11 @@ RUN /bin/bash -l -c "gem install bundler --no-ri --no-rdoc"
RUN apt-get update && apt-get install -y \ RUN apt-get update && apt-get install -y \
python-all-dev \ python-all-dev \
python3-all-dev \ python3-all-dev \
python-pip \ python-pip
python-virtualenv
# Install Python packages from PyPI # Install Python packages from PyPI
RUN pip install pip --upgrade
RUN pip install virtualenv
RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.0.0a2 RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.0.0a2
# For sanity test # For sanity test

@ -36,9 +36,4 @@ CONFIG=${CONFIG:-opt}
# change to grpc repo root # change to grpc repo root
cd $(dirname $0)/../.. cd $(dirname $0)/../..
export CXXFLAGS=-I`pwd`/include
export LDFLAGS=-L`pwd`/libs/$CONFIG
cd src/node
npm install --unsafe-perm npm install --unsafe-perm

@ -39,6 +39,33 @@ GRPCIO=$ROOT/src/python/grpcio
GRPCIO_TEST=$ROOT/src/python/grpcio_test GRPCIO_TEST=$ROOT/src/python/grpcio_test
GRPCIO_HEALTH_CHECKING=$ROOT/src/python/grpcio_health_checking GRPCIO_HEALTH_CHECKING=$ROOT/src/python/grpcio_health_checking
install_grpcio_deps() {
cd $GRPCIO
pip install -r requirements.txt
}
install_grpcio_test_deps() {
cd $GRPCIO_TEST
pip install -r requirements.txt
}
install_grpcio() {
CFLAGS="-I$ROOT/include -std=c89" LDFLAGS=-L$ROOT/libs/$CONFIG GRPC_PYTHON_BUILD_WITH_CYTHON=1 pip install $GRPCIO
}
install_grpcio_test() {
pip install $GRPCIO_TEST
}
install_grpcio_health_checking() {
pip install $GRPCIO_HEALTH_CHECKING
}
# Cleans the environment of previous installations
clean_grpcio_all() {
(yes | pip uninstall grpcio) || true
(yes | pip uninstall grpcio_test) || true
(yes | pip uninstall grpcio_health_checking) || true
}
# Builds the testing environment.
make_virtualenv() { make_virtualenv() {
virtualenv_name="python"$1"_virtual_environment" virtualenv_name="python"$1"_virtual_environment"
if [ ! -d $virtualenv_name ] if [ ! -d $virtualenv_name ]
@ -48,33 +75,29 @@ make_virtualenv() {
source $virtualenv_name/bin/activate source $virtualenv_name/bin/activate
# Install grpcio # Install grpcio
cd $GRPCIO install_grpcio_deps
pip install -r requirements.txt install_grpcio
CFLAGS="-I$ROOT/include -std=c89" LDFLAGS=-L$ROOT/libs/$CONFIG GRPC_PYTHON_BUILD_WITH_CYTHON=1 pip install $GRPCIO
# Install grpcio_test # Install grpcio_test
cd $GRPCIO_TEST install_grpcio_test_deps
pip install -r requirements.txt install_grpcio_test
pip install $GRPCIO_TEST
# Install grpcio_health_checking # Install grpcio_health_checking
pip install $GRPCIO_HEALTH_CHECKING install_grpcio_health_checking
else else
source $virtualenv_name/bin/activate source $virtualenv_name/bin/activate
# Uninstall and re-install the packages we care about. Don't use # Uninstall and re-install the packages we care about. Don't use
# --force-reinstall or --ignore-installed to avoid propagating this # --force-reinstall or --ignore-installed to avoid propagating this
# unnecessarily to dependencies. Don't use --no-deps to avoid missing # unnecessarily to dependencies. Don't use --no-deps to avoid missing
# dependency upgrades. # dependency upgrades.
(yes | pip uninstall grpcio) || true clean_grpcio_all
(yes | pip uninstall grpcio_test) || true install_grpcio || (
(yes | pip uninstall grpcio_health_checking) || true
(CFLAGS="-I$ROOT/include -std=c89" LDFLAGS=-L$ROOT/libs/$CONFIG GRPC_PYTHON_BUILD_WITH_CYTHON=1 pip install $GRPCIO) || (
# Fall back to rebuilding the entire environment # Fall back to rebuilding the entire environment
rm -rf $virtualenv_name rm -rf $virtualenv_name
make_virtualenv $1 make_virtualenv $1
) )
pip install $GRPCIO_TEST install_grpcio_test
pip install $GRPCIO_HEALTH_CHECKING install_grpcio_health_checking
fi fi
} }

@ -38,6 +38,18 @@ import socket
import sys import sys
import time import time
# increment this number whenever making a change to ensure that
# the changes are picked up by running CI servers
# note that all changes must be backwards compatible
_MY_VERSION = 2
if len(sys.argv) == 2 and sys.argv[1] == 'dump_version':
print _MY_VERSION
sys.exit(0)
argp = argparse.ArgumentParser(description='Server for httpcli_test') argp = argparse.ArgumentParser(description='Server for httpcli_test')
argp.add_argument('-p', '--port', default=12345, type=int) argp.add_argument('-p', '--port', default=12345, type=int)
args = argp.parse_args() args = argp.parse_args()
@ -47,9 +59,6 @@ print 'port server running on port %d' % args.port
pool = [] pool = []
in_use = {} in_use = {}
with open(__file__) as f:
_MY_VERSION = hashlib.sha1(f.read()).hexdigest()
def refill_pool(max_timeout, req): def refill_pool(max_timeout, req):
"""Scan for ports not marked for being in use""" """Scan for ports not marked for being in use"""
@ -113,7 +122,7 @@ class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
del in_use[p] del in_use[p]
pool.append(p) pool.append(p)
self.log_message('drop port %d' % p) self.log_message('drop port %d' % p)
elif self.path == '/version': elif self.path == '/version_number':
# fetch a version string and the current process pid # fetch a version string and the current process pid
self.send_response(200) self.send_response(200)
self.send_header('Content-Type', 'text/plain') self.send_header('Content-Type', 'text/plain')
@ -128,7 +137,7 @@ class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
self.end_headers() self.end_headers()
now = time.time() now = time.time()
self.wfile.write(yaml.dump({'pool': pool, 'in_use': dict((k, now - v) for k, v in in_use.iteritems())})) self.wfile.write(yaml.dump({'pool': pool, 'in_use': dict((k, now - v) for k, v in in_use.iteritems())}))
elif self.path == '/quit': elif self.path == '/quitquitquit':
self.send_response(200) self.send_response(200)
self.end_headers() self.end_headers()
keep_running = False keep_running = False

@ -37,19 +37,15 @@ cd $(dirname $0)/../..
root=`pwd` root=`pwd`
cd $root/src/node
export LD_LIBRARY_PATH=$root/libs/$CONFIG
if [ "$CONFIG" = "gcov" ] if [ "$CONFIG" = "gcov" ]
then then
./node_modules/.bin/istanbul cover --dir ../../reports/node_coverage \ ./node_modules/.bin/istanbul cover --dir reports/node_coverage \
./node_modules/.bin/_mocha -- --timeout 8000 ./node_modules/.bin/_mocha -- --timeout 8000 src/node/test
cd build cd build
gcov Release/obj.target/grpc/ext/*.o gcov Release/obj.target/grpc/ext/*.o
lcov --base-directory . --directory . -c -o coverage.info lcov --base-directory . --directory . -c -o coverage.info
genhtml -o ../../../reports/node_ext_coverage --num-spaces 2 \ genhtml -o ../reports/node_ext_coverage --num-spaces 2 \
-t 'Node gRPC test coverage' coverage.info -t 'Node gRPC test coverage' coverage.info
else else
./node_modules/mocha/bin/mocha --timeout 8000 ./node_modules/mocha/bin/mocha --timeout 8000 src/node/test
fi fi

@ -40,4 +40,4 @@ export DYLD_LIBRARY_PATH=$ROOT/libs/$CONFIG
export PATH=$ROOT/bins/$CONFIG:$ROOT/bins/$CONFIG/protobuf:$PATH export PATH=$ROOT/bins/$CONFIG:$ROOT/bins/$CONFIG/protobuf:$PATH
source "python"$PYVER"_virtual_environment"/bin/activate source "python"$PYVER"_virtual_environment"/bin/activate
"python"$PYVER $GRPCIO_TEST/setup.py test -a "-n8 --cov=grpc --junitxml=./report.xml --timeout=300" "python"$PYVER $GRPCIO_TEST/setup.py test -a "-n8 --cov=grpc --junitxml=./report.xml --timeout=300 -v --boxed --timeout_method=thread"

@ -181,45 +181,6 @@ class CLanguage(object):
def __str__(self): def __str__(self):
return self.make_target return self.make_target
def gyp_test_paths(travis, config=None):
binaries = get_c_tests(travis, 'c')
out = []
for target in binaries:
if config is not None and config.build_config in target['exclude_configs']:
continue
binary = 'out/Debug/%s' % target['name']
out.append(binary)
return sorted(out)
class GYPCLanguage(object):
def test_specs(self, config, travis):
return [config.job_spec([binary], [binary])
for binary in gyp_test_paths(travis, config)]
def pre_build_steps(self):
return [['gyp', '--depth=.', '--suffix=-gyp', 'grpc.gyp']]
def make_targets(self):
# HACK(ctiller): force fling_client and fling_server to be built, as fling_test
# needs these
return gyp_test_paths(False) + ['fling_client', 'fling_server']
def build_steps(self):
return []
def makefile_name(self):
return 'Makefile-gyp'
def supports_multi_config(self):
return False
def __str__(self):
return 'gyp'
class NodeLanguage(object): class NodeLanguage(object):
def test_specs(self, config, travis): def test_specs(self, config, travis):
@ -230,7 +191,7 @@ class NodeLanguage(object):
return [] return []
def make_targets(self): def make_targets(self):
return ['static_c', 'shared_c'] return []
def build_steps(self): def build_steps(self):
return [['tools/run_tests/build_node.sh']] return [['tools/run_tests/build_node.sh']]
@ -483,7 +444,6 @@ _DEFAULT = ['opt']
_LANGUAGES = { _LANGUAGES = {
'c++': CLanguage('cxx', 'c++'), 'c++': CLanguage('cxx', 'c++'),
'c': CLanguage('c', 'c'), 'c': CLanguage('c', 'c'),
'gyp': GYPCLanguage(),
'node': NodeLanguage(), 'node': NodeLanguage(),
'php': PhpLanguage(), 'php': PhpLanguage(),
'python': PythonLanguage(), 'python': PythonLanguage(),
@ -624,7 +584,9 @@ if platform.system() == 'Windows':
def make_jobspec(cfg, targets, makefile='Makefile'): def make_jobspec(cfg, targets, makefile='Makefile'):
extra_args = [] extra_args = []
# better do parallel compilation # better do parallel compilation
extra_args.extend(["/m"]) # empirically /m:2 gives the best performance/price and should prevent
# overloading the windows workers.
extra_args.extend(["/m:2"])
# disable PDB generation: it's broken, and we don't need it during CI # disable PDB generation: it's broken, and we don't need it during CI
extra_args.extend(["/p:Jenkins=true"]) extra_args.extend(["/p:Jenkins=true"])
return [ return [
@ -658,7 +620,7 @@ if make_targets:
make_commands = itertools.chain.from_iterable(make_jobspec(cfg, list(targets), makefile) for cfg in build_configs for (makefile, targets) in make_targets.iteritems()) make_commands = itertools.chain.from_iterable(make_jobspec(cfg, list(targets), makefile) for cfg in build_configs for (makefile, targets) in make_targets.iteritems())
build_steps.extend(set(make_commands)) build_steps.extend(set(make_commands))
build_steps.extend(set( build_steps.extend(set(
jobset.JobSpec(cmdline, environ={'CONFIG': cfg}) jobset.JobSpec(cmdline, environ={'CONFIG': cfg}, timeout_seconds=10*60)
for cfg in build_configs for cfg in build_configs
for l in languages for l in languages
for cmdline in l.build_steps())) for cmdline in l.build_steps()))
@ -713,20 +675,23 @@ def _start_port_server(port_server_port):
# if not running ==> start a new one # if not running ==> start a new one
# otherwise, leave it up # otherwise, leave it up
try: try:
version = urllib2.urlopen('http://localhost:%d/version' % port_server_port, version = int(urllib2.urlopen(
timeout=1).read() 'http://localhost:%d/version_number' % port_server_port,
print 'detected port server running' timeout=1).read())
print 'detected port server running version %d' % version
running = True running = True
except Exception: except Exception as e:
print 'failed to detect port server: %s' % sys.exc_info()[0] print 'failed to detect port server: %s' % sys.exc_info()[0]
print e.strerror
running = False running = False
if running: if running:
with open('tools/run_tests/port_server.py') as f: current_version = int(subprocess.check_output(
current_version = hashlib.sha1(f.read()).hexdigest() [sys.executable, 'tools/run_tests/port_server.py', 'dump_version']))
running = (version == current_version) print 'my port server is version %d' % current_version
running = (version >= current_version)
if not running: if not running:
print 'port_server version mismatch: killing the old one' print 'port_server version mismatch: killing the old one'
urllib2.urlopen('http://localhost:%d/quit' % port_server_port).read() urllib2.urlopen('http://localhost:%d/quitquitquit' % port_server_port).read()
time.sleep(1) time.sleep(1)
if not running: if not running:
print 'starting port_server' print 'starting port_server'
@ -773,7 +738,7 @@ def _build_and_run(
# start antagonists # start antagonists
antagonists = [subprocess.Popen(['tools/run_tests/antagonist.py']) antagonists = [subprocess.Popen(['tools/run_tests/antagonist.py'])
for _ in range(0, args.antagonists)] for _ in range(0, args.antagonists)]
port_server_port = 9999 port_server_port = 32767
_start_port_server(port_server_port) _start_port_server(port_server_port)
try: try:
infinite_runs = runs_per_test == 0 infinite_runs = runs_per_test == 0

Loading…
Cancel
Save