mirror of https://github.com/grpc/grpc.git
Merge branch 'master' of https://github.com/grpc/grpc into moiz-upb
commit
3de02bc8cd
277 changed files with 5540 additions and 2132 deletions
@ -0,0 +1,65 @@ |
||||
# Copyright 2019 The gRPC Authors |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
filegroup( |
||||
name = "_credentials_files", |
||||
testonly = 1, |
||||
srcs = [ |
||||
"credentials/localhost.key", |
||||
"credentials/localhost.crt", |
||||
"credentials/root.crt", |
||||
], |
||||
) |
||||
|
||||
py_library( |
||||
name = "_credentials", |
||||
testonly = 1, |
||||
srcs = ["_credentials.py"], |
||||
data = [":_credentials_files"], |
||||
) |
||||
|
||||
py_binary( |
||||
name = "customized_auth_client", |
||||
testonly = 1, |
||||
srcs = ["customized_auth_client.py"], |
||||
deps = [ |
||||
":_credentials", |
||||
"//src/python/grpcio/grpc:grpcio", |
||||
"//examples:py_helloworld", |
||||
], |
||||
) |
||||
|
||||
py_binary( |
||||
name = "customized_auth_server", |
||||
testonly = 1, |
||||
srcs = ["customized_auth_server.py"], |
||||
deps = [ |
||||
":_credentials", |
||||
"//src/python/grpcio/grpc:grpcio", |
||||
"//examples:py_helloworld", |
||||
|
||||
], |
||||
) |
||||
|
||||
py_test( |
||||
name = "_auth_example_test", |
||||
srcs = ["test/_auth_example_test.py"], |
||||
deps = [ |
||||
"//src/python/grpcio/grpc:grpcio", |
||||
"//examples:py_helloworld", |
||||
":customized_auth_client", |
||||
":customized_auth_server", |
||||
":_credentials", |
||||
], |
||||
) |
@ -0,0 +1,112 @@ |
||||
# Authentication Extension Example in gRPC Python |
||||
|
||||
## Check Our Guide First |
||||
|
||||
For most common usage of authentication in gRPC Python, please see our |
||||
[Authentication](https://grpc.io/docs/guides/auth/) guide's Python section. The |
||||
Guide includes following scenarios: |
||||
|
||||
1. Server SSL credential setup |
||||
2. Client SSL credential setup |
||||
3. Authenticate with Google using a JWT |
||||
4. Authenticate with Google using an Oauth2 token |
||||
|
||||
Also, the guide talks about gRPC specific credential types. |
||||
|
||||
### Channel credentials |
||||
|
||||
Channel credentials are attached to a `Channel` object, the most common use case |
||||
are SSL credentials. |
||||
|
||||
### Call credentials |
||||
|
||||
Call credentials are attached to a `Call` object (corresponding to an RPC). |
||||
Under the hood, the call credentials is a function that takes in information of |
||||
the RPC and modify metadata through callback. |
||||
|
||||
## About This Example |
||||
|
||||
This example focuses on extending gRPC authentication mechanism: |
||||
1) Customize authentication plugin; |
||||
2) Composite client side credentials; |
||||
3) Validation through interceptor on server side. |
||||
|
||||
## AuthMetadataPlugin: Manipulate metadata for each call |
||||
|
||||
Unlike TLS/SSL based authentication, the authentication extension in gRPC Python |
||||
lives at a much higher level of networking. It relies on the transmission of |
||||
metadata (HTTP Header) between client and server, instead of alternating the |
||||
transport protocol. |
||||
|
||||
gRPC Python provides a way to intercept an RPC and append authentication related |
||||
metadata through |
||||
[`AuthMetadataPlugin`](https://grpc.github.io/grpc/python/grpc.html#grpc.AuthMetadataPlugin). |
||||
Those in need of a custom authentication method may simply provide a concrete |
||||
implementation of the following interface: |
||||
|
||||
```Python |
||||
class AuthMetadataPlugin: |
||||
"""A specification for custom authentication.""" |
||||
|
||||
def __call__(self, context, callback): |
||||
"""Implements authentication by passing metadata to a callback. |
||||
|
||||
Implementations of this method must not block. |
||||
|
||||
Args: |
||||
context: An AuthMetadataContext providing information on the RPC that |
||||
the plugin is being called to authenticate. |
||||
callback: An AuthMetadataPluginCallback to be invoked either |
||||
synchronously or asynchronously. |
||||
""" |
||||
``` |
||||
|
||||
Then pass the instance of the concrete implementation to |
||||
`grpc.metadata_call_credentials` function to be converted into a |
||||
`CallCredentials` object. Please NOTE that it is possible to pass a Python |
||||
function object directly, but we recommend to inherit from the base class to |
||||
ensure implementation correctness. |
||||
|
||||
|
||||
```Python |
||||
def metadata_call_credentials(metadata_plugin, name=None): |
||||
"""Construct CallCredentials from an AuthMetadataPlugin. |
||||
|
||||
Args: |
||||
metadata_plugin: An AuthMetadataPlugin to use for authentication. |
||||
name: An optional name for the plugin. |
||||
|
||||
Returns: |
||||
A CallCredentials. |
||||
""" |
||||
``` |
||||
|
||||
The `CallCredentials` object can be passed directly into an RPC like: |
||||
|
||||
```Python |
||||
call_credentials = grpc.metadata_call_credentials(my_foo_plugin) |
||||
stub.FooRpc(request, credentials=call_credentials) |
||||
``` |
||||
|
||||
Or you can use `ChannelCredentials` and `CallCredentials` at the same time by |
||||
combining them: |
||||
|
||||
```Python |
||||
channel_credentials = ... |
||||
call_credentials = ... |
||||
composite_credentials = grpc.composite_channel_credentials( |
||||
channel_credential, |
||||
call_credentials) |
||||
channel = grpc.secure_channel(server_address, composite_credentials) |
||||
``` |
||||
|
||||
It is also possible to apply multiple `CallCredentials` to a single RPC: |
||||
|
||||
```Python |
||||
call_credentials_foo = ... |
||||
call_credentials_bar = ... |
||||
call_credentials = grpc.composite_call_credentials( |
||||
call_credentials_foo, |
||||
call_credentials_bar) |
||||
stub.FooRpc(request, credentials=call_credentials) |
||||
``` |
@ -0,0 +1,31 @@ |
||||
# Copyright 2019 The gRPC Authors |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""Loading SSL credentials for gRPC Python authentication example.""" |
||||
|
||||
from __future__ import absolute_import |
||||
from __future__ import division |
||||
from __future__ import print_function |
||||
|
||||
import os |
||||
|
||||
|
||||
def _load_credential_from_file(filepath): |
||||
real_path = os.path.join(os.path.dirname(__file__), filepath) |
||||
with open(real_path, 'rb') as f: |
||||
return f.read() |
||||
|
||||
|
||||
SERVER_CERTIFICATE = _load_credential_from_file('credentials/localhost.crt') |
||||
SERVER_CERTIFICATE_KEY = _load_credential_from_file('credentials/localhost.key') |
||||
ROOT_CERTIFICATE = _load_credential_from_file('credentials/root.crt') |
@ -0,0 +1,19 @@ |
||||
-----BEGIN CERTIFICATE----- |
||||
MIIDFjCCAf4CCQCzrLIhrWa55zANBgkqhkiG9w0BAQsFADBCMQswCQYDVQQGEwJV |
||||
UzETMBEGA1UECAwKQ2FsaWZvcm5pYTEPMA0GA1UECgwGR29vZ2xlMQ0wCwYDVQQL |
||||
DARnUlBDMCAXDTE5MDYyNDIyMjIzM1oYDzIxMTkwNTMxMjIyMjMzWjBWMQswCQYD |
||||
VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEPMA0GA1UECgwGR29vZ2xlMQ0w |
||||
CwYDVQQLDARnUlBDMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB |
||||
AQUAA4IBDwAwggEKAoIBAQCtCW0TjugnIUu8BEVIYvdMP+/2GENQDjZhZ8eKR5C6 |
||||
toDGbgjsDtt/GxISAg4cg70fIvy0XolnGPZodvfHDM4lJ7yHBOdZD8TXQoE6okR7 |
||||
HZuLUJ20M0pXgWqtRewKRUjuYsSDXBnzLiZw1dcv9nGpo+Bqa8NonpiGRRpEkshF |
||||
D6T9KU9Ts/x+wMQBIra2Gj0UMh79jPhUuxcYAQA0JQGivnOtdwuPiumpnUT8j8h6 |
||||
tWg5l01EsCZWJecCF85KnGpJEVYPyPqBqGsy0nGS9plGotOWF87+jyUQt+KD63xA |
||||
aBmTro86mKDDKEK4JvzjVeMGz2UbVcLPiiZnErTFaiXJAgMBAAEwDQYJKoZIhvcN |
||||
AQELBQADggEBAKsDgOPCWp5WCy17vJbRlgfgk05sVNIHZtzrmdswjBmvSg8MUpep |
||||
XqcPNUpsljAXsf9UM5IFEMRdilUsFGWvHjBEtNAW8WUK9UV18WRuU//0w1Mp5HAN |
||||
xUEKb4BoyZr65vlCnTR+AR5c9FfPvLibhr5qHs2RA8Y3GyLOcGqBWed87jhdQLCc |
||||
P1bxB+96le5JeXq0tw215lxonI2/3ZYVK4/ok9gwXrQoWm8YieJqitk/ZQ4S17/4 |
||||
pynHtDfdxLn23EXeGx+UTxJGfpRmhEZdJ+MN7QGYoomzx5qS5XoYKxRNrDlirJpr |
||||
OqXIn8E1it+6d5gOZfuHawcNGhRLplE/pfA= |
||||
-----END CERTIFICATE----- |
@ -0,0 +1,27 @@ |
||||
-----BEGIN RSA PRIVATE KEY----- |
||||
MIIEogIBAAKCAQEArQltE47oJyFLvARFSGL3TD/v9hhDUA42YWfHikeQuraAxm4I |
||||
7A7bfxsSEgIOHIO9HyL8tF6JZxj2aHb3xwzOJSe8hwTnWQ/E10KBOqJEex2bi1Cd |
||||
tDNKV4FqrUXsCkVI7mLEg1wZ8y4mcNXXL/ZxqaPgamvDaJ6YhkUaRJLIRQ+k/SlP |
||||
U7P8fsDEASK2tho9FDIe/Yz4VLsXGAEANCUBor5zrXcLj4rpqZ1E/I/IerVoOZdN |
||||
RLAmViXnAhfOSpxqSRFWD8j6gahrMtJxkvaZRqLTlhfO/o8lELfig+t8QGgZk66P |
||||
OpigwyhCuCb841XjBs9lG1XCz4omZxK0xWolyQIDAQABAoIBADeq/Kh6JT3RfGf0 |
||||
h8WN8TlaqHxnueAbcmtL0+oss+cdp7gu1jf7X6o4r0uT1a5ew40s2Fe+wj2kzkE1 |
||||
ZOlouTlC22gkr7j7Vbxa7PBMG/Pvxoa/XL0IczZLsGImSJXVTG1E4SvRiZeulTdf |
||||
1GbdxhtpWV1jZe5Wd4Na3+SHxF5S7m3PrHiZlYdz1ND+8XZs1NlL9+ej72qSFul9 |
||||
t/QjMWJ9pky/Wad5abnRLRyOsg+BsgnXbkUy2rD89ZxFMLda9pzXo3TPyAlBHonr |
||||
mkEsE4eRMWMpjBM79JbeyDdHn/cs/LjAZrzeDf7ugXr2CHQpKaM5O0PsNHezJII9 |
||||
L5kCfzECgYEA4M/rz1UP1/BJoSqigUlSs0tPAg8a5UlkVsh6Osuq72IPNo8qg/Fw |
||||
oV/IiIS+q+obRcFj1Od3PGdTpCJwW5dzd2fXBQGmGdj0HucnCrs13RtBh91JiF5i |
||||
y/YYI9KfgOG2ZT9gG68T0gTs6jRrS3Qd83npqjrkJqMOd7s00MK9tUcCgYEAxQq7 |
||||
T541oCYHSBRIIb0IrR25krZy9caxzCqPDwOcuuhaCqCiaq+ATvOWlSfgecm4eH0K |
||||
PCH0xlWxG0auPEwm4pA8+/WR/XJwscPZMuoht1EoKy1his4eKx/s7hHNeO6KOF0V |
||||
Y/zqIiuZnEwUoKbn7EqqNFSTT65PJKyGsICJFG8CgYAfaw9yl1myfQNdQb8aQGwN |
||||
YJ33FLNWje427qeeZe5KrDKiFloDvI9YDjHRWnPnRL1w/zj7fSm9yFb5HlMDieP6 |
||||
MQnsyjEzdY2QcA+VwVoiv3dmDHgFVeOKy6bOAtaFxYWfGr9MvygO9t9BT/gawGyb |
||||
JVORlc9i0vDnrMMR1dV7awKBgBpTWLtGc/u1mPt0Wj7HtsUKV6TWY32a0l5owTxM |
||||
S0BdksogtBJ06DukJ9Y9wawD23WdnyRxlPZ6tHLkeprrwbY7dypioOKvy4a0l+xJ |
||||
g7+uRCOgqIuXBkjUtx8HmeAyXp0xMo5tWArAsIFFWOwt4IadYygitJvMuh44PraO |
||||
NcJZAoGADEiV0dheXUCVr8DrtSom8DQMj92/G/FIYjXL8OUhh0+F+YlYP0+F8PEU |
||||
yYIWEqL/S5tVKYshimUXQa537JcRKsTVJBG/ZKD2kuqgOc72zQy3oplimXeJDCXY |
||||
h2eAQ0u8GN6tN9C4t8Kp4a3y6FGsxgu+UTxdnL3YQ+yHAVhtCzo= |
||||
-----END RSA PRIVATE KEY----- |
@ -0,0 +1,20 @@ |
||||
-----BEGIN CERTIFICATE----- |
||||
MIIDWTCCAkGgAwIBAgIJAPOConZMwykwMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNV |
||||
BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMQ8wDQYDVQQKDAZHb29nbGUxDTAL |
||||
BgNVBAsMBGdSUEMwIBcNMTkwNjI0MjIyMDA3WhgPMjExOTA1MzEyMjIwMDdaMEIx |
||||
CzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMQ8wDQYDVQQKDAZHb29n |
||||
bGUxDTALBgNVBAsMBGdSUEMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB |
||||
AQCwqei3TfyLidnQNDJ2lierMYo229K92DuORni7nSjJQ59Jc3dNMsmqGQJjCD8o |
||||
6mTlKM/oCbs27Wpx+OxcOLvT95j2kiDGca1fCvaMdguIod09SWiyMpv/hp0trLv7 |
||||
NJIKHznath6rHYX2Ii3fZ1yCPzyQbEPSAA+GNpoNm1v1ZWmWKke9v7vLlS3inNlW |
||||
Mt9jepK7DrtbNZnVDjeItnppBSbVYRMxIyNHkepFbqXx5TpkCvl4M4XQZw9bfSxQ |
||||
i3WZ3q+T1Tw//OUdPNc+OfMhu0MA0QoMwikskP0NaIC3dbJZ5Ogx0RcnaB4E+9C6 |
||||
O/znUEh3WuKVl5HXBF+UwWoFAgMBAAGjUDBOMB0GA1UdDgQWBBRm3JIgzgK4G97J |
||||
fbMGatWMZc7V3jAfBgNVHSMEGDAWgBRm3JIgzgK4G97JfbMGatWMZc7V3jAMBgNV |
||||
HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCNiV8x41if094ry2srS0YucpiN |
||||
3rTPk08FOLsENTMYai524TGXJti1P6ofGr5KXCL0uxTByHE3fEiMMud2TIY5iHQo |
||||
Y4mzDTTcb+Q7yKHwYZMlcp6nO8W+NeY5t+S0JPHhb8deKWepcN2UpXBUYQLw7AiE |
||||
l96T9Gi+vC9h/XE5IVwHFQXTxf5UYzXtW1nfapvrOONg/ms41dgmrRKIi+knWfiJ |
||||
FdHpHX2sfDAoJtnpEISX+nxRGNVTLY64utXWm4yxaZJshvy2s8zWJgRg7rtwAhTT |
||||
Np9E9MnihXLEmDI4Co9XlLPJyZFmqImsbmVuKFeQOCiLAoPJaMI2lbi7fiTo |
||||
-----END CERTIFICATE----- |
@ -0,0 +1,105 @@ |
||||
# Copyright 2019 The gRPC Authors |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""Client of the Python example of customizing authentication mechanism.""" |
||||
|
||||
from __future__ import absolute_import |
||||
from __future__ import division |
||||
from __future__ import print_function |
||||
|
||||
import argparse |
||||
import contextlib |
||||
import logging |
||||
|
||||
import grpc |
||||
from examples import helloworld_pb2 |
||||
from examples import helloworld_pb2_grpc |
||||
from examples.python.auth import _credentials |
||||
|
||||
_LOGGER = logging.getLogger(__name__) |
||||
_LOGGER.setLevel(logging.INFO) |
||||
|
||||
_ONE_DAY_IN_SECONDS = 60 * 60 * 24 |
||||
|
||||
_SERVER_ADDR_TEMPLATE = 'localhost:%d' |
||||
_SIGNATURE_HEADER_KEY = 'x-signature' |
||||
|
||||
|
||||
class AuthGateway(grpc.AuthMetadataPlugin): |
||||
|
||||
def __call__(self, context, callback): |
||||
"""Implements authentication by passing metadata to a callback. |
||||
|
||||
Implementations of this method must not block. |
||||
|
||||
Args: |
||||
context: An AuthMetadataContext providing information on the RPC that |
||||
the plugin is being called to authenticate. |
||||
callback: An AuthMetadataPluginCallback to be invoked either |
||||
synchronously or asynchronously. |
||||
""" |
||||
# Example AuthMetadataContext object: |
||||
# AuthMetadataContext( |
||||
# service_url=u'https://localhost:50051/helloworld.Greeter', |
||||
# method_name=u'SayHello') |
||||
signature = context.method_name[::-1] |
||||
callback(((_SIGNATURE_HEADER_KEY, signature),), None) |
||||
|
||||
|
||||
@contextlib.contextmanager |
||||
def create_client_channel(addr): |
||||
# Call credential object will be invoked for every single RPC |
||||
call_credentials = grpc.metadata_call_credentials( |
||||
AuthGateway(), name='auth gateway') |
||||
# Channel credential will be valid for the entire channel |
||||
channel_credential = grpc.ssl_channel_credentials( |
||||
_credentials.ROOT_CERTIFICATE) |
||||
# Combining channel credentials and call credentials together |
||||
composite_credentials = grpc.composite_channel_credentials( |
||||
channel_credential, |
||||
call_credentials, |
||||
) |
||||
channel = grpc.secure_channel(addr, composite_credentials) |
||||
yield channel |
||||
|
||||
|
||||
def send_rpc(channel): |
||||
stub = helloworld_pb2_grpc.GreeterStub(channel) |
||||
request = helloworld_pb2.HelloRequest(name='you') |
||||
try: |
||||
response = stub.SayHello(request) |
||||
except grpc.RpcError as rpc_error: |
||||
_LOGGER.error('Received error: %s', rpc_error) |
||||
return rpc_error |
||||
else: |
||||
_LOGGER.info('Received message: %s', response) |
||||
return response |
||||
|
||||
|
||||
def main(): |
||||
parser = argparse.ArgumentParser() |
||||
parser.add_argument( |
||||
'--port', |
||||
nargs='?', |
||||
type=int, |
||||
default=50051, |
||||
help='the address of server') |
||||
args = parser.parse_args() |
||||
|
||||
with create_client_channel(_SERVER_ADDR_TEMPLATE % args.port) as channel: |
||||
send_rpc(channel) |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
logging.basicConfig(level=logging.INFO) |
||||
main() |
@ -0,0 +1,110 @@ |
||||
# Copyright 2019 The gRPC Authors |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""Server of the Python example of customizing authentication mechanism.""" |
||||
|
||||
from __future__ import absolute_import |
||||
from __future__ import division |
||||
from __future__ import print_function |
||||
|
||||
import argparse |
||||
import contextlib |
||||
import logging |
||||
import time |
||||
from concurrent import futures |
||||
|
||||
import grpc |
||||
from examples import helloworld_pb2 |
||||
from examples import helloworld_pb2_grpc |
||||
from examples.python.auth import _credentials |
||||
|
||||
_LOGGER = logging.getLogger(__name__) |
||||
_LOGGER.setLevel(logging.INFO) |
||||
|
||||
_ONE_DAY_IN_SECONDS = 60 * 60 * 24 |
||||
|
||||
_LISTEN_ADDRESS_TEMPLATE = 'localhost:%d' |
||||
_SIGNATURE_HEADER_KEY = 'x-signature' |
||||
|
||||
|
||||
class SignatureValidationInterceptor(grpc.ServerInterceptor): |
||||
|
||||
def __init__(self): |
||||
|
||||
def abort(ignored_request, context): |
||||
context.abort(grpc.StatusCode.UNAUTHENTICATED, 'Invalid signature') |
||||
|
||||
self._abortion = grpc.unary_unary_rpc_method_handler(abort) |
||||
|
||||
def intercept_service(self, continuation, handler_call_details): |
||||
# Example HandlerCallDetails object: |
||||
# _HandlerCallDetails( |
||||
# method=u'/helloworld.Greeter/SayHello', |
||||
# invocation_metadata=...) |
||||
method_name = handler_call_details.method.split('/')[-1] |
||||
expected_metadata = (_SIGNATURE_HEADER_KEY, method_name[::-1]) |
||||
if expected_metadata in handler_call_details.invocation_metadata: |
||||
return continuation(handler_call_details) |
||||
else: |
||||
return self._abortion |
||||
|
||||
|
||||
class SimpleGreeter(helloworld_pb2_grpc.GreeterServicer): |
||||
|
||||
def SayHello(self, request, unused_context): |
||||
return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name) |
||||
|
||||
|
||||
@contextlib.contextmanager |
||||
def run_server(port): |
||||
# Bind interceptor to server |
||||
server = grpc.server( |
||||
futures.ThreadPoolExecutor(), |
||||
interceptors=(SignatureValidationInterceptor(),)) |
||||
helloworld_pb2_grpc.add_GreeterServicer_to_server(SimpleGreeter(), server) |
||||
|
||||
# Loading credentials |
||||
server_credentials = grpc.ssl_server_credentials((( |
||||
_credentials.SERVER_CERTIFICATE_KEY, |
||||
_credentials.SERVER_CERTIFICATE, |
||||
),)) |
||||
|
||||
# Pass down credentials |
||||
port = server.add_secure_port(_LISTEN_ADDRESS_TEMPLATE % port, |
||||
server_credentials) |
||||
|
||||
server.start() |
||||
try: |
||||
yield port |
||||
finally: |
||||
server.stop(0) |
||||
|
||||
|
||||
def main(): |
||||
parser = argparse.ArgumentParser() |
||||
parser.add_argument( |
||||
'--port', nargs='?', type=int, default=50051, help='the listening port') |
||||
args = parser.parse_args() |
||||
|
||||
with run_server(args.port) as port: |
||||
logging.info('Server is listening at port :%d', port) |
||||
try: |
||||
while True: |
||||
time.sleep(_ONE_DAY_IN_SECONDS) |
||||
except KeyboardInterrupt: |
||||
pass |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
logging.basicConfig(level=logging.INFO) |
||||
main() |
@ -0,0 +1,56 @@ |
||||
# Copyright 2019 The gRPC Authors |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""Test for gRPC Python authentication example.""" |
||||
|
||||
from __future__ import absolute_import |
||||
from __future__ import division |
||||
from __future__ import print_function |
||||
|
||||
import unittest |
||||
|
||||
import grpc |
||||
from examples.python.auth import _credentials |
||||
from examples.python.auth import customized_auth_client |
||||
from examples.python.auth import customized_auth_server |
||||
|
||||
_SERVER_ADDR_TEMPLATE = 'localhost:%d' |
||||
|
||||
|
||||
class AuthExampleTest(unittest.TestCase): |
||||
|
||||
def test_successful_call(self): |
||||
with customized_auth_server.run_server(0) as port: |
||||
with customized_auth_client.create_client_channel( |
||||
_SERVER_ADDR_TEMPLATE % port) as channel: |
||||
customized_auth_client.send_rpc(channel) |
||||
# No unhandled exception raised, test passed! |
||||
|
||||
def test_no_channel_credential(self): |
||||
with customized_auth_server.run_server(0) as port: |
||||
with grpc.insecure_channel(_SERVER_ADDR_TEMPLATE % port) as channel: |
||||
resp = customized_auth_client.send_rpc(channel) |
||||
self.assertEqual(resp.code(), grpc.StatusCode.UNAVAILABLE) |
||||
|
||||
def test_no_call_credential(self): |
||||
with customized_auth_server.run_server(0) as port: |
||||
channel_credential = grpc.ssl_channel_credentials( |
||||
_credentials.ROOT_CERTIFICATE) |
||||
with grpc.secure_channel(_SERVER_ADDR_TEMPLATE % port, |
||||
channel_credential) as channel: |
||||
resp = customized_auth_client.send_rpc(channel) |
||||
self.assertEqual(resp.code(), grpc.StatusCode.UNAUTHENTICATED) |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
unittest.main(verbosity=2) |
@ -0,0 +1,44 @@ |
||||
# Copyright 2019 The gRPC Authors |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
py_binary( |
||||
name = "server", |
||||
srcs = ["server.py"], |
||||
deps = [ |
||||
"//src/python/grpcio/grpc:grpcio", |
||||
"//examples:py_helloworld", |
||||
], |
||||
srcs_version = "PY2AND3", |
||||
) |
||||
|
||||
py_binary( |
||||
name = "client", |
||||
srcs = ["client.py"], |
||||
deps = [ |
||||
"//src/python/grpcio/grpc:grpcio", |
||||
"//examples:py_helloworld", |
||||
], |
||||
srcs_version = "PY2AND3", |
||||
) |
||||
|
||||
py_test( |
||||
name = "test/compression_example_test", |
||||
srcs = ["test/compression_example_test.py"], |
||||
srcs_version = "PY2AND3", |
||||
data = [ |
||||
":client", |
||||
":server", |
||||
], |
||||
size = "small", |
||||
) |
@ -0,0 +1,58 @@ |
||||
## Compression with gRPC Python |
||||
|
||||
gRPC offers lossless compression options in order to decrease the number of bits |
||||
transferred over the wire. Three levels of compression are available: |
||||
|
||||
- `grpc.Compression.NoCompression` - No compression is applied to the payload. (default) |
||||
- `grpc.Compression.Deflate` - The "Deflate" algorithm is applied to the payload. |
||||
- `grpc.Compression.Gzip` - The Gzip algorithm is applied to the payload. |
||||
|
||||
The default option on both clients and servers is `grpc.Compression.NoCompression`. |
||||
|
||||
See [the gRPC Compression Spec](https://github.com/grpc/grpc/blob/master/doc/compression.md) |
||||
for more information. |
||||
|
||||
### Client Side Compression |
||||
|
||||
Compression may be set at two levels on the client side. |
||||
|
||||
#### At the channel level |
||||
|
||||
```python |
||||
with grpc.insecure_channel('foo.bar:1234', compression=grpc.Compression.Gzip) as channel: |
||||
use_channel(channel) |
||||
``` |
||||
|
||||
#### At the call level |
||||
|
||||
Setting the compression method at the call level will override any settings on |
||||
the channel level. |
||||
|
||||
```python |
||||
stub = helloworld_pb2_grpc.GreeterStub(channel) |
||||
response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'), |
||||
compression=grpc.Compression.Deflate) |
||||
``` |
||||
|
||||
|
||||
### Server Side Compression |
||||
|
||||
Additionally, compression may be set at two levels on the server side. |
||||
|
||||
#### On the entire server |
||||
|
||||
```python |
||||
server = grpc.server(futures.ThreadPoolExecutor(), |
||||
compression=grpc.Compression.Gzip) |
||||
``` |
||||
|
||||
#### For an individual RPC |
||||
|
||||
```python |
||||
def SayHello(self, request, context): |
||||
context.set_response_compression(grpc.Compression.NoCompression) |
||||
return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name) |
||||
``` |
||||
|
||||
Setting the compression method for an individual RPC will override any setting |
||||
supplied at server creation time. |
@ -0,0 +1,76 @@ |
||||
# Copyright 2019 the gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""An example of compression on the client side with gRPC.""" |
||||
|
||||
from __future__ import absolute_import |
||||
from __future__ import division |
||||
from __future__ import print_function |
||||
|
||||
import argparse |
||||
import logging |
||||
import grpc |
||||
|
||||
from examples import helloworld_pb2 |
||||
from examples import helloworld_pb2_grpc |
||||
|
||||
_DESCRIPTION = 'A client capable of compression.' |
||||
_COMPRESSION_OPTIONS = { |
||||
"none": grpc.Compression.NoCompression, |
||||
"deflate": grpc.Compression.Deflate, |
||||
"gzip": grpc.Compression.Gzip, |
||||
} |
||||
|
||||
_LOGGER = logging.getLogger(__name__) |
||||
|
||||
|
||||
def run_client(channel_compression, call_compression, target): |
||||
with grpc.insecure_channel( |
||||
target, compression=channel_compression) as channel: |
||||
stub = helloworld_pb2_grpc.GreeterStub(channel) |
||||
response = stub.SayHello( |
||||
helloworld_pb2.HelloRequest(name='you'), |
||||
compression=call_compression, |
||||
wait_for_ready=True) |
||||
print("Response: {}".format(response)) |
||||
|
||||
|
||||
def main(): |
||||
parser = argparse.ArgumentParser(description=_DESCRIPTION) |
||||
parser.add_argument( |
||||
'--channel_compression', |
||||
default='none', |
||||
nargs='?', |
||||
choices=_COMPRESSION_OPTIONS.keys(), |
||||
help='The compression method to use for the channel.') |
||||
parser.add_argument( |
||||
'--call_compression', |
||||
default='none', |
||||
nargs='?', |
||||
choices=_COMPRESSION_OPTIONS.keys(), |
||||
help='The compression method to use for an individual call.') |
||||
parser.add_argument( |
||||
'--server', |
||||
default='localhost:50051', |
||||
type=str, |
||||
nargs='?', |
||||
help='The host-port pair at which to reach the server.') |
||||
args = parser.parse_args() |
||||
channel_compression = _COMPRESSION_OPTIONS[args.channel_compression] |
||||
call_compression = _COMPRESSION_OPTIONS[args.call_compression] |
||||
run_client(channel_compression, call_compression, args.server) |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
logging.basicConfig() |
||||
main() |
@ -0,0 +1,109 @@ |
||||
# Copyright 2019 the gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""An example of compression on the server side with gRPC.""" |
||||
|
||||
from __future__ import absolute_import |
||||
from __future__ import division |
||||
from __future__ import print_function |
||||
|
||||
from concurrent import futures |
||||
import argparse |
||||
import logging |
||||
import threading |
||||
import time |
||||
import grpc |
||||
|
||||
from examples import helloworld_pb2 |
||||
from examples import helloworld_pb2_grpc |
||||
|
||||
_DESCRIPTION = 'A server capable of compression.' |
||||
_COMPRESSION_OPTIONS = { |
||||
"none": grpc.Compression.NoCompression, |
||||
"deflate": grpc.Compression.Deflate, |
||||
"gzip": grpc.Compression.Gzip, |
||||
} |
||||
_LOGGER = logging.getLogger(__name__) |
||||
|
||||
_SERVER_HOST = 'localhost' |
||||
_ONE_DAY_IN_SECONDS = 60 * 60 * 24 |
||||
|
||||
|
||||
class Greeter(helloworld_pb2_grpc.GreeterServicer): |
||||
|
||||
def __init__(self, no_compress_every_n): |
||||
super(Greeter, self).__init__() |
||||
self._no_compress_every_n = 0 |
||||
self._request_counter = 0 |
||||
self._counter_lock = threading.RLock() |
||||
|
||||
def _should_suppress_compression(self): |
||||
suppress_compression = False |
||||
with self._counter_lock: |
||||
if self._no_compress_every_n and self._request_counter % self._no_compress_every_n == 0: |
||||
suppress_compression = True |
||||
self._request_counter += 1 |
||||
return suppress_compression |
||||
|
||||
def SayHello(self, request, context): |
||||
if self._should_suppress_compression(): |
||||
context.set_response_compression(grpc.Compression.NoCompression) |
||||
return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name) |
||||
|
||||
|
||||
def run_server(server_compression, no_compress_every_n, port): |
||||
server = grpc.server( |
||||
futures.ThreadPoolExecutor(), |
||||
compression=server_compression, |
||||
options=(('grpc.so_reuseport', 1),)) |
||||
helloworld_pb2_grpc.add_GreeterServicer_to_server( |
||||
Greeter(no_compress_every_n), server) |
||||
address = '{}:{}'.format(_SERVER_HOST, port) |
||||
server.add_insecure_port(address) |
||||
server.start() |
||||
print("Server listening at '{}'".format(address)) |
||||
try: |
||||
while True: |
||||
time.sleep(_ONE_DAY_IN_SECONDS) |
||||
except KeyboardInterrupt: |
||||
server.stop(None) |
||||
|
||||
|
||||
def main(): |
||||
parser = argparse.ArgumentParser(description=_DESCRIPTION) |
||||
parser.add_argument( |
||||
'--server_compression', |
||||
default='none', |
||||
nargs='?', |
||||
choices=_COMPRESSION_OPTIONS.keys(), |
||||
help='The default compression method for the server.') |
||||
parser.add_argument( |
||||
'--no_compress_every_n', |
||||
type=int, |
||||
default=0, |
||||
nargs='?', |
||||
help='If set, every nth reply will be uncompressed.') |
||||
parser.add_argument( |
||||
'--port', |
||||
type=int, |
||||
default=50051, |
||||
nargs='?', |
||||
help='The port on which the server will listen.') |
||||
args = parser.parse_args() |
||||
run_server(_COMPRESSION_OPTIONS[args.server_compression], |
||||
args.no_compress_every_n, args.port) |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
logging.basicConfig() |
||||
main() |
@ -0,0 +1,62 @@ |
||||
# Copyright 2019 the gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""Test for compression example.""" |
||||
|
||||
import contextlib |
||||
import os |
||||
import socket |
||||
import subprocess |
||||
import unittest |
||||
|
||||
_BINARY_DIR = os.path.realpath( |
||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')) |
||||
_SERVER_PATH = os.path.join(_BINARY_DIR, 'server') |
||||
_CLIENT_PATH = os.path.join(_BINARY_DIR, 'client') |
||||
|
||||
|
||||
@contextlib.contextmanager |
||||
def _get_port(): |
||||
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) |
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) |
||||
if sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT) == 0: |
||||
raise RuntimeError("Failed to set SO_REUSEPORT.") |
||||
sock.bind(('', 0)) |
||||
try: |
||||
yield sock.getsockname()[1] |
||||
finally: |
||||
sock.close() |
||||
|
||||
|
||||
class CompressionExampleTest(unittest.TestCase): |
||||
|
||||
def test_compression_example(self): |
||||
with _get_port() as test_port: |
||||
server_process = subprocess.Popen((_SERVER_PATH, '--port', |
||||
str(test_port), |
||||
'--server_compression', 'gzip')) |
||||
try: |
||||
server_target = 'localhost:{}'.format(test_port) |
||||
client_process = subprocess.Popen( |
||||
(_CLIENT_PATH, '--server', server_target, |
||||
'--channel_compression', 'gzip')) |
||||
client_return_code = client_process.wait() |
||||
self.assertEqual(0, client_return_code) |
||||
self.assertIsNone(server_process.poll()) |
||||
finally: |
||||
server_process.kill() |
||||
server_process.wait() |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
unittest.main(verbosity=2) |
@ -0,0 +1,24 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2019 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPCPP_COMPLETION_QUEUE_IMPL_H |
||||
#define GRPCPP_COMPLETION_QUEUE_IMPL_H |
||||
|
||||
#include <grpcpp/impl/codegen/completion_queue_impl.h> |
||||
|
||||
#endif // GRPCPP_COMPLETION_QUEUE_IMPL_H
|
@ -1,98 +0,0 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/lib/gpr/host_port.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/string_util.h> |
||||
|
||||
#include "src/core/lib/gpr/string.h" |
||||
|
||||
int gpr_join_host_port(char** out, const char* host, int port) { |
||||
if (host[0] != '[' && strchr(host, ':') != nullptr) { |
||||
/* IPv6 literals must be enclosed in brackets. */ |
||||
return gpr_asprintf(out, "[%s]:%d", host, port); |
||||
} else { |
||||
/* Ordinary non-bracketed host:port. */ |
||||
return gpr_asprintf(out, "%s:%d", host, port); |
||||
} |
||||
} |
||||
|
||||
int gpr_split_host_port(const char* name, char** host, char** port) { |
||||
const char* host_start; |
||||
size_t host_len; |
||||
const char* port_start; |
||||
|
||||
*host = nullptr; |
||||
*port = nullptr; |
||||
|
||||
if (name[0] == '[') { |
||||
/* Parse a bracketed host, typically an IPv6 literal. */ |
||||
const char* rbracket = strchr(name, ']'); |
||||
if (rbracket == nullptr) { |
||||
/* Unmatched [ */ |
||||
return 0; |
||||
} |
||||
if (rbracket[1] == '\0') { |
||||
/* ]<end> */ |
||||
port_start = nullptr; |
||||
} else if (rbracket[1] == ':') { |
||||
/* ]:<port?> */ |
||||
port_start = rbracket + 2; |
||||
} else { |
||||
/* ]<invalid> */ |
||||
return 0; |
||||
} |
||||
host_start = name + 1; |
||||
host_len = static_cast<size_t>(rbracket - host_start); |
||||
if (memchr(host_start, ':', host_len) == nullptr) { |
||||
/* Require all bracketed hosts to contain a colon, because a hostname or
|
||||
IPv4 address should never use brackets. */ |
||||
return 0; |
||||
} |
||||
} else { |
||||
const char* colon = strchr(name, ':'); |
||||
if (colon != nullptr && strchr(colon + 1, ':') == nullptr) { |
||||
/* Exactly 1 colon. Split into host:port. */ |
||||
host_start = name; |
||||
host_len = static_cast<size_t>(colon - name); |
||||
port_start = colon + 1; |
||||
} else { |
||||
/* 0 or 2+ colons. Bare hostname or IPv6 litearal. */ |
||||
host_start = name; |
||||
host_len = strlen(name); |
||||
port_start = nullptr; |
||||
} |
||||
} |
||||
|
||||
/* Allocate return values. */ |
||||
*host = static_cast<char*>(gpr_malloc(host_len + 1)); |
||||
memcpy(*host, host_start, host_len); |
||||
(*host)[host_len] = '\0'; |
||||
|
||||
if (port_start != nullptr) { |
||||
*port = gpr_strdup(port_start); |
||||
} |
||||
|
||||
return 1; |
||||
} |
@ -0,0 +1,105 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/lib/gprpp/host_port.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/string_util.h> |
||||
|
||||
#include "src/core/lib/gpr/string.h" |
||||
#include "src/core/lib/gprpp/string_view.h" |
||||
|
||||
namespace grpc_core { |
||||
int JoinHostPort(UniquePtr<char>* out, const char* host, int port) { |
||||
char* tmp; |
||||
int ret; |
||||
if (host[0] != '[' && strchr(host, ':') != nullptr) { |
||||
/* IPv6 literals must be enclosed in brackets. */ |
||||
ret = gpr_asprintf(&tmp, "[%s]:%d", host, port); |
||||
} else { |
||||
/* Ordinary non-bracketed host:port. */ |
||||
ret = gpr_asprintf(&tmp, "%s:%d", host, port); |
||||
} |
||||
out->reset(tmp); |
||||
return ret; |
||||
} |
||||
|
||||
bool SplitHostPort(StringView name, StringView* host, StringView* port) { |
||||
if (name[0] == '[') { |
||||
/* Parse a bracketed host, typically an IPv6 literal. */ |
||||
const size_t rbracket = name.find(']', 1); |
||||
if (rbracket == grpc_core::StringView::npos) { |
||||
/* Unmatched [ */ |
||||
return false; |
||||
} |
||||
if (rbracket == name.size() - 1) { |
||||
/* ]<end> */ |
||||
port->clear(); |
||||
} else if (name[rbracket + 1] == ':') { |
||||
/* ]:<port?> */ |
||||
*port = name.substr(rbracket + 2, name.size() - rbracket - 2); |
||||
} else { |
||||
/* ]<invalid> */ |
||||
return false; |
||||
} |
||||
*host = name.substr(1, rbracket - 1); |
||||
if (host->find(':') == grpc_core::StringView::npos) { |
||||
/* Require all bracketed hosts to contain a colon, because a hostname or
|
||||
IPv4 address should never use brackets. */ |
||||
host->clear(); |
||||
return false; |
||||
} |
||||
} else { |
||||
size_t colon = name.find(':'); |
||||
if (colon != grpc_core::StringView::npos && |
||||
name.find(':', colon + 1) == grpc_core::StringView::npos) { |
||||
/* Exactly 1 colon. Split into host:port. */ |
||||
*host = name.substr(0, colon); |
||||
*port = name.substr(colon + 1, name.size() - colon - 1); |
||||
} else { |
||||
/* 0 or 2+ colons. Bare hostname or IPv6 litearal. */ |
||||
*host = name; |
||||
port->clear(); |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
bool SplitHostPort(StringView name, UniquePtr<char>* host, |
||||
UniquePtr<char>* port) { |
||||
GPR_DEBUG_ASSERT(host != nullptr && *host == nullptr); |
||||
GPR_DEBUG_ASSERT(port != nullptr && *port == nullptr); |
||||
StringView host_view; |
||||
StringView port_view; |
||||
const bool ret = SplitHostPort(name, &host_view, &port_view); |
||||
if (ret) { |
||||
// We always set the host, but port is set only when it's non-empty,
|
||||
// to remain backward compatible with the old split_host_port API.
|
||||
*host = host_view.dup(); |
||||
if (!port_view.empty()) { |
||||
*port = port_view.dup(); |
||||
} |
||||
} |
||||
return ret; |
||||
} |
||||
} // namespace grpc_core
|
@ -0,0 +1,143 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2019 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
#ifndef GRPC_CORE_LIB_GPRPP_STRING_VIEW_H |
||||
#define GRPC_CORE_LIB_GPRPP_STRING_VIEW_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <grpc/impl/codegen/slice.h> |
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
|
||||
#include <algorithm> |
||||
#include <cstdint> |
||||
#include <cstring> |
||||
#include <limits> |
||||
|
||||
#include "src/core/lib/gpr/string.h" |
||||
#include "src/core/lib/gpr/useful.h" |
||||
#include "src/core/lib/gprpp/memory.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
// Provides a light-weight view over a char array or a slice, similar but not
|
||||
// identical to absl::string_view.
|
||||
//
|
||||
// Any method that has the same name as absl::string_view MUST HAVE identical
|
||||
// semantics to what absl::string_view provides.
|
||||
//
|
||||
// Methods that are not part of absl::string_view API, must be clearly
|
||||
// annotated.
|
||||
//
|
||||
// StringView does not own the buffers that back the view. Callers must ensure
|
||||
// the buffer stays around while the StringView is accessible.
|
||||
//
|
||||
// Pass StringView by value in functions, since it is exactly two pointers in
|
||||
// size.
|
||||
//
|
||||
// The interface used here is not identical to absl::string_view. Notably, we
|
||||
// need to support slices while we cannot support std::string, and gpr string
|
||||
// style functions such as strdup() and cmp(). Once we switch to
|
||||
// absl::string_view this class will inherit from absl::string_view and add the
|
||||
// gRPC-specific APIs.
|
||||
class StringView final { |
||||
public: |
||||
static constexpr size_t npos = std::numeric_limits<size_t>::max(); |
||||
|
||||
constexpr StringView(const char* ptr, size_t size) : ptr_(ptr), size_(size) {} |
||||
constexpr StringView(const char* ptr) |
||||
: StringView(ptr, ptr == nullptr ? 0 : strlen(ptr)) {} |
||||
// Not part of absl::string_view API.
|
||||
StringView(const grpc_slice& slice) |
||||
: StringView(reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(slice)), |
||||
GRPC_SLICE_LENGTH(slice)) {} |
||||
constexpr StringView() : StringView(nullptr, 0) {} |
||||
|
||||
constexpr const char* data() const { return ptr_; } |
||||
constexpr size_t size() const { return size_; } |
||||
constexpr bool empty() const { return size_ == 0; } |
||||
|
||||
StringView substr(size_t start, size_t size = npos) { |
||||
GPR_DEBUG_ASSERT(start + size <= size_); |
||||
return StringView(ptr_ + start, std::min(size, size_ - start)); |
||||
} |
||||
|
||||
constexpr const char& operator[](size_t i) const { return ptr_[i]; } |
||||
|
||||
const char& front() const { return ptr_[0]; } |
||||
const char& back() const { return ptr_[size_ - 1]; } |
||||
|
||||
void remove_prefix(size_t n) { |
||||
GPR_DEBUG_ASSERT(n <= size_); |
||||
ptr_ += n; |
||||
size_ -= n; |
||||
} |
||||
|
||||
void remove_suffix(size_t n) { |
||||
GPR_DEBUG_ASSERT(n <= size_); |
||||
size_ -= n; |
||||
} |
||||
|
||||
size_t find(char c, size_t pos = 0) const { |
||||
if (empty() || pos >= size_) return npos; |
||||
const char* result = |
||||
static_cast<const char*>(memchr(ptr_ + pos, c, size_ - pos)); |
||||
return result != nullptr ? result - ptr_ : npos; |
||||
} |
||||
|
||||
void clear() { |
||||
ptr_ = nullptr; |
||||
size_ = 0; |
||||
} |
||||
|
||||
// Creates a dup of the string viewed by this class.
|
||||
// Return value is null-terminated and never nullptr.
|
||||
//
|
||||
// Not part of absl::string_view API.
|
||||
grpc_core::UniquePtr<char> dup() const { |
||||
char* str = static_cast<char*>(gpr_malloc(size_ + 1)); |
||||
if (size_ > 0) memcpy(str, ptr_, size_); |
||||
str[size_] = '\0'; |
||||
return grpc_core::UniquePtr<char>(str); |
||||
} |
||||
|
||||
// Not part of absl::string_view API.
|
||||
int cmp(StringView other) const { |
||||
const size_t len = GPR_MIN(size(), other.size()); |
||||
const int ret = strncmp(data(), other.data(), len); |
||||
if (ret != 0) return ret; |
||||
if (size() == other.size()) return 0; |
||||
if (size() < other.size()) return -1; |
||||
return 1; |
||||
} |
||||
|
||||
private: |
||||
const char* ptr_; |
||||
size_t size_; |
||||
}; |
||||
|
||||
inline bool operator==(StringView lhs, StringView rhs) { |
||||
return lhs.size() == rhs.size() && |
||||
strncmp(lhs.data(), rhs.data(), lhs.size()) == 0; |
||||
} |
||||
|
||||
inline bool operator!=(StringView lhs, StringView rhs) { return !(lhs == rhs); } |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif /* GRPC_CORE_LIB_GPRPP_STRING_VIEW_H */ |
@ -0,0 +1,114 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2019 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/lib/iomgr/executor/mpmcqueue.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
DebugOnlyTraceFlag grpc_thread_pool_trace(false, "thread_pool"); |
||||
|
||||
inline void* InfLenFIFOQueue::PopFront() { |
||||
// Caller should already check queue is not empty and has already held the
|
||||
// mutex. This function will only do the job of removal.
|
||||
void* result = queue_head_->content; |
||||
Node* head_to_remove = queue_head_; |
||||
queue_head_ = queue_head_->next; |
||||
|
||||
count_.Store(count_.Load(MemoryOrder::RELAXED) - 1, MemoryOrder::RELAXED); |
||||
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_thread_pool_trace)) { |
||||
gpr_timespec wait_time = |
||||
gpr_time_sub(gpr_now(GPR_CLOCK_MONOTONIC), head_to_remove->insert_time); |
||||
|
||||
// Updates Stats info
|
||||
stats_.num_completed++; |
||||
stats_.total_queue_time = gpr_time_add(stats_.total_queue_time, wait_time); |
||||
stats_.max_queue_time = gpr_time_max( |
||||
gpr_convert_clock_type(stats_.max_queue_time, GPR_TIMESPAN), wait_time); |
||||
|
||||
if (count_.Load(MemoryOrder::RELAXED) == 0) { |
||||
stats_.busy_queue_time = |
||||
gpr_time_add(stats_.busy_queue_time, |
||||
gpr_time_sub(gpr_now(GPR_CLOCK_MONOTONIC), busy_time)); |
||||
} |
||||
|
||||
gpr_log(GPR_INFO, |
||||
"[InfLenFIFOQueue PopFront] num_completed: %" PRIu64 |
||||
" total_queue_time: %f max_queue_time: %f busy_queue_time: %f", |
||||
stats_.num_completed, |
||||
gpr_timespec_to_micros(stats_.total_queue_time), |
||||
gpr_timespec_to_micros(stats_.max_queue_time), |
||||
gpr_timespec_to_micros(stats_.busy_queue_time)); |
||||
} |
||||
|
||||
Delete(head_to_remove); |
||||
// Singal waiting thread
|
||||
if (count_.Load(MemoryOrder::RELAXED) > 0 && num_waiters_ > 0) { |
||||
wait_nonempty_.Signal(); |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
InfLenFIFOQueue::~InfLenFIFOQueue() { |
||||
GPR_ASSERT(count_.Load(MemoryOrder::RELAXED) == 0); |
||||
GPR_ASSERT(num_waiters_ == 0); |
||||
} |
||||
|
||||
void InfLenFIFOQueue::Put(void* elem) { |
||||
MutexLock l(&mu_); |
||||
|
||||
Node* new_node = New<Node>(elem); |
||||
if (count_.Load(MemoryOrder::RELAXED) == 0) { |
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_thread_pool_trace)) { |
||||
busy_time = gpr_now(GPR_CLOCK_MONOTONIC); |
||||
} |
||||
queue_head_ = queue_tail_ = new_node; |
||||
} else { |
||||
queue_tail_->next = new_node; |
||||
queue_tail_ = queue_tail_->next; |
||||
} |
||||
count_.Store(count_.Load(MemoryOrder::RELAXED) + 1, MemoryOrder::RELAXED); |
||||
// Updates Stats info
|
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_thread_pool_trace)) { |
||||
stats_.num_started++; |
||||
gpr_log(GPR_INFO, "[InfLenFIFOQueue Put] num_started: %" PRIu64, |
||||
stats_.num_started); |
||||
} |
||||
|
||||
if (num_waiters_ > 0) { |
||||
wait_nonempty_.Signal(); |
||||
} |
||||
} |
||||
|
||||
void* InfLenFIFOQueue::Get() { |
||||
MutexLock l(&mu_); |
||||
if (count_.Load(MemoryOrder::RELAXED) == 0) { |
||||
num_waiters_++; |
||||
do { |
||||
wait_nonempty_.Wait(&mu_); |
||||
} while (count_.Load(MemoryOrder::RELAXED) == 0); |
||||
num_waiters_--; |
||||
} |
||||
GPR_DEBUG_ASSERT(count_.Load(MemoryOrder::RELAXED) > 0); |
||||
return PopFront(); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -0,0 +1,128 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2019 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPC_CORE_LIB_IOMGR_EXECUTOR_MPMCQUEUE_H |
||||
#define GRPC_CORE_LIB_IOMGR_EXECUTOR_MPMCQUEUE_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/lib/debug/stats.h" |
||||
#include "src/core/lib/gprpp/abstract.h" |
||||
#include "src/core/lib/gprpp/atomic.h" |
||||
#include "src/core/lib/gprpp/sync.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
extern DebugOnlyTraceFlag grpc_thread_pool_trace; |
||||
|
||||
// Abstract base class of a Multiple-Producer-Multiple-Consumer(MPMC) queue
|
||||
// interface
|
||||
class MPMCQueueInterface { |
||||
public: |
||||
virtual ~MPMCQueueInterface() {} |
||||
|
||||
// Puts elem into queue immediately at the end of queue.
|
||||
// This might cause to block on full queue depending on implementation.
|
||||
virtual void Put(void* elem) GRPC_ABSTRACT; |
||||
|
||||
// Removes the oldest element from the queue and return it.
|
||||
// This might cause to block on empty queue depending on implementation.
|
||||
virtual void* Get() GRPC_ABSTRACT; |
||||
|
||||
// Returns number of elements in the queue currently
|
||||
virtual int count() const GRPC_ABSTRACT; |
||||
|
||||
GRPC_ABSTRACT_BASE_CLASS |
||||
}; |
||||
|
||||
class InfLenFIFOQueue : public MPMCQueueInterface { |
||||
public: |
||||
// Creates a new MPMC Queue. The queue created will have infinite length.
|
||||
InfLenFIFOQueue() {} |
||||
|
||||
// Releases all resources held by the queue. The queue must be empty, and no
|
||||
// one waits on conditional variables.
|
||||
~InfLenFIFOQueue(); |
||||
|
||||
// Puts elem into queue immediately at the end of queue. Since the queue has
|
||||
// infinite length, this routine will never block and should never fail.
|
||||
void Put(void* elem); |
||||
|
||||
// Removes the oldest element from the queue and returns it.
|
||||
// This routine will cause the thread to block if queue is currently empty.
|
||||
void* Get(); |
||||
|
||||
// Returns number of elements in queue currently.
|
||||
// There might be concurrently add/remove on queue, so count might change
|
||||
// quickly.
|
||||
int count() const { return count_.Load(MemoryOrder::RELAXED); } |
||||
|
||||
private: |
||||
// For Internal Use Only.
|
||||
// Removes the oldest element from the queue and returns it. This routine
|
||||
// will NOT check whether queue is empty, and it will NOT acquire mutex.
|
||||
// Caller should do the check and acquire mutex before callling.
|
||||
void* PopFront(); |
||||
|
||||
struct Node { |
||||
Node* next; // Linking
|
||||
void* content; // Points to actual element
|
||||
gpr_timespec insert_time; // Time for stats
|
||||
|
||||
Node(void* c) : content(c) { |
||||
next = nullptr; |
||||
insert_time = gpr_now(GPR_CLOCK_MONOTONIC); |
||||
} |
||||
}; |
||||
|
||||
// Stats of queue. This will only be collect when debug trace mode is on.
|
||||
// All printed stats info will have time measurement in microsecond.
|
||||
struct Stats { |
||||
uint64_t num_started; // Number of elements have been added to queue
|
||||
uint64_t num_completed; // Number of elements have been removed from
|
||||
// the queue
|
||||
gpr_timespec total_queue_time; // Total waiting time that all the
|
||||
// removed elements have spent in queue
|
||||
gpr_timespec max_queue_time; // Max waiting time among all removed
|
||||
// elements
|
||||
gpr_timespec busy_queue_time; // Accumulated amount of time that queue
|
||||
// was not empty
|
||||
|
||||
Stats() { |
||||
num_started = 0; |
||||
num_completed = 0; |
||||
total_queue_time = gpr_time_0(GPR_TIMESPAN); |
||||
max_queue_time = gpr_time_0(GPR_TIMESPAN); |
||||
busy_queue_time = gpr_time_0(GPR_TIMESPAN); |
||||
} |
||||
}; |
||||
|
||||
Mutex mu_; // Protecting lock
|
||||
CondVar wait_nonempty_; // Wait on empty queue on get
|
||||
int num_waiters_ = 0; // Number of waiters
|
||||
|
||||
Node* queue_head_ = nullptr; // Head of the queue, remove position
|
||||
Node* queue_tail_ = nullptr; // End of queue, insert position
|
||||
Atomic<int> count_{0}; // Number of elements in queue
|
||||
Stats stats_; // Stats info
|
||||
gpr_timespec busy_time; // Start time of busy queue
|
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif /* GRPC_CORE_LIB_IOMGR_EXECUTOR_MPMCQUEUE_H */ |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue