forked from
anil.recoil.org/ocaml-requests
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
6open Eio
7
8let () =
9 Crypto_rng_unix.use_default ();
10 Eio_main.run @@ fun env ->
11 Switch.run @@ fun sw ->
12 (* Example 1: Basic GET request *)
13 Printf.printf "\n=== Example 1: Basic GET Request ===\n%!";
14 let req = Requests.v ~sw env in
15 let resp1 = Requests.get req "https://httpbin.org/get" in
16 Printf.printf "Status: %d\n%!" (Requests.Response.status_code resp1);
17 let body1 =
18 Requests.Response.body resp1
19 |> Buf_read.of_flow ~max_size:max_int
20 |> Buf_read.take_all
21 in
22 Printf.printf "Body length: %d bytes\n%!" (String.length body1);
23
24 (* Example 2: POST with JSON body *)
25 Printf.printf "\n=== Example 2: POST with JSON ===\n%!";
26 let json_data =
27 Jsont.Object
28 ( [
29 ("name", Jsont.String "Alice");
30 ("email", Jsont.String "alice@example.com");
31 ("age", Jsont.Number 30.0);
32 ],
33 Jsont.Meta.none )
34 in
35 let resp2 =
36 Requests.post req
37 ~body:(Requests.Body.json json_data)
38 "https://httpbin.org/post"
39 in
40 Printf.printf "POST status: %d\n%!" (Requests.Response.status_code resp2);
41
42 (* Example 3: Custom headers and authentication *)
43 Printf.printf "\n=== Example 3: Custom Headers and Auth ===\n%!";
44 let headers =
45 Requests.Headers.empty
46 |> Requests.Headers.set "X-Custom-Header" "MyValue"
47 |> Requests.Headers.user_agent "OCaml-Requests-Example/1.0"
48 in
49 let resp3 =
50 Requests.get req ~headers
51 ~auth:(Requests.Auth.bearer ~token:"demo-token-123")
52 "https://httpbin.org/bearer"
53 in
54 Printf.printf "Auth status: %d\n%!" (Requests.Response.status_code resp3);
55
56 (* Example 4: Session with default headers *)
57 Printf.printf "\n=== Example 4: Session with Default Headers ===\n%!";
58 let req2 = Requests.v ~sw env in
59 let req2 =
60 Requests.set_default_header req2 "User-Agent" "OCaml-Requests/1.0"
61 in
62 let req2 = Requests.set_default_header req2 "Accept" "application/json" in
63
64 (* All requests with req2 will include these headers *)
65 let resp4 = Requests.get req2 "https://httpbin.org/headers" in
66 Printf.printf "Headers response status: %d\n%!"
67 (Requests.Response.status_code resp4);
68
69 (* Example 5: Query parameters *)
70 Printf.printf "\n=== Example 5: Query Parameters ===\n%!";
71 let resp5 =
72 Requests.get req
73 ~params:[ ("key1", "value1"); ("key2", "value2") ]
74 "https://httpbin.org/get"
75 in
76 Printf.printf "Query params status: %d\n%!"
77 (Requests.Response.status_code resp5);
78
79 (* Example 6: Form data submission *)
80 Printf.printf "\n=== Example 6: Form Data ===\n%!";
81 let form_body =
82 Requests.Body.form [ ("username", "demo"); ("password", "secret123") ]
83 in
84 let resp6 = Requests.post req ~body:form_body "https://httpbin.org/post" in
85 Printf.printf "Form POST status: %d\n%!" (Requests.Response.status_code resp6);
86
87 (* Example 7: Retry configuration *)
88 Printf.printf "\n=== Example 7: Retry Configuration ===\n%!";
89 let retry_config =
90 Requests.Retry.config ~max_retries:3 ~backoff_factor:0.5 ()
91 in
92 let req_with_retry = Requests.v ~sw ~retry:retry_config env in
93 let req_with_retry =
94 Requests.set_timeout req_with_retry (Requests.Timeout.v ~total:10.0 ())
95 in
96
97 (* This will retry on 5xx errors *)
98 (try
99 let resp7 = Requests.get req_with_retry "https://httpbin.org/status/200" in
100 Printf.printf "Retry test status: %d\n%!"
101 (Requests.Response.status_code resp7)
102 with _ -> Printf.printf "Request failed even after retries\n%!");
103
104 (* Example 8: Concurrent requests using Fiber.both *)
105 Printf.printf "\n=== Example 8: Concurrent Requests ===\n%!";
106 let start_time = Unix.gettimeofday () in
107
108 let r1, r2 =
109 Fiber.both
110 (fun () -> Requests.get req "https://httpbin.org/delay/1")
111 (fun () -> Requests.get req "https://httpbin.org/delay/1")
112 in
113
114 let elapsed = Unix.gettimeofday () -. start_time in
115 Printf.printf "Two 1-second delays completed in %.2f seconds (concurrent)\n%!"
116 elapsed;
117 Printf.printf "Response 1 status: %d\n%!" (Requests.Response.status_code r1);
118 Printf.printf "Response 2 status: %d\n%!" (Requests.Response.status_code r2);
119
120 (* Example 9: One-shot stateless request *)
121 Printf.printf "\n=== Example 9: One-Shot Stateless Request ===\n%!";
122 let resp9 =
123 Requests.One.get ~sw ~clock:env#clock ~net:env#net "https://httpbin.org/get"
124 in
125 Printf.printf "One-shot status: %d\n%!" (Requests.Response.status_code resp9);
126
127 (* Example 10: Error handling *)
128 Printf.printf "\n=== Example 10: Error Handling ===\n%!";
129 (try
130 let resp = Requests.get req "https://httpbin.org/status/404" in
131 (* By default, 4xx/5xx status codes don't raise exceptions *)
132 if Requests.Response.ok resp then Printf.printf "Success\n%!"
133 else
134 Printf.printf "Got %d response (no exception by default)\n%!"
135 (Requests.Response.status_code resp);
136 (* Use raise_for_status to get exception behavior *)
137 let _resp = Requests.Response.raise_for_status resp in
138 ()
139 with
140 | Eio.Io (Requests.Error.E (Requests.Error.Http_error_status _), _) ->
141 Printf.printf "HTTP error status raised via raise_for_status\n%!"
142 | exn -> Printf.printf "Other error: %s\n%!" (Printexc.to_string exn));
143
144 (* Example 11: Timeouts *)
145 Printf.printf "\n=== Example 11: Timeouts ===\n%!";
146 let req_timeout = Requests.v ~sw env in
147 let req_timeout =
148 Requests.set_timeout req_timeout (Requests.Timeout.v ~total:5.0 ())
149 in
150
151 (try
152 let resp11 = Requests.get req_timeout "https://httpbin.org/delay/2" in
153 Printf.printf "Timeout test completed: %d\n%!"
154 (Requests.Response.status_code resp11)
155 with
156 | Eio.Time.Timeout -> Printf.printf "Request correctly timed out\n%!"
157 | exn -> Printf.printf "Other timeout error: %s\n%!" (Printexc.to_string exn));
158
159 (* Example 12: Multiple concurrent requests with Fiber.all *)
160 Printf.printf "\n=== Example 12: Multiple Concurrent Requests ===\n%!";
161 let urls =
162 [
163 "https://httpbin.org/delay/1";
164 "https://httpbin.org/get";
165 "https://httpbin.org/headers";
166 ]
167 in
168
169 let start_time = Unix.gettimeofday () in
170 let responses = ref [] in
171
172 Fiber.all
173 (List.map
174 (fun url ->
175 fun () ->
176 let resp = Requests.get req url in
177 responses := resp :: !responses)
178 urls);
179
180 let elapsed = Unix.gettimeofday () -. start_time in
181 Printf.printf "Three requests completed in %.2f seconds (concurrent)\n%!"
182 elapsed;
183 List.iter
184 (fun r ->
185 Printf.printf " Status: %d\n%!" (Requests.Response.status_code r))
186 !responses;
187
188 Printf.printf "\n=== All examples completed successfully! ===\n%!"