LCOV - code coverage report
Current view: top level - waltz/grpc - fd_grpc_client.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 3 0.0 %
Date: 2026-11-11 12:40:43 Functions: 0 0 -

          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 "../fd_fqdn.h"
       9             : #include "../tlsrec/fd_tlsrec.h"
      10             : 
      11             : struct fd_grpc_client_private;
      12             : typedef struct fd_grpc_client_private fd_grpc_client_t;
      13             : 
      14             : struct fd_grpc_h2_stream;
      15             : typedef struct fd_grpc_h2_stream fd_grpc_h2_stream_t;
      16             : 
      17             : /* FD_GRPC_CLIENT_MAX_STREAMS specifies the max number of inflight
      18             :    unary and server-streaming requests.  Note that grpc_client does
      19             :    not scale well to large numbers due to O(n) algorithms. */
      20             : 
      21           0 : #define FD_GRPC_CLIENT_MAX_STREAMS 8
      22             : 
      23             : /* FD_GRPC_DEADLINE_* identify different types of request deadlines. */
      24             : 
      25           0 : #define FD_GRPC_DEADLINE_HEADER 1 /* deadline by which Response-Headers are received */
      26           0 : #define FD_GRPC_DEADLINE_RX_END 2 /* deadline by which 'end of stream' must have been reached */
      27             : 
      28             : /* fd_grpc_client_metrics_t hold counters that are incremented by a
      29             :    grpc_client. */
      30             : 
      31             : struct fd_grpc_client_metrics {
      32             : 
      33             :   /* wakeup_cnt counts the number of times the gRPC client was polled
      34             :      for I/O. */
      35             :   ulong wakeup_cnt;
      36             : 
      37             :   /* stream_err_cnt counts the number of survivable stream errors.
      38             :      These include out-of-memory conditions and decode failures. */
      39             :   ulong stream_err_cnt;
      40             : 
      41             :   /* conn_err_cnt counts the number of connection errors that resulted
      42             :      in connection termination.  These include protocol and I/O errors. */
      43             :   ulong conn_err_cnt;
      44             : 
      45             :   /* stream_chunks_tx_cnt increments whenever a DATA frame containing
      46             :      request bytes is sent.  stream_chunks_tx_bytes counts the number of
      47             :      stream bytes sent. */
      48             :   ulong stream_chunks_tx_cnt;
      49             :   ulong stream_chunks_tx_bytes;
      50             : 
      51             :   /* stream_chunks_rx_cnt increments whenever a DATA frame containing
      52             :      response bytes is received.  stream_chunks_rx_bytes counts the
      53             :      number of stream bytes received. */
      54             :   ulong stream_chunks_rx_cnt;
      55             :   ulong stream_chunks_rx_bytes;
      56             : 
      57             :   /* requests_sent increments whenever a gRPC request finished sending. */
      58             :   ulong requests_sent;
      59             : 
      60             :   /* streams_active is the number of streams not in 'closed' state. */
      61             :   long streams_active;
      62             : 
      63             :   /* rx_wait_ticks_cum is the cumulative time in ticks that incoming
      64             :      gRPC messages were in a "waiting" state.  The waiting state begins
      65             :      when the first byte of a HTTP/2 frame is received, and ends when
      66             :      all gRPC message bytes are received.
      67             : 
      68             :      This is a rough measure of server-to-client congestion.  On a
      69             :      healthy connection, this value should be close to zero. */
      70             :   long rx_wait_ticks_cum;
      71             : 
      72             :   /* tx_wait_ticks_cum is the cumulative time in ticks that an outgoing
      73             :      message was in a "waiting" state.  The waiting state begins when
      74             :      a message is ready to be sent, and ends when all message bytes were
      75             :      handed to the TCP layer.
      76             : 
      77             :      This is a rough measure of client-to-server congestion, which can
      78             :      be caused by the TCP server receive window, TCP client congestion
      79             :      control, or HTTP/2 server flow control.  On a healthy connection,
      80             :      this value should be close to zero. */
      81             :   long tx_wait_ticks_cum;
      82             : 
      83             : };
      84             : 
      85             : typedef struct fd_grpc_client_metrics fd_grpc_client_metrics_t;
      86             : 
      87             : /* fd_grpc_client_callbacks_t is a virtual function table containing
      88             :    grpc_client->app callbacks. */
      89             : 
      90             : struct fd_grpc_client_callbacks {
      91             : 
      92             :   /* conn_established is called when the initial HTTP/2 SETTINGS
      93             :      exchange concludes.  Technically, requests can be sent before this
      94             :      point, though. */
      95             : 
      96             :   void
      97             :   (* conn_established)( void * app_ctx );
      98             : 
      99             :   /* conn_dead is called when the HTTP/2 connection ends.  To recover,
     100             :      call fd_grpc_client_reset() after the invoking call returns. */
     101             : 
     102             :   void
     103             :   (* conn_dead)( void * app_ctx,
     104             :                  uint   h2_err,
     105             :                  int    closed_by );
     106             : 
     107             :   /* tx_complete marks the completion of a tx operation. */
     108             : 
     109             :   void
     110             :   (* tx_complete)( void * app_ctx,
     111             :                    ulong  request_ctx );
     112             : 
     113             :   /* rx_start signals that the server sent back a response header
     114             :      indicating success.  rx_start is always called before the first
     115             :      call to rx_msg for that request_ctx. */
     116             : 
     117             :   void
     118             :   (* rx_start)( void * app_ctx,
     119             :                 ulong  request_ctx );
     120             : 
     121             :   /* rx_msg delivers a gRPC message.  May be called multiple times for
     122             :      the same request (server streaming). */
     123             : 
     124             :   void
     125             :   (* rx_msg)( void *       app_ctx,
     126             :               void const * protobuf,
     127             :               ulong        protobuf_sz,
     128             :               ulong        request_ctx );
     129             : 
     130             :   /* rx_end indicates that no more rx_msg callbacks will be delivered
     131             :      for a request. */
     132             : 
     133             :   void
     134             :   (* rx_end)( void *                app_ctx,
     135             :               ulong                 request_ctx,
     136             :               fd_grpc_resp_hdrs_t * resp );
     137             : 
     138             :   /* rx_timeout indicates that a request deadline was exceeded.
     139             :      deadline_kind indicates which timer fired. */
     140             : 
     141             :   void
     142             :   (* rx_timeout)( void * app_ctx,
     143             :                   ulong  request_ctx,
     144             :                   int    deadline_kind );
     145             : 
     146             :   /* ping_ack delivers an acknowledgement of a PING that was previously
     147             :      sent by fd_h2_tx_ping. */
     148             : 
     149             :   void
     150             :   (* ping_ack)( void * app_ctx );
     151             : 
     152             : };
     153             : 
     154             : typedef struct fd_grpc_client_callbacks fd_grpc_client_callbacks_t;
     155             : 
     156             : FD_PROTOTYPES_BEGIN
     157             : 
     158             : /* Constructors */
     159             : 
     160             : ulong
     161             : fd_grpc_client_align( void );
     162             : 
     163             : ulong
     164             : fd_grpc_client_footprint( ulong buf_max );
     165             : 
     166             : fd_grpc_client_t *
     167             : fd_grpc_client_new( void *                             mem,
     168             :                     fd_grpc_client_callbacks_t const * callbacks,
     169             :                     fd_grpc_client_metrics_t *         metrics,
     170             :                     void *                             app_ctx,
     171             :                     ulong                              buf_max,
     172             :                     ulong                              rng_seed );
     173             : 
     174             : void *
     175             : fd_grpc_client_delete( fd_grpc_client_t * client );
     176             : 
     177             : /* fd_grpc_client_next_deadline returns the earliest stream deadline
     178             :    (header or rx-end) across all inflight requests, 0 if a timed-out
     179             :    stream's RST_STREAM can be sent now, or LONG_MAX if no stream has a
     180             :    deadline armed. */
     181             : 
     182             : FD_FN_PURE long
     183             : fd_grpc_client_next_deadline( fd_grpc_client_t const * client );
     184             : 
     185             : /* fd_grpc_client_tx_pending returns 1 if the client has HTTP/2 frame
     186             :    bytes buffered that could not yet be written to the transport. */
     187             : 
     188             : FD_FN_PURE int
     189             : fd_grpc_client_tx_pending( fd_grpc_client_t const * client );
     190             : 
     191             : /* fd_grpc_client_tls_rx_pending returns 1 if the client holds decrypted
     192             :    TLS plaintext not yet pushed into the HTTP/2 layer.  Such bytes do
     193             :    not make the socket readable, so the caller must step the client
     194             :    without waiting for an fd event.  fd_grpc_client_tls_tx_pending
     195             :    returns 1 if encrypted TLS bytes are waiting to be written to the
     196             :    socket (e.g. a handshake flight blocked on EAGAIN).  RX continues
     197             :    while it is set; arm EPOLLOUT to learn when the send can resume. */
     198             : 
     199             : FD_FN_PURE int
     200             : fd_grpc_client_tls_rx_pending( fd_grpc_client_t const * client );
     201             : 
     202             : FD_FN_PURE int
     203             : fd_grpc_client_tls_tx_pending( fd_grpc_client_t const * client );
     204             : 
     205             : /* fd_grpc_client_tx_starved returns the number of request bytes parked
     206             :    mid-send because the peer's HTTP/2 flow-control window (stream or
     207             :    connection) is exhausted, or 0 if not starved.  Only a WINDOW_UPDATE
     208             :    from the peer can resume it. */
     209             : 
     210             : FD_FN_PURE ulong
     211             : fd_grpc_client_tx_starved( fd_grpc_client_t const * client );
     212             : 
     213             : /* fd_grpc_client_reset cancels all inflight requests and abandons the
     214             :    HTTP/2 client connection.  Config params are kept intact (e.g. host,
     215             :    port, version).  Defer reset requested by a client callback until
     216             :    the call that invoked the callback returns. */
     217             : 
     218             : void
     219             : fd_grpc_client_reset( fd_grpc_client_t * client );
     220             : 
     221             : /* fd_grpc_client_set_version sets the gRPC client's version string
     222             :    (relayed via user-agent header).  No reference to the provided string
     223             :    is kept (the content is copied out to the client object).  version
     224             :    does not have to be null-terminated.  version_len must be
     225             :    FD_GRPC_CLIENT_VERSION_LEN_MAX or less, otherwise a warning is logged
     226             :    and the client's version string remains unchanged. */
     227             : 
     228             : #define FD_GRPC_CLIENT_VERSION_LEN_MAX (63UL)
     229             : 
     230             : void
     231             : fd_grpc_client_set_version( fd_grpc_client_t * client,
     232             :                             char const *       version,
     233             :                             ulong              version_len );
     234             : 
     235             : /* fd_grpc_client_set_authority sets the authority header to the
     236             :    specified hostname and port number.  host_len should be less than
     237             :    FD_FQDN_BUF_MAX, otherwise host is truncated. */
     238             : 
     239             : void
     240             : fd_grpc_client_set_authority( fd_grpc_client_t * client,
     241             :                               char const *       host,
     242             :                               ulong              host_len,
     243             :                               ushort             port );
     244             : 
     245             : /* fd_grpc_client_set_https selects the request :scheme header.  Defaults
     246             :    to HTTPS.  Set to zero before sending requests over plaintext HTTP.
     247             :    Preserved by fd_grpc_client_reset. */
     248             : 
     249             : void
     250             : fd_grpc_client_set_https( fd_grpc_client_t * client,
     251             :                           int                https );
     252             : 
     253             : /* fd_grpc_client_rxtx_socket drives I/O against a TCP socket.
     254             :    (recvmsg(2) and sendmsg(2)).  Uses MSG_NOSIGNAL|MSG_DONTWAIT flags.
     255             : 
     256             :    Returns -1 if an error was encountered, and errno will be set.
     257             :    Returns 1 if send would block with EAGAIN.  Otherwise, returns 0. */
     258             : 
     259             : int
     260             : fd_grpc_client_rxtx_socket( fd_grpc_client_t * client,
     261             :                             int                sock_fd,
     262             :                             long               now,
     263             :                             int *              charge_busy );
     264             : 
     265             : /* fd_grpc_client_tx_flush_socket attempts one sendmsg(2) of pending
     266             :    HTTP/2 frame bytes to the socket.  Returns -1 (errno set) on a hard
     267             :    send error, 1 if send would block with EAGAIN, and 0 otherwise (a
     268             :    short write may leave bytes pending; check fd_grpc_client_tx_pending). */
     269             : 
     270             : int
     271             : fd_grpc_client_tx_flush_socket( fd_grpc_client_t * client,
     272             :                                 int                sock_fd );
     273             : 
     274             : /* fd_grpc_client_tls_flush writes pending encrypted TLS bytes to the
     275             :    socket, looping until the TLS TX buffer is empty or send blocks.
     276             :    Returns -1 (errno set) on a hard send error, 1 if send would block
     277             :    with EAGAIN (bytes remain, see fd_grpc_client_tls_tx_pending), and 0
     278             :    if the buffer is now empty. */
     279             : 
     280             : int
     281             : fd_grpc_client_tls_flush( fd_grpc_client_t * client,
     282             :                           int                sock_fd );
     283             : 
     284             : /* fd_grpc_client_rxtx_tls drives I/O against a TCP socket with
     285             :    fd_tlsrec encryption.  Handles TLS handshake, encrypts outgoing
     286             :    frames, and decrypts incoming data.
     287             : 
     288             :    Returns 0 on success and -1 if there is an unrecoverable error. */
     289             : 
     290             : int
     291             : fd_grpc_client_rxtx_tls( fd_grpc_client_t * client,
     292             :                          fd_tlsrec_conn_t * tls_conn,
     293             :                          int                sock_fd,
     294             :                          long               now,
     295             :                          int *              charge_busy );
     296             : 
     297             : /* fd_grpc_client_request_start queues a gRPC request for send.  The
     298             :    request includes one Protobuf message (unary request).  The client
     299             :    can only write one request payload at a time, but can have multiple
     300             :    requests pending for responses.
     301             : 
     302             :    path is the HTTP request path which usually follows the pattern
     303             :    '/path.to.package/Service.Function'.  If auth_token_sz is greater
     304             :    than zero, adds a request header 'authorization: Bearer *auth_token'.
     305             : 
     306             :    request_ctx is an arbitrary number used to identify the request.  It
     307             :    echoes in callbacks.
     308             : 
     309             :    protobuf points to the serialized Protobuf message (protobuf_sz
     310             :    bytes).  protobuf==NULL sends only the request headers (requires
     311             :    is_streaming).
     312             : 
     313             :    auth_token is an optional authorization header.  The header value is
     314             :    prepended with "Bearer ".  auth_token_sz==0 omits the auth header.
     315             : 
     316             :    is_streaming: If 0, this is a unary request and the stream is closed
     317             :    after sending the first message (END_STREAM flag set).  If non-zero,
     318             :    this is a client streaming request and the stream remains open for
     319             :    additional messages via fd_grpc_client_stream_send_msg().  The stream
     320             :    must be explicitly closed with fd_grpc_client_stream_close().
     321             : 
     322             :    Conditions for starting send:
     323             :    - The connection is not dead and the HTTP/2 handshake is complete.
     324             :    - Client has quota to open a new stream (MAX_CONCURRENT_STREAMS)
     325             :    - There is no other request still sending.
     326             :    - The message serialized size does not exceed buf_max (set in
     327             :      fd_grpc_client_new())
     328             :    - rbuf_tx is empty.  (HTTP/2 frames all flushed out to sockets) */
     329             : 
     330             : fd_grpc_h2_stream_t *
     331             : fd_grpc_client_request_start(
     332             :     fd_grpc_client_t *   client,
     333             :     char const *         path,
     334             :     ulong                path_len, /* in [0,128) */
     335             :     ulong                request_ctx,
     336             :     uchar const *        protobuf,
     337             :     ulong                protobuf_sz,
     338             :     char const *         auth_token,
     339             :     ulong                auth_token_sz,
     340             :     int                  is_streaming );
     341             : 
     342             : /* fd_grpc_client_stream_send_msg sends an additional message on an
     343             :    already-open client streaming request.  This function can only be
     344             :    called after fd_grpc_client_request_start() was called with
     345             :    is_streaming=1.
     346             : 
     347             :    Returns 1 on success, 0 if the operation failed (connection dead,
     348             :    buffers blocked, or encoding failure).
     349             : 
     350             :    Conditions for sending:
     351             :    - The connection is alive
     352             :    - No other send operation is in progress
     353             :    - rbuf_tx is empty
     354             :    - The message serialized size does not exceed buf_max */
     355             : 
     356             : int
     357             : fd_grpc_client_stream_send_msg(
     358             :     fd_grpc_client_t *    client,
     359             :     fd_grpc_h2_stream_t * stream,
     360             :     uchar const *         protobuf,
     361             :     ulong                 protobuf_sz );
     362             : 
     363             : /* fd_grpc_client_stream_close explicitly closes a client streaming
     364             :    request by sending an empty DATA frame with the END_STREAM flag.
     365             :    This signals to the server that no more messages will be sent.
     366             : 
     367             :    This function should be called after all messages have been sent via
     368             :    fd_grpc_client_stream_send_msg() to complete the client stream.
     369             : 
     370             :    Returns 1 on success, 0 if the operation failed (connection dead or
     371             :    buffers blocked).
     372             : 
     373             :    Conditions for closing:
     374             :    - The connection is alive
     375             :    - No other send operation is in progress
     376             :    - rbuf_tx is empty */
     377             : 
     378             : int
     379             : fd_grpc_client_stream_close(
     380             :     fd_grpc_client_t *    client,
     381             :     fd_grpc_h2_stream_t * stream
     382             : );
     383             : 
     384             : /* fd_grpc_client_stream_cancel aborts a request with RST_STREAM CANCEL
     385             :    and frees the stream without calling back.  Must not be called from
     386             :    a callback for the same stream.  Returns 1 on success, 0 if there is
     387             :    no TX space (retry later). */
     388             : 
     389             : int
     390             : fd_grpc_client_stream_cancel( fd_grpc_client_t *    client,
     391             :                               fd_grpc_h2_stream_t * stream );
     392             : 
     393             : /* fd_grpc_client_deadline_set sets a request deadline (used to
     394             :    configure timeouts).  deadline_kind is FD_GRPC_DEADLINE_*.  Logs
     395             :    error and aborts app if deadline_kind is unsupported.
     396             : 
     397             :    Behavior for different deadline kinds:
     398             :    - HEADER: Deadline by which gRPC Response-Headers must have been
     399             :              received
     400             :    - RX_END: Deadline by which the response stream must have been ended.
     401             :              For unary responses, this is the point at which the message
     402             :              has been fully received.  For server-streaming responses,
     403             :              it is the point at which the last message has been
     404             :              received, and there are no more messages remaining.  (Under
     405             :              the hood, this is indicated by the HTTP/2 END_STREAM flag.) */
     406             : 
     407             : void
     408             : fd_grpc_client_deadline_set( fd_grpc_h2_stream_t * stream,
     409             :                              int                   deadline_kind,
     410             :                              long                  ts_nanos );
     411             : 
     412             : /* fd_grpc_client_is_connected returns 1 if HTTP/2 SETTINGS were
     413             :    exchanged, the TLS handshake is complete (if applicable), and the
     414             :    conn hasn't died.  Otherwise, returns 0. */
     415             : 
     416             : int
     417             : fd_grpc_client_is_connected( fd_grpc_client_t * client );
     418             : 
     419             : /* fd_grpc_client_request_is_blocked returns 1 if a call to
     420             :    fd_grpc_client_request_start would certainly fail.  Reasons include
     421             :    SSL / HTTP/2 handshake not complete, or buffers blocked. */
     422             : 
     423             : int
     424             : fd_grpc_client_request_is_blocked( fd_grpc_client_t * client );
     425             : 
     426             : /* fd_grpc_client_stream_send_is_blocked returns 1 if a message cannot
     427             :    currently be sent on an already open stream (conn dead, handshake
     428             :    incomplete, tx buffer non-empty, or a send already in flight), 0
     429             :    otherwise.  Unlike fd_grpc_client_request_is_blocked it does not
     430             :    require headroom to open a new stream, so a server advertising a low
     431             :    SETTINGS_MAX_CONCURRENT_STREAMS cannot stall sends on the open
     432             :    stream. */
     433             : 
     434             : int
     435             : fd_grpc_client_stream_send_is_blocked( fd_grpc_client_t * client );
     436             : 
     437             : int
     438             : fd_grpc_client_request_stream_busy( fd_grpc_client_t * client );
     439             : 
     440             : /* Pointers to internals for testing */
     441             : 
     442             : fd_h2_rbuf_t *
     443             : fd_grpc_client_rbuf_tx( fd_grpc_client_t * client );
     444             : 
     445             : fd_h2_rbuf_t *
     446             : fd_grpc_client_rbuf_rx( fd_grpc_client_t * client );
     447             : 
     448             : fd_h2_conn_t *
     449             : fd_grpc_client_h2_conn( fd_grpc_client_t * client );
     450             : 
     451             : extern fd_h2_callbacks_t const fd_grpc_client_h2_callbacks;
     452             : 
     453             : FD_PROTOTYPES_END
     454             : 
     455             : #endif /* HEADER_fd_src_waltz_grpc_fd_grpc_client_h */

Generated by: LCOV version 1.14