tree: eb4de3895e4a75d2c62e9f73bea605fa95ce5695 [path history] [tgz]
  1. async_grpc_client.cc
  2. async_grpc_client.h
  3. async_grpc_client_server_test.cc
  4. async_grpc_constants.cc
  5. async_grpc_constants.h
  6. async_grpc_server.cc
  7. async_grpc_server.h
  8. async_grpc_server_test.cc
  9. grpc_completion_queue_dispatcher.cc
  10. grpc_completion_queue_dispatcher.h
  11. grpc_completion_queue_dispatcher_test.cc
  12. OWNERS
  13. README.md
  14. rpc_state.cc
  15. rpc_state.h
  16. test_rpcs.proto
  17. time_util.cc
  18. time_util.h
libbrillo/brillo/grpc/README.md

This is an adapter for using the gRPC library with a MessageLoop. Its interface consists of two classes:

  • AsyncGrpcClient can be used to initiate RPCs and receive the response as a Callback.
  • AsyncGrpcServer can be used to receive RPCs.

At the moment, no authentication is supported, becuase this utility is expected to be used by two components communicating on the same machine.

Glossary of chosen names / terms

  • The AsyncGrpcServer accepts incoming gRPC RPCs.
  • The AsyncGrpcClient initiates outgoing gRPC RPCs.
  • The Application is the code using the AsyncGrpcServer/AsyncGrpcClient.
  • A Handler is a function which is called for an incoming RPCs. A Handler must be registered to bind an RPC to the function.
  • A RpcState is an implementation detail for the AsyncGrpcServer. It holds the necessary state for an expected or incoming RPC.

AsyncGrpcClient

The AsyncGrpcClient accepts the address to send RPCs to and a TaskRunner in its constructor. Example for sending RPCs:

void OnRpcResponse(grpc::Status status,
                   std::unique_ptr<SomeRpcResponse> response) {
  // Process |status| and |response|.
}

std::string outgoing_address = ...;
AsyncGrpcClient<SomeService> client(message_loop.task_runner(),
                                    outgoing_address);
SomeRpcRequest request;
client.CallRpc(&SomeService::Stub::SomeRpc, request, base::Bind(&OnRpcResponse);

AsyncGrpcServer

The AsyncGrpcServer accepts the address to listen on and a TaskRunner in its constructor. Then, AsyncGrpcServer::RegisterHandler must be called for each RPC that this server should process, binding it to a Callback. The AsyncGrpcServer must be started afterwards to start listening for requests.

void OnSomeRpc(
    std::unique_ptr<SomeRpcRequest> request,
    const base::Callback<void(grpc::Status, std::unique_ptr<SomeRpcResponse>)>&
            response_callback) {
  // Call |response_callback.Run(status, response)| when you have a response!
}

std::string listening_address  = ...;
AsyncGrpcServer<SomeService::AsyncService> server(
    message_loop.task_runner(), listening_address);
server.RegisterHandler(&SomeService::AsyncService::RequestSomeRpc,
                       base::Bind(&onSomeRpc));
server.Start();

AsyncGrpcServer implementation notes

RpcStateBase and RpcState

The AsyncGrpcServerBase class does not call gRPC functions or the Handler directly. Instead, it only contains the general logic for driving RPCs, using the RpcStateBase interface. In contrast, RpcState objects implement the RpcStateBase interface and know details that are specific to the request / response type of the RPC.

The lifecycle of a RpcState is:

  1. AsyncGrpcServerBase instantiates a RpcState using a factory function. The factory function has been created by registering a Handler for an RPC.
  2. AsyncGrpcServerBase calls the RpcStateBase:: to request the RPC in gRPC.
  3. When the RPC is incoming: AsyncGrpcServerBase calls RpcStateBase::CallHandler to call the Handler.
  4. When the Handler is done: AsyncGrpcServerBase calls RpcStateBase::Cancel or RpcStateBase::SendResponse to cancel the RPC or send the response provided by the handler.
  5. The RpcState object is destroyed.

In short, the responsibilities of a RpcState are:

  • holding memory for the RPC request and response,
  • requesting the RPC in gRPC,
  • providing a response to gRPC. All these responsibilities require knowledge of the RequestType or the ResponseType.