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 */