tangled
alpha
login
or
join now
anil.recoil.org
/
ocaml-requests
1
fork
atom
A batteries included HTTP/1.1 client in OCaml
1
fork
atom
overview
issues
pulls
pipelines
fix build
anil.recoil.org
2 months ago
c9f9f8c5
ad9ae69d
+53
-52
2 changed files
expand all
collapse all
unified
split
lib
requests.ml
requests.mli
+28
-28
lib/requests.ml
···
23
23
24
24
(* Main API - Session functionality with connection pooling *)
25
25
26
26
-
type ('clock, 'net) t = {
26
26
+
type t = T : {
27
27
sw : Eio.Switch.t;
28
28
-
clock : 'clock;
29
29
-
net : 'net;
30
30
-
http_pool : ('clock, 'net) Conpool.t;
31
31
-
https_pool : ('clock, 'net) Conpool.t;
28
28
+
clock : [> float Eio.Time.clock_ty] Eio.Resource.t;
29
29
+
net : [> [> `Generic] Eio.Net.ty] Eio.Resource.t;
30
30
+
http_pool : Conpool.t;
31
31
+
https_pool : Conpool.t;
32
32
cookie_jar : Cookeio_jar.t;
33
33
cookie_mutex : Eio.Mutex.t;
34
34
default_headers : Headers.t;
···
49
49
mutable requests_made : int;
50
50
mutable total_time : float;
51
51
mutable retries_count : int;
52
52
-
}
52
52
+
} -> t
53
53
54
54
let create
55
55
~sw
···
136
136
Cookeio_jar.create ()
137
137
in
138
138
139
139
-
{
139
139
+
T {
140
140
sw;
141
141
clock;
142
142
net;
···
159
159
retries_count = 0;
160
160
}
161
161
162
162
-
let set_default_header t key value =
163
163
-
{ t with default_headers = Headers.set key value t.default_headers }
162
162
+
let set_default_header (T t) key value =
163
163
+
T { t with default_headers = Headers.set key value t.default_headers }
164
164
165
165
-
let remove_default_header t key =
166
166
-
{ t with default_headers = Headers.remove key t.default_headers }
165
165
+
let remove_default_header (T t) key =
166
166
+
T { t with default_headers = Headers.remove key t.default_headers }
167
167
168
168
-
let set_auth t auth =
168
168
+
let set_auth (T t) auth =
169
169
Log.debug (fun m -> m "Setting authentication method");
170
170
-
{ t with auth = Some auth }
170
170
+
T { t with auth = Some auth }
171
171
172
172
-
let clear_auth t =
172
172
+
let clear_auth (T t) =
173
173
Log.debug (fun m -> m "Clearing authentication");
174
174
-
{ t with auth = None }
174
174
+
T { t with auth = None }
175
175
176
176
-
let set_timeout t timeout =
176
176
+
let set_timeout (T t) timeout =
177
177
Log.debug (fun m -> m "Setting timeout: %a" Timeout.pp timeout);
178
178
-
{ t with timeout }
178
178
+
T { t with timeout }
179
179
180
180
-
let set_retry t config =
180
180
+
let set_retry (T t) config =
181
181
Log.debug (fun m -> m "Setting retry config: max_retries=%d" config.Retry.max_retries);
182
182
-
{ t with retry = Some config }
182
182
+
T { t with retry = Some config }
183
183
184
184
-
let cookies t = t.cookie_jar
185
185
-
let clear_cookies t = Cookeio_jar.clear t.cookie_jar
184
184
+
let cookies (T t) = t.cookie_jar
185
185
+
let clear_cookies (T t) = Cookeio_jar.clear t.cookie_jar
186
186
187
187
(* Internal request function using connection pools *)
188
188
-
let make_request_internal t ?headers ?body ?auth ?timeout ?follow_redirects ?max_redirects ~method_ url =
188
188
+
let make_request_internal (T t) ?headers ?body ?auth ?timeout ?follow_redirects ?max_redirects ~method_ url =
189
189
let start_time = Unix.gettimeofday () in
190
190
let method_str = Method.to_string method_ in
191
191
···
238
238
List.iter (fun cookie_str ->
239
239
let now = fun () -> Ptime.of_float_s (Eio.Time.now t.clock) |> Option.get in
240
240
match Cookeio.of_set_cookie_header ~now ~domain:cookie_domain ~path:cookie_path cookie_str with
241
241
-
| Some cookie ->
241
241
+
| Ok cookie ->
242
242
Log.debug (fun m -> m "Storing cookie: %s" (Cookeio.name cookie));
243
243
Cookeio_jar.add_cookie t.cookie_jar cookie
244
244
-
| None ->
245
245
-
Log.warn (fun m -> m "Failed to parse cookie: %s" cookie_str)
244
244
+
| Error msg ->
245
245
+
Log.warn (fun m -> m "Failed to parse cookie: %s (%s)" cookie_str msg)
246
246
) cookie_headers
247
247
)
248
248
in
···
414
414
response
415
415
416
416
(* Public request function - executes synchronously with retry support *)
417
417
-
let request t ?headers ?body ?auth ?timeout ?follow_redirects ?max_redirects ~method_ url =
417
417
+
let request (T t as wrapped_t) ?headers ?body ?auth ?timeout ?follow_redirects ?max_redirects ~method_ url =
418
418
match t.retry with
419
419
| None ->
420
420
(* No retry configured, execute directly *)
421
421
-
make_request_internal t ?headers ?body ?auth ?timeout
421
421
+
make_request_internal wrapped_t ?headers ?body ?auth ?timeout
422
422
?follow_redirects ?max_redirects ~method_ url
423
423
| Some retry_config ->
424
424
(* Wrap in retry logic *)
···
436
436
(Method.to_string method_) url);
437
437
438
438
try
439
439
-
let response = make_request_internal t ?headers ?body ?auth ?timeout
439
439
+
let response = make_request_internal wrapped_t ?headers ?body ?auth ?timeout
440
440
?follow_redirects ?max_redirects ~method_ url in
441
441
let status = Response.status_code response in
442
442
+25
-24
lib/requests.mli
···
198
198
Use Eio.Fiber.both or Eio.Fiber.all for concurrent execution.
199
199
*)
200
200
201
201
-
type ('clock, 'net) t
201
201
+
type t
202
202
(** A stateful HTTP client that maintains cookies, auth, configuration, and
203
203
-
connection pools across requests. *)
203
203
+
connection pools across requests. The clock and network resources are
204
204
+
existentially quantified and hidden behind this abstract type. *)
204
205
205
206
(** {2 Creation and Configuration} *)
206
207
207
208
val create :
208
209
sw:Eio.Switch.t ->
209
209
-
?http_pool:('clock Eio.Time.clock, 'net Eio.Net.t) Conpool.t ->
210
210
-
?https_pool:('clock Eio.Time.clock, 'net Eio.Net.t) Conpool.t ->
210
210
+
?http_pool:Conpool.t ->
211
211
+
?https_pool:Conpool.t ->
211
212
?cookie_jar:Cookeio_jar.t ->
212
213
?default_headers:Headers.t ->
213
214
?auth:Auth.t ->
···
222
223
?retry:Retry.config ->
223
224
?persist_cookies:bool ->
224
225
?xdg:Xdge.t ->
225
225
-
< clock: 'clock Eio.Resource.t; net: 'net Eio.Resource.t; fs: Eio.Fs.dir_ty Eio.Path.t; .. > ->
226
226
-
('clock Eio.Resource.t, 'net Eio.Resource.t) t
226
226
+
< clock: _ Eio.Time.clock; net: _ Eio.Net.t; fs: Eio.Fs.dir_ty Eio.Path.t; .. > ->
227
227
+
t
227
228
(** Create a new requests instance with persistent state and connection pooling.
228
229
All resources are bound to the provided switch and will be cleaned up automatically.
229
230
···
251
252
252
253
(** {2 Configuration Management} *)
253
254
254
254
-
val set_default_header : ('clock, 'net) t -> string -> string -> ('clock, 'net) t
255
255
+
val set_default_header : t -> string -> string -> t
255
256
(** Add or update a default header. Returns a new session with the updated header.
256
257
The original session's connection pools are shared. *)
257
258
258
258
-
val remove_default_header : ('clock, 'net) t -> string -> ('clock, 'net) t
259
259
+
val remove_default_header : t -> string -> t
259
260
(** Remove a default header. Returns a new session without the header. *)
260
261
261
261
-
val set_auth : ('clock, 'net) t -> Auth.t -> ('clock, 'net) t
262
262
+
val set_auth : t -> Auth.t -> t
262
263
(** Set default authentication. Returns a new session with auth configured. *)
263
264
264
264
-
val clear_auth : ('clock, 'net) t -> ('clock, 'net) t
265
265
+
val clear_auth : t -> t
265
266
(** Clear authentication. Returns a new session without auth. *)
266
267
267
267
-
val set_timeout : ('clock, 'net) t -> Timeout.t -> ('clock, 'net) t
268
268
+
val set_timeout : t -> Timeout.t -> t
268
269
(** Set default timeout. Returns a new session with the timeout configured. *)
269
270
270
270
-
val set_retry : ('clock, 'net) t -> Retry.config -> ('clock, 'net) t
271
271
+
val set_retry : t -> Retry.config -> t
271
272
(** Set retry configuration. Returns a new session with retry configured. *)
272
273
273
274
(** {2 Request Methods}
···
348
349
*)
349
350
350
351
val request :
351
351
-
(_ Eio.Time.clock, _ Eio.Net.t) t ->
352
352
+
t ->
352
353
?headers:Headers.t ->
353
354
?body:Body.t ->
354
355
?auth:Auth.t ->
···
361
362
(** Make a concurrent HTTP request *)
362
363
363
364
val get :
364
364
-
(_ Eio.Time.clock, _ Eio.Net.t) t ->
365
365
+
t ->
365
366
?headers:Headers.t ->
366
367
?auth:Auth.t ->
367
368
?timeout:Timeout.t ->
···
371
372
(** Concurrent GET request *)
372
373
373
374
val post :
374
374
-
(_ Eio.Time.clock, _ Eio.Net.t) t ->
375
375
+
t ->
375
376
?headers:Headers.t ->
376
377
?body:Body.t ->
377
378
?auth:Auth.t ->
···
381
382
(** Concurrent POST request *)
382
383
383
384
val put :
384
384
-
(_ Eio.Time.clock, _ Eio.Net.t) t ->
385
385
+
t ->
385
386
?headers:Headers.t ->
386
387
?body:Body.t ->
387
388
?auth:Auth.t ->
···
391
392
(** Concurrent PUT request *)
392
393
393
394
val patch :
394
394
-
(_ Eio.Time.clock, _ Eio.Net.t) t ->
395
395
+
t ->
395
396
?headers:Headers.t ->
396
397
?body:Body.t ->
397
398
?auth:Auth.t ->
···
401
402
(** Concurrent PATCH request *)
402
403
403
404
val delete :
404
404
-
(_ Eio.Time.clock, _ Eio.Net.t) t ->
405
405
+
t ->
405
406
?headers:Headers.t ->
406
407
?auth:Auth.t ->
407
408
?timeout:Timeout.t ->
···
410
411
(** Concurrent DELETE request *)
411
412
412
413
val head :
413
413
-
(_ Eio.Time.clock, _ Eio.Net.t) t ->
414
414
+
t ->
414
415
?headers:Headers.t ->
415
416
?auth:Auth.t ->
416
417
?timeout:Timeout.t ->
···
419
420
(** Concurrent HEAD request *)
420
421
421
422
val options :
422
422
-
(_ Eio.Time.clock, _ Eio.Net.t) t ->
423
423
+
t ->
423
424
?headers:Headers.t ->
424
425
?auth:Auth.t ->
425
426
?timeout:Timeout.t ->
···
429
430
430
431
(** {2 Cookie Management} *)
431
432
432
432
-
val cookies : ('clock, 'net) t -> Cookeio_jar.t
433
433
+
val cookies : t -> Cookeio_jar.t
433
434
(** Get the cookie jar for direct manipulation *)
434
435
435
435
-
val clear_cookies : ('clock, 'net) t -> unit
436
436
+
val clear_cookies : t -> unit
436
437
(** Clear all cookies *)
437
438
438
439
(** {1 Cmdliner Integration} *)
···
458
459
verbose_http : bool; (** Enable verbose HTTP-level logging *)
459
460
}
460
461
461
461
-
val create : config -> < clock: ([> float Eio.Time.clock_ty ] as 'clock) Eio.Resource.t; net: ([> [>`Generic] Eio.Net.ty ] as 'net) Eio.Resource.t; fs: Eio.Fs.dir_ty Eio.Path.t; .. > -> Eio.Switch.t -> ('clock Eio.Resource.t, 'net Eio.Resource.t) t
462
462
+
val create : config -> < clock: _ Eio.Time.clock; net: _ Eio.Net.t; fs: Eio.Fs.dir_ty Eio.Path.t; .. > -> Eio.Switch.t -> t
462
463
(** [create config env sw] creates a requests instance from command-line configuration *)
463
464
464
465
(** {2 Individual Terms} *)
···
529
530
Cmd.eval cmd
530
531
]} *)
531
532
532
532
-
val requests_term : string -> < clock: ([> float Eio.Time.clock_ty ] as 'clock) Eio.Resource.t; net: ([> [>`Generic] Eio.Net.ty ] as 'net) Eio.Resource.t; fs: Eio.Fs.dir_ty Eio.Path.t; .. > -> Eio.Switch.t -> ('clock Eio.Resource.t, 'net Eio.Resource.t) t Cmdliner.Term.t
533
533
+
val requests_term : string -> < clock: _ Eio.Time.clock; net: _ Eio.Net.t; fs: Eio.Fs.dir_ty Eio.Path.t; .. > -> Eio.Switch.t -> t Cmdliner.Term.t
533
534
(** [requests_term app_name env sw] creates a term that directly produces a requests instance.
534
535
535
536
This is a convenience function that combines configuration parsing