1# Copyright 2020 The gRPC Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Client of the Python AsyncIO example of customizing authentication mechanism.""" 15 16import argparse 17import asyncio 18import logging 19 20import _credentials 21import grpc 22 23helloworld_pb2, helloworld_pb2_grpc = grpc.protos_and_services( 24 "helloworld.proto" 25) 26 27_LOGGER = logging.getLogger(__name__) 28_LOGGER.setLevel(logging.INFO) 29 30_SERVER_ADDR_TEMPLATE = "localhost:%d" 31_SIGNATURE_HEADER_KEY = "x-signature" 32 33 34class AuthGateway(grpc.AuthMetadataPlugin): 35 def __call__( 36 self, 37 context: grpc.AuthMetadataContext, 38 callback: grpc.AuthMetadataPluginCallback, 39 ) -> None: 40 """Implements authentication by passing metadata to a callback. 41 42 Implementations of this method must not block. 43 44 Args: 45 context: An AuthMetadataContext providing information on the RPC that 46 the plugin is being called to authenticate. 47 callback: An AuthMetadataPluginCallback to be invoked either 48 synchronously or asynchronously. 49 """ 50 # Example AuthMetadataContext object: 51 # AuthMetadataContext( 52 # service_url=u'https://localhost:50051/helloworld.Greeter', 53 # method_name=u'SayHello') 54 signature = context.method_name[::-1] 55 callback(((_SIGNATURE_HEADER_KEY, signature),), None) 56 57 58def create_client_channel(addr: str) -> grpc.aio.Channel: 59 # Call credential object will be invoked for every single RPC 60 call_credentials = grpc.metadata_call_credentials( 61 AuthGateway(), name="auth gateway" 62 ) 63 # Channel credential will be valid for the entire channel 64 channel_credential = grpc.ssl_channel_credentials( 65 _credentials.ROOT_CERTIFICATE 66 ) 67 # Combining channel credentials and call credentials together 68 composite_credentials = grpc.composite_channel_credentials( 69 channel_credential, 70 call_credentials, 71 ) 72 channel = grpc.aio.secure_channel(addr, composite_credentials) 73 return channel 74 75 76async def send_rpc(channel: grpc.aio.Channel) -> helloworld_pb2.HelloReply: 77 stub = helloworld_pb2_grpc.GreeterStub(channel) 78 request = helloworld_pb2.HelloRequest(name="you") 79 try: 80 response = await stub.SayHello(request) 81 except grpc.RpcError as rpc_error: 82 _LOGGER.error("Received error: %s", rpc_error) 83 return rpc_error 84 else: 85 _LOGGER.info("Received message: %s", response) 86 return response 87 88 89async def main() -> None: 90 parser = argparse.ArgumentParser() 91 parser.add_argument( 92 "--port", 93 nargs="?", 94 type=int, 95 default=50051, 96 help="the address of server", 97 ) 98 args = parser.parse_args() 99 100 channel = create_client_channel(_SERVER_ADDR_TEMPLATE % args.port) 101 await send_rpc(channel) 102 await channel.close() 103 104 105if __name__ == "__main__": 106 logging.basicConfig(level=logging.INFO) 107 asyncio.run(main()) 108