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