A batteries included HTTP/1.1 client in OCaml
at main 262 lines 8.6 kB view raw
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. *)