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 36 : #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 3 : #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 36 : fd_h2_stream_init( fd_h2_stream_t * stream ) { 35 36 : *stream = (fd_h2_stream_t){0}; 36 36 : return stream; 37 36 : } 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 const * conn, 47 36 : uint stream_id ) { 48 36 : *stream = (fd_h2_stream_t) { 49 36 : .stream_id = stream_id, 50 36 : .state = FD_H2_STREAM_STATE_OPEN, 51 36 : .tx_wnd = conn->peer_settings.initial_window_size, 52 36 : .rx_wnd = conn->self_settings.initial_window_size, 53 36 : .hdrs_seq = 0U 54 36 : }; 55 36 : return stream; 56 36 : } 57 : 58 : static inline void 59 : fd_h2_stream_error( fd_h2_stream_t * stream, 60 : fd_h2_rbuf_t * rbuf_tx, 61 3 : uint h2_err ) { 62 3 : fd_h2_tx_rst_stream( rbuf_tx, stream->stream_id, h2_err ); 63 3 : stream->state = FD_H2_STREAM_STATE_CLOSED; 64 3 : } 65 : 66 : /* fd_h2_rst_stream generates a RST_STREAM frame on the given stream. 67 : On return, the stream is in CLOSED state, and the underlying stream 68 : object and map entry can be discarded. conn->active_stream_cnt is 69 : decremented accordingly. */ 70 : 71 : void 72 : fd_h2_rst_stream( fd_h2_conn_t * conn, 73 : fd_h2_rbuf_t * rbuf_tx, 74 : fd_h2_stream_t * stream ); 75 : 76 : static inline void 77 : fd_h2_stream_private_deactivate( fd_h2_stream_t * stream, 78 0 : fd_h2_conn_t * conn ) { 79 0 : conn->stream_active_cnt[ (stream->stream_id&1) ^ (conn->rx_stream_id&1) ]--; 80 0 : } 81 : 82 : static inline void 83 : fd_h2_stream_close_rx( fd_h2_stream_t * stream, 84 0 : fd_h2_conn_t * conn ) { 85 0 : switch( stream->state ) { 86 0 : case FD_H2_STREAM_STATE_OPEN: 87 0 : stream->state = FD_H2_STREAM_STATE_CLOSING_RX; 88 0 : break; 89 0 : case FD_H2_STREAM_STATE_CLOSING_TX: 90 0 : stream->state = FD_H2_STREAM_STATE_CLOSED; 91 0 : fd_h2_stream_private_deactivate( stream, conn ); 92 0 : break; 93 0 : default: 94 0 : stream->state = FD_H2_STREAM_STATE_ILLEGAL; 95 0 : break; 96 0 : } 97 0 : } 98 : 99 : static inline void 100 : fd_h2_stream_close_tx( fd_h2_stream_t * stream, 101 0 : fd_h2_conn_t * conn ) { 102 0 : switch( stream->state ) { 103 0 : case FD_H2_STREAM_STATE_OPEN: 104 0 : stream->state = FD_H2_STREAM_STATE_CLOSING_TX; 105 0 : break; 106 0 : case FD_H2_STREAM_STATE_CLOSING_RX: 107 0 : stream->state = FD_H2_STREAM_STATE_CLOSED; 108 0 : fd_h2_stream_private_deactivate( stream, conn ); 109 0 : break; 110 0 : default: 111 0 : stream->state = FD_H2_STREAM_STATE_ILLEGAL; 112 0 : break; 113 0 : } 114 0 : } 115 : 116 : static inline void 117 : fd_h2_stream_reset( fd_h2_stream_t * stream, 118 0 : fd_h2_conn_t * conn ) { 119 0 : switch( stream->state ) { 120 0 : case FD_H2_STREAM_STATE_OPEN: 121 0 : case FD_H2_STREAM_STATE_CLOSING_TX: 122 0 : case FD_H2_STREAM_STATE_CLOSING_RX: 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_rx_headers( fd_h2_stream_t * stream, 134 : fd_h2_conn_t * conn, 135 0 : ulong flags ) { 136 0 : if( stream->state == FD_H2_STREAM_STATE_IDLE ) { 137 : /* FIXME This is probably redundant */ 138 0 : stream->state = FD_H2_STREAM_STATE_OPEN; 139 0 : } 140 0 : if( flags & FD_H2_FLAG_END_STREAM ) { 141 0 : fd_h2_stream_close_rx( stream, conn ); 142 0 : } 143 0 : if( flags & FD_H2_FLAG_END_HEADERS ) { 144 0 : stream->hdrs_seq = (uchar)( stream->hdrs_seq + 1 ); 145 0 : } 146 0 : } 147 : 148 : static inline void 149 : fd_h2_stream_rx_data( fd_h2_stream_t * stream, 150 : fd_h2_conn_t * conn, 151 0 : ulong flags ) { 152 0 : if( flags & FD_H2_FLAG_END_STREAM ) { 153 0 : fd_h2_stream_close_rx( stream, conn ); 154 0 : } 155 0 : } 156 : 157 : FD_PROTOTYPES_END 158 : 159 : #endif /* HEADER_fd_src_waltz_h2_fd_h2_stream_h */