xref: /aosp_15_r20/external/pigweed/pw_rpc/pwpb/docs.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _module-pw_rpc_pw_protobuf:
2
3-----------
4pw_protobuf
5-----------
6.. caution::
7
8   If you're starting a new project, Pigweed recommends Nanopb over
9   ``pw_protobuf``. See :ref:`module-pw_rpc-guides-headers`.
10
11``pw_rpc`` can generate services which encode/decode RPC requests and responses
12as ``pw_protobuf`` message structs.
13
14Usage
15=====
16Define a ``pw_proto_library`` containing the .proto file defining your service
17(and optionally other related protos), then depend on the ``pwpb_rpc``
18version of that library in the code implementing the service.
19
20.. code-block::
21
22   # chat/BUILD.gn
23
24   import("$dir_pw_build/target_types.gni")
25   import("$dir_pw_protobuf_compiler/proto.gni")
26
27   pw_proto_library("chat_protos") {
28     sources = [ "chat_protos/chat_service.proto" ]
29   }
30
31   # Library that implements the Chat service.
32   pw_source_set("chat_service") {
33     sources = [
34       "chat_service.cc",
35       "chat_service.h",
36     ]
37     public_deps = [ ":chat_protos.pwpb_rpc" ]
38   }
39
40A C++ header file is generated for each input .proto file, with the ``.proto``
41extension replaced by ``.rpc.pwpb.h``. For example, given the input file
42``chat_protos/chat_service.proto``, the generated header file will be placed
43at the include path ``"chat_protos/chat_service.rpc.pwpb.h"``.
44
45Generated code API
46==================
47All examples in this document use the following RPC service definition.
48
49.. code-block:: protobuf
50
51   // chat/chat_protos/chat_service.proto
52
53   syntax = "proto3";
54
55   service Chat {
56     // Returns information about a chatroom.
57     rpc GetRoomInformation(RoomInfoRequest) returns (RoomInfoResponse) {}
58
59     // Lists all of the users in a chatroom. The response is streamed as there
60     // may be a large amount of users.
61     rpc ListUsersInRoom(ListUsersRequest) returns (stream ListUsersResponse) {}
62
63     // Uploads a file, in chunks, to a chatroom.
64     rpc UploadFile(stream UploadFileRequest) returns (UploadFileResponse) {}
65
66     // Sends messages to a chatroom while receiving messages from other users.
67     rpc Chat(stream ChatMessage) returns (stream ChatMessage) {}
68   }
69
70Server-side
71-----------
72A C++ class is generated for each service in the .proto file. The class is
73located within a special ``pw_rpc::pwpb`` sub-namespace of the file's package.
74
75The generated class is a base class which must be derived to implement the
76service's methods. The base class is templated on the derived class.
77
78.. code-block:: c++
79
80   #include "chat_protos/chat_service.rpc.pwpb.h"
81
82   class ChatService final : public pw_rpc::pwpb::Chat::Service<ChatService> {
83    public:
84     // Implementations of the service's RPC methods; see below.
85   };
86
87Unary RPC
88^^^^^^^^^
89A unary RPC is implemented as a function which takes in the RPC's request struct
90and populates a response struct to send back, with a status indicating whether
91the request succeeded.
92
93.. code-block:: c++
94
95   pw::Status GetRoomInformation(const RoomInfoRequest::Message& request,
96                                 RoomInfoResponse::Message& response);
97
98Server streaming RPC
99^^^^^^^^^^^^^^^^^^^^
100A server streaming RPC receives the client's request message alongside a
101``ServerWriter``, used to stream back responses.
102
103.. code-block:: c++
104
105   void ListUsersInRoom(const ListUsersRequest::Message& request,
106                        pw::rpc::ServerWriter<ListUsersResponse::Message>& writer);
107
108The ``ServerWriter`` object is movable, and remains active until it is manually
109closed or goes out of scope. The writer has a simple API to return responses:
110
111.. cpp:function:: Status PwpbServerWriter::Write(const T::Message& response)
112
113  Writes a single response message to the stream. The returned status indicates
114  whether the write was successful.
115
116.. cpp:function:: void PwpbServerWriter::Finish(Status status = OkStatus())
117
118  Closes the stream and sends back the RPC's overall status to the client.
119
120.. cpp:function:: Status PwpbServerWriter::TryFinish(Status status = OkStatus())
121
122  Closes the stream and sends back the RPC's overall status to the client only
123  if the final packet is successfully sent.
124
125Once a ``ServerWriter`` has been closed, all future ``Write`` calls will fail.
126
127.. attention::
128
129  Make sure to use ``std::move`` when passing the ``ServerWriter`` around to
130  avoid accidentally closing it and ending the RPC.
131
132Client streaming RPC
133^^^^^^^^^^^^^^^^^^^^
134.. attention:: Supported, but the documentation is still under construction.
135
136Bidirectional streaming RPC
137^^^^^^^^^^^^^^^^^^^^^^^^^^^
138.. attention:: Supported, but the documentation is still under construction.
139
140
141.. _module-pw_rpc_pw_protobuf-client:
142
143Client-side
144-----------
145A corresponding client class is generated for every service defined in the proto
146file. To allow multiple types of clients to exist, it is placed under the
147``pw_rpc::pwpb`` namespace. The ``Client`` class is nested under
148``pw_rpc::pwpb::ServiceName``. For example, the ``Chat`` service would create
149``pw_rpc::pwpb::Chat::Client``.
150
151Service clients are instantiated with a reference to the RPC client through
152which they will send requests, and the channel ID they will use.
153
154.. code-block:: c++
155
156   // Nested under pw_rpc::pwpb::ServiceName.
157   class Client {
158    public:
159     Client(::pw::rpc::Client& client, uint32_t channel_id);
160
161     pw::rpc::PwpbUnaryReceiver<RoomInfoResponse::Message> GetRoomInformation(
162         const RoomInfoRequest::Message& request,
163         ::pw::Function<void(Status, const RoomInfoResponse::Message&)> on_response,
164         ::pw::Function<void(Status)> on_rpc_error = nullptr);
165
166     // ...and more (see below).
167   };
168
169RPCs can also be invoked individually as free functions:
170
171.. code-block:: c++
172
173   pw::rpc::PwpbUnaryReceiver<RoomInfoResponse::Message> call = pw_rpc::pwpb::Chat::GetRoomInformation(
174       client, channel_id, request, on_response, on_rpc_error);
175
176The client class has member functions for each method defined within the
177service's protobuf descriptor. The arguments to these methods vary depending on
178the type of RPC. Each method returns a client call object which stores the
179context of the ongoing RPC call. For more information on call objects, refer to
180the :ref:`core RPC docs <module-pw_rpc-making-calls>`.
181
182If dynamic allocation is enabled (:c:macro:`PW_RPC_DYNAMIC_ALLOCATION` is 1), a
183``DynamicClient`` is generated, which dynamically allocates the call object with
184:c:macro:`PW_RPC_MAKE_UNIQUE_PTR`. For example:
185
186.. code-block:: c++
187
188   my_namespace::pw_rpc::pwpb::ServiceName::DynamicClient dynamic_client(
189       client, channel_id);
190   auto call = dynamic_client.TestUnaryRpc(request, response_callback);
191
192   if (call->active()) {  // Access the call as a std::unique_ptr
193     // ...
194
195.. admonition:: Callback invocation
196
197  RPC callbacks are invoked synchronously from ``Client::ProcessPacket``.
198
199Method APIs
200^^^^^^^^^^^
201The arguments provided when invoking a method depend on its type.
202
203Unary RPC
204~~~~~~~~~
205A unary RPC call takes the request struct and a callback to invoke when a
206response is received. The callback receives the RPC's status and response
207struct.
208
209An optional second callback can be provided to handle internal errors.
210
211.. code-block:: c++
212
213   pw::rpc::PwpbUnaryReceiver<RoomInfoResponse::Message> GetRoomInformation(
214       const RoomInfoRequest::Message& request,
215       ::pw::Function<void(const RoomInfoResponse::Message&, Status)> on_response,
216       ::pw::Function<void(Status)> on_rpc_error = nullptr);
217
218Server streaming RPC
219~~~~~~~~~~~~~~~~~~~~
220A server streaming RPC call takes the initial request struct and two callbacks.
221The first is invoked on every stream response received, and the second is
222invoked once the stream is complete with its overall status.
223
224An optional third callback can be provided to handle internal errors.
225
226.. code-block:: c++
227
228   pw::rpc::PwpbClientReader<ListUsersResponse::Message> ListUsersInRoom(
229       const ListUsersRequest::Message& request,
230       ::pw::Function<void(const ListUsersResponse::Message&)> on_response,
231       ::pw::Function<void(Status)> on_stream_end,
232       ::pw::Function<void(Status)> on_rpc_error = nullptr);
233
234Client streaming RPC
235~~~~~~~~~~~~~~~~~~~~
236.. attention:: Supported, but the documentation is still under construction.
237
238Bidirectional streaming RPC
239~~~~~~~~~~~~~~~~~~~~~~~~~~~
240.. attention:: Supported, but the documentation is still under construction.
241
242Example usage
243^^^^^^^^^^^^^
244The following example demonstrates how to call an RPC method using a pw_protobuf
245service client and receive the response.
246
247.. code-block:: c++
248
249   #include "chat_protos/chat_service.rpc.pwpb.h"
250
251   namespace {
252
253     using ChatClient = pw_rpc::pwpb::Chat::Client;
254
255     MyChannelOutput output;
256     pw::rpc::Channel channels[] = {pw::rpc::Channel::Create<1>(&output)};
257     pw::rpc::Client client(channels);
258
259     // Callback function for GetRoomInformation.
260     void LogRoomInformation(const RoomInfoResponse::Message& response,
261                             Status status);
262
263   }  // namespace
264
265   void InvokeSomeRpcs() {
266     // Instantiate a service client to call Chat service methods on channel 1.
267     ChatClient chat_client(client, 1);
268
269     // The RPC will remain active as long as `call` is alive.
270     auto call = chat_client.GetRoomInformation(
271         {.room = "pigweed"}, LogRoomInformation);
272     if (!call.active()) {
273       // The invocation may fail. This could occur due to an invalid channel ID,
274       // for example. The failure status is forwarded to the to call's
275       // on_rpc_error callback.
276       return;
277     }
278
279     // For simplicity, block until the call completes. An actual implementation
280     // would likely std::move the call somewhere to keep it active while doing
281     // other work.
282     while (call.active()) {
283       Wait();
284     }
285
286     // Do other stuff now that we have the room information.
287   }
288