mirror of https://github.com/grpc/grpc.git
commit
1d39a06d06
88 changed files with 1798 additions and 886 deletions
@ -0,0 +1,77 @@ |
||||
# gRPC command line tool |
||||
|
||||
## Overview |
||||
|
||||
This document describes the command line tool that comes with gRPC repository. It is desireable to have command line |
||||
tools written in other languages to roughly follow the same syntax and flags. |
||||
|
||||
At this point, the tool needs to be built from source, and it should be moved out to grpc-tools repository as a stand |
||||
alone application once it is mature enough. |
||||
|
||||
## Core functionality |
||||
|
||||
The command line tool can do the following things: |
||||
|
||||
- Send unary rpc. |
||||
- Attach metadata and display received metadata. |
||||
- Handle common authentication to server. |
||||
- Find the request/response types from a given proto file. |
||||
- Read proto request in text form. |
||||
- Read request in wire form (for protobuf messages, this means serialized binary form). |
||||
- Display proto response in text form. |
||||
- Write response in wire form to a file. |
||||
|
||||
The command line tool should support the following things: |
||||
|
||||
- List server services and methods through server reflection. |
||||
- Infer request/response types from server reflection result. |
||||
- Fine-grained auth control (such as, use this oauth token to talk to the server). |
||||
- Send streaming rpc. |
||||
|
||||
## Code location |
||||
|
||||
To use the tool, you need to get the grpc repository and in the grpc directory execute |
||||
|
||||
``` |
||||
$ make grpc_cli |
||||
``` |
||||
|
||||
The main file can be found at |
||||
https://github.com/grpc/grpc/blob/master/test/cpp/util/grpc_cli.cc |
||||
|
||||
## Usage |
||||
|
||||
### Basic usage |
||||
|
||||
Send a rpc to a helloworld server at `localhost:50051`: |
||||
|
||||
``` |
||||
$ bins/opt/grpc_cli call localhost:50051 SayHello examples/protos/helloworld.proto \ |
||||
"name: 'world'" --enable_ssl=false |
||||
``` |
||||
|
||||
On success, the tool will print out |
||||
|
||||
``` |
||||
Rpc succeeded with OK status |
||||
Response: |
||||
message: "Hello world" |
||||
``` |
||||
|
||||
The `localhost:50051` part indicates the server you are connecting to. `SayHello` is (part of) the |
||||
gRPC method string. Then there is the path to the proto file containing the service definition, |
||||
if it is not under current directory, you can use `--proto_path` to specify a new search root. |
||||
`"name: 'world'"` is the text format of the request proto message. |
||||
We are not using ssl here by `--enable_ssl=false`. For information on more |
||||
flags, look at the comments of `grpc_cli.cc`. |
||||
|
||||
### Send non-proto rpc |
||||
|
||||
For using gRPC with protocols other than probobuf, you will need the exact method name string |
||||
and a file containing the raw bytes to be sent on the wire |
||||
|
||||
``` |
||||
$ bins/opt/grpc_cli call localhost:50051 /helloworld.Greeter/SayHello --input_binary_file=input.bin \ |
||||
--output_binary_file=output.bin |
||||
``` |
||||
On success, you will need to read or decode the response from the `output.bin` file. |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,39 @@ |
||||
# Copyright 2016, 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. |
||||
|
||||
|
||||
# This function will ascii encode unicode string inputs if neccesary. |
||||
# In Python3, unicode strings are the default str type. |
||||
cdef bytes str_to_bytes(object s): |
||||
if s is None or isinstance(s, bytes): |
||||
return s |
||||
elif isinstance(s, unicode): |
||||
return s.encode('ascii') |
||||
else: |
||||
raise TypeError('Expected bytes, str, or unicode, not {}'.format(type(s))) |
@ -0,0 +1,137 @@ |
||||
# Copyright 2016, 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. |
||||
|
||||
import unittest |
||||
|
||||
import grpc |
||||
from grpc.framework.foundation import logging_pool |
||||
|
||||
from tests.unit.framework.common import test_constants |
||||
|
||||
_REQUEST = b'' |
||||
_RESPONSE = b'' |
||||
|
||||
_UNARY_UNARY = b'/test/UnaryUnary' |
||||
_UNARY_STREAM = b'/test/UnaryStream' |
||||
_STREAM_UNARY = b'/test/StreamUnary' |
||||
_STREAM_STREAM = b'/test/StreamStream' |
||||
|
||||
|
||||
def handle_unary_unary(request, servicer_context): |
||||
return _RESPONSE |
||||
|
||||
|
||||
def handle_unary_stream(request, servicer_context): |
||||
for _ in range(test_constants.STREAM_LENGTH): |
||||
yield _RESPONSE |
||||
|
||||
|
||||
def handle_stream_unary(request_iterator, servicer_context): |
||||
for request in request_iterator: |
||||
pass |
||||
return _RESPONSE |
||||
|
||||
|
||||
def handle_stream_stream(request_iterator, servicer_context): |
||||
for request in request_iterator: |
||||
yield _RESPONSE |
||||
|
||||
|
||||
class _MethodHandler(grpc.RpcMethodHandler): |
||||
|
||||
def __init__(self, request_streaming, response_streaming): |
||||
self.request_streaming = request_streaming |
||||
self.response_streaming = response_streaming |
||||
self.request_deserializer = None |
||||
self.response_serializer = None |
||||
self.unary_unary = None |
||||
self.unary_stream = None |
||||
self.stream_unary = None |
||||
self.stream_stream = None |
||||
if self.request_streaming and self.response_streaming: |
||||
self.stream_stream = handle_stream_stream |
||||
elif self.request_streaming: |
||||
self.stream_unary = handle_stream_unary |
||||
elif self.response_streaming: |
||||
self.unary_stream = handle_unary_stream |
||||
else: |
||||
self.unary_unary = handle_unary_unary |
||||
|
||||
|
||||
class _GenericHandler(grpc.GenericRpcHandler): |
||||
|
||||
def service(self, handler_call_details): |
||||
if handler_call_details.method == _UNARY_UNARY: |
||||
return _MethodHandler(False, False) |
||||
elif handler_call_details.method == _UNARY_STREAM: |
||||
return _MethodHandler(False, True) |
||||
elif handler_call_details.method == _STREAM_UNARY: |
||||
return _MethodHandler(True, False) |
||||
elif handler_call_details.method == _STREAM_STREAM: |
||||
return _MethodHandler(True, True) |
||||
else: |
||||
return None |
||||
|
||||
|
||||
class EmptyMessageTest(unittest.TestCase): |
||||
|
||||
def setUp(self): |
||||
self._server_pool = logging_pool.pool(test_constants.THREAD_CONCURRENCY) |
||||
self._server = grpc.server((_GenericHandler(),), self._server_pool) |
||||
port = self._server.add_insecure_port('[::]:0') |
||||
self._server.start() |
||||
self._channel = grpc.insecure_channel('localhost:%d' % port) |
||||
|
||||
def tearDown(self): |
||||
self._server.stop(0) |
||||
|
||||
def testUnaryUnary(self): |
||||
response = self._channel.unary_unary(_UNARY_UNARY)(_REQUEST) |
||||
self.assertEqual(_RESPONSE, response) |
||||
|
||||
def testUnaryStream(self): |
||||
response_iterator = self._channel.unary_stream(_UNARY_STREAM)(_REQUEST) |
||||
self.assertSequenceEqual( |
||||
[_RESPONSE] * test_constants.STREAM_LENGTH, list(response_iterator)) |
||||
|
||||
def testStreamUnary(self): |
||||
response = self._channel.stream_unary(_STREAM_UNARY)( |
||||
[_REQUEST] * test_constants.STREAM_LENGTH) |
||||
self.assertEqual(_RESPONSE, response) |
||||
|
||||
def testStreamStream(self): |
||||
response_iterator = self._channel.stream_stream(_STREAM_STREAM)( |
||||
[_REQUEST] * test_constants.STREAM_LENGTH) |
||||
self.assertSequenceEqual( |
||||
[_RESPONSE] * test_constants.STREAM_LENGTH, list(response_iterator)) |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
unittest.main(verbosity=2) |
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue