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 138 : #define FD_H2_STREAM_STATE_OPEN 1 23 9 : #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 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 129 : fd_h2_stream_init( fd_h2_stream_t * stream ) { 35 129 : *stream = (fd_h2_stream_t){0}; 36 129 : return stream; 37 129 : } 38 : 39 : /* fd_h2_stream_open transitions a stream from 'IDLE' to 'OPEN'. 40 : In fd_h2, this happens when the local or peer side sends a HEADERS 41 : frame. The TX side of the stream assumes the peer's default send 42 : window. */ 43 : 44 : static inline fd_h2_stream_t * 45 : fd_h2_stream_open( fd_h2_stream_t * stream, 46 : fd_h2_conn_t * conn, 47 129 : uint stream_id ) { 48 129 : *stream = (fd_h2_stream_t) { 49 129 : .stream_id = stream_id, 50 129 : .state = FD_H2_STREAM_STATE_OPEN, 51 129 : .tx_wnd = conn->peer_settings.initial_window_size, 52 129 : .rx_wnd = conn->self_settings.initial_window_size, 53 129 : .hdrs_seq = 0U 54 129 : }; 55 129 : conn->stream_active_cnt[ (stream_id&1) ^ (conn->rx_stream_id&1) ]++; 56 129 : return stream; 57 129 : } 58 : 59 : static inline void 60 : fd_h2_stream_error( fd_h2_stream_t * stream, 61 : fd_h2_rbuf_t * rbuf_tx, 62 6 : uint h2_err ) { 63 6 : fd_h2_tx_rst_stream( rbuf_tx, stream->stream_id, h2_err ); 64 6 : stream->state = FD_H2_STREAM_STATE_CLOSED; 65 6 : } 66 : 67 : /* fd_h2_rst_stream generates a RST_STREAM frame on the given stream. 68 : On return, the stream is in CLOSED state, and the underlying stream 69 : object and map entry can be discarded. conn->active_stream_cnt is 70 : decremented accordingly. */ 71 : 72 : void 73 : fd_h2_rst_stream( fd_h2_conn_t * conn, 74 : fd_h2_rbuf_t * rbuf_tx, 75 : fd_h2_stream_t * stream ); 76 : 77 : static inline void 78 : fd_h2_stream_private_deactivate( fd_h2_stream_t * stream, 79 0 : fd_h2_conn_t * conn ) { 80 0 : conn->stream_active_cnt[ (stream->stream_id&1) ^ (conn->rx_stream_id&1) ]--; 81 0 : } 82 : 83 : static inline void 84 : fd_h2_stream_close_rx( fd_h2_stream_t * stream, 85 0 : fd_h2_conn_t * conn ) { 86 0 : switch( stream->state ) { 87 0 : case FD_H2_STREAM_STATE_OPEN: 88 0 : stream->state = FD_H2_STREAM_STATE_CLOSING_RX; 89 0 : break; 90 0 : case FD_H2_STREAM_STATE_CLOSING_TX: 91 0 : stream->state = FD_H2_STREAM_STATE_CLOSED; 92 0 : fd_h2_stream_private_deactivate( stream, conn ); 93 0 : break; 94 0 : default: 95 0 : stream->state = FD_H2_STREAM_STATE_ILLEGAL; 96 0 : break; 97 0 : } 98 0 : } 99 : 100 : static inline void 101 : fd_h2_stream_close_tx( fd_h2_stream_t * stream, 102 9 : fd_h2_conn_t * conn ) { 103 9 : switch( stream->state ) { 104 9 : case FD_H2_STREAM_STATE_OPEN: 105 9 : stream->state = FD_H2_STREAM_STATE_CLOSING_TX; 106 9 : break; 107 0 : case FD_H2_STREAM_STATE_CLOSING_RX: 108 0 : stream->state = FD_H2_STREAM_STATE_CLOSED; 109 0 : fd_h2_stream_private_deactivate( stream, conn ); 110 0 : break; 111 0 : default: 112 0 : stream->state = FD_H2_STREAM_STATE_ILLEGAL; 113 0 : break; 114 9 : } 115 9 : } 116 : 117 : static inline void 118 : fd_h2_stream_reset( fd_h2_stream_t * stream, 119 0 : fd_h2_conn_t * conn ) { 120 0 : switch( stream->state ) { 121 0 : case FD_H2_STREAM_STATE_OPEN: 122 0 : case FD_H2_STREAM_STATE_CLOSING_TX: 123 0 : case FD_H2_STREAM_STATE_CLOSING_RX: 124 0 : stream->state = FD_H2_STREAM_STATE_CLOSED; 125 0 : fd_h2_stream_private_deactivate( stream, conn ); 126 0 : break; 127 0 : default: 128 0 : stream->state = FD_H2_STREAM_STATE_ILLEGAL; 129 0 : break; 130 0 : } 131 0 : } 132 : 133 : static inline void 134 : fd_h2_stream_rx_headers( fd_h2_stream_t * stream, 135 : fd_h2_conn_t * conn, 136 0 : ulong flags ) { 137 0 : if( stream->state == FD_H2_STREAM_STATE_IDLE ) { 138 : /* FIXME This is probably redundant */ 139 0 : stream->state = FD_H2_STREAM_STATE_OPEN; 140 0 : } 141 0 : if( flags & FD_H2_FLAG_END_STREAM ) { 142 0 : fd_h2_stream_close_rx( stream, conn ); 143 0 : } 144 0 : if( flags & FD_H2_FLAG_END_HEADERS ) { 145 0 : stream->hdrs_seq = (uchar)( stream->hdrs_seq + 1 ); 146 0 : } 147 0 : } 148 : 149 : static inline void 150 : fd_h2_stream_rx_data( fd_h2_stream_t * stream, 151 : fd_h2_conn_t * conn, 152 0 : ulong flags ) { 153 0 : if( flags & FD_H2_FLAG_END_STREAM ) { 154 0 : fd_h2_stream_close_rx( stream, conn ); 155 0 : } 156 0 : } 157 : 158 : FD_PROTOTYPES_END 159 : 160 : #endif /* HEADER_fd_src_waltz_h2_fd_h2_stream_h */