Line data Source code
1 : #ifndef HEADER_fd_src_waltz_grpc_fd_grpc_client_h
2 : #define HEADER_fd_src_waltz_grpc_fd_grpc_client_h
3 :
4 : /* fd_grpc_client.h provides an API for dispatching unary and server-
5 : streaming gRPC requests over HTTP/2+TLS. */
6 :
7 : #include "fd_grpc_codec.h"
8 : #include "../../ballet/nanopb/pb_firedancer.h" /* pb_msgdesc_t */
9 : #if FD_HAS_OPENSSL
10 : #include <openssl/types.h> /* SSL */
11 : #endif
12 :
13 : struct fd_grpc_client_private;
14 : typedef struct fd_grpc_client_private fd_grpc_client_t;
15 :
16 : struct fd_grpc_h2_stream;
17 : typedef struct fd_grpc_h2_stream fd_grpc_h2_stream_t;
18 :
19 : /* FD_GRPC_CLIENT_MAX_STREAMS specifies the max number of inflight
20 : unary and server-streaming requests. Note that grpc_client does
21 : not scale well to large numbers due to O(n) algorithms. */
22 :
23 30 : #define FD_GRPC_CLIENT_MAX_STREAMS 8
24 :
25 : /* FD_GRPC_DEADLINE_* identify different types of request deadlines. */
26 :
27 15 : #define FD_GRPC_DEADLINE_HEADER 1 /* deadline by which Response-Headers are recevied */
28 9 : #define FD_GRPC_DEADLINE_RX_END 2 /* deadline by which 'end of stream' must have been reached */
29 :
30 : /* fd_grpc_client_metrics_t hold counters that are incremented by a
31 : grpc_client. */
32 :
33 : struct fd_grpc_client_metrics {
34 :
35 : /* wakeup_cnt counts the number of times the gRPC client was polled
36 : for I/O. */
37 : ulong wakeup_cnt;
38 :
39 : /* stream_err_cnt counts the number of survivable stream errors.
40 : These include out-of-memory conditions and decode failures. */
41 : ulong stream_err_cnt;
42 :
43 : /* conn_err_cnt counts the number of connection errors that resulted
44 : in connection termination. These include protocol and I/O errors. */
45 : ulong conn_err_cnt;
46 :
47 : /* stream_chunks_tx_cnt increments whenever a DATA frame containing
48 : request bytes is sent. stream_chunks_tx_bytes counts the number of
49 : stream bytes sent. */
50 : ulong stream_chunks_tx_cnt;
51 : ulong stream_chunks_tx_bytes;
52 :
53 : /* stream_chunks_rx_cnt increments whenever a DATA frame containing
54 : response bytes is received. stream_chunks_rx_bytes counts the
55 : number of stream bytes received. */
56 : ulong stream_chunks_rx_cnt;
57 : ulong stream_chunks_rx_bytes;
58 :
59 : /* requests_sent increments whenever a gRPC request finished sending. */
60 : ulong requests_sent;
61 :
62 : /* streams_active is the number of streams not in 'closed' state. */
63 : long streams_active;
64 :
65 : /* rx_wait_ticks_cum is the cumulative time in ticks that incoming
66 : gRPC messages were in a "waiting" state. The waiting state begins
67 : when the first byte of a HTTP/2 frame is received, and ends when
68 : all gRPC message bytes are received.
69 :
70 : This is a rough measure of server-to-client congestion. On a
71 : healthy connection, this value should be close to zero. */
72 : long rx_wait_ticks_cum;
73 :
74 : /* tx_wait_ticks_cum is the cumulative time in ticks that an outgoing
75 : message was in a "waiting" state. The waiting state begins when
76 : a message is ready to be sent, and ends when all message bytes were
77 : handed to the TCP layer.
78 :
79 : This is a rough measure of client-to-server congestion, which can
80 : be caused by the TCP server receive window, TCP client congestion
81 : control, or HTTP/2 server flow control. On a healthy connection,
82 : this value should be close to zero. */
83 : long tx_wait_ticks_cum;
84 :
85 : };
86 :
87 : typedef struct fd_grpc_client_metrics fd_grpc_client_metrics_t;
88 :
89 : /* fd_grpc_client_callbacks_t is a virtual function table containing
90 : grpc_client->app callbacks. */
91 :
92 : struct fd_grpc_client_callbacks {
93 :
94 : /* conn_established is called when the initial HTTP/2 SETTINGS
95 : exchange concludes. Technically, requests can be sent before this
96 : point, though. */
97 :
98 : void
99 : (* conn_established)( void * app_ctx );
100 :
101 : /* conn_dead is called when the HTTP/2 connection ends. To recover
102 : from this condition, call fd_grpc_client_reset(). */
103 :
104 : void
105 : (* conn_dead)( void * app_ctx,
106 : uint h2_err,
107 : int closed_by );
108 :
109 : /* tx_complete marks the completion of a tx operation. */
110 :
111 : void
112 : (* tx_complete)( void * app_ctx,
113 : ulong request_ctx );
114 :
115 : /* rx_start signals that the server sent back a response header
116 : indicating success. rx_start is always called before the first
117 : call to rx_msg for that request_ctx. */
118 :
119 : void
120 : (* rx_start)( void * app_ctx,
121 : ulong request_ctx );
122 :
123 : /* rx_msg delivers a gRPC message. May be called multiple times for
124 : the same request (server streaming). */
125 :
126 : void
127 : (* rx_msg)( void * app_ctx,
128 : void const * protobuf,
129 : ulong protobuf_sz,
130 : ulong request_ctx );
131 :
132 : /* rx_end indicates that no more rx_msg callbacks will be delivered
133 : for a request. */
134 :
135 : void
136 : (* rx_end)( void * app_ctx,
137 : ulong request_ctx,
138 : fd_grpc_resp_hdrs_t * resp );
139 :
140 : /* rx_timeout indicates that a request deadline was exceeded.
141 : deadline_kind indicates which timer fired. */
142 :
143 : void
144 : (* rx_timeout)( void * app_ctx,
145 : ulong request_ctx,
146 : int deadline_kind );
147 :
148 : /* ping_ack delivers an acknowledgement of a PING that was previously
149 : sent by fd_h2_tx_ping. */
150 :
151 : void
152 : (* ping_ack)( void * app_ctx );
153 :
154 : };
155 :
156 : typedef struct fd_grpc_client_callbacks fd_grpc_client_callbacks_t;
157 :
158 : FD_PROTOTYPES_BEGIN
159 :
160 : /* Constructors */
161 :
162 : ulong
163 : fd_grpc_client_align( void );
164 :
165 : ulong
166 : fd_grpc_client_footprint( ulong buf_max );
167 :
168 : fd_grpc_client_t *
169 : fd_grpc_client_new( void * mem,
170 : fd_grpc_client_callbacks_t const * callbacks,
171 : fd_grpc_client_metrics_t * metrics,
172 : void * app_ctx,
173 : ulong buf_max,
174 : ulong rng_seed );
175 :
176 : void *
177 : fd_grpc_client_delete( fd_grpc_client_t * client );
178 :
179 : /* fd_grpc_client_reset cancels all inflight requests and abandons the
180 : HTTP/2 client connection. Config params are kept intact (e.g. host,
181 : port, version). */
182 :
183 : void
184 : fd_grpc_client_reset( fd_grpc_client_t * client );
185 :
186 : /* fd_grpc_client_set_version sets the gRPC client's version string
187 : (relayed via user-agent header). No reference to the provided string
188 : is kept (the content is copied out to the client object). version
189 : does not have to be null-terminated. version_len must be
190 : FD_GRPC_CLIENT_VERSION_LEN_MAX or less, otherwise a warning is logged
191 : and the client's version string remains unchanged. */
192 :
193 : #define FD_GRPC_CLIENT_VERSION_LEN_MAX (63UL)
194 :
195 : void
196 : fd_grpc_client_set_version( fd_grpc_client_t * client,
197 : char const * version,
198 : ulong version_len );
199 :
200 : /* fd_grpc_client_set_authority sets the authority header to the
201 : specified hostname and port number. host_len should be <= 255,
202 : otherwise host is truncated. */
203 :
204 : void
205 : fd_grpc_client_set_authority( fd_grpc_client_t * client,
206 : char const * host,
207 : ulong host_len,
208 : ushort port );
209 :
210 : #if FD_HAS_OPENSSL
211 :
212 : /* fd_grpc_client_rxtx_ossl drives I/O against the SSL object
213 : (SSL_read_ex and SSL_write_ex).
214 :
215 : This function currently copies back-and-forth between SSL and
216 : fd_h2 rbuf. This could be improved by adding an interface to allow
217 : OpenSSL->h2 or h2->OpenSSL writes to directly place data into the
218 : target buffer.
219 :
220 : Returns 1 on success and 0 if there is an unrecoverable SSL error. */
221 :
222 : int
223 : fd_grpc_client_rxtx_ossl( fd_grpc_client_t * client,
224 : SSL * ssl,
225 : int * charge_busy );
226 :
227 : #endif /* FD_HAS_OPENSSL */
228 :
229 : /* fd_grpc_client_rxtx_socket drives I/O against a TCP socket.
230 : (recvmsg(2) and sendmsg(2)). Uses MSG_NOSIGNAL|MSG_DONTWAIT flags. */
231 :
232 : int
233 : fd_grpc_client_rxtx_socket( fd_grpc_client_t * client,
234 : int sock_fd,
235 : int * charge_busy );
236 :
237 : /* fd_grpc_client_request_start queues a gRPC request for send. The
238 : request includes one Protobuf message (unary request). The client
239 : can only write one request payload at a time, but can have multiple
240 : requests pending for responses.
241 :
242 : path is the HTTP request path which usually follows the pattern
243 : '/path.to.package/Service.Function'. If auth_token_sz is greater
244 : than zero, adds a request header 'authorization: Bearer *auth_token'.
245 :
246 : request_ctx is an arbitrary number used to identify the request. It
247 : echoes in callbacks.
248 :
249 : fields points to a generated nanopb descriptor. message points to a
250 : generated nanopb struct that the user filled in with info. Calls
251 : pb_encode() internally.
252 :
253 : auth_token is an optional authorization header. The header value is
254 : prepended with "Bearer ". auth_token_sz==0 omits the auth header.
255 :
256 : Conditions for starting send:
257 : - The connection is not dead and the HTTP/2 handshake is complete.
258 : - Client has quota to open a new stream (MAX_CONCURRENT_STREAMS)
259 : - There is no other request still sending.
260 : - The message serialized size does not exceed buf_max (set in
261 : fd_grpc_client_new())
262 : - rbuf_tx is empty. (HTTP/2 frames all flushed out to sockets) */
263 :
264 : fd_grpc_h2_stream_t *
265 : fd_grpc_client_request_start(
266 : fd_grpc_client_t * client,
267 : char const * path,
268 : ulong path_len, /* in [0,128) */
269 : ulong request_ctx,
270 : pb_msgdesc_t const * fields,
271 : void const * message,
272 : char const * auth_token,
273 : ulong auth_token_sz
274 : );
275 :
276 : /* fd_grpc_client_deadline_set sets a request deadline (used to
277 : configure timeouts). deadline_kind is FD_GRPC_DEADLINE_*. Logs
278 : error and aborts app if deadline_kind is unsupported.
279 :
280 : Behavior for different deadline kinds:
281 : - HEADER: Deadline by which gRPC Response-Headers must have been
282 : received
283 : - RX_END: Deadline by which the response stream must have been ended.
284 : For unary responses, this is the point at which the message
285 : has been fully received. For server-streaming responses,
286 : it is the point at which the last message has been
287 : received, and there are no more messages remaining. (Under
288 : the hood, this is indicated by the HTTP/2 END_STREAM flag.) */
289 :
290 : void
291 : fd_grpc_client_deadline_set( fd_grpc_h2_stream_t * stream,
292 : int deadline_kind,
293 : long ts_nanos );
294 :
295 : /* fd_grpc_client_is_connected returns 1 if HTTP/2 SETTINGS were
296 : exchanged, the TLS handshake is complete (if applicable), and the
297 : conn hasn't died. Otherwise, returns 0. */
298 :
299 : int
300 : fd_grpc_client_is_connected( fd_grpc_client_t * client );
301 :
302 : /* fd_grpc_client_request_is_blocked returns 1 if a call to
303 : fd_grpc_client_request_start would certainly fail. Reasons include
304 : SSL / HTTP/2 handshake not complete, or buffers blocked. */
305 :
306 : int
307 : fd_grpc_client_request_is_blocked( fd_grpc_client_t * client );
308 :
309 : /* Pointers to internals for testing */
310 :
311 : fd_h2_rbuf_t *
312 : fd_grpc_client_rbuf_tx( fd_grpc_client_t * client );
313 :
314 : fd_h2_rbuf_t *
315 : fd_grpc_client_rbuf_rx( fd_grpc_client_t * client );
316 :
317 : fd_h2_conn_t *
318 : fd_grpc_client_h2_conn( fd_grpc_client_t * client );
319 :
320 : extern fd_h2_callbacks_t const fd_grpc_client_h2_callbacks;
321 :
322 : FD_PROTOTYPES_END
323 :
324 : #endif /* HEADER_fd_src_waltz_grpc_fd_grpc_client_h */
|