HTTP/2 framing, HPACK and connection state, I/O-free
0

Configure Feed

Select the types of activity you want to include in your feed.

ocaml-h2 / lib / stream.mli
11 kB 279 lines
1(*--------------------------------------------------------------------------- 2 Copyright (c) 2019 Antonio Nuno Monteiro. Copyright (c) 2025 Anil Madhavapeddy 3 <anil@recoil.org>. 4 5 All rights reserved. 6 7 Redistribution and use in source and binary forms, with or without 8 modification, are permitted provided that the following conditions are met: 9 10 1. Redistributions of source code must retain the above copyright notice, this 11 list of conditions and the following disclaimer. 12 13 2. Redistributions in binary form must reproduce the above copyright notice, 14 this list of conditions and the following disclaimer in the documentation 15 and/or other materials provided with the distribution. 16 17 3. Neither the name of the copyright holder nor the names of its contributors 18 may be used to endorse or promote products derived from this software without 19 specific prior written permission. 20 21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 SPDX-License-Identifier: BSD-3-Clause 32 ---------------------------------------------------------------------------*) 33 34(** HTTP/2 Stream State Machine per RFC 9113 Section 5.1. 35 36 This module implements the HTTP/2 stream lifecycle as defined in 37 {{:https://datatracker.ietf.org/doc/html/rfc9113#section-5.1}RFC 9113 38 Section 5.1}. 39 40 {2 Stream States} 41 42 {v 43 +--------+ 44 send PP | | recv PP 45 ,--------+ idle +--------. 46 / | | \ 47 v +--------+ v 48 +----------+ | +----------+ 49 | | | send H / | | 50 ,------+ reserved | | recv H | reserved +------. 51 | | (local) | | | (remote) | | 52 | +---+------+ v +------+---+ | 53 | | +--------+ | | 54 | | recv ES | | send ES | | 55 | send H | ,-------+ open +-------. | recv H | 56 | | / | | \ | | 57 | v v +---+----+ v v | 58 | +----------+ | +----------+ | 59 | | half- | | | half- | | 60 | | closed | | send R / | closed | | 61 | | (remote) | | recv R | (local) | | 62 | +----+-----+ | +-----+----+ | 63 | | | | | 64 | | send ES / | recv ES / | | 65 | | send R / v send R / | | 66 | | recv R +--------+ recv R | | 67 | send R / `----------->| |<-----------' send R / | 68 | recv R | closed | recv R | 69 `----------------------->| |<-----------------------' 70 +--------+ 71 v} 72 73 {2 Usage} 74 75 {[ 76 open H2 77 78 let demo () = 79 let stream = Stream.v 1l in 80 match 81 Stream.apply_event stream (Stream.Send_headers { end_stream = false }) 82 with 83 | Ok _stream -> () 84 | Error (_code, _msg) -> () 85 ]} *) 86 87(** {1 Types} *) 88 89(** Why a stream was closed. *) 90type closed_reason = 91 | Finished (** Both endpoints sent END_STREAM. *) 92 | Reset_by_us of Frame.error_code (** We sent RST_STREAM. *) 93 | Reset_by_them of Frame.error_code (** Peer sent RST_STREAM. *) 94 95(** Stream state per RFC 9113 Section 5.1. *) 96type state = 97 | Idle (** Initial state. *) 98 | Reserved_local (** We sent PUSH_PROMISE. *) 99 | Reserved_remote (** Peer sent PUSH_PROMISE. *) 100 | Open (** Both sides can send frames. *) 101 | Half_closed_local (** We sent END_STREAM. *) 102 | Half_closed_remote (** Peer sent END_STREAM. *) 103 | Closed of closed_reason (** Terminal state. *) 104 105(** Events that cause state transitions. *) 106type event = 107 | Send_headers of { end_stream : bool } 108 | Recv_headers of { end_stream : bool } 109 | Send_data of { end_stream : bool } 110 | Recv_data of { end_stream : bool } 111 | Send_push_promise 112 | Recv_push_promise 113 | Send_rst_stream of Frame.error_code 114 | Recv_rst_stream of Frame.error_code 115 | Send_end_stream 116 | Recv_end_stream 117 118type t 119(** A single HTTP/2 stream. *) 120 121(** {1 Stream Creation} *) 122 123val default_initial_window_size : int 124(** Default initial flow control window size: 65535 bytes. *) 125 126val v : ?initial_send_window:int -> ?initial_recv_window:int -> int32 -> t 127(** [v ?initial_send_window ?initial_recv_window id] creates a new stream. 128 Stream starts in {!constructor-Idle} state. *) 129 130(** {1 Stream Properties} *) 131 132val id : t -> int32 133(** [id t] returns the stream identifier. *) 134 135val state : t -> state 136(** [state t] returns the current stream state. *) 137 138val is_idle : t -> bool 139(** [is_idle t] returns true if stream is in Idle state. *) 140 141val is_active : t -> bool 142(** [is_active t] returns true if stream is Open or Half-closed. *) 143 144val is_open : t -> bool 145(** [is_open t] returns true if stream is fully Open. *) 146 147val is_closed : t -> bool 148(** [is_closed t] returns true if stream is Closed. *) 149 150val can_send : t -> bool 151(** [can_send t] returns true if we can send frames on this stream. *) 152 153val can_recv : t -> bool 154(** [can_recv t] returns true if we can receive frames on this stream. *) 155 156(** {1 State Transitions} *) 157 158val transition : state -> event -> (state, Frame.error_code * string) result 159(** [transition state event] computes the new state after applying [event]. 160 Returns [Ok new_state] or [Error (code, msg)] if transition is invalid. *) 161 162val apply_event : t -> event -> (t, Frame.error_code * string) result 163(** [apply_event t event] applies [event] to stream [t], returning the advanced 164 stream, or [Error (code, msg)] on protocol error. *) 165 166val reset : t -> Frame.error_code -> t 167(** [reset t code] is [t] reset with the given error code. *) 168 169(** {1 Flow Control} *) 170 171val send_window : t -> int 172(** [send_window t] returns bytes available in send window. *) 173 174val recv_window : t -> int 175(** [recv_window t] returns bytes available in receive window. *) 176 177val initial_recv_window : t -> int 178(** [initial_recv_window t] returns the initial receive window size. *) 179 180val consume_send_window : t -> int -> int * t 181(** [consume_send_window t bytes] consumes up to [bytes] from the send window, 182 returning the number actually consumed and the advanced stream. *) 183 184val max_flow_window : int64 185(** The RFC 9113 s6.9.1 flow-control window bound, 2^31-1, as an int64 so the 186 comparison is exact on every int width. *) 187 188val credit_send_window : t -> int -> (t, Frame.error_code * string) result 189(** [credit_send_window t increment] adds [increment] to the send window, 190 returning the advanced stream or an error on overflow. *) 191 192val consume_recv_window : t -> int -> t 193(** [consume_recv_window t bytes] consumes [bytes] from the receive window. *) 194 195val credit_recv_window : t -> int -> t 196(** [credit_recv_window t increment] adds [increment] to the receive window. *) 197 198val update_initial_window_size : 199 t -> int -> (t, Frame.error_code * string) result 200(** [update_initial_window_size t new_size] updates the initial window size from 201 a SETTINGS frame, adjusting the current window by the delta. *) 202 203(** {1 Send Queue} 204 205 Outbound DATA the application has handed over but flow control has not yet 206 permitted on the wire. The endpoint drains it as the send window opens. *) 207 208val enqueue_send : t -> ?end_stream:bool -> Rope.t -> t 209(** [enqueue_send t ?end_stream payload] appends [payload] to the send queue. 210 [end_stream] records that the queued data is the last to send. *) 211 212val pending_send : t -> int 213(** [pending_send t] is the number of queued bytes not yet emitted. *) 214 215val send_fin : t -> bool 216(** [send_fin t] is whether END_STREAM has been requested for the queued data. 217*) 218 219val fin_sent : t -> bool 220(** [fin_sent t] is whether the END_STREAM DATA frame has been emitted. *) 221 222val mark_fin_sent : t -> t 223(** [mark_fin_sent t] records that the END_STREAM DATA frame has been emitted. 224*) 225 226val take_send : t -> int -> Rope.t * t 227(** [take_send t n] removes the first [n] queued bytes ([n] is clamped to 228 {!pending_send}), returning them and the advanced stream. *) 229 230(** {1 Stream Identifiers} *) 231 232val connection_stream_id : int32 233(** Stream ID 0 is used for connection-level frames. *) 234 235val is_client_initiated : int32 -> bool 236(** [is_client_initiated id] returns true if [id] is odd (client-initiated). *) 237 238val is_server_initiated : int32 -> bool 239(** [is_server_initiated id] returns true if [id] is even and non-zero. *) 240 241val is_valid_id : int32 -> bool 242(** [is_valid_id id] returns true if [id] is greater than 0. *) 243 244val is_connection_stream : int32 -> bool 245(** [is_connection_stream id] returns true if [id] is 0. *) 246 247(** {1 Headers} *) 248 249val request_headers : t -> Hpack.header list option 250(** [request_headers t] returns the request headers if set. *) 251 252val set_request_headers : t -> Hpack.header list -> t 253(** [set_request_headers t headers] sets the request headers. *) 254 255val response_headers : t -> Hpack.header list option 256(** [response_headers t] returns the response headers if set. *) 257 258val set_response_headers : t -> Hpack.header list -> t 259(** [set_response_headers t headers] sets the response headers. *) 260 261val trailers : t -> Hpack.header list option 262(** [trailers t] returns the trailers if set. *) 263 264val set_trailers : t -> Hpack.header list -> t 265(** [set_trailers t headers] sets the trailers. *) 266 267(** {1 Pretty Printing} *) 268 269val pp_state : Format.formatter -> state -> unit 270(** Pretty print stream state. *) 271 272val pp_closed_reason : Format.formatter -> closed_reason -> unit 273(** Pretty print closed reason. *) 274 275val string_of_state : state -> string 276(** Convert state to string. *) 277 278val pp : Format.formatter -> t -> unit 279(** Pretty print a stream. *)