Something went wrong. Try again.
A batteries included HTTP/1.1 client in OCaml
Something went wrong. Try again.
6.7 kB · 186 lines
OCaml
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187(*--------------------------------------------------------------------------- Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved. SPDX-License-Identifier: ISC ---------------------------------------------------------------------------*)
open Eio
let () = Crypto_rng_unix.use_default (); Eio_main.run @@ fun env -> Switch.run @@ fun sw -> (* Example 1: Basic GET request *) Fmt.pr "\n=== Example 1: Basic GET Request ===\n%!"; let req = Requests.v ~sw env in let resp1 = Requests.get req "https://httpbin.org/get" in Fmt.pr "Status: %d\n%!" (Requests.Response.status_code resp1); let body1 = Requests.Response.body resp1 |> Buf_read.of_flow ~max_size:max_int |> Buf_read.take_all in Fmt.pr "Body length: %d bytes\n%!" (String.length body1);
(* Example 2: POST with JSON body *) Fmt.pr "\n=== Example 2: POST with JSON ===\n%!"; let json_data = Json.Object ( [ (("name", Json.Meta.none), Json.String ("Alice", Json.Meta.none)); ( ("email", Json.Meta.none), Json.String ("alice@example.com", Json.Meta.none) ); (("age", Json.Meta.none), Json.Number (30.0, Json.Meta.none)); ], Json.Meta.none ) in let resp2 = Requests.post req ~body:(Requests.Body.json json_data) "https://httpbin.org/post" in Fmt.pr "POST status: %d\n%!" (Requests.Response.status_code resp2);
(* Example 3: Custom headers and authentication *) Fmt.pr "\n=== Example 3: Custom Headers and Auth ===\n%!"; let headers = Requests.Headers.empty |> Requests.Headers.set (Requests.Header_name.of_string "X-Custom-Header") "MyValue" |> Requests.Headers.user_agent "OCaml-Requests-Example/1.0" in let resp3 = Requests.get req ~headers ~auth:(Requests.Auth.bearer ~token:"demo-token-123") "https://httpbin.org/bearer" in Fmt.pr "Auth status: %d\n%!" (Requests.Response.status_code resp3);
(* Example 4: Session with default headers *) Fmt.pr "\n=== Example 4: Session with Default Headers ===\n%!"; let req2 = Requests.v ~sw env in let req2 = Requests.set_default_header req2 "User-Agent" "OCaml-Requests/1.0" in let req2 = Requests.set_default_header req2 "Accept" "application/json" in
(* All requests with req2 will include these headers *) let resp4 = Requests.get req2 "https://httpbin.org/headers" in Fmt.pr "Headers response status: %d\n%!" (Requests.Response.status_code resp4);
(* Example 5: Query parameters *) Fmt.pr "\n=== Example 5: Query Parameters ===\n%!"; let resp5 = Requests.get req ~params:[ ("key1", "value1"); ("key2", "value2") ] "https://httpbin.org/get" in Fmt.pr "Query params status: %d\n%!" (Requests.Response.status_code resp5);
(* Example 6: Form data submission *) Fmt.pr "\n=== Example 6: Form Data ===\n%!"; let form_body = Requests.Body.form [ ("username", "demo"); ("password", "secret123") ] in let resp6 = Requests.post req ~body:form_body "https://httpbin.org/post" in Fmt.pr "Form POST status: %d\n%!" (Requests.Response.status_code resp6);
(* Example 7: Retry configuration *) Fmt.pr "\n=== Example 7: Retry Configuration ===\n%!"; let retry_config = Requests.Retry.config ~max_retries:3 ~backoff_factor:0.5 () in let req_with_retry = Requests.v ~sw ~retry:retry_config env in let req_with_retry = Requests.set_timeout req_with_retry (Requests.Timeout.v ~total:10.0 ()) in
(* This will retry on 5xx errors *) (try let resp7 = Requests.get req_with_retry "https://httpbin.org/status/200" in Fmt.pr "Retry test status: %d\n%!" (Requests.Response.status_code resp7) with _ -> Fmt.pr "Request failed even after retries\n%!");
(* Example 8: Concurrent requests using Fiber.both *) Fmt.pr "\n=== Example 8: Concurrent Requests ===\n%!"; let start_time = Unix.gettimeofday () in
let r1, r2 = Fiber.pair (fun () -> Requests.get req "https://httpbin.org/delay/1") (fun () -> Requests.get req "https://httpbin.org/delay/1") in
let elapsed = Unix.gettimeofday () -. start_time in Fmt.pr "Two 1-second delays completed in %.2f seconds (concurrent)\n%!" elapsed; Fmt.pr "Response 1 status: %d\n%!" (Requests.Response.status_code r1); Fmt.pr "Response 2 status: %d\n%!" (Requests.Response.status_code r2);
(* Example 9: One-shot stateless request *) Fmt.pr "\n=== Example 9: One-Shot Stateless Request ===\n%!"; let resp9 = Requests.One.get ~sw ~clock:env#clock ~net:env#net "https://httpbin.org/get" in Fmt.pr "One-shot status: %d\n%!" (Requests.Response.status_code resp9);
(* Example 10: Error handling *) Fmt.pr "\n=== Example 10: Error Handling ===\n%!"; (try let resp = Requests.get req "https://httpbin.org/status/404" in (* By default, 4xx/5xx status codes don't raise exceptions *) if Requests.Response.ok resp then Fmt.pr "Success\n%!" else Fmt.pr "Got %d response (no exception by default)\n%!" (Requests.Response.status_code resp); (* Use raise_for_status to get exception behavior *) let _resp = Requests.Response.raise_for_status resp in () with | Eio.Io (Requests.Error.E (Requests.Error.Http_error _), _) -> Fmt.pr "HTTP error status raised via raise_for_status\n%!" | exn -> Fmt.pr "Other error: %s\n%!" (Printexc.to_string exn));
(* Example 11: Timeouts *) Fmt.pr "\n=== Example 11: Timeouts ===\n%!"; let req_timeout = Requests.v ~sw env in let req_timeout = Requests.set_timeout req_timeout (Requests.Timeout.v ~total:5.0 ()) in
(try let resp11 = Requests.get req_timeout "https://httpbin.org/delay/2" in Fmt.pr "Timeout test completed: %d\n%!" (Requests.Response.status_code resp11) with | Eio.Time.Timeout -> Fmt.pr "Request correctly timed out\n%!" | exn -> Fmt.pr "Other timeout error: %s\n%!" (Printexc.to_string exn));
(* Example 12: Multiple concurrent requests with Fiber.all *) Fmt.pr "\n=== Example 12: Multiple Concurrent Requests ===\n%!"; let urls = [ "https://httpbin.org/delay/1"; "https://httpbin.org/get"; "https://httpbin.org/headers"; ] in
let start_time = Unix.gettimeofday () in let responses = ref [] in
Fiber.all (List.map (fun url -> fun () -> let resp = Requests.get req url in responses := resp :: !responses) urls);
let elapsed = Unix.gettimeofday () -. start_time in Fmt.pr "Three requests completed in %.2f seconds (concurrent)\n%!" elapsed; List.iter (fun r -> Fmt.pr " Status: %d\n%!" (Requests.Response.status_code r)) !responses;
Fmt.pr "\n=== All examples completed successfully! ===\n%!"