aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/node
diff options
context:
space:
mode:
authorGravatar Craig Tiller <ctiller@google.com>2015-10-08 14:53:19 -0700
committerGravatar Craig Tiller <ctiller@google.com>2015-10-08 14:53:19 -0700
commitb37391ef45c330812d0e5e7a4720253facc8d524 (patch)
tree65c36a4bb2b7a58f99638ace3c5ef518a7dfc043 /src/node
parentdd25694733cbb82e7ce42efcedab2ff21bcf6477 (diff)
parent9f06aba55c7c07d21055f3620e48885566f9e47b (diff)
Merge github.com:grpc/grpc into latent-see
Diffstat (limited to 'src/node')
-rw-r--r--src/node/.istanbul.yml6
-rw-r--r--src/node/LICENSE28
-rw-r--r--src/node/README.md36
-rw-r--r--src/node/bin/README.md16
-rwxr-xr-xsrc/node/bin/service_packager2
-rw-r--r--src/node/binding.gyp100
-rw-r--r--src/node/cli/service_packager.js142
-rw-r--r--src/node/cli/service_packager/index.js36
-rw-r--r--src/node/cli/service_packager/package.json.template17
-rw-r--r--src/node/examples/stock.proto62
-rw-r--r--src/node/examples/stock_client.js47
-rw-r--r--src/node/examples/stock_server.js87
-rw-r--r--src/node/ext/call.cc29
-rw-r--r--src/node/ext/call.h5
-rw-r--r--src/node/ext/call_credentials.cc259
-rw-r--r--src/node/ext/call_credentials.h100
-rw-r--r--src/node/ext/channel.cc8
-rw-r--r--src/node/ext/channel_credentials.cc (renamed from src/node/ext/credentials.cc)106
-rw-r--r--src/node/ext/channel_credentials.h (renamed from src/node/ext/credentials.h)23
-rw-r--r--src/node/ext/node_grpc.cc8
-rw-r--r--src/node/index.js31
-rw-r--r--src/node/interop/empty.proto43
-rw-r--r--src/node/interop/interop_client.js230
-rw-r--r--src/node/interop/interop_server.js52
-rw-r--r--src/node/interop/messages.proto132
-rw-r--r--src/node/interop/test.proto72
-rw-r--r--src/node/package.json64
-rw-r--r--src/node/performance/perf_test.js (renamed from src/node/examples/perf_test.js)2
-rw-r--r--src/node/performance/qps_test.js (renamed from src/node/examples/qps_test.js)2
-rw-r--r--src/node/src/client.js352
-rw-r--r--src/node/src/credentials.js163
-rw-r--r--src/node/src/metadata.js5
-rw-r--r--src/node/src/server.js2
-rw-r--r--src/node/test/async_test.js4
-rw-r--r--src/node/test/call_test.js4
-rw-r--r--src/node/test/channel_test.js7
-rw-r--r--src/node/test/constant_test.js2
-rw-r--r--src/node/test/credentials_test.js243
-rw-r--r--src/node/test/end_to_end_test.js4
-rw-r--r--src/node/test/health_test.js2
-rw-r--r--src/node/test/interop_sanity_test.js4
-rw-r--r--src/node/test/math/math.proto (renamed from src/node/examples/math.proto)0
-rw-r--r--src/node/test/math/math_server.js (renamed from src/node/examples/math_server.js)2
-rw-r--r--src/node/test/math_client_test.js6
-rw-r--r--src/node/test/server_test.js2
-rw-r--r--src/node/test/surface_test.js32
46 files changed, 1285 insertions, 1294 deletions
diff --git a/src/node/.istanbul.yml b/src/node/.istanbul.yml
deleted file mode 100644
index 9ff1379f51..0000000000
--- a/src/node/.istanbul.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-reporting:
- watermarks:
- statements: [80, 95]
- lines: [80, 95]
- functions: [80, 95]
- branches: [80, 95]
diff --git a/src/node/LICENSE b/src/node/LICENSE
deleted file mode 100644
index 0209b570e1..0000000000
--- a/src/node/LICENSE
+++ /dev/null
@@ -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.
diff --git a/src/node/README.md b/src/node/README.md
index 7719d08290..5d89e2228d 100644
--- a/src/node/README.md
+++ b/src/node/README.md
@@ -5,51 +5,19 @@ Beta
## 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.
-- [homebrew][] on Mac OS X. These simplify the installation of the gRPC C core.
## 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
```sh
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
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`.
-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
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.
-
-[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/
diff --git a/src/node/bin/README.md b/src/node/bin/README.md
deleted file mode 100644
index 2f856e428e..0000000000
--- a/src/node/bin/README.md
+++ /dev/null
@@ -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') }
-```
diff --git a/src/node/bin/service_packager b/src/node/bin/service_packager
deleted file mode 100755
index c7f2460997..0000000000
--- a/src/node/bin/service_packager
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/usr/bin/env node
-require(__dirname+'/../cli/service_packager.js').main(process.argv.slice(2)); \ No newline at end of file
diff --git a/src/node/binding.gyp b/src/node/binding.gyp
deleted file mode 100644
index 247719e981..0000000000
--- a/src/node/binding.gyp
+++ /dev/null
@@ -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"
- ]
- }
- ]
-}
diff --git a/src/node/cli/service_packager.js b/src/node/cli/service_packager.js
deleted file mode 100644
index c92c450a01..0000000000
--- a/src/node/cli/service_packager.js
+++ /dev/null
@@ -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;
diff --git a/src/node/cli/service_packager/index.js b/src/node/cli/service_packager/index.js
deleted file mode 100644
index 811e08b89a..0000000000
--- a/src/node/cli/service_packager/index.js
+++ /dev/null
@@ -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');
diff --git a/src/node/cli/service_packager/package.json.template b/src/node/cli/service_packager/package.json.template
deleted file mode 100644
index 9f9019172e..0000000000
--- a/src/node/cli/service_packager/package.json.template
+++ /dev/null
@@ -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"
- ]
-}
diff --git a/src/node/examples/stock.proto b/src/node/examples/stock.proto
deleted file mode 100644
index 5ee2bcbce6..0000000000
--- a/src/node/examples/stock.proto
+++ /dev/null
@@ -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) {
- }
-
-}
diff --git a/src/node/examples/stock_client.js b/src/node/examples/stock_client.js
deleted file mode 100644
index ab9b050e9b..0000000000
--- a/src/node/examples/stock_client.js
+++ /dev/null
@@ -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;
diff --git a/src/node/examples/stock_server.js b/src/node/examples/stock_server.js
deleted file mode 100644
index 12e5479584..0000000000
--- a/src/node/examples/stock_server.js
+++ /dev/null
@@ -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;
diff --git a/src/node/ext/call.cc b/src/node/ext/call.cc
index b08a9f96d8..b63e294f9a 100644
--- a/src/node/ext/call.cc
+++ b/src/node/ext/call.cc
@@ -39,12 +39,14 @@
#include "grpc/support/log.h"
#include "grpc/grpc.h"
+#include "grpc/grpc_security.h"
#include "grpc/support/alloc.h"
#include "grpc/support/time.h"
#include "byte_buffer.h"
#include "call.h"
#include "channel.h"
#include "completion_queue_async_worker.h"
+#include "call_credentials.h"
#include "timeval.h"
using std::unique_ptr;
@@ -168,8 +170,9 @@ Local<Value> ParseMetadata(const grpc_metadata_array *metadata_array) {
}
if (EndsWith(elem->key, "-bin")) {
Nan::Set(array, index_map[elem->key],
- Nan::CopyBuffer(elem->value,
- elem->value_length).ToLocalChecked());
+ MakeFastBuffer(
+ Nan::CopyBuffer(elem->value,
+ elem->value_length).ToLocalChecked()));
} else {
Nan::Set(array, index_map[elem->key],
Nan::New(elem->value).ToLocalChecked());
@@ -501,6 +504,7 @@ void Call::Init(Local<Object> exports) {
Nan::SetPrototypeMethod(tpl, "cancel", Cancel);
Nan::SetPrototypeMethod(tpl, "cancelWithStatus", CancelWithStatus);
Nan::SetPrototypeMethod(tpl, "getPeer", GetPeer);
+ Nan::SetPrototypeMethod(tpl, "setCredentials", SetCredentials);
fun_tpl.Reset(tpl);
Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(exports, Nan::New("Call").ToLocalChecked(), ctr);
@@ -724,5 +728,26 @@ NAN_METHOD(Call::GetPeer) {
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 grpc
diff --git a/src/node/ext/call.h b/src/node/ext/call.h
index 2f8e1f17aa..dd6c38e4f8 100644
--- a/src/node/ext/call.h
+++ b/src/node/ext/call.h
@@ -73,6 +73,10 @@ struct Resources {
std::vector<unique_ptr<PersistentValue> > handles;
};
+bool CreateMetadataArray(v8::Local<v8::Object> metadata,
+ grpc_metadata_array *array,
+ shared_ptr<Resources> resources);
+
class Op {
public:
virtual v8::Local<v8::Value> GetNodeValue() const = 0;
@@ -122,6 +126,7 @@ class Call : public Nan::ObjectWrap {
static NAN_METHOD(Cancel);
static NAN_METHOD(CancelWithStatus);
static NAN_METHOD(GetPeer);
+ static NAN_METHOD(SetCredentials);
static Nan::Callback *constructor;
// Used for typechecking instances of this javascript class
static Nan::Persistent<v8::FunctionTemplate> fun_tpl;
diff --git a/src/node/ext/call_credentials.cc b/src/node/ext/call_credentials.cc
new file mode 100644
index 0000000000..839bb567e4
--- /dev/null
+++ b/src/node/ext/call_credentials.cc
@@ -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
diff --git a/src/node/ext/call_credentials.h b/src/node/ext/call_credentials.h
new file mode 100644
index 0000000000..618292d19e
--- /dev/null
+++ b/src/node/ext/call_credentials.h
@@ -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_
diff --git a/src/node/ext/channel.cc b/src/node/ext/channel.cc
index 6eb1e77688..a328c01713 100644
--- a/src/node/ext/channel.cc
+++ b/src/node/ext/channel.cc
@@ -42,7 +42,7 @@
#include "call.h"
#include "channel.h"
#include "completion_queue_async_worker.h"
-#include "credentials.h"
+#include "channel_credentials.h"
#include "timeval.h"
namespace grpc {
@@ -112,11 +112,11 @@ NAN_METHOD(Channel::New) {
// Owned by the Channel object
Utf8String host(info[0]);
grpc_credentials *creds;
- if (!Credentials::HasInstance(info[1])) {
+ if (!ChannelCredentials::HasInstance(info[1])) {
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());
creds = creds_object->GetWrappedCredentials();
grpc_channel_args *channel_args_ptr;
diff --git a/src/node/ext/credentials.cc b/src/node/ext/channel_credentials.cc
index 4f41c92f6a..3d47ff293d 100644
--- a/src/node/ext/credentials.cc
+++ b/src/node/ext/channel_credentials.cc
@@ -36,7 +36,9 @@
#include "grpc/grpc.h"
#include "grpc/grpc_security.h"
#include "grpc/support/log.h"
-#include "credentials.h"
+#include "channel_credentials.h"
+#include "call_credentials.h"
+#include "call.h"
namespace grpc {
namespace node {
@@ -60,51 +62,40 @@ using v8::Object;
using v8::ObjectTemplate;
using v8::Value;
-Nan::Callback *Credentials::constructor;
-Persistent<FunctionTemplate> Credentials::fun_tpl;
+Nan::Callback *ChannelCredentials::constructor;
+Persistent<FunctionTemplate> ChannelCredentials::fun_tpl;
-Credentials::Credentials(grpc_credentials *credentials)
+ChannelCredentials::ChannelCredentials(grpc_credentials *credentials)
: wrapped_credentials(credentials) {}
-Credentials::~Credentials() {
+ChannelCredentials::~ChannelCredentials() {
grpc_credentials_release(wrapped_credentials);
}
-void Credentials::Init(Local<Object> exports) {
+void ChannelCredentials::Init(Local<Object> exports) {
HandleScope scope;
Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
- tpl->SetClassName(Nan::New("Credentials").ToLocalChecked());
+ tpl->SetClassName(Nan::New("ChannelCredentials").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("createDefault").ToLocalChecked(),
- Nan::GetFunction(
- Nan::New<FunctionTemplate>(CreateDefault)).ToLocalChecked());
Nan::Set(ctr, Nan::New("createSsl").ToLocalChecked(),
Nan::GetFunction(
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::GetFunction(
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);
}
-bool Credentials::HasInstance(Local<Value> val) {
+bool ChannelCredentials::HasInstance(Local<Value> val) {
HandleScope scope;
return Nan::New(fun_tpl)->HasInstance(val);
}
-Local<Value> Credentials::WrapStruct(grpc_credentials *credentials) {
+Local<Value> ChannelCredentials::WrapStruct(grpc_credentials *credentials) {
EscapableHandleScope scope;
const int argc = 1;
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;
}
-NAN_METHOD(Credentials::New) {
+NAN_METHOD(ChannelCredentials::New) {
if (info.IsConstructCall()) {
if (!info[0]->IsExternal()) {
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>();
grpc_credentials *creds_value =
reinterpret_cast<grpc_credentials *>(ext->Value());
- Credentials *credentials = new Credentials(creds_value);
+ ChannelCredentials *credentials = new ChannelCredentials(creds_value);
credentials->Wrap(info.This());
info.GetReturnValue().Set(info.This());
return;
@@ -149,16 +140,7 @@ NAN_METHOD(Credentials::New) {
}
}
-NAN_METHOD(Credentials::CreateDefault) {
- grpc_credentials *creds = grpc_google_default_credentials_create();
- if (creds == NULL) {
- info.GetReturnValue().SetNull();
- } else {
- info.GetReturnValue().Set(WrapStruct(creds));
- }
-}
-
-NAN_METHOD(Credentials::CreateSsl) {
+NAN_METHOD(ChannelCredentials::CreateSsl) {
char *root_certs = NULL;
grpc_ssl_pem_key_cert_pair key_cert_pair = {NULL, NULL};
if (::node::Buffer::HasInstance(info[0])) {
@@ -188,49 +170,25 @@ NAN_METHOD(Credentials::CreateSsl) {
}
}
-NAN_METHOD(Credentials::CreateComposite) {
- if (!HasInstance(info[0])) {
+NAN_METHOD(ChannelCredentials::Compose) {
+ if (!ChannelCredentials::HasInstance(info.This())) {
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(
- "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());
- Credentials *creds2 = ObjectWrap::Unwrap<Credentials>(
- Nan::To<Object>(info[1]).ToLocalChecked());
grpc_credentials *creds = grpc_composite_credentials_create(
- creds1->wrapped_credentials, creds2->wrapped_credentials, 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);
+ self->wrapped_credentials, other->GetWrappedCredentials(), NULL);
if (creds == NULL) {
info.GetReturnValue().SetNull();
} else {
@@ -238,7 +196,7 @@ NAN_METHOD(Credentials::CreateIam) {
}
}
-NAN_METHOD(Credentials::CreateInsecure) {
+NAN_METHOD(ChannelCredentials::CreateInsecure) {
info.GetReturnValue().Set(WrapStruct(NULL));
}
diff --git a/src/node/ext/credentials.h b/src/node/ext/channel_credentials.h
index 1b211175d4..31ea0987bc 100644
--- a/src/node/ext/credentials.h
+++ b/src/node/ext/channel_credentials.h
@@ -31,8 +31,8 @@
*
*/
-#ifndef NET_GRPC_NODE_CREDENTIALS_H_
-#define NET_GRPC_NODE_CREDENTIALS_H_
+#ifndef NET_GRPC_NODE_CHANNEL_CREDENTIALS_H_
+#define NET_GRPC_NODE_CHANNEL_CREDENTIALS_H_
#include <node.h>
#include <nan.h>
@@ -43,7 +43,7 @@ namespace grpc {
namespace node {
/* Wrapper class for grpc_credentials structs */
-class Credentials : public Nan::ObjectWrap {
+class ChannelCredentials : public Nan::ObjectWrap {
public:
static void Init(v8::Local<v8::Object> exports);
static bool HasInstance(v8::Local<v8::Value> val);
@@ -54,21 +54,18 @@ class Credentials : public Nan::ObjectWrap {
grpc_credentials *GetWrappedCredentials();
private:
- explicit Credentials(grpc_credentials *credentials);
- ~Credentials();
+ explicit ChannelCredentials(grpc_credentials *credentials);
+ ~ChannelCredentials();
// Prevent copying
- Credentials(const Credentials &);
- Credentials &operator=(const Credentials &);
+ ChannelCredentials(const ChannelCredentials &);
+ ChannelCredentials &operator=(const ChannelCredentials &);
static NAN_METHOD(New);
- static NAN_METHOD(CreateDefault);
static NAN_METHOD(CreateSsl);
- static NAN_METHOD(CreateComposite);
- static NAN_METHOD(CreateGce);
- static NAN_METHOD(CreateFake);
- static NAN_METHOD(CreateIam);
static NAN_METHOD(CreateInsecure);
+
+ static NAN_METHOD(Compose);
static Nan::Callback *constructor;
// Used for typechecking instances of this javascript class
static Nan::Persistent<v8::FunctionTemplate> fun_tpl;
@@ -79,4 +76,4 @@ class Credentials : public Nan::ObjectWrap {
} // namespace node
} // namespace grpc
-#endif // NET_GRPC_NODE_CREDENTIALS_H_
+#endif // NET_GRPC_NODE_CHANNEL_CREDENTIALS_H_
diff --git a/src/node/ext/node_grpc.cc b/src/node/ext/node_grpc.cc
index caca0fc452..5b5f3c1c5b 100644
--- a/src/node/ext/node_grpc.cc
+++ b/src/node/ext/node_grpc.cc
@@ -37,10 +37,11 @@
#include "grpc/grpc.h"
#include "call.h"
+#include "call_credentials.h"
#include "channel.h"
+#include "channel_credentials.h"
#include "server.h"
#include "completion_queue_async_worker.h"
-#include "credentials.h"
#include "server_credentials.h"
using v8::Local;
@@ -240,11 +241,12 @@ void init(Local<Object> exports) {
InitWriteFlags(exports);
grpc::node::Call::Init(exports);
+ grpc::node::CallCredentials::Init(exports);
grpc::node::Channel::Init(exports);
+ grpc::node::ChannelCredentials::Init(exports);
grpc::node::Server::Init(exports);
grpc::node::CompletionQueueAsyncWorker::Init(exports);
- grpc::node::Credentials::Init(exports);
grpc::node::ServerCredentials::Init(exports);
}
-NODE_MODULE(grpc, init)
+NODE_MODULE(grpc_node, init)
diff --git a/src/node/index.js b/src/node/index.js
index 02b73f66ee..591d9dd915 100644
--- a/src/node/index.js
+++ b/src/node/index.js
@@ -43,7 +43,7 @@ var server = require('./src/server.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.
@@ -90,37 +90,10 @@ exports.load = function load(filename, format) {
default:
throw new Error('Unrecognized format "' + format + '"');
}
-
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
*/
exports.Server = server.Server;
@@ -153,7 +126,7 @@ exports.writeFlags = grpc.writeFlags;
/**
* Credentials factories
*/
-exports.Credentials = grpc.Credentials;
+exports.credentials = require('./src/credentials.js');
/**
* ServerCredentials factories
diff --git a/src/node/interop/empty.proto b/src/node/interop/empty.proto
deleted file mode 100644
index 6d0eb937d6..0000000000
--- a/src/node/interop/empty.proto
+++ /dev/null
@@ -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 {}
diff --git a/src/node/interop/interop_client.js b/src/node/interop/interop_client.js
index 215d42121c..14cc6c0efe 100644
--- a/src/node/interop/interop_client.js
+++ b/src/node/interop/interop_client.js
@@ -37,7 +37,9 @@ var fs = require('fs');
var path = require('path');
var _ = require('lodash');
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 assert = require('assert');
@@ -49,6 +51,9 @@ var AUTH_USER = ('155450119199-vefjjaekcc6cmsd5914v6lqufunmh9ue' +
var COMPUTE_ENGINE_USER = ('155450119199-r5aaqa2vqoa9g5mv2m6s3m1l293rlmel' +
'@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
* @param {number} size The length of the buffer
@@ -61,6 +66,27 @@ function zeroBuffer(size) {
}
/**
+ * 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
* @param {Client} client The client to test against
* @param {function} done Callback to call when the test is completed. Included
@@ -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.
* @param {string} expected_user The expected username in the response
@@ -280,33 +354,26 @@ function timeoutOnSleepingServer(client, done) {
* primarily for use with mocha
*/
function authTest(expected_user, scope, client, done) {
- (new GoogleAuth()).getApplicationDefault(function(err, credential) {
+ var arg = {
+ response_type: 'COMPRESSABLE',
+ response_size: 314159,
+ payload: {
+ body: zeroBuffer(271828)
+ },
+ fill_username: true,
+ fill_oauth_scope: true
+ };
+ client.unaryCall(arg, function(err, resp) {
assert.ifError(err);
- if (credential.createScopedRequired() && scope) {
- credential = credential.createScoped(scope);
+ assert.strictEqual(resp.payload.type, 'COMPRESSABLE');
+ assert.strictEqual(resp.payload.body.length, 314159);
+ assert.strictEqual(resp.username, expected_user);
+ if (scope) {
+ assert.strictEqual(resp.oauth_scope, AUTH_SCOPE_RESPONSE);
+ }
+ if (done) {
+ done();
}
- client.$updateMetadata = grpc.getGoogleAuthDelegate(credential);
- var arg = {
- response_type: 'COMPRESSABLE',
- response_size: 314159,
- payload: {
- body: zeroBuffer(271828)
- },
- fill_username: true,
- fill_oauth_scope: true
- };
- client.unaryCall(arg, function(err, resp) {
- assert.ifError(err);
- assert.strictEqual(resp.payload.type, 'COMPRESSABLE');
- assert.strictEqual(resp.payload.body.length, 314159);
- assert.strictEqual(resp.username, expected_user);
- if (scope) {
- assert.strictEqual(resp.oauth_scope, AUTH_SCOPE_RESPONSE);
- }
- if (done) {
- 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
*/
var test_cases = {
- empty_unary: emptyUnary,
- large_unary: largeUnary,
- client_streaming: clientStreaming,
- server_streaming: serverStreaming,
- ping_pong: pingPong,
- empty_stream: emptyStream,
- cancel_after_begin: cancelAfterBegin,
- cancel_after_first_response: cancelAfterFirstResponse,
- timeout_on_sleeping_server: timeoutOnSleepingServer,
- compute_engine_creds: _.partial(authTest, COMPUTE_ENGINE_USER, null),
- service_account_creds: _.partial(authTest, AUTH_USER, AUTH_SCOPE),
- jwt_token_creds: _.partial(authTest, AUTH_USER, null),
- oauth2_auth_token: _.partial(oauth2Test, AUTH_USER, AUTH_SCOPE, false),
- per_rpc_creds: _.partial(oauth2Test, AUTH_USER, AUTH_SCOPE, true)
+ empty_unary: {run: emptyUnary},
+ large_unary: {run: largeUnary},
+ client_streaming: {run: clientStreaming},
+ server_streaming: {run: serverStreaming},
+ ping_pong: {run: pingPong},
+ empty_stream: {run: emptyStream},
+ cancel_after_begin: {run: cancelAfterBegin},
+ cancel_after_first_response: {run: cancelAfterFirstResponse},
+ timeout_on_sleeping_server: {run: timeoutOnSleepingServer},
+ custom_metadata: {run: customMetadata},
+ compute_engine_creds: {run: _.partial(authTest, COMPUTE_ENGINE_USER, null),
+ getCreds: _.partial(getApplicationCreds, null)},
+ service_account_creds: {run: _.partial(authTest, AUTH_USER, AUTH_SCOPE),
+ 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;
}
var ca_data = fs.readFileSync(ca_path);
- creds = grpc.Credentials.createSsl(ca_data);
+ creds = grpc.credentials.createSsl(ca_data);
if (host_override) {
options['grpc.ssl_target_name_override'] = host_override;
options['grpc.default_authority'] = host_override;
}
} else {
- creds = grpc.Credentials.createInsecure();
+ creds = grpc.credentials.createInsecure();
}
- var client = new testProto.TestService(address, creds, options);
+ var test = test_cases[test_case];
- test_cases[test_case](client, done);
+ var execute = function(err, creds) {
+ assert.ifError(err);
+ var client = new testProto.TestService(address, creds, options);
+ test.run(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) {
diff --git a/src/node/interop/interop_server.js b/src/node/interop/interop_server.js
index 99155e9958..3e83304faa 100644
--- a/src/node/interop/interop_server.js
+++ b/src/node/interop/interop_server.js
@@ -37,7 +37,12 @@ var fs = require('fs');
var path = require('path');
var _ = require('lodash');
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
@@ -51,6 +56,34 @@ function zeroBuffer(size) {
}
/**
+ * 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.
* NOTE: this currently does not work due to issue #137
* @param {Call} call Call to handle
@@ -58,7 +91,8 @@ function zeroBuffer(size) {
* or error
*/
function handleEmpty(call, callback) {
- callback(null, {});
+ echoHeader(call);
+ callback(null, {}, getEchoTrailer(call));
}
/**
@@ -68,6 +102,7 @@ function handleEmpty(call, callback) {
* error
*/
function handleUnary(call, callback) {
+ echoHeader(call);
var req = call.request;
var zeros = zeroBuffer(req.response_size);
var payload_type = req.response_type;
@@ -75,7 +110,8 @@ function handleUnary(call, callback) {
payload_type = ['COMPRESSABLE',
'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
*/
function handleStreamingInput(call, callback) {
+ echoHeader(call);
var aggregate_size = 0;
call.on('data', function(value) {
aggregate_size += value.payload.body.length;
});
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
*/
function handleStreamingOutput(call) {
+ echoHeader(call);
var req = call.request;
var payload_type = req.response_type;
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
*/
function handleFullDuplex(call) {
+ echoHeader(call);
call.on('data', function(value) {
var payload_type = value.response_type;
if (payload_type === 'RANDOM') {
@@ -138,7 +178,7 @@ function handleFullDuplex(call) {
});
});
call.on('end', function() {
- call.end();
+ call.end(getEchoTrailer(call));
});
}
diff --git a/src/node/interop/messages.proto b/src/node/interop/messages.proto
deleted file mode 100644
index 7df85e3c13..0000000000
--- a/src/node/interop/messages.proto
+++ /dev/null
@@ -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;
-}
diff --git a/src/node/interop/test.proto b/src/node/interop/test.proto
deleted file mode 100644
index 24e67497fa..0000000000
--- a/src/node/interop/test.proto
+++ /dev/null
@@ -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);
-}
diff --git a/src/node/package.json b/src/node/package.json
deleted file mode 100644
index 0aeca5f659..0000000000
--- a/src/node/package.json
+++ /dev/null
@@ -1,64 +0,0 @@
-{
- "name": "grpc",
- "version": "0.11.1",
- "author": "Google Inc.",
- "description": "gRPC Library for Node",
- "homepage": "http://www.grpc.io/",
- "repository": {
- "type": "git",
- "url": "https://github.com/grpc/grpc.git"
- },
- "bugs": "https://github.com/grpc/grpc/issues",
- "contributors": [
- {
- "name": "Michael Lumish",
- "email": "mlumish@google.com"
- }
- ],
- "directories": {
- "lib": "src",
- "example": "examples"
- },
- "scripts": {
- "lint": "node ./node_modules/jshint/bin/jshint src test examples interop index.js",
- "test": "./node_modules/.bin/mocha && npm run-script lint",
- "gen_docs": "./node_modules/.bin/jsdoc -c jsdoc_conf.json",
- "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha"
- },
- "dependencies": {
- "bindings": "^1.2.0",
- "lodash": "^3.9.3",
- "nan": "^2.0.0",
- "protobufjs": "^4.0.0"
- },
- "devDependencies": {
- "async": "^0.9.0",
- "google-auth-library": "^0.9.2",
- "istanbul": "^0.3.21",
- "jsdoc": "^3.3.2",
- "jshint": "^2.5.0",
- "minimist": "^1.1.0",
- "mocha": "~1.21.0",
- "mustache": "^2.0.0",
- "strftime": "^0.8.2"
- },
- "engines": {
- "node": ">=0.10.13"
- },
- "files": [
- "LICENSE",
- "README.md",
- "index.js",
- "binding.gyp",
- "bin",
- "cli",
- "examples",
- "ext",
- "health_check",
- "interop",
- "src",
- "test"
- ],
- "main": "index.js",
- "license": "BSD-3-Clause"
-}
diff --git a/src/node/examples/perf_test.js b/src/node/performance/perf_test.js
index ba8fbf88d2..fe51e4a603 100644
--- a/src/node/examples/perf_test.js
+++ b/src/node/performance/perf_test.js
@@ -42,7 +42,7 @@ function runTest(iterations, callback) {
var testServer = interop_server.getServer(0, false);
testServer.server.start();
var client = new testProto.TestService('localhost:' + testServer.port,
- grpc.Credentials.createInsecure());
+ grpc.credentials.createInsecure());
function runIterations(finish) {
var start = process.hrtime();
diff --git a/src/node/examples/qps_test.js b/src/node/performance/qps_test.js
index ec968b8540..491f47364c 100644
--- a/src/node/examples/qps_test.js
+++ b/src/node/performance/qps_test.js
@@ -62,7 +62,7 @@ function runTest(concurrent_calls, seconds, callback) {
var testServer = interop_server.getServer(0, false);
testServer.server.start();
var client = new testProto.TestService('localhost:' + testServer.port,
- grpc.Credentials.createInsecure());
+ grpc.credentials.createInsecure());
var warmup_num = 100;
diff --git a/src/node/src/client.js b/src/node/src/client.js
index 7f510231b3..909376e766 100644
--- a/src/node/src/client.js
+++ b/src/node/src/client.js
@@ -40,7 +40,7 @@
var _ = require('lodash');
-var grpc = require('bindings')('grpc.node');
+var grpc = require('bindings')('grpc_node');
var common = require('./common');
@@ -54,7 +54,7 @@ var Readable = stream.Readable;
var Writable = stream.Writable;
var Duplex = stream.Duplex;
var util = require('util');
-var version = require('../package.json').version;
+var version = require('../../../package.json').version;
util.inherits(ClientWritableStream, Writable);
@@ -233,17 +233,23 @@ function getCall(channel, method, options) {
var host;
var parent;
var propagate_flags;
+ var credentials;
if (options) {
deadline = options.deadline;
host = options.host;
parent = _.get(options, 'parent.call');
propagate_flags = options.propagate_flags;
+ credentials = options.credentials;
}
if (deadline === undefined) {
deadline = Infinity;
}
- return new grpc.Call(channel, method, deadline, host,
- parent, propagate_flags);
+ var call = new grpc.Call(channel, method, deadline, host,
+ parent, propagate_flags);
+ if (credentials) {
+ call.setCredentials(credentials);
+ }
+ return call;
}
/**
@@ -282,60 +288,53 @@ function makeUnaryRequestFunction(method, serialize, deserialize) {
emitter.getPeer = function getPeer() {
return call.getPeer();
};
- this.$updateMetadata(this.$auth_uri, metadata, function(error, metadata) {
- if (error) {
- call.cancel();
- callback(error);
- return;
- }
- var client_batch = {};
- var message = serialize(argument);
- if (options) {
- message.grpcWriteFlags = options.flags;
- }
- client_batch[grpc.opType.SEND_INITIAL_METADATA] =
- metadata._getCoreRepresentation();
- client_batch[grpc.opType.SEND_MESSAGE] = message;
- client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
- client_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
- client_batch[grpc.opType.RECV_MESSAGE] = true;
- client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
- call.startBatch(client_batch, function(err, response) {
- response.status.metadata = Metadata._fromCoreRepresentation(
- response.status.metadata);
- var status = response.status;
- var error;
- var deserialized;
- if (status.code === grpc.status.OK) {
- if (err) {
- // Got a batch error, but OK status. Something went wrong
- callback(err);
- return;
- } else {
- try {
- deserialized = deserialize(response.read);
- } catch (e) {
- /* Change status to indicate bad server response. This will result
- * in passing an error to the callback */
- status = {
- code: grpc.status.INTERNAL,
- details: 'Failed to parse server response'
- };
- }
- }
- }
- if (status.code !== grpc.status.OK) {
- error = new Error(response.status.details);
- error.code = status.code;
- error.metadata = status.metadata;
- callback(error);
+ var client_batch = {};
+ var message = serialize(argument);
+ if (options) {
+ message.grpcWriteFlags = options.flags;
+ }
+ client_batch[grpc.opType.SEND_INITIAL_METADATA] =
+ metadata._getCoreRepresentation();
+ client_batch[grpc.opType.SEND_MESSAGE] = message;
+ client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
+ client_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
+ client_batch[grpc.opType.RECV_MESSAGE] = true;
+ client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
+ call.startBatch(client_batch, function(err, response) {
+ response.status.metadata = Metadata._fromCoreRepresentation(
+ response.status.metadata);
+ var status = response.status;
+ var error;
+ var deserialized;
+ if (status.code === grpc.status.OK) {
+ if (err) {
+ // Got a batch error, but OK status. Something went wrong
+ callback(err);
+ return;
} else {
- callback(null, deserialized);
+ try {
+ deserialized = deserialize(response.read);
+ } catch (e) {
+ /* Change status to indicate bad server response. This will result
+ * in passing an error to the callback */
+ status = {
+ code: grpc.status.INTERNAL,
+ details: 'Failed to parse server response'
+ };
+ }
}
- emitter.emit('status', status);
- emitter.emit('metadata', Metadata._fromCoreRepresentation(
- response.metadata));
- });
+ }
+ if (status.code !== grpc.status.OK) {
+ error = new Error(response.status.details);
+ error.code = status.code;
+ error.metadata = status.metadata;
+ callback(error);
+ } else {
+ callback(null, deserialized);
+ }
+ emitter.emit('status', status);
+ emitter.emit('metadata', Metadata._fromCoreRepresentation(
+ response.metadata));
});
return emitter;
}
@@ -371,62 +370,55 @@ function makeClientStreamRequestFunction(method, serialize, deserialize) {
metadata = metadata.clone();
}
var stream = new ClientWritableStream(call, serialize);
- this.$updateMetadata(this.$auth_uri, metadata, function(error, metadata) {
- if (error) {
- call.cancel();
- callback(error);
+ var metadata_batch = {};
+ metadata_batch[grpc.opType.SEND_INITIAL_METADATA] =
+ metadata._getCoreRepresentation();
+ metadata_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
+ call.startBatch(metadata_batch, function(err, response) {
+ if (err) {
+ // The call has stopped for some reason. A non-OK status will arrive
+ // in the other batch.
return;
}
- var metadata_batch = {};
- metadata_batch[grpc.opType.SEND_INITIAL_METADATA] =
- metadata._getCoreRepresentation();
- metadata_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
- call.startBatch(metadata_batch, function(err, response) {
+ stream.emit('metadata', Metadata._fromCoreRepresentation(
+ response.metadata));
+ });
+ var client_batch = {};
+ client_batch[grpc.opType.RECV_MESSAGE] = true;
+ client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
+ call.startBatch(client_batch, function(err, response) {
+ response.status.metadata = Metadata._fromCoreRepresentation(
+ response.status.metadata);
+ var status = response.status;
+ var error;
+ var deserialized;
+ if (status.code === grpc.status.OK) {
if (err) {
- // The call has stopped for some reason. A non-OK status will arrive
- // in the other batch.
+ // Got a batch error, but OK status. Something went wrong
+ callback(err);
return;
- }
- stream.emit('metadata', Metadata._fromCoreRepresentation(
- response.metadata));
- });
- var client_batch = {};
- client_batch[grpc.opType.RECV_MESSAGE] = true;
- client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
- call.startBatch(client_batch, function(err, response) {
- response.status.metadata = Metadata._fromCoreRepresentation(
- response.status.metadata);
- var status = response.status;
- var error;
- var deserialized;
- if (status.code === grpc.status.OK) {
- if (err) {
- // Got a batch error, but OK status. Something went wrong
- callback(err);
- return;
- } else {
- try {
- deserialized = deserialize(response.read);
- } catch (e) {
- /* Change status to indicate bad server response. This will result
- * in passing an error to the callback */
- status = {
- code: grpc.status.INTERNAL,
- details: 'Failed to parse server response'
- };
- }
- }
- }
- if (status.code !== grpc.status.OK) {
- error = new Error(response.status.details);
- error.code = status.code;
- error.metadata = status.metadata;
- callback(error);
} else {
- callback(null, deserialized);
+ try {
+ deserialized = deserialize(response.read);
+ } catch (e) {
+ /* Change status to indicate bad server response. This will result
+ * in passing an error to the callback */
+ status = {
+ code: grpc.status.INTERNAL,
+ details: 'Failed to parse server response'
+ };
+ }
}
- stream.emit('status', status);
- });
+ }
+ if (status.code !== grpc.status.OK) {
+ error = new Error(response.status.details);
+ error.code = status.code;
+ error.metadata = status.metadata;
+ callback(error);
+ } else {
+ callback(null, deserialized);
+ }
+ stream.emit('status', status);
});
return stream;
}
@@ -462,51 +454,44 @@ function makeServerStreamRequestFunction(method, serialize, deserialize) {
metadata = metadata.clone();
}
var stream = new ClientReadableStream(call, deserialize);
- this.$updateMetadata(this.$auth_uri, metadata, function(error, metadata) {
- if (error) {
- call.cancel();
- stream.emit('error', error);
+ var start_batch = {};
+ var message = serialize(argument);
+ if (options) {
+ message.grpcWriteFlags = options.flags;
+ }
+ start_batch[grpc.opType.SEND_INITIAL_METADATA] =
+ metadata._getCoreRepresentation();
+ start_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
+ start_batch[grpc.opType.SEND_MESSAGE] = message;
+ start_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
+ call.startBatch(start_batch, function(err, response) {
+ if (err) {
+ // The call has stopped for some reason. A non-OK status will arrive
+ // in the other batch.
return;
}
- var start_batch = {};
- var message = serialize(argument);
- if (options) {
- message.grpcWriteFlags = options.flags;
- }
- start_batch[grpc.opType.SEND_INITIAL_METADATA] =
- metadata._getCoreRepresentation();
- start_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
- start_batch[grpc.opType.SEND_MESSAGE] = message;
- start_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true;
- call.startBatch(start_batch, function(err, response) {
+ stream.emit('metadata', Metadata._fromCoreRepresentation(
+ response.metadata));
+ });
+ var status_batch = {};
+ status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
+ call.startBatch(status_batch, function(err, response) {
+ response.status.metadata = Metadata._fromCoreRepresentation(
+ response.status.metadata);
+ stream.emit('status', response.status);
+ if (response.status.code !== grpc.status.OK) {
+ var error = new Error(response.status.details);
+ error.code = response.status.code;
+ error.metadata = response.status.metadata;
+ stream.emit('error', error);
+ return;
+ } else {
if (err) {
- // The call has stopped for some reason. A non-OK status will arrive
- // in the other batch.
- return;
- }
- stream.emit('metadata', Metadata._fromCoreRepresentation(
- response.metadata));
- });
- var status_batch = {};
- status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
- call.startBatch(status_batch, function(err, response) {
- response.status.metadata = Metadata._fromCoreRepresentation(
- response.status.metadata);
- stream.emit('status', response.status);
- if (response.status.code !== grpc.status.OK) {
- var error = new Error(response.status.details);
- error.code = response.status.code;
- error.metadata = response.status.metadata;
- stream.emit('error', error);
+ // Got a batch error, but OK status. Something went wrong
+ stream.emit('error', err);
return;
- } else {
- if (err) {
- // Got a batch error, but OK status. Something went wrong
- stream.emit('error', err);
- return;
- }
}
- });
+ }
});
return stream;
}
@@ -540,45 +525,38 @@ function makeBidiStreamRequestFunction(method, serialize, deserialize) {
metadata = metadata.clone();
}
var stream = new ClientDuplexStream(call, serialize, deserialize);
- this.$updateMetadata(this.$auth_uri, metadata, function(error, metadata) {
- if (error) {
- call.cancel();
- stream.emit('error', error);
+ var start_batch = {};
+ start_batch[grpc.opType.SEND_INITIAL_METADATA] =
+ metadata._getCoreRepresentation();
+ start_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
+ call.startBatch(start_batch, function(err, response) {
+ if (err) {
+ // The call has stopped for some reason. A non-OK status will arrive
+ // in the other batch.
return;
}
- var start_batch = {};
- start_batch[grpc.opType.SEND_INITIAL_METADATA] =
- metadata._getCoreRepresentation();
- start_batch[grpc.opType.RECV_INITIAL_METADATA] = true;
- call.startBatch(start_batch, function(err, response) {
+ stream.emit('metadata', Metadata._fromCoreRepresentation(
+ response.metadata));
+ });
+ var status_batch = {};
+ status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
+ call.startBatch(status_batch, function(err, response) {
+ response.status.metadata = Metadata._fromCoreRepresentation(
+ response.status.metadata);
+ stream.emit('status', response.status);
+ if (response.status.code !== grpc.status.OK) {
+ var error = new Error(response.status.details);
+ error.code = response.status.code;
+ error.metadata = response.status.metadata;
+ stream.emit('error', error);
+ return;
+ } else {
if (err) {
- // The call has stopped for some reason. A non-OK status will arrive
- // in the other batch.
+ // Got a batch error, but OK status. Something went wrong
+ stream.emit('error', err);
return;
}
- stream.emit('metadata', Metadata._fromCoreRepresentation(
- response.metadata));
- });
- var status_batch = {};
- status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true;
- call.startBatch(status_batch, function(err, response) {
- response.status.metadata = Metadata._fromCoreRepresentation(
- response.status.metadata);
- stream.emit('status', response.status);
- if (response.status.code !== grpc.status.OK) {
- var error = new Error(response.status.details);
- error.code = response.status.code;
- error.metadata = response.status.metadata;
- stream.emit('error', error);
- return;
- } else {
- if (err) {
- // Got a batch error, but OK status. Something went wrong
- stream.emit('error', err);
- return;
- }
- }
- });
+ }
});
return stream;
}
@@ -618,15 +596,8 @@ exports.makeClientConstructor = function(methods, serviceName) {
* @param {grpc.Credentials} credentials Credentials to use to connect
* to the server
* @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) {
- if (!updateMetadata) {
- updateMetadata = function(uri, metadata, callback) {
- callback(null, metadata);
- };
- }
+ function Client(address, credentials, options) {
if (!options) {
options = {};
}
@@ -634,11 +605,6 @@ exports.makeClientConstructor = function(methods, serviceName) {
/* Private fields use $ as a prefix instead of _ because it is an invalid
* prefix of a method name */
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) {
diff --git a/src/node/src/credentials.js b/src/node/src/credentials.js
new file mode 100644
index 0000000000..009ff63306
--- /dev/null
+++ b/src/node/src/credentials.js
@@ -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;
diff --git a/src/node/src/metadata.js b/src/node/src/metadata.js
index c1da70b197..5c24e46c9b 100644
--- a/src/node/src/metadata.js
+++ b/src/node/src/metadata.js
@@ -59,6 +59,7 @@ function normalizeKey(key) {
function validate(key, value) {
if (_.endsWith(key, '-bin')) {
if (!(value instanceof Buffer)) {
+ console.log(value.constructor.toString());
throw new Error('keys that end with \'-bin\' must have Buffer values');
}
} else {
@@ -173,7 +174,9 @@ Metadata.prototype._getCoreRepresentation = function() {
Metadata._fromCoreRepresentation = function(metadata) {
var newMetadata = new Metadata();
if (metadata) {
- newMetadata._internal_repr = _.cloneDeep(metadata);
+ _.forOwn(metadata, function(value, key) {
+ newMetadata._internal_repr[key] = _.clone(value);
+ });
}
return newMetadata;
};
diff --git a/src/node/src/server.js b/src/node/src/server.js
index 70b4a9d80e..87b5b7ad06 100644
--- a/src/node/src/server.js
+++ b/src/node/src/server.js
@@ -40,7 +40,7 @@
var _ = require('lodash');
-var grpc = require('bindings')('grpc.node');
+var grpc = require('bindings')('grpc_node');
var common = require('./common');
diff --git a/src/node/test/async_test.js b/src/node/test/async_test.js
index e81de62bc9..ce3ce50a2d 100644
--- a/src/node/test/async_test.js
+++ b/src/node/test/async_test.js
@@ -36,7 +36,7 @@
var assert = require('assert');
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
*/
-var getServer = require('../examples/math_server.js');
+var getServer = require('./math/math_server.js');
var server = getServer();
diff --git a/src/node/test/call_test.js b/src/node/test/call_test.js
index e7f071bcd5..c316fe7f10 100644
--- a/src/node/test/call_test.js
+++ b/src/node/test/call_test.js
@@ -34,7 +34,7 @@
'use strict';
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
@@ -48,7 +48,7 @@ function getDeadline(timeout_secs) {
return deadline;
}
-var insecureCreds = grpc.Credentials.createInsecure();
+var insecureCreds = grpc.ChannelCredentials.createInsecure();
describe('call', function() {
var channel;
diff --git a/src/node/test/channel_test.js b/src/node/test/channel_test.js
index d81df2a36d..b86f89b8a8 100644
--- a/src/node/test/channel_test.js
+++ b/src/node/test/channel_test.js
@@ -34,7 +34,7 @@
'use strict';
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
@@ -56,7 +56,7 @@ function multiDone(done, count) {
}
};
}
-var insecureCreds = grpc.Credentials.createInsecure();
+var insecureCreds = grpc.ChannelCredentials.createInsecure();
describe('channel', function() {
describe('constructor', function() {
@@ -149,12 +149,13 @@ describe('channel', function() {
afterEach(function() {
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 deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 1);
channel.watchConnectivityState(old_state, deadline, function(err, value) {
assert(err);
+ console.log('Callback from watchConnectivityState');
done();
});
});
diff --git a/src/node/test/constant_test.js b/src/node/test/constant_test.js
index fa06ad4e4d..b17cd339cb 100644
--- a/src/node/test/constant_test.js
+++ b/src/node/test/constant_test.js
@@ -34,7 +34,7 @@
'use strict';
var assert = require('assert');
-var grpc = require('bindings')('grpc.node');
+var grpc = require('bindings')('grpc_node');
/**
* List of all status names
diff --git a/src/node/test/credentials_test.js b/src/node/test/credentials_test.js
new file mode 100644
index 0000000000..8eb91ee69e
--- /dev/null
+++ b/src/node/test/credentials_test.js
@@ -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();
+ });
+ });
+ });
+});
diff --git a/src/node/test/end_to_end_test.js b/src/node/test/end_to_end_test.js
index 4b8da3bfb1..0f6c5941c4 100644
--- a/src/node/test/end_to_end_test.js
+++ b/src/node/test/end_to_end_test.js
@@ -34,7 +34,7 @@
'use strict';
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
@@ -57,7 +57,7 @@ function multiDone(done, count) {
};
}
-var insecureCreds = grpc.Credentials.createInsecure();
+var insecureCreds = grpc.ChannelCredentials.createInsecure();
describe('end-to-end', function() {
var server;
diff --git a/src/node/test/health_test.js b/src/node/test/health_test.js
index 9267bff7eb..a4dc24cf46 100644
--- a/src/node/test/health_test.js
+++ b/src/node/test/health_test.js
@@ -54,7 +54,7 @@ describe('Health Checking', function() {
grpc.ServerCredentials.createInsecure());
healthServer.start();
healthClient = new health.Client('localhost:' + port_num,
- grpc.Credentials.createInsecure());
+ grpc.credentials.createInsecure());
});
after(function() {
healthServer.forceShutdown();
diff --git a/src/node/test/interop_sanity_test.js b/src/node/test/interop_sanity_test.js
index 2ca07c1d50..804c1d45e4 100644
--- a/src/node/test/interop_sanity_test.js
+++ b/src/node/test/interop_sanity_test.js
@@ -90,4 +90,8 @@ describe('Interop tests', function() {
interop_client.runTest(port, name_override, 'timeout_on_sleeping_server',
true, true, done);
});
+ it('should pass custom_metadata', function(done) {
+ interop_client.runTest(port, name_override, 'custom_metadata',
+ true, true, done);
+ });
});
diff --git a/src/node/examples/math.proto b/src/node/test/math/math.proto
index 311e148c02..311e148c02 100644
--- a/src/node/examples/math.proto
+++ b/src/node/test/math/math.proto
diff --git a/src/node/examples/math_server.js b/src/node/test/math/math_server.js
index a4b237aeeb..9d06596f3d 100644
--- a/src/node/examples/math_server.js
+++ b/src/node/test/math/math_server.js
@@ -33,7 +33,7 @@
'use strict';
-var grpc = require('..');
+var grpc = require('../..');
var math = grpc.load(__dirname + '/math.proto').math;
/**
diff --git a/src/node/test/math_client_test.js b/src/node/test/math_client_test.js
index 6a6607ec74..6361d97857 100644
--- a/src/node/test/math_client_test.js
+++ b/src/node/test/math_client_test.js
@@ -36,7 +36,7 @@
var assert = require('assert');
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.
@@ -46,7 +46,7 @@ var math_client;
/**
* Server to test against
*/
-var getServer = require('../examples/math_server.js');
+var getServer = require('./math/math_server.js');
var server = getServer();
@@ -56,7 +56,7 @@ describe('Math client', function() {
grpc.ServerCredentials.createInsecure());
server.start();
math_client = new math.Math('localhost:' + port_num,
- grpc.Credentials.createInsecure());
+ grpc.credentials.createInsecure());
done();
});
after(function() {
diff --git a/src/node/test/server_test.js b/src/node/test/server_test.js
index 1e69d52e58..999a183b3c 100644
--- a/src/node/test/server_test.js
+++ b/src/node/test/server_test.js
@@ -36,7 +36,7 @@
var assert = require('assert');
var fs = require('fs');
var path = require('path');
-var grpc = require('bindings')('grpc.node');
+var grpc = require('bindings')('grpc_node');
describe('server', function() {
describe('constructor', function() {
diff --git a/src/node/test/surface_test.js b/src/node/test/surface_test.js
index 989fe5fd65..395ea887ec 100644
--- a/src/node/test/surface_test.js
+++ b/src/node/test/surface_test.js
@@ -41,7 +41,7 @@ var ProtoBuf = require('protobufjs');
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');
@@ -163,7 +163,7 @@ describe('waitForClientReady', function() {
Client = surface_client.makeProtobufClientConstructor(mathService);
});
beforeEach(function() {
- client = new Client('localhost:' + port, grpc.Credentials.createInsecure());
+ client = new Client('localhost:' + port, grpc.credentials.createInsecure());
});
after(function() {
server.forceShutdown();
@@ -217,7 +217,7 @@ describe('Echo service', function() {
});
var port = server.bind('localhost:0', server_insecure_creds);
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();
});
after(function() {
@@ -263,7 +263,7 @@ describe('Generic client and server', function() {
server.start();
var Client = grpc.makeGenericClientConstructor(string_service_attrs);
client = new Client('localhost:' + port,
- grpc.Credentials.createInsecure());
+ grpc.credentials.createInsecure());
});
after(function() {
server.forceShutdown();
@@ -311,7 +311,7 @@ describe('Echo metadata', function() {
});
var port = server.bind('localhost:0', server_insecure_creds);
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();
metadata = new grpc.Metadata();
metadata.set('key', 'value');
@@ -356,7 +356,7 @@ describe('Echo metadata', function() {
call.end();
});
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); },
metadata);
call.on('metadata', function(metadata) {
@@ -437,7 +437,7 @@ describe('Other conditions', function() {
});
port = server.bind('localhost:0', server_insecure_creds);
Client = surface_client.makeProtobufClientConstructor(test_service);
- client = new Client('localhost:' + port, grpc.Credentials.createInsecure());
+ client = new Client('localhost:' + port, grpc.credentials.createInsecure());
server.start();
});
after(function() {
@@ -484,7 +484,7 @@ describe('Other conditions', function() {
var Client = surface_client.makeClientConstructor(test_service_attrs,
'TestService');
misbehavingClient = new Client('localhost:' + port,
- grpc.Credentials.createInsecure());
+ grpc.credentials.createInsecure());
});
it('should respond correctly to a unary call', function(done) {
misbehavingClient.unary(badArg, function(err, data) {
@@ -711,7 +711,7 @@ describe('Call propagation', function() {
});
var port = server.bind('localhost:0', server_insecure_creds);
Client = surface_client.makeProtobufClientConstructor(test_service);
- client = new Client('localhost:' + port, grpc.Credentials.createInsecure());
+ client = new Client('localhost:' + port, grpc.credentials.createInsecure());
server.start();
});
after(function() {
@@ -748,7 +748,7 @@ describe('Call propagation', function() {
var proxy_port = proxy.bind('localhost:0', server_insecure_creds);
proxy.start();
var proxy_client = new Client('localhost:' + proxy_port,
- grpc.Credentials.createInsecure());
+ grpc.credentials.createInsecure());
var call = proxy_client.unary({}, function(err, value) {
done();
});
@@ -771,7 +771,7 @@ describe('Call propagation', function() {
var proxy_port = proxy.bind('localhost:0', server_insecure_creds);
proxy.start();
var proxy_client = new Client('localhost:' + proxy_port,
- grpc.Credentials.createInsecure());
+ grpc.credentials.createInsecure());
var call = proxy_client.clientStream(function(err, value) {
done();
});
@@ -792,7 +792,7 @@ describe('Call propagation', function() {
var proxy_port = proxy.bind('localhost:0', server_insecure_creds);
proxy.start();
var proxy_client = new Client('localhost:' + proxy_port,
- grpc.Credentials.createInsecure());
+ grpc.credentials.createInsecure());
var call = proxy_client.serverStream({});
call.on('error', function(err) {
done();
@@ -813,7 +813,7 @@ describe('Call propagation', function() {
var proxy_port = proxy.bind('localhost:0', server_insecure_creds);
proxy.start();
var proxy_client = new Client('localhost:' + proxy_port,
- grpc.Credentials.createInsecure());
+ grpc.credentials.createInsecure());
var call = proxy_client.bidiStream();
call.on('error', function(err) {
done();
@@ -842,7 +842,7 @@ describe('Call propagation', function() {
var proxy_port = proxy.bind('localhost:0', server_insecure_creds);
proxy.start();
var proxy_client = new Client('localhost:' + proxy_port,
- grpc.Credentials.createInsecure());
+ grpc.credentials.createInsecure());
var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 1);
proxy_client.clientStream(function(err, value) {
@@ -865,7 +865,7 @@ describe('Call propagation', function() {
var proxy_port = proxy.bind('localhost:0', server_insecure_creds);
proxy.start();
var proxy_client = new Client('localhost:' + proxy_port,
- grpc.Credentials.createInsecure());
+ grpc.credentials.createInsecure());
var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 1);
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 Client = surface_client.makeProtobufClientConstructor(mathService);
- client = new Client('localhost:' + port, grpc.Credentials.createInsecure());
+ client = new Client('localhost:' + port, grpc.credentials.createInsecure());
server.start();
});
after(function() {