mirror of https://github.com/grpc/grpc.git
commit
68a77e3260
97 changed files with 2463 additions and 17274 deletions
@ -0,0 +1,95 @@ |
||||
# Copyright 2015, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
{ |
||||
"variables" : { |
||||
'config': '<!(echo $CONFIG)' |
||||
}, |
||||
"targets" : [ |
||||
{ |
||||
'include_dirs': [ |
||||
"<!(node -e \"require('nan')\")" |
||||
], |
||||
'cflags': [ |
||||
'-std=c++0x', |
||||
'-Wall', |
||||
'-pthread', |
||||
'-g', |
||||
'-zdefs', |
||||
'-Werror', |
||||
'-Wno-error=deprecated-declarations' |
||||
], |
||||
'ldflags': [ |
||||
'-g' |
||||
], |
||||
"conditions": [ |
||||
['OS != "win"', { |
||||
'conditions': [ |
||||
['config=="gcov"', { |
||||
'cflags': [ |
||||
'-ftest-coverage', |
||||
'-fprofile-arcs', |
||||
'-O0' |
||||
], |
||||
'ldflags': [ |
||||
'-ftest-coverage', |
||||
'-fprofile-arcs' |
||||
] |
||||
} |
||||
] |
||||
] |
||||
}], |
||||
['OS == "mac"', { |
||||
'xcode_settings': { |
||||
'MACOSX_DEPLOYMENT_TARGET': '10.9', |
||||
'OTHER_CFLAGS': [ |
||||
'-std=c++11', |
||||
'-stdlib=libc++' |
||||
] |
||||
} |
||||
}] |
||||
], |
||||
"target_name": "grpc_node", |
||||
"sources": [ |
||||
"src/node/ext/byte_buffer.cc", |
||||
"src/node/ext/call.cc", |
||||
"src/node/ext/call_credentials.cc", |
||||
"src/node/ext/channel.cc", |
||||
"src/node/ext/channel_credentials.cc", |
||||
"src/node/ext/completion_queue_async_worker.cc", |
||||
"src/node/ext/node_grpc.cc", |
||||
"src/node/ext/server.cc", |
||||
"src/node/ext/server_credentials.cc", |
||||
"src/node/ext/timeval.cc" |
||||
], |
||||
"dependencies": [ |
||||
"grpc.gyp:grpc" |
||||
] |
||||
} |
||||
] |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -1,10 +0,0 @@ |
||||
{ |
||||
"name": "grpc-demo", |
||||
"version": "0.11.0", |
||||
"dependencies": { |
||||
"async": "^0.9.0", |
||||
"grpc": "~0.11.0", |
||||
"minimist": "^1.1.0", |
||||
"underscore": "^1.8.2" |
||||
} |
||||
} |
@ -0,0 +1,55 @@ |
||||
gRPC in 3 minutes (Python) |
||||
======================== |
||||
|
||||
Background |
||||
------------- |
||||
For this sample, we've already generated the server and client stubs from |
||||
[helloworld.proto][] and we'll be using a specific reference platform. |
||||
|
||||
Prerequisites |
||||
------------- |
||||
|
||||
- Debian 8.2 "Jessie" platform with `root` access |
||||
- `git` |
||||
- `python2.7` |
||||
- `pip` |
||||
- Python development headers |
||||
|
||||
Set-up |
||||
------- |
||||
```sh |
||||
$ # install the gRPC Core: |
||||
$ sudo apt-get install libgrpc-dev |
||||
$ # install gRPC Python: |
||||
$ sudo pip install -U grpcio==0.11.0b1 |
||||
$ # Since this "hello, world" example uses protocol buffers: |
||||
$ sudo pip install -U protobuf==3.0.0a3 |
||||
$ # Clone the repository to get the example code: |
||||
$ git clone https://github.com/grpc/grpc |
||||
$ # Navigate to the "hello, world" Python example: |
||||
$ cd grpc/examples/python/helloworld |
||||
``` |
||||
|
||||
Try it! |
||||
------- |
||||
|
||||
- Run the server |
||||
|
||||
```sh |
||||
$ python2.7 greeter_server.py & |
||||
``` |
||||
|
||||
- Run the client |
||||
|
||||
```sh |
||||
$ python2.7 greeter_client.py |
||||
``` |
||||
|
||||
Tutorial |
||||
-------- |
||||
|
||||
You can find a more detailed tutorial in [gRPC Basics: Python][] |
||||
|
||||
[helloworld.proto]:../protos/helloworld.proto |
||||
[Install gRPC Python]:../../src/python#installation |
||||
[gRPC Basics: Python]:http://www.grpc.io/docs/tutorials/basic/python.html |
@ -1,113 +1 @@ |
||||
# gRPC Python Hello World |
||||
|
||||
This is a quick introduction with a simple example and installation instructions: for a more complete tutorial see [gRPC Basics: Python](../route_guide). |
||||
|
||||
### Install gRPC |
||||
Make sure you have built gRPC Python from source on your system. Follow the instructions here: |
||||
[https://github.com/grpc/grpc/blob/master/src/python/README.md](https://github.com/grpc/grpc/blob/master/src/python/README.md). |
||||
|
||||
This gives you a python virtual environment with installed gRPC Python |
||||
in GRPC_ROOT/python2.7_virtual_environment. GRPC_ROOT is the path to which you |
||||
have cloned the [gRPC git repo](https://github.com/grpc/grpc). |
||||
|
||||
### Get the source code |
||||
|
||||
The example code for our Hello World and our other examples live in the `examples` |
||||
directory. Clone this repository to your local machine by running the |
||||
following command: |
||||
|
||||
|
||||
```sh |
||||
$ git clone https://github.com/grpc/grpc.git |
||||
``` |
||||
|
||||
Change your current directory to examples/python/helloworld |
||||
|
||||
```sh |
||||
$ cd examples/python/helloworld/ |
||||
``` |
||||
|
||||
### Defining a service |
||||
|
||||
The first step in creating our example is to define a *service*: an RPC |
||||
service specifies the methods that can be called remotely with their parameters |
||||
and return types. As you saw in the |
||||
[overview](#protocolbuffers) above, gRPC does this using [protocol |
||||
buffers](https://developers.google.com/protocol-buffers/docs/overview). We |
||||
use the protocol buffers interface definition language (IDL) to define our |
||||
service methods, and define the parameters and return |
||||
types as protocol buffer message types. Both the client and the |
||||
server use interface code generated from the service definition. |
||||
|
||||
Here's our example service definition. The `Greeting` |
||||
service has one method, `hello`, that lets the server receive a single |
||||
`HelloRequest` |
||||
message from the remote client containing the user's name, then send back |
||||
a greeting in a single `HelloReply`. This is the simplest type of RPC you |
||||
can specify in gRPC. |
||||
|
||||
``` |
||||
syntax = "proto3"; |
||||
|
||||
option java_package = "io.grpc.examples"; |
||||
|
||||
package helloworld; |
||||
|
||||
// The greeting service definition. |
||||
service Greeter { |
||||
// Sends a greeting |
||||
rpc SayHello (HelloRequest) returns (HelloReply) {} |
||||
} |
||||
|
||||
// The request message containing the user's name. |
||||
message HelloRequest { |
||||
string name = 1; |
||||
} |
||||
|
||||
// The response message containing the greetings |
||||
message HelloReply { |
||||
string message = 1; |
||||
} |
||||
|
||||
``` |
||||
|
||||
<a name="generating"></a> |
||||
### Generating gRPC code |
||||
|
||||
Once we've defined our service, we use the protocol buffer compiler |
||||
`protoc` to generate the special client and server code we need to create |
||||
our application. The generated code contains both stub code for clients to |
||||
use and an abstract interface for servers to implement, both with the method |
||||
defined in our `Greeting` service. |
||||
|
||||
To generate the client and server side interfaces: |
||||
|
||||
```sh |
||||
$ ./run_codegen.sh |
||||
``` |
||||
Which internally invokes the proto-compiler as: |
||||
|
||||
```sh |
||||
$ protoc -I ../../protos --python_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_python_plugin` ../../protos/helloworld.proto |
||||
``` |
||||
|
||||
### The client |
||||
|
||||
Client-side code can be found in [greeter_client.py](greeter_client.py). |
||||
|
||||
You can run the client using: |
||||
|
||||
```sh |
||||
$ ./run_client.sh |
||||
``` |
||||
|
||||
|
||||
### The server |
||||
|
||||
Server side code can be found in [greeter_server.py](greeter_server.py). |
||||
|
||||
You can run the server using: |
||||
|
||||
```sh |
||||
$ ./run_server.sh |
||||
``` |
||||
[This code's documentation lives on the grpc.io site.](http://www.grpc.io/docs) |
||||
|
@ -0,0 +1,202 @@ |
||||
# Generated by the protocol buffer compiler. DO NOT EDIT! |
||||
# source: helloworld.proto |
||||
|
||||
from google.protobuf import descriptor as _descriptor |
||||
from google.protobuf import message as _message |
||||
from google.protobuf import reflection as _reflection |
||||
from google.protobuf import symbol_database as _symbol_database |
||||
from google.protobuf import descriptor_pb2 |
||||
# @@protoc_insertion_point(imports) |
||||
|
||||
_sym_db = _symbol_database.Default() |
||||
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor( |
||||
name='helloworld.proto', |
||||
package='helloworld', |
||||
syntax='proto3', |
||||
serialized_pb=b'\n\x10helloworld.proto\x12\nhelloworld\"\x1c\n\x0cHelloRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"\x1d\n\nHelloReply\x12\x0f\n\x07message\x18\x01 \x01(\t2I\n\x07Greeter\x12>\n\x08SayHello\x12\x18.helloworld.HelloRequest\x1a\x16.helloworld.HelloReply\"\x00\x42\x18\n\x10io.grpc.examples\xa2\x02\x03HLWb\x06proto3' |
||||
) |
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR) |
||||
|
||||
|
||||
|
||||
|
||||
_HELLOREQUEST = _descriptor.Descriptor( |
||||
name='HelloRequest', |
||||
full_name='helloworld.HelloRequest', |
||||
filename=None, |
||||
file=DESCRIPTOR, |
||||
containing_type=None, |
||||
fields=[ |
||||
_descriptor.FieldDescriptor( |
||||
name='name', full_name='helloworld.HelloRequest.name', index=0, |
||||
number=1, type=9, cpp_type=9, label=1, |
||||
has_default_value=False, default_value=b"".decode('utf-8'), |
||||
message_type=None, enum_type=None, containing_type=None, |
||||
is_extension=False, extension_scope=None, |
||||
options=None), |
||||
], |
||||
extensions=[ |
||||
], |
||||
nested_types=[], |
||||
enum_types=[ |
||||
], |
||||
options=None, |
||||
is_extendable=False, |
||||
syntax='proto3', |
||||
extension_ranges=[], |
||||
oneofs=[ |
||||
], |
||||
serialized_start=32, |
||||
serialized_end=60, |
||||
) |
||||
|
||||
|
||||
_HELLOREPLY = _descriptor.Descriptor( |
||||
name='HelloReply', |
||||
full_name='helloworld.HelloReply', |
||||
filename=None, |
||||
file=DESCRIPTOR, |
||||
containing_type=None, |
||||
fields=[ |
||||
_descriptor.FieldDescriptor( |
||||
name='message', full_name='helloworld.HelloReply.message', index=0, |
||||
number=1, type=9, cpp_type=9, label=1, |
||||
has_default_value=False, default_value=b"".decode('utf-8'), |
||||
message_type=None, enum_type=None, containing_type=None, |
||||
is_extension=False, extension_scope=None, |
||||
options=None), |
||||
], |
||||
extensions=[ |
||||
], |
||||
nested_types=[], |
||||
enum_types=[ |
||||
], |
||||
options=None, |
||||
is_extendable=False, |
||||
syntax='proto3', |
||||
extension_ranges=[], |
||||
oneofs=[ |
||||
], |
||||
serialized_start=62, |
||||
serialized_end=91, |
||||
) |
||||
|
||||
DESCRIPTOR.message_types_by_name['HelloRequest'] = _HELLOREQUEST |
||||
DESCRIPTOR.message_types_by_name['HelloReply'] = _HELLOREPLY |
||||
|
||||
HelloRequest = _reflection.GeneratedProtocolMessageType('HelloRequest', (_message.Message,), dict( |
||||
DESCRIPTOR = _HELLOREQUEST, |
||||
__module__ = 'helloworld_pb2' |
||||
# @@protoc_insertion_point(class_scope:helloworld.HelloRequest) |
||||
)) |
||||
_sym_db.RegisterMessage(HelloRequest) |
||||
|
||||
HelloReply = _reflection.GeneratedProtocolMessageType('HelloReply', (_message.Message,), dict( |
||||
DESCRIPTOR = _HELLOREPLY, |
||||
__module__ = 'helloworld_pb2' |
||||
# @@protoc_insertion_point(class_scope:helloworld.HelloReply) |
||||
)) |
||||
_sym_db.RegisterMessage(HelloReply) |
||||
|
||||
|
||||
DESCRIPTOR.has_options = True |
||||
DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), b'\n\020io.grpc.examples\242\002\003HLW') |
||||
import abc |
||||
from grpc.beta import implementations as beta_implementations |
||||
from grpc.early_adopter import implementations as early_adopter_implementations |
||||
from grpc.framework.alpha import utilities as alpha_utilities |
||||
from grpc.framework.common import cardinality |
||||
from grpc.framework.interfaces.face import utilities as face_utilities |
||||
class EarlyAdopterGreeterServicer(object): |
||||
"""<fill me in later!>""" |
||||
__metaclass__ = abc.ABCMeta |
||||
@abc.abstractmethod |
||||
def SayHello(self, request, context): |
||||
raise NotImplementedError() |
||||
class EarlyAdopterGreeterServer(object): |
||||
"""<fill me in later!>""" |
||||
__metaclass__ = abc.ABCMeta |
||||
@abc.abstractmethod |
||||
def start(self): |
||||
raise NotImplementedError() |
||||
@abc.abstractmethod |
||||
def stop(self): |
||||
raise NotImplementedError() |
||||
class EarlyAdopterGreeterStub(object): |
||||
"""<fill me in later!>""" |
||||
__metaclass__ = abc.ABCMeta |
||||
@abc.abstractmethod |
||||
def SayHello(self, request): |
||||
raise NotImplementedError() |
||||
SayHello.async = None |
||||
def early_adopter_create_Greeter_server(servicer, port, private_key=None, certificate_chain=None): |
||||
import helloworld_pb2 |
||||
import helloworld_pb2 |
||||
method_service_descriptions = { |
||||
"SayHello": alpha_utilities.unary_unary_service_description( |
||||
servicer.SayHello, |
||||
helloworld_pb2.HelloRequest.FromString, |
||||
helloworld_pb2.HelloReply.SerializeToString, |
||||
), |
||||
} |
||||
return early_adopter_implementations.server("helloworld.Greeter", method_service_descriptions, port, private_key=private_key, certificate_chain=certificate_chain) |
||||
def early_adopter_create_Greeter_stub(host, port, metadata_transformer=None, secure=False, root_certificates=None, private_key=None, certificate_chain=None, server_host_override=None): |
||||
import helloworld_pb2 |
||||
import helloworld_pb2 |
||||
method_invocation_descriptions = { |
||||
"SayHello": alpha_utilities.unary_unary_invocation_description( |
||||
helloworld_pb2.HelloRequest.SerializeToString, |
||||
helloworld_pb2.HelloReply.FromString, |
||||
), |
||||
} |
||||
return early_adopter_implementations.stub("helloworld.Greeter", method_invocation_descriptions, host, port, metadata_transformer=metadata_transformer, secure=secure, root_certificates=root_certificates, private_key=private_key, certificate_chain=certificate_chain, server_host_override=server_host_override) |
||||
|
||||
class BetaGreeterServicer(object): |
||||
"""<fill me in later!>""" |
||||
__metaclass__ = abc.ABCMeta |
||||
@abc.abstractmethod |
||||
def SayHello(self, request, context): |
||||
raise NotImplementedError() |
||||
|
||||
class BetaGreeterStub(object): |
||||
"""The interface to which stubs will conform.""" |
||||
__metaclass__ = abc.ABCMeta |
||||
@abc.abstractmethod |
||||
def SayHello(self, request, timeout): |
||||
raise NotImplementedError() |
||||
SayHello.future = None |
||||
|
||||
def beta_create_Greeter_server(servicer, pool=None, pool_size=None, default_timeout=None, maximum_timeout=None): |
||||
import helloworld_pb2 |
||||
import helloworld_pb2 |
||||
request_deserializers = { |
||||
('helloworld.Greeter', 'SayHello'): helloworld_pb2.HelloRequest.FromString, |
||||
} |
||||
response_serializers = { |
||||
('helloworld.Greeter', 'SayHello'): helloworld_pb2.HelloReply.SerializeToString, |
||||
} |
||||
method_implementations = { |
||||
('helloworld.Greeter', 'SayHello'): face_utilities.unary_unary_inline(servicer.SayHello), |
||||
} |
||||
server_options = beta_implementations.server_options(request_deserializers=request_deserializers, response_serializers=response_serializers, thread_pool=pool, thread_pool_size=pool_size, default_timeout=default_timeout, maximum_timeout=maximum_timeout) |
||||
return beta_implementations.server(method_implementations, options=server_options) |
||||
|
||||
def beta_create_Greeter_stub(channel, host=None, metadata_transformer=None, pool=None, pool_size=None): |
||||
import helloworld_pb2 |
||||
import helloworld_pb2 |
||||
request_serializers = { |
||||
('helloworld.Greeter', 'SayHello'): helloworld_pb2.HelloRequest.SerializeToString, |
||||
} |
||||
response_deserializers = { |
||||
('helloworld.Greeter', 'SayHello'): helloworld_pb2.HelloReply.FromString, |
||||
} |
||||
cardinalities = { |
||||
'SayHello': cardinality.Cardinality.UNARY_UNARY, |
||||
} |
||||
stub_options = beta_implementations.stub_options(host=host, metadata_transformer=metadata_transformer, request_serializers=request_serializers, response_deserializers=response_deserializers, thread_pool=pool, thread_pool_size=pool_size) |
||||
return beta_implementations.dynamic_stub(channel, 'helloworld.Greeter', cardinalities, options=stub_options) |
||||
# @@protoc_insertion_point(module_scope) |
@ -1,299 +1 @@ |
||||
#gRPC Basics: Python |
||||
|
||||
This tutorial provides a basic Python programmer's introduction to working with gRPC. By walking through this example you'll learn how to: |
||||
|
||||
- Define a service in a .proto file. |
||||
- Generate server and client code using the protocol buffer compiler. |
||||
- Use the Python gRPC API to write a simple client and server for your service. |
||||
|
||||
It assumes that you have read the [Getting started](https://github.com/grpc/grpc/tree/master/examples) guide and are familiar with [protocol buffers] (https://developers.google.com/protocol-buffers/docs/overview). Note that the example in this tutorial uses the proto3 version of the protocol buffers language, which is currently in alpha release:you can find out more in the [proto3 language guide](https://developers.google.com/protocol-buffers/docs/proto3) and see the [release notes](https://github.com/google/protobuf/releases) for the new version in the protocol buffers Github repository. |
||||
|
||||
This isn't a comprehensive guide to using gRPC in Python: more reference documentation is coming soon. |
||||
|
||||
|
||||
## Why use gRPC? |
||||
|
||||
This example is a simple route mapping application that lets clients get information about features on their route, create a summary of their route, and exchange route information such as traffic updates with the server and other clients. |
||||
|
||||
With gRPC you can define your service once in a .proto file and implement clients and servers in any of gRPC's supported languages, which in turn can be run in environments ranging from servers inside Google to your own tablet, with all the complexity of communication between different languages and environments is handled for you by gRPC. You also get all the advantages of working with protocol buffers, including efficient serialization, a simple IDL, and easy interface updating. |
||||
|
||||
## Example code and setup |
||||
|
||||
The example code for this tutorial is in [examples/python/route_guide](.). To download the example, clone this repository by running the following command: |
||||
```shell |
||||
$ git clone https://github.com/grpc/grpc.git |
||||
``` |
||||
|
||||
Then change your current directory to `examples/python/route_guide`: |
||||
```shell |
||||
$ cd examples/python/route_guide |
||||
``` |
||||
|
||||
You also should have the relevant tools installed to generate the server and client interface code - if you don't already, follow the setup instructions in [the Python quick start guide](../helloworld). |
||||
|
||||
## Defining the service |
||||
|
||||
Your first step (as you'll know from [Getting started](https://github.com/grpc/grpc/tree/master/examples)) is to define the gRPC *service* and the method *request* and *response* types using [protocol buffers](https://developers.google.com/protocol-buffers/docs/overview). You can see the complete .proto file in [`examples/protos/route_guide.proto`](../../protos/route_guide.proto). |
||||
|
||||
To define a service, you specify a named `service` in your .proto file: |
||||
|
||||
```protobuf |
||||
service RouteGuide { |
||||
// (Method definitions not shown) |
||||
} |
||||
``` |
||||
|
||||
Then you define `rpc` methods inside your service definition, specifying their request and response types. gRPC lets you define four kinds of service method, all of which are used in the `RouteGuide` service: |
||||
|
||||
- A *simple RPC* where the client sends a request to the server using the stub and waits for a response to come back, just like a normal function call. |
||||
```protobuf |
||||
// Obtains the feature at a given position. |
||||
rpc GetFeature(Point) returns (Feature) {} |
||||
``` |
||||
|
||||
- A *response-streaming RPC* where the client sends a request to the server and gets a stream to read a sequence of messages back. The client reads from the returned stream until there are no more messages. As you can see in the example, you specify a response-streaming method by placing the `stream` keyword before the *response* type. |
||||
```protobuf |
||||
// Obtains the Features available within the given Rectangle. Results are |
||||
// streamed rather than returned at once (e.g. in a response message with a |
||||
// repeated field), as the rectangle may cover a large area and contain a |
||||
// huge number of features. |
||||
rpc ListFeatures(Rectangle) returns (stream Feature) {} |
||||
``` |
||||
|
||||
- A *request-streaming RPC* where the client writes a sequence of messages and sends them to the server, again using a provided stream. Once the client has finished writing the messages, it waits for the server to read them all and return its response. You specify a request-streaming method by placing the `stream` keyword before the *request* type. |
||||
```protobuf |
||||
// Accepts a stream of Points on a route being traversed, returning a |
||||
// RouteSummary when traversal is completed. |
||||
rpc RecordRoute(stream Point) returns (RouteSummary) {} |
||||
``` |
||||
|
||||
- A *bidirectionally-streaming RPC* where both sides send a sequence of messages using a read-write stream. The two streams operate independently, so clients and servers can read and write in whatever order they like: for example, the server could wait to receive all the client messages before writing its responses, or it could alternately read a message then write a message, or some other combination of reads and writes. The order of messages in each stream is preserved. You specify this type of method by placing the `stream` keyword before both the request and the response. |
||||
```protobuf |
||||
// Accepts a stream of RouteNotes sent while a route is being traversed, |
||||
// while receiving other RouteNotes (e.g. from other users). |
||||
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} |
||||
``` |
||||
|
||||
Your .proto file also contains protocol buffer message type definitions for all the request and response types used in our service methods - for example, here's the `Point` message type: |
||||
```protobuf |
||||
// Points are represented as latitude-longitude pairs in the E7 representation |
||||
// (degrees multiplied by 10**7 and rounded to the nearest integer). |
||||
// Latitudes should be in the range +/- 90 degrees and longitude should be in |
||||
// the range +/- 180 degrees (inclusive). |
||||
message Point { |
||||
int32 latitude = 1; |
||||
int32 longitude = 2; |
||||
} |
||||
``` |
||||
|
||||
## Generating client and server code |
||||
|
||||
Next you need to generate the gRPC client and server interfaces from your .proto service definition. You do this using the protocol buffer compiler `protoc` with a special gRPC Python plugin. Make sure you've installed protoc and followed the gRPC Python plugin [installation instructions](https://github.com/grpc/grpc/blob/master/INSTALL) first): |
||||
|
||||
With `protoc` and the gRPC Python plugin installed, use the following command to generate the Python code: |
||||
|
||||
```shell |
||||
$ protoc -I ../../protos --python_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_python_plugin` ../../protos/route_guide.proto |
||||
``` |
||||
|
||||
Note that as we've already provided a version of the generated code in the example repository, running this command regenerates the appropriate file rather than creates a new one. The generated code file is called `route_guide_pb2.py` and contains: |
||||
- classes for the messages defined in route_guide.proto |
||||
- abstract classes for the service defined in route_guide.proto |
||||
- `BetaRouteGuideServicer`, which defines the interface for implementations of the RouteGuide service |
||||
- `BetaRouteGuideStub`, which can be used by clients to invoke RouteGuide RPCs |
||||
- functions for application use |
||||
- `beta_create_RouteGuide_server`, which creates a gRPC server given a `BetaRouteGuideServicer` object |
||||
- `beta_create_RouteGuide_stub`, which can be used by clients to create a stub object |
||||
|
||||
<a name="server"></a> |
||||
## Creating the server |
||||
|
||||
First let's look at how you create a `RouteGuide` server. If you're only interested in creating gRPC clients, you can skip this section and go straight to [Creating the client](#client) (though you might find it interesting anyway!). |
||||
|
||||
Creating and running a `RouteGuide` server breaks down into two work items: |
||||
- Implementing the servicer interface generated from our service definition with functions that perform the actual "work" of the service. |
||||
- Running a gRPC server to listen for requests from clients and transmit responses. |
||||
|
||||
You can find the example `RouteGuide` server in [route_guide_server.py](route_guide_server.py). |
||||
|
||||
### Implementing RouteGuide |
||||
|
||||
`route_guide_server.py` has a `RouteGuideServicer` class that implements the generated interface `route_guide_pb2.BetaRouteGuideServicer`: |
||||
|
||||
```python |
||||
# RouteGuideServicer provides an implementation of the methods of the RouteGuide service. |
||||
class RouteGuideServicer(route_guide_pb2.BetaRouteGuideServicer): |
||||
``` |
||||
|
||||
`RouteGuideServicer` implements all the `RouteGuide` service methods. |
||||
|
||||
#### Simple RPC |
||||
|
||||
Let's look at the simplest type first, `GetFeature`, which just gets a `Point` from the client and returns the corresponding feature information from its database in a `Feature`. |
||||
|
||||
```python |
||||
def GetFeature(self, request, context): |
||||
feature = get_feature(self.db, request) |
||||
if feature is None: |
||||
return route_guide_pb2.Feature(name="", location=request) |
||||
else: |
||||
return feature |
||||
``` |
||||
|
||||
The method is passed a `route_guide_pb2.Point` request for the RPC, and a `ServicerContext` object that provides RPC-specific information such as timeout limits. It returns a `route_guide_pb2.Feature` response. |
||||
|
||||
#### Response-streaming RPC |
||||
|
||||
Now let's look at the next method. `ListFeatures` is a response-streaming RPC that sends multiple `Feature`s to the client. |
||||
|
||||
```python |
||||
def ListFeatures(self, request, context): |
||||
left = min(request.lo.longitude, request.hi.longitude) |
||||
right = max(request.lo.longitude, request.hi.longitude) |
||||
top = max(request.lo.latitude, request.hi.latitude) |
||||
bottom = min(request.lo.latitude, request.hi.latitude) |
||||
for feature in self.db: |
||||
if (feature.location.longitude >= left and |
||||
feature.location.longitude <= right and |
||||
feature.location.latitude >= bottom and |
||||
feature.location.latitude <= top): |
||||
yield feature |
||||
``` |
||||
|
||||
Here the request message is a `route_guide_pb2.Rectangle` within which the client wants to find `Feature`s. Instead of returning a single response the method yields zero or more responses. |
||||
|
||||
#### Request-streaming RPC |
||||
|
||||
The request-streaming method `RecordRoute` uses an [iterator](https://docs.python.org/2/library/stdtypes.html#iterator-types) of request values and returns a single response value. |
||||
|
||||
```python |
||||
def RecordRoute(self, request_iterator, context): |
||||
point_count = 0 |
||||
feature_count = 0 |
||||
distance = 0.0 |
||||
prev_point = None |
||||
|
||||
start_time = time.time() |
||||
for point in request_iterator: |
||||
point_count += 1 |
||||
if get_feature(self.db, point): |
||||
feature_count += 1 |
||||
if prev_point: |
||||
distance += get_distance(prev_point, point) |
||||
prev_point = point |
||||
|
||||
elapsed_time = time.time() - start_time |
||||
return route_guide_pb2.RouteSummary(point_count=point_count, |
||||
feature_count=feature_count, |
||||
distance=int(distance), |
||||
elapsed_time=int(elapsed_time)) |
||||
``` |
||||
|
||||
#### Bidirectional streaming RPC |
||||
|
||||
Lastly let's look at the bidirectionally-streaming method `RouteChat`. |
||||
|
||||
```python |
||||
def RouteChat(self, request_iterator, context): |
||||
prev_notes = [] |
||||
for new_note in request_iterator: |
||||
for prev_note in prev_notes: |
||||
if prev_note.location == new_note.location: |
||||
yield prev_note |
||||
prev_notes.append(new_note) |
||||
``` |
||||
|
||||
This method's semantics are a combination of those of the request-streaming method and the response-streaming method. It is passed an iterator of request values and is itself an iterator of response values. |
||||
|
||||
### Starting the server |
||||
|
||||
Once you have implemented all the `RouteGuide` methods, the next step is to start up a gRPC server so that clients can actually use your service: |
||||
|
||||
```python |
||||
def serve(): |
||||
server = route_guide_pb2.beta_create_RouteGuide_server(RouteGuideServicer()) |
||||
server.add_insecure_port('[::]:50051') |
||||
server.start() |
||||
``` |
||||
|
||||
Because `start()` does not block you may need to sleep-loop if there is nothing else for your code to do while serving. |
||||
|
||||
<a name="client"></a> |
||||
## Creating the client |
||||
|
||||
You can see the complete example client code in [route_guide_client.py](route_guide_client.py). |
||||
|
||||
### Creating a stub |
||||
|
||||
To call service methods, we first need to create a *stub*. |
||||
|
||||
We use the `beta_create_RouteGuide_stub` function of the `route_guide_pb2` module, generated from our .proto. |
||||
|
||||
```python |
||||
channel = implementations.insecure_channel('localhost', 50051) |
||||
stub = beta_create_RouteGuide_stub(channel) |
||||
``` |
||||
|
||||
The returned object implements all the methods defined by the `BetaRouteGuideStub` interface. |
||||
|
||||
### Calling service methods |
||||
|
||||
For RPC methods that return a single response ("response-unary" methods), gRPC Python supports both synchronous (blocking) and asynchronous (non-blocking) control flow semantics. For response-streaming RPC methods, calls immediately return an iterator of response values. Calls to that iterator's `next()` method block until the response to be yielded from the iterator becomes available. |
||||
|
||||
#### Simple RPC |
||||
|
||||
A synchronous call to the simple RPC `GetFeature` is nearly as straightforward as calling a local method. The RPC call waits for the server to respond, and will either return a response or raise an exception: |
||||
|
||||
```python |
||||
feature = stub.GetFeature(point, timeout_in_seconds) |
||||
``` |
||||
|
||||
An asynchronous call to `GetFeature` is similar, but like calling a local method asynchronously in a thread pool: |
||||
|
||||
```python |
||||
feature_future = stub.GetFeature.future(point, timeout_in_seconds) |
||||
feature = feature_future.result() |
||||
``` |
||||
|
||||
#### Response-streaming RPC |
||||
|
||||
Calling the response-streaming `ListFeatures` is similar to working with sequence types: |
||||
|
||||
```python |
||||
for feature in stub.ListFeatures(rectangle, timeout_in_seconds): |
||||
``` |
||||
|
||||
#### Request-streaming RPC |
||||
|
||||
Calling the request-streaming `RecordRoute` is similar to passing a sequence to a local method. Like the simple RPC above that also returns a single response, it can be called synchronously or asynchronously: |
||||
|
||||
```python |
||||
route_summary = stub.RecordRoute(point_sequence, timeout_in_seconds) |
||||
``` |
||||
|
||||
```python |
||||
route_summary_future = stub.RecordRoute.future(point_sequence, timeout_in_seconds) |
||||
route_summary = route_summary_future.result() |
||||
``` |
||||
|
||||
#### Bidirectional streaming RPC |
||||
|
||||
Calling the bidirectionally-streaming `RouteChat` has (as is the case on the service-side) a combination of the request-streaming and response-streaming semantics: |
||||
|
||||
```python |
||||
for received_route_note in stub.RouteChat(sent_routes, timeout_in_seconds): |
||||
``` |
||||
|
||||
## Try it out! |
||||
|
||||
Run the server, which will listen on port 50051: |
||||
|
||||
```shell |
||||
$ python route_guide_server.py |
||||
``` |
||||
|
||||
Run the client (in a different terminal): |
||||
|
||||
```shell |
||||
$ python route_guide_client.py |
||||
``` |
||||
[This code's documentation lives on the grpc.io site.](http://www.grpc.io/docs/tutorials/basic/python.html) |
||||
|
@ -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. |
@ -1,16 +0,0 @@ |
||||
# Command Line Tools |
||||
|
||||
# Service Packager |
||||
|
||||
The command line tool `bin/service_packager`, when called with the following command line: |
||||
|
||||
```bash |
||||
service_packager proto_file -o output_path -n name -v version [-i input_path...] |
||||
``` |
||||
|
||||
Populates `output_path` with a node package consisting of a `package.json` populated with `name` and `version`, an `index.js`, a `LICENSE` file copied from gRPC, and a `service.json`, which is compiled from `proto_file` and the given `input_path`s. `require('output_path')` returns an object that is equivalent to |
||||
|
||||
```js |
||||
{ client: require('grpc').load('service.json'), |
||||
auth: require('google-auth-library') } |
||||
``` |
@ -1,2 +0,0 @@ |
||||
#!/usr/bin/env node |
||||
require(__dirname+'/../cli/service_packager.js').main(process.argv.slice(2)); |
@ -1,100 +0,0 @@ |
||||
{ |
||||
"variables" : { |
||||
'config': '<!(echo $CONFIG)' |
||||
}, |
||||
"targets" : [ |
||||
{ |
||||
'include_dirs': [ |
||||
"<!(node -e \"require('nan')\")" |
||||
], |
||||
'cflags': [ |
||||
'-std=c++0x', |
||||
'-Wall', |
||||
'-pthread', |
||||
'-g', |
||||
'-zdefs', |
||||
'-Werror', |
||||
'-Wno-error=deprecated-declarations' |
||||
], |
||||
'ldflags': [ |
||||
'-g' |
||||
], |
||||
"conditions": [ |
||||
['OS != "win"', { |
||||
'variables': { |
||||
'pkg_config_grpc': '<!(pkg-config --exists grpc >/dev/null 2>&1 && echo true || echo false)' |
||||
}, |
||||
'conditions': [ |
||||
['config=="gcov"', { |
||||
'cflags': [ |
||||
'-ftest-coverage', |
||||
'-fprofile-arcs', |
||||
'-O0' |
||||
], |
||||
'ldflags': [ |
||||
'-ftest-coverage', |
||||
'-fprofile-arcs' |
||||
] |
||||
} |
||||
], |
||||
['pkg_config_grpc == "true"', { |
||||
'link_settings': { |
||||
'libraries': [ |
||||
'<!@(pkg-config --libs-only-l --static grpc)' |
||||
] |
||||
}, |
||||
'cflags': [ |
||||
'<!@(pkg-config --cflags grpc)' |
||||
], |
||||
'libraries': [ |
||||
'<!@(pkg-config --libs-only-L --static grpc)' |
||||
], |
||||
'ldflags': [ |
||||
'<!@(pkg-config --libs-only-other --static grpc)' |
||||
] |
||||
}, { |
||||
'link_settings': { |
||||
'libraries': [ |
||||
'-lpthread', |
||||
'-lgrpc', |
||||
'-lgpr' |
||||
], |
||||
}, |
||||
'conditions':[ |
||||
['OS != "mac"', { |
||||
'link_settings': { |
||||
'libraries': [ |
||||
'-lrt' |
||||
] |
||||
} |
||||
}] |
||||
] |
||||
} |
||||
] |
||||
] |
||||
}], |
||||
['OS == "mac"', { |
||||
'xcode_settings': { |
||||
'MACOSX_DEPLOYMENT_TARGET': '10.9', |
||||
'OTHER_CFLAGS': [ |
||||
'-std=c++11', |
||||
'-stdlib=libc++' |
||||
] |
||||
} |
||||
}] |
||||
], |
||||
"target_name": "grpc", |
||||
"sources": [ |
||||
"ext/byte_buffer.cc", |
||||
"ext/call.cc", |
||||
"ext/channel.cc", |
||||
"ext/completion_queue_async_worker.cc", |
||||
"ext/credentials.cc", |
||||
"ext/node_grpc.cc", |
||||
"ext/server.cc", |
||||
"ext/server_credentials.cc", |
||||
"ext/timeval.cc" |
||||
] |
||||
} |
||||
] |
||||
} |
@ -1,142 +0,0 @@ |
||||
/* |
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
'use strict'; |
||||
|
||||
var fs = require('fs'); |
||||
var path = require('path'); |
||||
|
||||
var _ = require('lodash'); |
||||
var async = require('async'); |
||||
var pbjs = require('protobufjs/cli/pbjs'); |
||||
var parseArgs = require('minimist'); |
||||
var Mustache = require('mustache'); |
||||
|
||||
var package_json = require('../package.json'); |
||||
|
||||
var template_path = path.resolve(__dirname, 'service_packager'); |
||||
|
||||
var package_tpl_path = path.join(template_path, 'package.json.template'); |
||||
|
||||
var arg_format = { |
||||
string: ['include', 'out', 'name', 'version'], |
||||
alias: { |
||||
include: 'i', |
||||
out: 'o', |
||||
name: 'n', |
||||
version: 'v' |
||||
} |
||||
}; |
||||
|
||||
// TODO(mlumish): autogenerate README.md from proto file
|
||||
|
||||
/** |
||||
* Render package.json file from template using provided parameters. |
||||
* @param {Object} params Map of parameter names to values |
||||
* @param {function(Error, string)} callback Callback to pass rendered template |
||||
* text to |
||||
*/ |
||||
function generatePackage(params, callback) { |
||||
fs.readFile(package_tpl_path, {encoding: 'utf-8'}, function(err, template) { |
||||
if (err) { |
||||
callback(err); |
||||
} else { |
||||
var rendered = Mustache.render(template, params); |
||||
callback(null, rendered); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* Copy a file |
||||
* @param {string} src_path The filepath to copy from |
||||
* @param {string} dest_path The filepath to copy to |
||||
*/ |
||||
function copyFile(src_path, dest_path) { |
||||
fs.createReadStream(src_path).pipe(fs.createWriteStream(dest_path)); |
||||
} |
||||
|
||||
/** |
||||
* Run the script. Copies the index.js and LICENSE files to the output path, |
||||
* renders the package.json template to the output path, and generates a |
||||
* service.json file from the input proto files using pbjs. The arguments are |
||||
* taken directly from the command line, and handled as follows: |
||||
* -i (--include) : An include path for pbjs (can be dpulicated) |
||||
* -o (--output): The output path |
||||
* -n (--name): The name of the package |
||||
* -v (--version): The package version |
||||
* @param {Array} argv The argument vector |
||||
*/ |
||||
function main(argv) { |
||||
var args = parseArgs(argv, arg_format); |
||||
var out_path = path.resolve(args.out); |
||||
var include_dirs = []; |
||||
if (args.include) { |
||||
include_dirs = _.map(_.flatten([args.include]), function(p) { |
||||
return path.resolve(p); |
||||
}); |
||||
} |
||||
args.grpc_version = package_json.version; |
||||
generatePackage(args, function(err, rendered) { |
||||
if (err) throw err; |
||||
fs.writeFile(path.join(out_path, 'package.json'), rendered, function(err) { |
||||
if (err) throw err; |
||||
}); |
||||
}); |
||||
copyFile(path.join(template_path, 'index.js'), |
||||
path.join(out_path, 'index.js')); |
||||
copyFile(path.join(__dirname, '..', 'LICENSE'), |
||||
path.join(out_path, 'LICENSE')); |
||||
|
||||
var service_stream = fs.createWriteStream(path.join(out_path, |
||||
'service.json')); |
||||
var pbjs_args = _.flatten(['node', 'pbjs', |
||||
args._[0], |
||||
'-legacy', |
||||
_.map(include_dirs, function(dir) { |
||||
return "-path=" + dir; |
||||
})]); |
||||
var old_stdout = process.stdout; |
||||
process.__defineGetter__('stdout', function() { |
||||
return service_stream; |
||||
}); |
||||
var pbjs_status = pbjs.main(pbjs_args); |
||||
process.__defineGetter__('stdout', function() { |
||||
return old_stdout; |
||||
}); |
||||
if (pbjs_status !== pbjs.STATUS_OK) { |
||||
throw new Error('pbjs failed with status code ' + pbjs_status); |
||||
} |
||||
} |
||||
|
||||
exports.main = main; |
@ -1,36 +0,0 @@ |
||||
/* |
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
var grpc = require('grpc'); |
||||
exports.client = grpc.load(__dirname + '/service.json', 'json'); |
||||
exports.auth = require('google-auth-library'); |
@ -1,17 +0,0 @@ |
||||
{ |
||||
"name": "{{{name}}}", |
||||
"version": "{{{version}}}", |
||||
"author": "Google Inc.", |
||||
"description": "Client library for {{{name}}} built on gRPC", |
||||
"license": "Apache-2.0", |
||||
"dependencies": { |
||||
"grpc": "{{{grpc_version}}}", |
||||
"google-auth-library": "^0.9.2" |
||||
}, |
||||
"main": "index.js", |
||||
"files": [ |
||||
"LICENSE", |
||||
"index.js", |
||||
"service.json" |
||||
] |
||||
} |
@ -1,62 +0,0 @@ |
||||
// Copyright 2015, Google Inc. |
||||
// All rights reserved. |
||||
// |
||||
// Redistribution and use in source and binary forms, with or without |
||||
// modification, are permitted provided that the following conditions are |
||||
// met: |
||||
// |
||||
// * Redistributions of source code must retain the above copyright |
||||
// notice, this list of conditions and the following disclaimer. |
||||
// * Redistributions in binary form must reproduce the above |
||||
// copyright notice, this list of conditions and the following disclaimer |
||||
// in the documentation and/or other materials provided with the |
||||
// distribution. |
||||
// * Neither the name of Google Inc. nor the names of its |
||||
// contributors may be used to endorse or promote products derived from |
||||
// this software without specific prior written permission. |
||||
// |
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
syntax = "proto3"; |
||||
|
||||
package examples; |
||||
|
||||
// Protocol type definitions |
||||
message StockRequest { |
||||
string symbol = 1; |
||||
int32 num_trades_to_watch = 2; |
||||
} |
||||
|
||||
message StockReply { |
||||
float price = 1; |
||||
string symbol = 2; |
||||
} |
||||
|
||||
|
||||
// Interface exported by the server |
||||
service Stock { |
||||
// Simple blocking RPC |
||||
rpc GetLastTradePrice(StockRequest) returns (StockReply) { |
||||
} |
||||
// Bidirectional streaming RPC |
||||
rpc GetLastTradePriceMultiple(stream StockRequest) returns |
||||
(stream StockReply) { |
||||
} |
||||
// Unidirectional server-to-client streaming RPC |
||||
rpc WatchFutureTrades(StockRequest) returns (stream StockReply) { |
||||
} |
||||
// Unidirectional client-to-server streaming RPC |
||||
rpc GetHighestTradePrice(stream StockRequest) returns (StockReply) { |
||||
} |
||||
|
||||
} |
@ -1,47 +0,0 @@ |
||||
/* |
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
var grpc = require('..'); |
||||
var examples = grpc.load(__dirname + '/stock.proto').examples; |
||||
|
||||
/** |
||||
* This exports a client constructor for the Stock service. The usage looks like |
||||
* |
||||
* var StockClient = require('stock_client.js'); |
||||
* var stockClient = new StockClient(server_address, |
||||
* grpc.Credentials.createInsecure()); |
||||
* stockClient.getLastTradePrice({symbol: 'GOOG'}, function(error, response) { |
||||
* console.log(error || response); |
||||
* }); |
||||
*/ |
||||
module.exports = examples.Stock; |
@ -1,87 +0,0 @@ |
||||
/* |
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
'use strict'; |
||||
|
||||
var _ = require('lodash'); |
||||
var grpc = require('..'); |
||||
var examples = grpc.load(__dirname + '/stock.proto').examples; |
||||
|
||||
function getLastTradePrice(call, callback) { |
||||
callback(null, {symbol: call.request.symbol, price: 88}); |
||||
} |
||||
|
||||
function watchFutureTrades(call) { |
||||
for (var i = 0; i < call.request.num_trades_to_watch; i++) { |
||||
call.write({price: 88.00 + i * 10.00}); |
||||
} |
||||
call.end(); |
||||
} |
||||
|
||||
function getHighestTradePrice(call, callback) { |
||||
var trades = []; |
||||
call.on('data', function(data) { |
||||
trades.push({symbol: data.symbol, price: _.random(0, 100)}); |
||||
}); |
||||
call.on('end', function() { |
||||
if(_.isEmpty(trades)) { |
||||
callback(null, {}); |
||||
} else { |
||||
callback(null, _.max(trades, function(trade){return trade.price;})); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
function getLastTradePriceMultiple(call) { |
||||
call.on('data', function(data) { |
||||
call.write({price: 88}); |
||||
}); |
||||
call.on('end', function() { |
||||
call.end(); |
||||
}); |
||||
} |
||||
|
||||
var stockServer = new grpc.Server(); |
||||
stockServer.addProtoService(examples.Stock.service, { |
||||
getLastTradePrice: getLastTradePrice, |
||||
getLastTradePriceMultiple: getLastTradePriceMultiple, |
||||
watchFutureTrades: watchFutureTrades, |
||||
getHighestTradePrice: getHighestTradePrice |
||||
}); |
||||
|
||||
if (require.main === module) { |
||||
stockServer.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure()); |
||||
stockServer.start(); |
||||
} |
||||
|
||||
module.exports = stockServer; |
@ -0,0 +1,259 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#include <node.h> |
||||
|
||||
#include "grpc/grpc.h" |
||||
#include "grpc/grpc_security.h" |
||||
#include "grpc/support/log.h" |
||||
#include "call_credentials.h" |
||||
#include "call.h" |
||||
|
||||
namespace grpc { |
||||
namespace node { |
||||
|
||||
using Nan::Callback; |
||||
using Nan::EscapableHandleScope; |
||||
using Nan::HandleScope; |
||||
using Nan::Maybe; |
||||
using Nan::MaybeLocal; |
||||
using Nan::ObjectWrap; |
||||
using Nan::Persistent; |
||||
using Nan::Utf8String; |
||||
|
||||
using v8::Exception; |
||||
using v8::External; |
||||
using v8::Function; |
||||
using v8::FunctionTemplate; |
||||
using v8::Integer; |
||||
using v8::Local; |
||||
using v8::Object; |
||||
using v8::ObjectTemplate; |
||||
using v8::Value; |
||||
|
||||
Nan::Callback *CallCredentials::constructor; |
||||
Persistent<FunctionTemplate> CallCredentials::fun_tpl; |
||||
|
||||
CallCredentials::CallCredentials(grpc_credentials *credentials) |
||||
: wrapped_credentials(credentials) {} |
||||
|
||||
CallCredentials::~CallCredentials() { |
||||
grpc_credentials_release(wrapped_credentials); |
||||
} |
||||
|
||||
void CallCredentials::Init(Local<Object> exports) { |
||||
HandleScope scope; |
||||
Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New); |
||||
tpl->SetClassName(Nan::New("CallCredentials").ToLocalChecked()); |
||||
tpl->InstanceTemplate()->SetInternalFieldCount(1); |
||||
Nan::SetPrototypeMethod(tpl, "compose", Compose); |
||||
fun_tpl.Reset(tpl); |
||||
Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked(); |
||||
Nan::Set(ctr, Nan::New("createFromPlugin").ToLocalChecked(), |
||||
Nan::GetFunction( |
||||
Nan::New<FunctionTemplate>(CreateFromPlugin)).ToLocalChecked()); |
||||
Nan::Set(exports, Nan::New("CallCredentials").ToLocalChecked(), ctr); |
||||
constructor = new Nan::Callback(ctr); |
||||
} |
||||
|
||||
bool CallCredentials::HasInstance(Local<Value> val) { |
||||
HandleScope scope; |
||||
return Nan::New(fun_tpl)->HasInstance(val); |
||||
} |
||||
|
||||
Local<Value> CallCredentials::WrapStruct(grpc_credentials *credentials) { |
||||
EscapableHandleScope scope; |
||||
const int argc = 1; |
||||
if (credentials == NULL) { |
||||
return scope.Escape(Nan::Null()); |
||||
} |
||||
Local<Value> argv[argc] = { |
||||
Nan::New<External>(reinterpret_cast<void *>(credentials))}; |
||||
MaybeLocal<Object> maybe_instance = Nan::NewInstance( |
||||
constructor->GetFunction(), argc, argv); |
||||
if (maybe_instance.IsEmpty()) { |
||||
return scope.Escape(Nan::Null()); |
||||
} else { |
||||
return scope.Escape(maybe_instance.ToLocalChecked()); |
||||
} |
||||
} |
||||
|
||||
grpc_credentials *CallCredentials::GetWrappedCredentials() { |
||||
return wrapped_credentials; |
||||
} |
||||
|
||||
NAN_METHOD(CallCredentials::New) { |
||||
if (info.IsConstructCall()) { |
||||
if (!info[0]->IsExternal()) { |
||||
return Nan::ThrowTypeError( |
||||
"CallCredentials can only be created with the provided functions"); |
||||
} |
||||
Local<External> ext = info[0].As<External>(); |
||||
grpc_credentials *creds_value = |
||||
reinterpret_cast<grpc_credentials *>(ext->Value()); |
||||
CallCredentials *credentials = new CallCredentials(creds_value); |
||||
credentials->Wrap(info.This()); |
||||
info.GetReturnValue().Set(info.This()); |
||||
return; |
||||
} else { |
||||
const int argc = 1; |
||||
Local<Value> argv[argc] = {info[0]}; |
||||
MaybeLocal<Object> maybe_instance = constructor->GetFunction()->NewInstance( |
||||
argc, argv); |
||||
if (maybe_instance.IsEmpty()) { |
||||
// There's probably a pending exception
|
||||
return; |
||||
} else { |
||||
info.GetReturnValue().Set(maybe_instance.ToLocalChecked()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
NAN_METHOD(CallCredentials::Compose) { |
||||
if (!CallCredentials::HasInstance(info.This())) { |
||||
return Nan::ThrowTypeError( |
||||
"compose can only be called on CallCredentials objects"); |
||||
} |
||||
if (!CallCredentials::HasInstance(info[0])) { |
||||
return Nan::ThrowTypeError( |
||||
"compose's first argument must be a CallCredentials object"); |
||||
} |
||||
CallCredentials *self = ObjectWrap::Unwrap<CallCredentials>(info.This()); |
||||
CallCredentials *other = ObjectWrap::Unwrap<CallCredentials>( |
||||
Nan::To<Object>(info[0]).ToLocalChecked()); |
||||
grpc_credentials *creds = grpc_composite_credentials_create( |
||||
self->wrapped_credentials, other->wrapped_credentials, NULL); |
||||
info.GetReturnValue().Set(WrapStruct(creds)); |
||||
} |
||||
|
||||
|
||||
|
||||
NAN_METHOD(CallCredentials::CreateFromPlugin) { |
||||
if (!info[0]->IsFunction()) { |
||||
return Nan::ThrowTypeError( |
||||
"createFromPlugin's argument must be a function"); |
||||
} |
||||
grpc_metadata_credentials_plugin plugin; |
||||
plugin_state *state = new plugin_state; |
||||
state->callback = new Nan::Callback(info[0].As<Function>()); |
||||
plugin.get_metadata = plugin_get_metadata; |
||||
plugin.destroy = plugin_destroy_state; |
||||
plugin.state = reinterpret_cast<void*>(state); |
||||
grpc_credentials *creds = grpc_metadata_credentials_create_from_plugin(plugin, |
||||
NULL); |
||||
info.GetReturnValue().Set(WrapStruct(creds)); |
||||
} |
||||
|
||||
NAN_METHOD(PluginCallback) { |
||||
// Arguments: status code, error details, metadata
|
||||
if (!info[0]->IsUint32()) { |
||||
return Nan::ThrowTypeError( |
||||
"The callback's first argument must be a status code"); |
||||
} |
||||
if (!info[1]->IsString()) { |
||||
return Nan::ThrowTypeError( |
||||
"The callback's second argument must be a string"); |
||||
} |
||||
if (!info[2]->IsObject()) { |
||||
return Nan::ThrowTypeError( |
||||
"The callback's third argument must be an object"); |
||||
} |
||||
shared_ptr<Resources> resources(new Resources); |
||||
grpc_status_code code = static_cast<grpc_status_code>( |
||||
Nan::To<uint32_t>(info[0]).FromJust()); |
||||
char *details = *Utf8String(info[1]); |
||||
grpc_metadata_array array; |
||||
if (!CreateMetadataArray(Nan::To<Object>(info[2]).ToLocalChecked(), |
||||
&array, resources)){ |
||||
return Nan::ThrowError("Failed to parse metadata"); |
||||
} |
||||
grpc_credentials_plugin_metadata_cb cb = |
||||
reinterpret_cast<grpc_credentials_plugin_metadata_cb>( |
||||
Nan::Get(info.Callee(), |
||||
Nan::New("cb").ToLocalChecked() |
||||
).ToLocalChecked().As<External>()->Value()); |
||||
void *user_data = |
||||
Nan::Get(info.Callee(), |
||||
Nan::New("user_data").ToLocalChecked() |
||||
).ToLocalChecked().As<External>()->Value(); |
||||
cb(user_data, array.metadata, array.count, code, details); |
||||
} |
||||
|
||||
NAUV_WORK_CB(SendPluginCallback) { |
||||
Nan::HandleScope scope; |
||||
plugin_callback_data *data = reinterpret_cast<plugin_callback_data*>( |
||||
async->data); |
||||
// Attach cb and user_data to plugin_callback so that it can access them later
|
||||
v8::Local<v8::Function> plugin_callback = Nan::GetFunction( |
||||
Nan::New<v8::FunctionTemplate>(PluginCallback)).ToLocalChecked(); |
||||
Nan::Set(plugin_callback, Nan::New("cb").ToLocalChecked(), |
||||
Nan::New<v8::External>(reinterpret_cast<void*>(data->cb))); |
||||
Nan::Set(plugin_callback, Nan::New("user_data").ToLocalChecked(), |
||||
Nan::New<v8::External>(data->user_data)); |
||||
const int argc = 2; |
||||
v8::Local<v8::Value> argv[argc] = { |
||||
Nan::New(data->service_url).ToLocalChecked(), |
||||
plugin_callback |
||||
}; |
||||
Nan::Callback *callback = data->state->callback; |
||||
callback->Call(argc, argv); |
||||
delete data; |
||||
uv_unref((uv_handle_t *)async); |
||||
uv_close((uv_handle_t *)async, (uv_close_cb)free); |
||||
} |
||||
|
||||
void plugin_get_metadata(void *state, const char *service_url, |
||||
grpc_credentials_plugin_metadata_cb cb, |
||||
void *user_data) { |
||||
uv_async_t *async = static_cast<uv_async_t*>(malloc(sizeof(uv_async_t))); |
||||
uv_async_init(uv_default_loop(), |
||||
async, |
||||
SendPluginCallback); |
||||
plugin_callback_data *data = new plugin_callback_data; |
||||
data->state = reinterpret_cast<plugin_state*>(state); |
||||
data->service_url = service_url; |
||||
data->cb = cb; |
||||
data->user_data = user_data; |
||||
async->data = data; |
||||
/* libuv says that it will coalesce calls to uv_async_send. If there is ever a
|
||||
* problem with a callback not getting called, that is probably the reason */ |
||||
uv_async_send(async); |
||||
} |
||||
|
||||
void plugin_destroy_state(void *ptr) { |
||||
plugin_state *state = reinterpret_cast<plugin_state *>(ptr); |
||||
delete state->callback; |
||||
} |
||||
|
||||
} // namespace node
|
||||
} // namespace grpc
|
@ -0,0 +1,100 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPC_NODE_CALL_CREDENTIALS_H_ |
||||
#define GRPC_NODE_CALL_CREDENTIALS_H_ |
||||
|
||||
#include <node.h> |
||||
#include <nan.h> |
||||
#include "grpc/grpc_security.h" |
||||
|
||||
namespace grpc { |
||||
namespace node { |
||||
|
||||
class CallCredentials : public Nan::ObjectWrap { |
||||
public: |
||||
static void Init(v8::Local<v8::Object> exports); |
||||
static bool HasInstance(v8::Local<v8::Value> val); |
||||
/* Wrap a grpc_credentials struct in a javascript object */ |
||||
static v8::Local<v8::Value> WrapStruct(grpc_credentials *credentials); |
||||
|
||||
/* Returns the grpc_credentials struct that this object wraps */ |
||||
grpc_credentials *GetWrappedCredentials(); |
||||
|
||||
private: |
||||
explicit CallCredentials(grpc_credentials *credentials); |
||||
~CallCredentials(); |
||||
|
||||
// Prevent copying
|
||||
CallCredentials(const CallCredentials &); |
||||
CallCredentials &operator=(const CallCredentials &); |
||||
|
||||
static NAN_METHOD(New); |
||||
static NAN_METHOD(CreateSsl); |
||||
static NAN_METHOD(CreateFromPlugin); |
||||
|
||||
static NAN_METHOD(Compose); |
||||
static Nan::Callback *constructor; |
||||
// Used for typechecking instances of this javascript class
|
||||
static Nan::Persistent<v8::FunctionTemplate> fun_tpl; |
||||
|
||||
grpc_credentials *wrapped_credentials; |
||||
}; |
||||
|
||||
/* Auth metadata plugin functionality */ |
||||
|
||||
typedef struct plugin_state { |
||||
Nan::Callback *callback; |
||||
} plugin_state; |
||||
|
||||
typedef struct plugin_callback_data { |
||||
plugin_state *state; |
||||
const char *service_url; |
||||
grpc_credentials_plugin_metadata_cb cb; |
||||
void *user_data; |
||||
} plugin_callback_data; |
||||
|
||||
void plugin_get_metadata(void *state, const char *service_url, |
||||
grpc_credentials_plugin_metadata_cb cb, |
||||
void *user_data); |
||||
|
||||
void plugin_destroy_state(void *state); |
||||
|
||||
NAN_METHOD(PluginCallback); |
||||
|
||||
NAUV_WORK_CB(SendPluginCallback); |
||||
|
||||
} // namespace node
|
||||
} // namepsace grpc
|
||||
|
||||
#endif // GRPC_NODE_CALL_CREDENTIALS_H_
|
@ -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 {} |
@ -1,132 +0,0 @@ |
||||
|
||||
// Copyright 2015, Google Inc. |
||||
// All rights reserved. |
||||
// |
||||
// Redistribution and use in source and binary forms, with or without |
||||
// modification, are permitted provided that the following conditions are |
||||
// met: |
||||
// |
||||
// * Redistributions of source code must retain the above copyright |
||||
// notice, this list of conditions and the following disclaimer. |
||||
// * Redistributions in binary form must reproduce the above |
||||
// copyright notice, this list of conditions and the following disclaimer |
||||
// in the documentation and/or other materials provided with the |
||||
// distribution. |
||||
// * Neither the name of Google Inc. nor the names of its |
||||
// contributors may be used to endorse or promote products derived from |
||||
// this software without specific prior written permission. |
||||
// |
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
// Message definitions to be used by integration test service definitions. |
||||
|
||||
syntax = "proto3"; |
||||
|
||||
package grpc.testing; |
||||
|
||||
// The type of payload that should be returned. |
||||
enum PayloadType { |
||||
// Compressable text format. |
||||
COMPRESSABLE = 0; |
||||
|
||||
// Uncompressable binary format. |
||||
UNCOMPRESSABLE = 1; |
||||
|
||||
// Randomly chosen from all other formats defined in this enum. |
||||
RANDOM = 2; |
||||
} |
||||
|
||||
// A block of data, to simply increase gRPC message size. |
||||
message Payload { |
||||
// The type of data in body. |
||||
PayloadType type = 1; |
||||
// Primary contents of payload. |
||||
bytes body = 2; |
||||
} |
||||
|
||||
// Unary request. |
||||
message SimpleRequest { |
||||
// Desired payload type in the response from the server. |
||||
// If response_type is RANDOM, server randomly chooses one from other formats. |
||||
PayloadType response_type = 1; |
||||
|
||||
// Desired payload size in the response from the server. |
||||
// If response_type is COMPRESSABLE, this denotes the size before compression. |
||||
int32 response_size = 2; |
||||
|
||||
// Optional input payload sent along with the request. |
||||
Payload payload = 3; |
||||
|
||||
// Whether SimpleResponse should include username. |
||||
bool fill_username = 4; |
||||
|
||||
// Whether SimpleResponse should include OAuth scope. |
||||
bool fill_oauth_scope = 5; |
||||
} |
||||
|
||||
// Unary response, as configured by the request. |
||||
message SimpleResponse { |
||||
// Payload to increase message size. |
||||
Payload payload = 1; |
||||
// The user the request came from, for verifying authentication was |
||||
// successful when the client expected it. |
||||
string username = 2; |
||||
// OAuth scope. |
||||
string oauth_scope = 3; |
||||
} |
||||
|
||||
// Client-streaming request. |
||||
message StreamingInputCallRequest { |
||||
// Optional input payload sent along with the request. |
||||
Payload payload = 1; |
||||
|
||||
// Not expecting any payload from the response. |
||||
} |
||||
|
||||
// Client-streaming response. |
||||
message StreamingInputCallResponse { |
||||
// Aggregated size of payloads received from the client. |
||||
int32 aggregated_payload_size = 1; |
||||
} |
||||
|
||||
// Configuration for a particular response. |
||||
message ResponseParameters { |
||||
// Desired payload sizes in responses from the server. |
||||
// If response_type is COMPRESSABLE, this denotes the size before compression. |
||||
int32 size = 1; |
||||
|
||||
// Desired interval between consecutive responses in the response stream in |
||||
// microseconds. |
||||
int32 interval_us = 2; |
||||
} |
||||
|
||||
// Server-streaming request. |
||||
message StreamingOutputCallRequest { |
||||
// Desired payload type in the response from the server. |
||||
// If response_type is RANDOM, the payload from each response in the stream |
||||
// might be of different types. This is to simulate a mixed type of payload |
||||
// stream. |
||||
PayloadType response_type = 1; |
||||
|
||||
// Configuration for each expected response message. |
||||
repeated ResponseParameters response_parameters = 2; |
||||
|
||||
// Optional input payload sent along with the request. |
||||
Payload payload = 3; |
||||
} |
||||
|
||||
// Server-streaming response, as configured by the request and parameters. |
||||
message StreamingOutputCallResponse { |
||||
// Payload to increase response size. |
||||
Payload payload = 1; |
||||
} |
@ -1,72 +0,0 @@ |
||||
|
||||
// Copyright 2015, Google Inc. |
||||
// All rights reserved. |
||||
// |
||||
// Redistribution and use in source and binary forms, with or without |
||||
// modification, are permitted provided that the following conditions are |
||||
// met: |
||||
// |
||||
// * Redistributions of source code must retain the above copyright |
||||
// notice, this list of conditions and the following disclaimer. |
||||
// * Redistributions in binary form must reproduce the above |
||||
// copyright notice, this list of conditions and the following disclaimer |
||||
// in the documentation and/or other materials provided with the |
||||
// distribution. |
||||
// * Neither the name of Google Inc. nor the names of its |
||||
// contributors may be used to endorse or promote products derived from |
||||
// this software without specific prior written permission. |
||||
// |
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
// An integration test service that covers all the method signature permutations |
||||
// of unary/streaming requests/responses. |
||||
|
||||
syntax = "proto3"; |
||||
|
||||
import "empty.proto"; |
||||
import "messages.proto"; |
||||
|
||||
package grpc.testing; |
||||
|
||||
// A simple service to test the various types of RPCs and experiment with |
||||
// performance with various types of payload. |
||||
service TestService { |
||||
// One empty request followed by one empty response. |
||||
rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty); |
||||
|
||||
// One request followed by one response. |
||||
rpc UnaryCall(SimpleRequest) returns (SimpleResponse); |
||||
|
||||
// One request followed by a sequence of responses (streamed download). |
||||
// The server returns the payload with client desired type and sizes. |
||||
rpc StreamingOutputCall(StreamingOutputCallRequest) |
||||
returns (stream StreamingOutputCallResponse); |
||||
|
||||
// A sequence of requests followed by one response (streamed upload). |
||||
// The server returns the aggregated size of client payload as the result. |
||||
rpc StreamingInputCall(stream StreamingInputCallRequest) |
||||
returns (StreamingInputCallResponse); |
||||
|
||||
// A sequence of requests with each request served by the server immediately. |
||||
// As one request could lead to multiple responses, this interface |
||||
// demonstrates the idea of full duplexing. |
||||
rpc FullDuplexCall(stream StreamingOutputCallRequest) |
||||
returns (stream StreamingOutputCallResponse); |
||||
|
||||
// A sequence of requests followed by a sequence of responses. |
||||
// The server buffers all the client requests and then serves them in order. A |
||||
// stream of responses are returned to the client when the server starts with |
||||
// first request. |
||||
rpc HalfDuplexCall(stream StreamingOutputCallRequest) |
||||
returns (stream StreamingOutputCallResponse); |
||||
} |
@ -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; |
@ -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(); |
||||
}); |
||||
}); |
||||
}); |
||||
}); |
Loading…
Reference in new issue