LCOV - code coverage report
Current view: top level - waltz/h2 - fd_h2_stream.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 59 104 56.7 %
Date: 2026-07-09 05:37:44 Functions: 11 160 6.9 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_waltz_h2_fd_h2_stream_h
       2             : #define HEADER_fd_src_waltz_h2_fd_h2_stream_h
       3             : 
       4             : /* fd_h2_stream.h provides the HTTP/2 stream state machine. */
       5             : 
       6             : #include "fd_h2_base.h"
       7             : #include "fd_h2_proto.h"
       8             : #include "fd_h2_conn.h"
       9             : 
      10             : /* The fd_h2_stream_t object holds the stream state machine.  */
      11             : 
      12             : struct fd_h2_stream {
      13             :   uint stream_id;
      14             :   uint tx_wnd; /* transmit quota available */
      15             :   uint rx_wnd; /* receive window bytes remaining */
      16             : 
      17             :   uchar state;
      18             :   uchar hdrs_seq;
      19             : };
      20             : 
      21           0 : #define FD_H2_STREAM_STATE_IDLE       0
      22         123 : #define FD_H2_STREAM_STATE_OPEN       1
      23          12 : #define FD_H2_STREAM_STATE_CLOSING_TX 2 /* half-closed (local) */
      24           9 : #define FD_H2_STREAM_STATE_CLOSING_RX 3 /* half-closed (remote) */
      25           6 : #define FD_H2_STREAM_STATE_CLOSED     4
      26           0 : #define FD_H2_STREAM_STATE_ILLEGAL    5
      27             : 
      28             : FD_PROTOTYPES_BEGIN
      29             : 
      30             : /* fd_h2_stream_init initializes a stream object.  On return, the stream
      31             :    is in 'IDLE' state and does not have an assigned stream ID. */
      32             : 
      33             : static inline fd_h2_stream_t *
      34         108 : fd_h2_stream_init( fd_h2_stream_t * stream ) {
      35         108 :   *stream = (fd_h2_stream_t){0};
      36         108 :   return stream;
      37         108 : }
      38             : 
      39             : /* fd_h2_stream_is_tx returns 1 if the stream is a "TX stream" (i.e. a
      40             :    stream initiated by this endpoint), or 0 if the stream is a "RX
      41             :    stream" (i.e. a stream initiated by the peer). */
      42             : static inline int
      43             : fd_h2_stream_is_tx( fd_h2_stream_t * stream,
      44         114 :                     fd_h2_conn_t *   conn ) {
      45             :   /* Streams initiated by a client MUST use odd-numbered stream identifiers; */
      46         114 :   uint is_client_stream = stream->stream_id & 1;
      47             :   /* Are we a client or a server?  This is determined by the parity of the first stream ID we
      48             :      send which stored in the parity of conn->tx_stream_next. */
      49         114 :   uint is_client_self   = conn->tx_stream_next & 1;
      50         114 :   return is_client_stream == is_client_self;
      51         114 : }
      52             : 
      53             : /* fd_h2_stream_open transitions a stream from 'IDLE' to 'OPEN'.
      54             :    In fd_h2, this happens when the local or peer side sends a HEADERS
      55             :    frame.  The TX side of the stream assumes the peer's default send
      56             :    window. */
      57             : 
      58             : static inline fd_h2_stream_t *
      59             : fd_h2_stream_open( fd_h2_stream_t * stream,
      60             :                    fd_h2_conn_t *   conn,
      61         108 :                    uint             stream_id ) {
      62         108 :   *stream = (fd_h2_stream_t) {
      63         108 :     .stream_id = stream_id,
      64         108 :     .state     = FD_H2_STREAM_STATE_OPEN,
      65         108 :     .tx_wnd    = conn->peer_settings.initial_window_size,
      66         108 :     .rx_wnd    = conn->self_settings.initial_window_size,
      67         108 :     .hdrs_seq  = 0U
      68         108 :   };
      69         108 :   conn->stream_active_cnt[ fd_h2_stream_is_tx( stream, conn ) ]++;
      70         108 :   return stream;
      71         108 : }
      72             : 
      73             : static inline void
      74             : fd_h2_stream_private_deactivate( fd_h2_stream_t * stream,
      75           6 :                                  fd_h2_conn_t *   conn ) {
      76           6 :   int is_tx_stream = fd_h2_stream_is_tx( stream, conn );
      77           6 :   FD_CHECK_CRIT( conn->stream_active_cnt[ is_tx_stream ]>0U, "corrupt state" );
      78           6 :   conn->stream_active_cnt[ is_tx_stream ]--;
      79           6 : }
      80             : 
      81             : static inline void
      82             : fd_h2_stream_error( fd_h2_stream_t * stream,
      83             :                     fd_h2_conn_t *   conn,
      84             :                     fd_h2_rbuf_t *   rbuf_tx,
      85           6 :                     uint             h2_err ) {
      86           6 :   fd_h2_tx_rst_stream( rbuf_tx, stream->stream_id, h2_err );
      87           6 :   switch( stream->state ) {
      88           3 :   case FD_H2_STREAM_STATE_OPEN:
      89           3 :   case FD_H2_STREAM_STATE_CLOSING_TX:
      90           6 :   case FD_H2_STREAM_STATE_CLOSING_RX:
      91           6 :     stream->state = FD_H2_STREAM_STATE_CLOSED;
      92           6 :     fd_h2_stream_private_deactivate( stream, conn );
      93           6 :     break;
      94           0 :   case FD_H2_STREAM_STATE_CLOSED:
      95           0 :     break;
      96           0 :   default:
      97           0 :     stream->state = FD_H2_STREAM_STATE_ILLEGAL;
      98           0 :     break;
      99           6 :   }
     100           6 : }
     101             : 
     102             : /* fd_h2_rst_stream generates a RST_STREAM frame on the given stream.
     103             :    On return, the stream is in CLOSED state, and the underlying stream
     104             :    object and map entry can be discarded.  conn->active_stream_cnt is
     105             :    decremented accordingly. */
     106             : 
     107             : void
     108             : fd_h2_rst_stream( fd_h2_conn_t *   conn,
     109             :                   fd_h2_rbuf_t *   rbuf_tx,
     110             :                   fd_h2_stream_t * stream );
     111             : 
     112             : static inline void
     113             : fd_h2_stream_close_rx( fd_h2_stream_t * stream,
     114           3 :                        fd_h2_conn_t *   conn ) {
     115           3 :   switch( stream->state ) {
     116           3 :   case FD_H2_STREAM_STATE_OPEN:
     117           3 :     stream->state = FD_H2_STREAM_STATE_CLOSING_RX;
     118           3 :     break;
     119           0 :   case FD_H2_STREAM_STATE_CLOSING_TX:
     120           0 :     stream->state = FD_H2_STREAM_STATE_CLOSED;
     121           0 :     fd_h2_stream_private_deactivate( stream, conn );
     122           0 :     break;
     123           0 :   default:
     124           0 :     stream->state = FD_H2_STREAM_STATE_ILLEGAL;
     125           0 :     break;
     126           3 :   }
     127           3 : }
     128             : 
     129             : static inline void
     130             : fd_h2_stream_close_tx( fd_h2_stream_t * stream,
     131           9 :                        fd_h2_conn_t *   conn ) {
     132           9 :   switch( stream->state ) {
     133           9 :   case FD_H2_STREAM_STATE_OPEN:
     134           9 :     stream->state = FD_H2_STREAM_STATE_CLOSING_TX;
     135           9 :     break;
     136           0 :   case FD_H2_STREAM_STATE_CLOSING_RX:
     137           0 :     stream->state = FD_H2_STREAM_STATE_CLOSED;
     138           0 :     fd_h2_stream_private_deactivate( stream, conn );
     139           0 :     break;
     140           0 :   default:
     141           0 :     stream->state = FD_H2_STREAM_STATE_ILLEGAL;
     142           0 :     break;
     143           9 :   }
     144           9 : }
     145             : 
     146             : static inline void
     147             : fd_h2_stream_reset( fd_h2_stream_t * stream,
     148           0 :                     fd_h2_conn_t *   conn ) {
     149           0 :   switch( stream->state ) {
     150           0 :   case FD_H2_STREAM_STATE_OPEN:
     151           0 :   case FD_H2_STREAM_STATE_CLOSING_TX:
     152           0 :   case FD_H2_STREAM_STATE_CLOSING_RX:
     153           0 :     stream->state = FD_H2_STREAM_STATE_CLOSED;
     154           0 :     fd_h2_stream_private_deactivate( stream, conn );
     155           0 :     break;
     156           0 :   default:
     157           0 :     stream->state = FD_H2_STREAM_STATE_ILLEGAL;
     158           0 :     break;
     159           0 :   }
     160           0 : }
     161             : 
     162             : static inline void
     163             : fd_h2_stream_rx_headers( fd_h2_stream_t * stream,
     164             :                          fd_h2_conn_t *   conn,
     165           0 :                          ulong            flags ) {
     166           0 :   if( stream->state == FD_H2_STREAM_STATE_IDLE ) {
     167             :     /* FIXME This is probably redundant */
     168           0 :     stream->state = FD_H2_STREAM_STATE_OPEN;
     169           0 :   }
     170           0 :   if( flags & FD_H2_FLAG_END_STREAM ) {
     171           0 :     fd_h2_stream_close_rx( stream, conn );
     172           0 :   }
     173           0 :   if( flags & FD_H2_FLAG_END_HEADERS ) {
     174           0 :     stream->hdrs_seq = (uchar)( stream->hdrs_seq + 1 );
     175           0 :   }
     176           0 : }
     177             : 
     178             : static inline void
     179             : fd_h2_stream_rx_data( fd_h2_stream_t * stream,
     180             :                       fd_h2_conn_t *   conn,
     181           9 :                       ulong            flags ) {
     182           9 :   if( flags & FD_H2_FLAG_END_STREAM ) {
     183           3 :     fd_h2_stream_close_rx( stream, conn );
     184           3 :   }
     185           9 : }
     186             : 
     187             : FD_PROTOTYPES_END
     188             : 
     189             : #endif /* HEADER_fd_src_waltz_h2_fd_h2_stream_h */

Generated by: LCOV version 1.14