| 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::BindOnce(&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, |
| base::OnceCallback<void(grpc::Status, std::unique_ptr<SomeRpcResponse>)> |
| response_callback) { |
| // Call |std::move(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::BindRepeating(&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. |