A batteries included HTTP/1.1 client in OCaml
1(*---------------------------------------------------------------------------
2 Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved.
3 SPDX-License-Identifier: ISC
4 ---------------------------------------------------------------------------*)
5
6(** One-shot HTTP/1.1 client for stateless requests
7
8 The One module provides a stateless HTTP client for single requests without
9 session state like cookies, connection pooling, or persistent configuration.
10 Each request opens a new TCP connection (with TLS for https://) that is
11 closed when the Eio switch closes.
12
13 Implements {{:https://datatracker.ietf.org/doc/html/rfc9112}RFC 9112} (HTTP/1.1)
14 with {{:https://datatracker.ietf.org/doc/html/rfc9110}RFC 9110} semantics.
15
16 For stateful requests with automatic cookie handling, connection pooling,
17 and persistent configuration, use the main {!Requests} module instead.
18
19 {2 Examples}
20
21 {[
22 open Eio_main
23
24 let () = run @@ fun env ->
25 Switch.run @@ fun sw ->
26
27 (* Simple GET request *)
28 let response = One.get ~sw
29 ~clock:env#clock ~net:env#net
30 "https://example.com" in
31 Printf.printf "Status: %d\n" (Response.status_code response);
32 Response.close response;
33
34 (* POST with JSON body *)
35 let response = One.post ~sw
36 ~clock:env#clock ~net:env#net
37 ~body:(Body.json {|{"key": "value"}|})
38 ~headers:(Headers.empty |> Headers.content_type Mime.json)
39 "https://api.example.com/data" in
40 Response.close response;
41
42 (* Download file with streaming *)
43 One.download ~sw
44 ~clock:env#clock ~net:env#net
45 "https://example.com/large-file.zip"
46 ~sink:(Eio.Path.(fs / "download.zip" |> sink))
47 ]}
48*)
49
50(** Log source for one-shot request operations *)
51val src : Logs.Src.t
52
53(** {1 TLS Configuration} *)
54
55(** Minimum TLS version configuration.
56 Per security recommendations, allows enforcing minimum TLS version. *)
57type tls_version =
58 | TLS_1_2 (** TLS 1.2 minimum (default, widely compatible) *)
59 | TLS_1_3 (** TLS 1.3 minimum (most secure, may not work with older servers) *)
60
61(** {1 HTTP Request Methods}
62
63 All functions are stateless - they open a new TCP connection for each request
64 and close it when the switch closes. No connection pooling or reuse. *)
65
66val request :
67 sw:Eio.Switch.t ->
68 clock:_ Eio.Time.clock ->
69 net:_ Eio.Net.t ->
70 ?headers:Headers.t ->
71 ?body:Body.t ->
72 ?auth:Auth.t ->
73 ?timeout:Timeout.t ->
74 ?follow_redirects:bool ->
75 ?max_redirects:int ->
76 ?verify_tls:bool ->
77 ?tls_config:Tls.Config.client ->
78 ?auto_decompress:bool ->
79 ?min_tls_version:tls_version ->
80 ?expect_100_continue:Expect_continue.config ->
81 ?allow_insecure_auth:bool ->
82 ?proxy:Proxy.config ->
83 method_:Method.t ->
84 string ->
85 Response.t
86(** [request ~sw ~clock ~net ?headers ?body ?auth ?timeout ?follow_redirects
87 ?max_redirects ?verify_tls ?tls_config ?auto_decompress ?min_tls_version
88 ?expect_100_continue ?allow_insecure_auth ~method_ url]
89 makes a single HTTP request without connection pooling.
90
91 Each call opens a new TCP connection (with TLS if https://), makes the
92 request, and closes the connection when the switch closes.
93
94 @param sw Switch for resource management (response/connection bound to this)
95 @param clock Clock for timeouts
96 @param net Network interface for TCP connections
97 @param headers Request headers (default: empty)
98 @param body Request body (default: none)
99 @param auth Authentication to apply (default: none)
100 @param timeout Request timeout (default: 30s connect, 60s read)
101 @param follow_redirects Whether to follow HTTP redirects (default: true)
102 @param max_redirects Maximum redirects to follow (default: 10)
103 @param verify_tls Whether to verify TLS certificates (default: true)
104 @param tls_config Custom TLS configuration (default: system CA certs)
105 @param auto_decompress Whether to automatically decompress gzip/deflate responses (default: true)
106 @param min_tls_version Minimum TLS version to accept (default: TLS_1_2)
107 @param expect_100_continue HTTP 100-continue configuration (default: [`Threshold 1_048_576L]).
108 Use [`Disabled] to never send, [`Always] for all bodies, or [`Threshold n] for bodies >= n bytes.
109 @param allow_insecure_auth Allow Basic/Bearer/Digest auth over HTTP (default: false).
110 Per RFC 7617 Section 4 and RFC 6750 Section 5.1, these auth methods
111 MUST be used over TLS. Set to [true] only for testing environments.
112 @param proxy HTTP/HTTPS proxy configuration. When set, requests are routed through the proxy.
113 HTTP requests use absolute-URI form (RFC 9112 Section 3.2.2).
114 HTTPS requests use CONNECT tunneling (RFC 9110 Section 9.3.6).
115 @param method_ HTTP method (GET, POST, etc.)
116 @param url URL to request
117*)
118
119val get :
120 sw:Eio.Switch.t ->
121 clock:_ Eio.Time.clock ->
122 net:_ Eio.Net.t ->
123 ?headers:Headers.t ->
124 ?auth:Auth.t ->
125 ?timeout:Timeout.t ->
126 ?follow_redirects:bool ->
127 ?max_redirects:int ->
128 ?verify_tls:bool ->
129 ?tls_config:Tls.Config.client ->
130 ?min_tls_version:tls_version ->
131 ?allow_insecure_auth:bool ->
132 ?proxy:Proxy.config ->
133 string ->
134 Response.t
135(** GET request. See {!request} for parameter details. *)
136
137val post :
138 sw:Eio.Switch.t ->
139 clock:_ Eio.Time.clock ->
140 net:_ Eio.Net.t ->
141 ?headers:Headers.t ->
142 ?body:Body.t ->
143 ?auth:Auth.t ->
144 ?timeout:Timeout.t ->
145 ?verify_tls:bool ->
146 ?tls_config:Tls.Config.client ->
147 ?min_tls_version:tls_version ->
148 ?expect_100_continue:Expect_continue.config ->
149 ?allow_insecure_auth:bool ->
150 ?proxy:Proxy.config ->
151 string ->
152 Response.t
153(** POST request with 100-continue support. See {!request} for parameter details. *)
154
155val put :
156 sw:Eio.Switch.t ->
157 clock:_ Eio.Time.clock ->
158 net:_ Eio.Net.t ->
159 ?headers:Headers.t ->
160 ?body:Body.t ->
161 ?auth:Auth.t ->
162 ?timeout:Timeout.t ->
163 ?verify_tls:bool ->
164 ?tls_config:Tls.Config.client ->
165 ?min_tls_version:tls_version ->
166 ?expect_100_continue:Expect_continue.config ->
167 ?allow_insecure_auth:bool ->
168 ?proxy:Proxy.config ->
169 string ->
170 Response.t
171(** PUT request with 100-continue support. See {!request} for parameter details. *)
172
173val delete :
174 sw:Eio.Switch.t ->
175 clock:_ Eio.Time.clock ->
176 net:_ Eio.Net.t ->
177 ?headers:Headers.t ->
178 ?auth:Auth.t ->
179 ?timeout:Timeout.t ->
180 ?verify_tls:bool ->
181 ?tls_config:Tls.Config.client ->
182 ?min_tls_version:tls_version ->
183 ?allow_insecure_auth:bool ->
184 ?proxy:Proxy.config ->
185 string ->
186 Response.t
187(** DELETE request. See {!request} for parameter details. *)
188
189val head :
190 sw:Eio.Switch.t ->
191 clock:_ Eio.Time.clock ->
192 net:_ Eio.Net.t ->
193 ?headers:Headers.t ->
194 ?auth:Auth.t ->
195 ?timeout:Timeout.t ->
196 ?verify_tls:bool ->
197 ?tls_config:Tls.Config.client ->
198 ?min_tls_version:tls_version ->
199 ?allow_insecure_auth:bool ->
200 ?proxy:Proxy.config ->
201 string ->
202 Response.t
203(** HEAD request. See {!request} for parameter details. *)
204
205val patch :
206 sw:Eio.Switch.t ->
207 clock:_ Eio.Time.clock ->
208 net:_ Eio.Net.t ->
209 ?headers:Headers.t ->
210 ?body:Body.t ->
211 ?auth:Auth.t ->
212 ?timeout:Timeout.t ->
213 ?verify_tls:bool ->
214 ?tls_config:Tls.Config.client ->
215 ?min_tls_version:tls_version ->
216 ?expect_100_continue:Expect_continue.config ->
217 ?allow_insecure_auth:bool ->
218 ?proxy:Proxy.config ->
219 string ->
220 Response.t
221(** PATCH request with 100-continue support. See {!request} for parameter details. *)
222
223val upload :
224 sw:Eio.Switch.t ->
225 clock:_ Eio.Time.clock ->
226 net:_ Eio.Net.t ->
227 ?headers:Headers.t ->
228 ?auth:Auth.t ->
229 ?timeout:Timeout.t ->
230 ?method_:Method.t ->
231 ?mime:Mime.t ->
232 ?length:int64 ->
233 ?on_progress:(sent:int64 -> total:int64 option -> unit) ->
234 ?verify_tls:bool ->
235 ?tls_config:Tls.Config.client ->
236 ?min_tls_version:tls_version ->
237 ?expect_100_continue:Expect_continue.config ->
238 ?allow_insecure_auth:bool ->
239 ?proxy:Proxy.config ->
240 source:Eio.Flow.source_ty Eio.Resource.t ->
241 string ->
242 Response.t
243(** Upload from stream with 100-continue support (default: [`Threshold 1MB]).
244 See {!request} for parameter details. *)
245
246val download :
247 sw:Eio.Switch.t ->
248 clock:_ Eio.Time.clock ->
249 net:_ Eio.Net.t ->
250 ?headers:Headers.t ->
251 ?auth:Auth.t ->
252 ?timeout:Timeout.t ->
253 ?on_progress:(received:int64 -> total:int64 option -> unit) ->
254 ?verify_tls:bool ->
255 ?tls_config:Tls.Config.client ->
256 ?min_tls_version:tls_version ->
257 ?allow_insecure_auth:bool ->
258 ?proxy:Proxy.config ->
259 string ->
260 sink:Eio.Flow.sink_ty Eio.Resource.t ->
261 unit
262(** Download to stream. See {!request} for parameter details. *)