H2_clientHTTP/2 Client with Concurrent Stream Multiplexing.
This module provides a client for making HTTP/2 requests over an established TLS connection. It handles:
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.
(* 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 clientSee RFC 9113.
val create : ?settings:H2_connection.settings -> unit -> tcreate ?settings () creates a new HTTP/2 client. Default settings are used if not specified.
val connection : t -> H2_connection.tconnection t returns the underlying connection state.
val is_open : t -> boolis_open t returns true if the connection is still usable.
val active_streams : t -> intactive_streams t returns the number of streams currently waiting for responses. Useful for connection pool management.
val handshake :
[> Eio.Flow.two_way_ty ] Eio.Resource.t ->
t ->
(unit, string) resulthandshake 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
val close : [> Eio.Flow.two_way_ty ] Eio.Resource.t -> t -> unitclose flow t gracefully closes the connection. Sends GOAWAY and marks connection as closed.
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) ->
unitstart_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.
val request :
sw:Eio.Switch.t ->
[> Eio.Flow.two_way_ty ] Eio.Resource.t ->
t ->
H2_protocol.request ->
(H2_protocol.response, string) resultrequest ~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.
val request_sync :
[> Eio.Flow.two_way_ty ] Eio.Resource.t ->
t ->
H2_protocol.request ->
(H2_protocol.response, string) resultrequest_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.
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) resultone_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.
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) resultone_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.
val write_frame :
[> Eio.Flow.sink_ty ] Eio.Resource.t ->
H2_frame.frame ->
unitwrite_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 optionread_frame flow reads a frame from the connection. Returns None on EOF or error.