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