Module H2_client

HTTP/2 Client with Concurrent Stream Multiplexing.

This module provides a client for making HTTP/2 requests over an established TLS connection. It handles:

Architecture

The client uses a centralized frame reader fiber that dispatches incoming frames to per-stream event queues. This allows multiple concurrent requests to share a single HTTP/2 connection without blocking each other.

Usage

  (* For a single request *)
  Eio.Switch.run @@ fun sw ->
  let response = H2_client.one_request ~sw flow
    ~meth:"GET"
    ~uri:(Uri.of_string "https://example.com/")
    ()
  in
  match response with
  | Ok r -> Printf.printf "Status: %d\n" r.status
  | Error msg -> Printf.printf "Error: %s\n" msg

  (* For multiple concurrent requests on same connection *)
  Eio.Switch.run @@ fun sw ->
  let client = H2_client.create () in
  match H2_client.handshake flow client with
  | Error msg -> failwith msg
  | Ok () ->
      (* Concurrent requests in parallel fibers *)
      let req1 = H2_protocol.make_request ~meth:"GET" ~uri:uri1 () in
      let req2 = H2_protocol.make_request ~meth:"GET" ~uri:uri2 () in
      Eio.Fiber.both
        (fun () -> H2_client.request ~sw flow client req1)
        (fun () -> H2_client.request ~sw flow client req2);
      H2_client.close flow client

See RFC 9113.

Client State

type t

HTTP/2 client state.

val create : ?settings:H2_connection.settings -> unit -> t

create ?settings () creates a new HTTP/2 client. Default settings are used if not specified.

val connection : t -> H2_connection.t

connection t returns the underlying connection state.

val is_open : t -> bool

is_open t returns true if the connection is still usable.

val active_streams : t -> int

active_streams t returns the number of streams currently waiting for responses. Useful for connection pool management.

Connection Lifecycle

val handshake : [> Eio.Flow.two_way_ty ] Eio.Resource.t -> t -> (unit, string) result

handshake flow t performs the HTTP/2 connection handshake.

This must be called before making requests. It: 1. Sends the connection preface and SETTINGS frame 2. Waits for server SETTINGS 3. Sends SETTINGS ACK 4. Waits for server SETTINGS ACK

  • parameter flow

    The underlying connection (must be TLS with ALPN "h2")

  • parameter t

    Client state

  • returns

    Ok () on success, Error msg on failure

val close : [> Eio.Flow.two_way_ty ] Eio.Resource.t -> t -> unit

close flow t gracefully closes the connection. Sends GOAWAY and marks connection as closed.

Background Reader

val start_reader : sw:Eio.Switch.t -> [> Eio.Flow.two_way_ty ] Eio.Resource.t -> t -> on_goaway: (last_stream_id:int32 -> error_code:H2_frame.error_code -> debug:string -> unit) -> unit

start_reader ~sw flow t ~on_goaway starts the background frame reader fiber.

The reader fiber dispatches incoming frames to stream handlers and must be running for concurrent requests to work. The sw parameter should be a connection-lifetime switch.

  • parameter sw

    Switch for the reader fiber (connection-lifetime)

  • parameter flow

    The underlying connection

  • parameter t

    Client state

  • parameter on_goaway

    Callback invoked when GOAWAY is received from peer

Making Requests

request ~sw flow t req sends a request and waits for the response.

This function can be called concurrently from multiple fibers on the same client. The background frame reader (started on first request) dispatches frames to the appropriate stream handlers.

  • parameter sw

    Switch for the background reader fiber

  • parameter flow

    The underlying connection

  • parameter t

    Client state

  • parameter req

    The request to send

  • returns

    Response on success, Error msg on failure

request_sync flow t req sends a request and waits for the response using synchronous I/O without spawning a background reader fiber.

This is more efficient for single requests but does not support concurrent multiplexing. Use this for one-shot requests or when connection pooling doesn't require multiplexing.

  • parameter flow

    The underlying connection

  • parameter t

    Client state

  • parameter req

    The request to send

  • returns

    Response on success, Error msg on failure

val one_request : sw:Eio.Switch.t -> [> Eio.Flow.two_way_ty ] Eio.Resource.t -> meth:string -> uri:Uri.t -> ?headers:(string * string) list -> ?body:string -> unit -> (H2_protocol.response, string) result

one_request ~sw flow ~meth ~uri ?headers ?body () makes a single request.

This is a convenience function that creates a client, performs handshake, sends the request, and returns the response. Use this for one-off requests; for multiple requests on the same connection, use create, handshake, and request directly.

  • parameter sw

    Switch for the background reader fiber

  • parameter flow

    The underlying TLS connection

  • parameter meth

    HTTP method (GET, POST, etc)

  • parameter uri

    Request URI

  • parameter headers

    Optional request headers

  • parameter body

    Optional request body

  • returns

    Response on success, Error msg on failure

val one_request_strings : sw:Eio.Switch.t -> [> Eio.Flow.two_way_ty ] Eio.Resource.t -> meth:string -> scheme:string -> host:string -> ?port:int -> ?path:string -> ?query:(string * string list) list -> ?headers:(string * string) list -> ?body:string -> unit -> (H2_protocol.response, string) result

one_request_strings ~sw flow ~meth ~scheme ~host ?port ?path ?query ?headers ?body () makes a single request using string components instead of Uri.t.

This is useful when calling from libraries that have their own Uri module which would conflict with the external uri library.

  • parameter sw

    Switch for the background reader fiber

  • parameter flow

    The underlying TLS connection

  • parameter meth

    HTTP method (GET, POST, etc)

  • parameter scheme

    URL scheme (http or https)

  • parameter host

    Hostname

  • parameter port

    Optional port number

  • parameter path

    Request path (defaults to "/")

  • parameter query

    Optional query parameters

  • parameter headers

    Optional request headers

  • parameter body

    Optional request body

  • returns

    Response on success, Error msg on failure

Low-level Frame I/O

val write_frame : [> Eio.Flow.sink_ty ] Eio.Resource.t -> H2_frame.frame -> unit

write_frame flow frame writes a frame to the connection. Exposed for testing and advanced use cases.

val read_frame : [> Eio.Flow.source_ty ] Eio.Resource.t -> H2_frame.frame option

read_frame flow reads a frame from the connection. Returns None on EOF or error.