LCOV - code coverage report
Current view: top level - waltz/h2 - fd_h2_stream.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 106 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 180 0.0 %

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

Generated by: LCOV version 1.14