LCOV - code coverage report
Current view: top level - waltz/h2 - fd_h2_stream.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 63 91 69.2 %
Date: 2026-04-01 06:30:45 Functions: 16 130 12.3 %

          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           6 : #define FD_H2_STREAM_STATE_IDLE       0
      22         156 : #define FD_H2_STREAM_STATE_OPEN       1
      23           9 : #define FD_H2_STREAM_STATE_CLOSING_TX 2 /* half-closed (local) */
      24          12 : #define FD_H2_STREAM_STATE_CLOSING_RX 3 /* half-closed (remote) */
      25          18 : #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         141 : fd_h2_stream_init( fd_h2_stream_t * stream ) {
      35         141 :   *stream = (fd_h2_stream_t){0};
      36         141 :   return stream;
      37         141 : }
      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         147 :                     fd_h2_conn_t *   conn ) {
      45             :   /* Streams initiated by a client MUST use odd-numbered stream identifiers; */
      46         147 :   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         147 :   uint is_client_self   = conn->tx_stream_next & 1;
      50         147 :   return is_client_stream == is_client_self;
      51         147 : }
      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         141 :                    uint             stream_id ) {
      62         141 :   *stream = (fd_h2_stream_t) {
      63         141 :     .stream_id = stream_id,
      64         141 :     .state     = FD_H2_STREAM_STATE_OPEN,
      65         141 :     .tx_wnd    = conn->peer_settings.initial_window_size,
      66         141 :     .rx_wnd    = conn->self_settings.initial_window_size,
      67         141 :     .hdrs_seq  = 0U
      68         141 :   };
      69         141 :   conn->stream_active_cnt[ fd_h2_stream_is_tx( stream, conn ) ]++;
      70         141 :   return stream;
      71         141 : }
      72             : 
      73             : static inline void
      74             : fd_h2_stream_error( fd_h2_stream_t * stream,
      75             :                     fd_h2_rbuf_t *   rbuf_tx,
      76           6 :                     uint             h2_err ) {
      77           6 :   fd_h2_tx_rst_stream( rbuf_tx, stream->stream_id, h2_err );
      78           6 :   stream->state = FD_H2_STREAM_STATE_CLOSED;
      79           6 : }
      80             : 
      81             : /* fd_h2_rst_stream generates a RST_STREAM frame on the given stream.
      82             :    On return, the stream is in CLOSED state, and the underlying stream
      83             :    object and map entry can be discarded.  conn->active_stream_cnt is
      84             :    decremented accordingly. */
      85             : 
      86             : void
      87             : fd_h2_rst_stream( fd_h2_conn_t *   conn,
      88             :                   fd_h2_rbuf_t *   rbuf_tx,
      89             :                   fd_h2_stream_t * stream );
      90             : 
      91             : static inline void
      92             : fd_h2_stream_private_deactivate( fd_h2_stream_t * stream,
      93           6 :                                  fd_h2_conn_t *   conn ) {
      94           6 :   int is_tx_stream = fd_h2_stream_is_tx( stream, conn );
      95             : #ifdef FD_H2_USE_HANDHOLDING
      96             :   FD_TEST( conn->stream_active_cnt[ is_tx_stream ]>0U );
      97             : #endif
      98           6 :   conn->stream_active_cnt[ is_tx_stream ]--;
      99           6 : }
     100             : 
     101             : static inline void
     102             : fd_h2_stream_close_rx( fd_h2_stream_t * stream,
     103           6 :                        fd_h2_conn_t *   conn ) {
     104           6 :   switch( stream->state ) {
     105           6 :   case FD_H2_STREAM_STATE_OPEN:
     106           6 :     stream->state = FD_H2_STREAM_STATE_CLOSING_RX;
     107           6 :     break;
     108           0 :   case FD_H2_STREAM_STATE_CLOSING_TX:
     109           0 :     stream->state = FD_H2_STREAM_STATE_CLOSED;
     110           0 :     fd_h2_stream_private_deactivate( stream, conn );
     111           0 :     break;
     112           0 :   default:
     113           0 :     stream->state = FD_H2_STREAM_STATE_ILLEGAL;
     114           0 :     break;
     115           6 :   }
     116           6 : }
     117             : 
     118             : static inline void
     119             : fd_h2_stream_close_tx( fd_h2_stream_t * stream,
     120          15 :                        fd_h2_conn_t *   conn ) {
     121          15 :   switch( stream->state ) {
     122           9 :   case FD_H2_STREAM_STATE_OPEN:
     123           9 :     stream->state = FD_H2_STREAM_STATE_CLOSING_TX;
     124           9 :     break;
     125           6 :   case FD_H2_STREAM_STATE_CLOSING_RX:
     126           6 :     stream->state = FD_H2_STREAM_STATE_CLOSED;
     127           6 :     fd_h2_stream_private_deactivate( stream, conn );
     128           6 :     break;
     129           0 :   default:
     130           0 :     stream->state = FD_H2_STREAM_STATE_ILLEGAL;
     131           0 :     break;
     132          15 :   }
     133          15 : }
     134             : 
     135             : static inline void
     136             : fd_h2_stream_reset( fd_h2_stream_t * stream,
     137           0 :                     fd_h2_conn_t *   conn ) {
     138           0 :   switch( stream->state ) {
     139           0 :   case FD_H2_STREAM_STATE_OPEN:
     140           0 :   case FD_H2_STREAM_STATE_CLOSING_TX:
     141           0 :   case FD_H2_STREAM_STATE_CLOSING_RX:
     142           0 :     stream->state = FD_H2_STREAM_STATE_CLOSED;
     143           0 :     fd_h2_stream_private_deactivate( stream, conn );
     144           0 :     break;
     145           0 :   default:
     146           0 :     stream->state = FD_H2_STREAM_STATE_ILLEGAL;
     147           0 :     break;
     148           0 :   }
     149           0 : }
     150             : 
     151             : static inline void
     152             : fd_h2_stream_rx_headers( fd_h2_stream_t * stream,
     153             :                          fd_h2_conn_t *   conn,
     154           6 :                          ulong            flags ) {
     155           6 :   if( stream->state == FD_H2_STREAM_STATE_IDLE ) {
     156             :     /* FIXME This is probably redundant */
     157           0 :     stream->state = FD_H2_STREAM_STATE_OPEN;
     158           0 :   }
     159           6 :   if( flags & FD_H2_FLAG_END_STREAM ) {
     160           0 :     fd_h2_stream_close_rx( stream, conn );
     161           0 :   }
     162           6 :   if( flags & FD_H2_FLAG_END_HEADERS ) {
     163           6 :     stream->hdrs_seq = (uchar)( stream->hdrs_seq + 1 );
     164           6 :   }
     165           6 : }
     166             : 
     167             : static inline void
     168             : fd_h2_stream_rx_data( fd_h2_stream_t * stream,
     169             :                       fd_h2_conn_t *   conn,
     170          18 :                       ulong            flags ) {
     171          18 :   if( flags & FD_H2_FLAG_END_STREAM ) {
     172           6 :     fd_h2_stream_close_rx( stream, conn );
     173           6 :   }
     174          18 : }
     175             : 
     176             : FD_PROTOTYPES_END
     177             : 
     178             : #endif /* HEADER_fd_src_waltz_h2_fd_h2_stream_h */

Generated by: LCOV version 1.14