Module H2_stream

HTTP/2 Stream State Machine per RFC 9113 Section 5.1.

This module implements the HTTP/2 stream lifecycle as defined in RFC 9113 Section 5.1.

Stream States

                                +--------+
                        send PP |        | recv PP
                       ,--------+  idle  +--------.
                      /         |        |         \
                     v          +--------+          v
              +----------+          |           +----------+
              |          |          | send H /  |          |
       ,------+ reserved |          | recv H    | reserved +------.
       |      | (local)  |          |           | (remote) |      |
       |      +---+------+          v           +------+---+      |
       |          |             +--------+             |          |
       |          |     recv ES |        | send ES     |          |
       |   send H |     ,-------+  open  +-------.     | recv H   |
       |          |    /        |        |        \    |          |
       |          v   v         +---+----+         v   v          |
       |      +----------+          |           +----------+      |
       |      |   half-  |          |           |   half-  |      |
       |      |  closed  |          | send R /  |  closed  |      |
       |      | (remote) |          | recv R    | (local)  |      |
       |      +----+-----+          |           +-----+----+      |
       |           |                |                 |           |
       |           | send ES /      |       recv ES / |           |
       |           |  send R /      v        send R / |           |
       |           |  recv R    +--------+   recv R   |           |
       | send R /  `----------->|        |<-----------'  send R / |
       | recv R                 | closed |               recv R   |
       `----------------------->|        |<-----------------------'
                                +--------+

Usage

  (* Create a new stream *)
  let stream = H2_stream.create 1l in

  (* Apply events to transition state *)
  match H2_stream.apply_event stream (Send_headers { end_stream = false }) with
  | Ok () -> (* stream is now Open *)
  | Error (code, msg) -> (* protocol error *)

Types

type closed_reason =
  1. | Finished
    (*

    Both endpoints sent END_STREAM.

    *)
  2. | ResetByUs of H2_frame.error_code
    (*

    We sent RST_STREAM.

    *)
  3. | ResetByThem of H2_frame.error_code
    (*

    Peer sent RST_STREAM.

    *)

Why a stream was closed.

type state =
  1. | Idle
    (*

    Initial state.

    *)
  2. | Reserved_local
    (*

    We sent PUSH_PROMISE.

    *)
  3. | Reserved_remote
    (*

    Peer sent PUSH_PROMISE.

    *)
  4. | Open
    (*

    Both sides can send frames.

    *)
  5. | Half_closed_local
    (*

    We sent END_STREAM.

    *)
  6. | Half_closed_remote
    (*

    Peer sent END_STREAM.

    *)
  7. | Closed of closed_reason
    (*

    Terminal state.

    *)

Stream state per RFC 9113 Section 5.1.

type event =
  1. | Send_headers of {
    1. end_stream : bool;
    }
  2. | Recv_headers of {
    1. end_stream : bool;
    }
  3. | Send_data of {
    1. end_stream : bool;
    }
  4. | Recv_data of {
    1. end_stream : bool;
    }
  5. | Send_push_promise
  6. | Recv_push_promise
  7. | Send_rst_stream of H2_frame.error_code
  8. | Recv_rst_stream of H2_frame.error_code
  9. | Send_end_stream
  10. | Recv_end_stream

Events that cause state transitions.

type transition_result =
  1. | Transition_ok of state
  2. | Transition_error of H2_frame.error_code * string

Result of applying an event to a state.

type t

A single HTTP/2 stream.

Stream Creation

val default_initial_window_size : int

Default initial flow control window size: 65535 bytes.

val create : ?initial_send_window:int -> ?initial_recv_window:int -> int32 -> t

create ?initial_send_window ?initial_recv_window id creates a new stream. Stream starts in Idle state.

Stream Properties

val id : t -> int32

id t returns the stream identifier.

val state : t -> state

state t returns the current stream state.

val is_idle : t -> bool

is_idle t returns true if stream is in Idle state.

val is_active : t -> bool

is_active t returns true if stream is Open or Half-closed.

val is_open : t -> bool

is_open t returns true if stream is fully Open.

val is_closed : t -> bool

is_closed t returns true if stream is Closed.

val can_send : t -> bool

can_send t returns true if we can send frames on this stream.

val can_recv : t -> bool

can_recv t returns true if we can receive frames on this stream.

State Transitions

val transition : state -> event -> transition_result

transition state event computes the new state after applying event. Returns Ok new_state or Error (code, msg) if transition is invalid.

val apply_event : t -> event -> (unit, H2_frame.error_code * string) result

apply_event t event applies event to stream t, updating its state. Returns Ok () on success or Error (code, msg) on protocol error.

val reset : t -> H2_frame.error_code -> unit

reset t code resets the stream with the given error code.

Flow Control

val send_window : t -> int

send_window t returns bytes available in send window.

val recv_window : t -> int

recv_window t returns bytes available in receive window.

val initial_recv_window : t -> int

initial_recv_window t returns the initial receive window size.

val consume_send_window : t -> int -> int

consume_send_window t bytes consumes up to bytes from send window. Returns number of bytes actually consumed.

val credit_send_window : t -> int -> (unit, H2_frame.error_code * string) result

credit_send_window t increment adds increment to send window. Returns error on overflow.

val consume_recv_window : t -> int -> unit

consume_recv_window t bytes consumes bytes from receive window.

val credit_recv_window : t -> int -> unit

credit_recv_window t increment adds increment to receive window.

val update_initial_window_size : t -> int -> (unit, H2_frame.error_code * string) result

update_initial_window_size t new_size updates the initial window size from a SETTINGS frame. Adjusts current window by the delta.

Stream Identifiers

val connection_stream_id : int32

Stream ID 0 is used for connection-level frames.

val is_client_initiated : int32 -> bool

is_client_initiated id returns true if id is odd (client-initiated).

val is_server_initiated : int32 -> bool

is_server_initiated id returns true if id is even and non-zero.

val is_valid_id : int32 -> bool

is_valid_id id returns true if id is greater than 0.

val is_connection_stream : int32 -> bool

is_connection_stream id returns true if id is 0.

Headers

val request_headers : t -> H2_hpack.header list option

request_headers t returns the request headers if set.

val set_request_headers : t -> H2_hpack.header list -> unit

set_request_headers t headers sets the request headers.

val response_headers : t -> H2_hpack.header list option

response_headers t returns the response headers if set.

val set_response_headers : t -> H2_hpack.header list -> unit

set_response_headers t headers sets the response headers.

val trailers : t -> H2_hpack.header list option

trailers t returns the trailers if set.

val set_trailers : t -> H2_hpack.header list -> unit

set_trailers t headers sets the trailers.

Pretty Printing

val pp_state : Format.formatter -> state -> unit

Pretty print stream state.

val pp_closed_reason : Format.formatter -> closed_reason -> unit

Pretty print closed reason.

val state_to_string : state -> string

Convert state to string.

val pp : Format.formatter -> t -> unit

Pretty print a stream.