···5151 let doc = "Show progress bar for downloads" in
5252 Arg.(value & flag & info ["progress-bar"] ~doc)
53535454-let enable_cache =
5555- let doc = "Enable HTTP response caching for GET and HEAD requests" in
5656- let env_info = Cmdliner.Cmd.Env.info "OCURL_ENABLE_CACHE" in
5757- Arg.(value & flag & info ["enable-cache"] ~env:env_info ~doc)
5858-5954(* Logging setup *)
6055(* Setup logging using Logs_cli for standard logging options *)
6156let setup_log =
···223218 Error (url_str, exn)
224219225220(* Main function using Requests with concurrent fetching *)
226226-let run_request env sw persist_cookies verify_tls enable_cache timeout follow_redirects max_redirects
221221+let run_request env sw persist_cookies verify_tls timeout follow_redirects max_redirects
227222 method_ urls headers data json_data output include_headers
228223 auth _show_progress () =
229224···234229235230 (* Create requests instance with configuration *)
236231 let timeout_obj = Option.map (fun t -> Requests.Timeout.create ~total:t ()) timeout in
237237- let req = Requests.create ~sw ~xdg ~persist_cookies ~verify_tls ~enable_cache
232232+ let req = Requests.create ~sw ~xdg ~persist_cookies ~verify_tls
238233 ~follow_redirects ~max_redirects ?timeout:timeout_obj env in
239234240235 (* Set authentication if provided *)
···315310316311(* Main entry point *)
317312let main method_ urls headers data json_data output include_headers
318318- auth show_progress persist_cookies verify_tls enable_cache
313313+ auth show_progress persist_cookies verify_tls
319314 timeout follow_redirects max_redirects () =
320315321316 Eio_main.run @@ fun env ->
322317 Mirage_crypto_rng_unix.use_default ();
323318 Switch.run @@ fun sw ->
324319325325- run_request env sw persist_cookies verify_tls enable_cache timeout follow_redirects max_redirects
320320+ run_request env sw persist_cookies verify_tls timeout follow_redirects max_redirects
326321 method_ urls headers data json_data output include_headers auth
327322 show_progress ()
328323···351346 `P "Custom headers:";
352347 `Pre " $(tname) -H 'Accept: application/json' -H 'X-Api-Key: secret' https://api.example.com";
353348 `P "With persistent cookies:";
354354- `Pre " $(tname) --persist-cookies --cache-dir ~/.ocurl https://example.com";
355355- `P "Enable response caching:";
356356- `Pre " $(tname) --enable-cache https://api.example.com";
349349+ `Pre " $(tname) --persist-cookies https://example.com";
357350 `P "Disable TLS verification (insecure):";
358351 `Pre " $(tname) --no-verify-tls https://self-signed.example.com";
359352 `S "LOGGING OPTIONS";
···383376 show_progress $
384377 Requests.Cmd.persist_cookies_term app_name $
385378 Requests.Cmd.verify_tls_term app_name $
386386- enable_cache $
387379 Requests.Cmd.timeout_term app_name $
388380 Requests.Cmd.follow_redirects_term app_name $
389381 Requests.Cmd.max_redirects_term app_name $
-488
stack/requests/lib/cache.ml
···11-let src = Logs.Src.create "requests.cache" ~doc:"HTTP cache with cacheio"
22-module Log = (val Logs.src_log src : Logs.LOG)
33-44-type cached_response = {
55- status : Cohttp.Code.status_code;
66- headers : Cohttp.Header.t;
77- body : string;
88-}
99-1010-type t = {
1111- sw : Eio.Switch.t;
1212- enabled : bool;
1313- cache_get_requests : bool;
1414- cache_range_requests : bool;
1515- cacheio : Cacheio.t option;
1616- memory_cache : (string, cached_response * float) Hashtbl.t;
1717-}
1818-1919-let create ~sw ~enabled ?(cache_get_requests=true) ?(cache_range_requests=true) ~cache_dir () =
2020- let cacheio =
2121- match cache_dir with
2222- | Some dir when enabled ->
2323- (try
2424- Some (Cacheio.create ~base_dir:dir)
2525- with e ->
2626- Log.warn (fun m -> m "Failed to create cacheio backend: %s. Using memory cache only."
2727- (Printexc.to_string e));
2828- None)
2929- | _ -> None
3030- in
3131- { sw; enabled; cache_get_requests; cache_range_requests; cacheio;
3232- memory_cache = Hashtbl.create 100 }
3333-3434-let make_cache_key ~method_ ~url ~headers =
3535- let method_str = match method_ with
3636- | `GET -> "GET" | `HEAD -> "HEAD"
3737- | _ -> "OTHER"
3838- in
3939- let url_str = Uri.to_string url in
4040- let range_str = match Cohttp.Header.get headers "range" with
4141- | Some r -> "_range:" ^ r
4242- | None -> ""
4343- in
4444- Printf.sprintf "%s_%s%s" method_str url_str range_str
4545-4646-let is_cacheable ~method_ ~status ~headers =
4747- match method_ with
4848- | `GET | `HEAD ->
4949- let code = Cohttp.Code.code_of_status status in
5050- if code >= 200 && code < 300 then
5151- match Cohttp.Header.get headers "cache-control" with
5252- | Some cc ->
5353- let cc_lower = String.lowercase_ascii cc in
5454- let rec contains s sub pos =
5555- if pos + String.length sub > String.length s then false
5656- else if String.sub s pos (String.length sub) = sub then true
5757- else contains s sub (pos + 1)
5858- in
5959- not (contains cc_lower "no-store" 0 ||
6060- contains cc_lower "no-cache" 0 ||
6161- contains cc_lower "private" 0)
6262- | None -> true
6363- else
6464- code = 301 || code = 308
6565- | _ -> false
6666-6767-let parse_max_age headers =
6868- match Cohttp.Header.get headers "cache-control" with
6969- | Some cc ->
7070- let parts = String.split_on_char ',' cc |> List.map String.trim in
7171- List.find_map (fun part ->
7272- let prefix = "max-age=" in
7373- if String.starts_with ~prefix part then
7474- let value = String.sub part (String.length prefix)
7575- (String.length part - String.length prefix) in
7676- try Some (float_of_string value) with _ -> None
7777- else None
7878- ) parts
7979- | None -> None
8080-8181-(* JSON codec for cache metadata *)
8282-module Metadata = struct
8383- type t = {
8484- status_code : int;
8585- headers : (string * string) list;
8686- }
8787-8888- let make status_code headers = { status_code; headers }
8989- let status_code t = t.status_code
9090- let headers t = t.headers
9191-9292- let t_jsont =
9393- let header_pair_jsont =
9494- let dec x y = (x, y) in
9595- let enc (x, y) i = if i = 0 then x else y in
9696- Jsont.t2 ~dec ~enc Jsont.string
9797- in
9898- Jsont.Object.map ~kind:"CacheMetadata" make
9999- |> Jsont.Object.mem "status_code" Jsont.int ~enc:status_code
100100- |> Jsont.Object.mem "headers" (Jsont.list header_pair_jsont) ~enc:headers
101101- |> Jsont.Object.finish
102102-end
103103-104104-let serialize_metadata ~status ~headers =
105105- let status_code = Cohttp.Code.code_of_status status in
106106- let headers_assoc = Cohttp.Header.to_list headers in
107107- let metadata = Metadata.make status_code headers_assoc in
108108- match Jsont_bytesrw.encode_string' Metadata.t_jsont metadata with
109109- | Ok s -> s
110110- | Error e -> failwith (Fmt.str "Failed to serialize metadata: %s" (Jsont.Error.to_string e))
111111-112112-let deserialize_metadata json_str =
113113- try
114114- match Jsont_bytesrw.decode_string' Metadata.t_jsont json_str with
115115- | Ok metadata ->
116116- let status = Cohttp.Code.status_of_code (Metadata.status_code metadata) in
117117- let headers = Cohttp.Header.of_list (Metadata.headers metadata) in
118118- Some (status, headers)
119119- | Error _ -> None
120120- with _ -> None
121121-122122-let get t ~method_ ~url ~headers =
123123- if not t.enabled then None
124124- else if method_ = `GET && not t.cache_get_requests then None
125125- else
126126- let key = make_cache_key ~method_ ~url ~headers in
127127-128128- (* Try cacheio first *)
129129- match t.cacheio with
130130- | Some cache ->
131131- (* Check for metadata entry *)
132132- let metadata_key = key ^ ".meta" in
133133- let body_key = key ^ ".body" in
134134-135135- if Cacheio.exists cache ~key:metadata_key && Cacheio.exists cache ~key:body_key then
136136- Eio.Switch.run @@ fun sw ->
137137- (* Read metadata *)
138138- let metadata_opt = match Cacheio.get cache ~key:metadata_key ~sw with
139139- | Some source ->
140140- let buf = Buffer.create 256 in
141141- Eio.Flow.copy source (Eio.Flow.buffer_sink buf);
142142- deserialize_metadata (Buffer.contents buf)
143143- | None -> None
144144- in
145145-146146- (match metadata_opt with
147147- | Some (status, resp_headers) ->
148148- (* Read body *)
149149- (match Cacheio.get cache ~key:body_key ~sw with
150150- | Some source ->
151151- let buf = Buffer.create 4096 in
152152- Eio.Flow.copy source (Eio.Flow.buffer_sink buf);
153153- let body = Buffer.contents buf in
154154- Log.debug (fun m -> m "Cache hit for %s" (Uri.to_string url));
155155- Some { status; headers = resp_headers; body }
156156- | None ->
157157- Log.debug (fun m -> m "Cache body missing for %s" (Uri.to_string url));
158158- None)
159159- | None ->
160160- Log.debug (fun m -> m "Cache metadata missing for %s" (Uri.to_string url));
161161- None)
162162- else
163163- (Log.debug (fun m -> m "Cache miss for %s" (Uri.to_string url));
164164- None)
165165- | None ->
166166- (* Fall back to memory cache *)
167167- match Hashtbl.find_opt t.memory_cache key with
168168- | Some (response, expiry) when expiry > Unix.gettimeofday () ->
169169- Log.debug (fun m -> m "Memory cache hit for %s" (Uri.to_string url));
170170- Some response
171171- | _ ->
172172- Log.debug (fun m -> m "Cache miss for %s" (Uri.to_string url));
173173- None
174174-175175-let get_stream t ~method_ ~url ~headers ~sw =
176176- if not t.enabled then None
177177- else if method_ = `GET && not t.cache_get_requests then None
178178- else
179179- let key = make_cache_key ~method_ ~url ~headers in
180180-181181- match t.cacheio with
182182- | Some cache ->
183183- let metadata_key = key ^ ".meta" in
184184- let body_key = key ^ ".body" in
185185-186186- if Cacheio.exists cache ~key:metadata_key && Cacheio.exists cache ~key:body_key then
187187- (* Read metadata first *)
188188- let metadata_opt =
189189- match Cacheio.get cache ~key:metadata_key ~sw with
190190- | Some source ->
191191- let buf = Buffer.create 256 in
192192- Eio.Flow.copy source (Eio.Flow.buffer_sink buf);
193193- deserialize_metadata (Buffer.contents buf)
194194- | None -> None
195195- in
196196-197197- (match metadata_opt with
198198- | Some (status, resp_headers) ->
199199- (* Return body stream directly *)
200200- (match Cacheio.get cache ~key:body_key ~sw with
201201- | Some source ->
202202- Log.debug (fun m -> m "Streaming cache hit for %s" (Uri.to_string url));
203203- Some (status, resp_headers, source)
204204- | None -> None)
205205- | None -> None)
206206- else None
207207- | None -> None
208208-209209-let put t ~method_ ~url ~request_headers ~status ~headers ~body =
210210- if not t.enabled then ()
211211- else if is_cacheable ~method_ ~status ~headers then
212212- let key = make_cache_key ~method_ ~url ~headers:request_headers in
213213- let ttl = parse_max_age headers in
214214-215215- Log.debug (fun m -> m "Caching response for %s (ttl: %s)"
216216- (Uri.to_string url)
217217- (match ttl with Some t -> Printf.sprintf "%.0fs" t | None -> "3600s"));
218218-219219- (match t.cacheio with
220220- | Some cache ->
221221- Eio.Switch.run @@ fun _sw ->
222222- let metadata_key = key ^ ".meta" in
223223- let metadata = serialize_metadata ~status ~headers in
224224- let metadata_source = Eio.Flow.string_source metadata in
225225- Cacheio.put cache ~key:metadata_key ~source:metadata_source ~ttl ();
226226-227227- let body_key = key ^ ".body" in
228228- let body_source = Eio.Flow.string_source body in
229229- Cacheio.put cache ~key:body_key ~source:body_source ~ttl ()
230230- | None -> ());
231231-232232- let cached_resp = { status; headers; body } in
233233- let expiry = Unix.gettimeofday () +. Option.value ttl ~default:3600.0 in
234234- Hashtbl.replace t.memory_cache key (cached_resp, expiry)
235235-236236-let put_stream t ~method_ ~url ~request_headers ~status ~headers ~body_source ~ttl =
237237- if not t.enabled then ()
238238- else if is_cacheable ~method_ ~status ~headers then
239239- let key = make_cache_key ~method_ ~url ~headers:request_headers in
240240-241241- Log.debug (fun m -> m "Caching streamed response for %s (ttl: %s)"
242242- (Uri.to_string url)
243243- (match ttl with Some t -> Printf.sprintf "%.0fs" t | None -> "3600s"));
244244-245245- match t.cacheio with
246246- | Some cache ->
247247- Eio.Switch.run @@ fun _sw ->
248248-249249- (* Store metadata *)
250250- let metadata_key = key ^ ".meta" in
251251- let metadata = serialize_metadata ~status ~headers in
252252- let metadata_source = Eio.Flow.string_source metadata in
253253- Cacheio.put cache ~key:metadata_key ~source:metadata_source ~ttl ();
254254-255255- (* Store body directly from source *)
256256- let body_key = key ^ ".body" in
257257- Cacheio.put cache ~key:body_key ~source:body_source ~ttl ()
258258- | None -> ()
259259-260260-module Range = struct
261261- type t = {
262262- start : int64;
263263- end_ : int64 option; (* None means to end of file *)
264264- }
265265-266266- let of_header header =
267267- (* Parse Range: bytes=start-end *)
268268- let prefix = "bytes=" in
269269- let prefix_len = String.length prefix in
270270- if String.length header >= prefix_len &&
271271- String.sub header 0 prefix_len = prefix then
272272- let range_str = String.sub header prefix_len (String.length header - prefix_len) in
273273- match String.split_on_char '-' range_str with
274274- | [start; ""] ->
275275- (* bytes=N- means from N to end *)
276276- (try Some { start = Int64.of_string start; end_ = None }
277277- with _ -> None)
278278- | [start; end_] ->
279279- (* bytes=N-M *)
280280- (try Some {
281281- start = Int64.of_string start;
282282- end_ = Some (Int64.of_string end_)
283283- }
284284- with _ -> None)
285285- | _ -> None
286286- else None
287287-288288- let to_header t =
289289- match t.end_ with
290290- | None -> Printf.sprintf "bytes=%Ld-" t.start
291291- | Some e -> Printf.sprintf "bytes=%Ld-%Ld" t.start e
292292-293293- let to_cacheio_range t ~total_size =
294294- let end_ = match t.end_ with
295295- | None -> Int64.pred total_size
296296- | Some e -> min e (Int64.pred total_size)
297297- in
298298- (* Convert to Cacheio.Range.t *)
299299- Cacheio.Range.create ~start:t.start ~end_
300300-end
301301-302302-let download_range t ~sw ~url ~range ~on_chunk =
303303- let range_header = Range.to_header range in
304304- Log.debug (fun m -> m "Range request for %s: %s"
305305- (Uri.to_string url) range_header);
306306-307307- match t.cacheio with
308308- | Some cache ->
309309- let key = Uri.to_string url in
310310- let cacheio_range = Range.to_cacheio_range range ~total_size:Int64.max_int in
311311-312312- (match Cacheio.get_range cache ~key ~range:cacheio_range ~sw with
313313- | `Complete source ->
314314- let rec read_chunks () =
315315- let chunk = Cstruct.create 8192 in
316316- try
317317- let n = Eio.Flow.single_read source chunk in
318318- if n > 0 then begin
319319- on_chunk (Cstruct.to_string ~off:0 ~len:n chunk);
320320- read_chunks ()
321321- end
322322- with End_of_file -> ()
323323- in
324324- read_chunks ();
325325- Some true
326326- | `Chunks chunk_sources ->
327327- List.iter (fun (_range, source) ->
328328- let rec read_chunk () =
329329- let chunk = Cstruct.create 8192 in
330330- try
331331- let n = Eio.Flow.single_read source chunk in
332332- if n > 0 then begin
333333- on_chunk (Cstruct.to_string ~off:0 ~len:n chunk);
334334- read_chunk ()
335335- end
336336- with End_of_file -> ()
337337- in
338338- read_chunk ()
339339- ) chunk_sources;
340340- Some true
341341- | `Not_found -> None)
342342- | None -> None
343343-344344-let put_chunk t ~url ~range ~data =
345345- if not t.enabled || not t.cache_range_requests then ()
346346- else
347347- match t.cacheio with
348348- | Some cache ->
349349- let key = Uri.to_string url in
350350- let cacheio_range = Range.to_cacheio_range range ~total_size:Int64.max_int in
351351- Eio.Switch.run @@ fun _sw ->
352352- let source = Eio.Flow.string_source data in
353353- Cacheio.put_chunk cache ~key ~range:cacheio_range ~source ()
354354- | None ->
355355- Log.debug (fun m -> m "Cannot cache chunk for %s: no cacheio backend"
356356- (Uri.to_string url))
357357-358358-let has_complete t ~url ~total_size =
359359- if not t.enabled then false
360360- else
361361- match t.cacheio with
362362- | Some cache ->
363363- let key = Uri.to_string url in
364364- Cacheio.has_complete_chunks cache ~key ~total_size
365365- | None -> false
366366-367367-let missing_ranges t ~url ~total_size =
368368- if not t.enabled then
369369- [{ Range.start = 0L; end_ = Some (Int64.pred total_size) }]
370370- else
371371- match t.cacheio with
372372- | Some cache ->
373373- let key = Uri.to_string url in
374374- let cacheio_ranges = Cacheio.missing_ranges cache ~key ~total_size in
375375- List.map (fun r ->
376376- { Range.start = Cacheio.Range.start r;
377377- end_ = Some (Cacheio.Range.end_ r) }
378378- ) cacheio_ranges
379379- | None ->
380380- [{ Range.start = 0L; end_ = Some (Int64.pred total_size) }]
381381-382382-let coalesce_chunks t ~url =
383383- if not t.enabled then false
384384- else
385385- match t.cacheio with
386386- | Some cache ->
387387- let key = Uri.to_string url in
388388- let promise = Cacheio.coalesce_chunks cache ~key ~verify:true () in
389389- (match Eio.Promise.await promise with
390390- | Ok () ->
391391- Log.info (fun m -> m "Successfully coalesced chunks for %s" key);
392392- true
393393- | Error exn ->
394394- Log.warn (fun m -> m "Failed to coalesce chunks for %s: %s"
395395- key (Printexc.to_string exn));
396396- false)
397397- | None -> false
398398-399399-let evict t ~url =
400400- if not t.enabled then ()
401401- else
402402- let key = make_cache_key ~method_:`GET ~url ~headers:(Cohttp.Header.init ()) in
403403- (match t.cacheio with
404404- | Some cache ->
405405- Cacheio.delete cache ~key:(key ^ ".meta");
406406- Cacheio.delete cache ~key:(key ^ ".body")
407407- | None -> ());
408408- Log.debug (fun m -> m "Evicting cache for %s" (Uri.to_string url));
409409- Hashtbl.remove t.memory_cache key
410410-411411-let clear t =
412412- Log.info (fun m -> m "Clearing entire cache");
413413- (match t.cacheio with
414414- | Some cache -> Cacheio.clear cache
415415- | None -> ());
416416- Hashtbl.clear t.memory_cache
417417-418418-module Stats = struct
419419- type cacheio_stats = {
420420- total_entries : int;
421421- total_bytes : int;
422422- expired_entries : int;
423423- pinned_entries : int;
424424- temporary_entries : int;
425425- }
426426-427427- type t = {
428428- memory_cache_entries : int;
429429- cache_backend : string;
430430- enabled : bool;
431431- cache_get_requests : bool;
432432- cache_range_requests : bool;
433433- cacheio_stats : cacheio_stats option;
434434- }
435435-436436- let make_cacheio_stats total_entries total_bytes expired_entries pinned_entries temporary_entries =
437437- { total_entries; total_bytes; expired_entries; pinned_entries; temporary_entries }
438438-439439- let make memory_cache_entries cache_backend enabled cache_get_requests cache_range_requests cacheio_stats =
440440- { memory_cache_entries; cache_backend; enabled; cache_get_requests; cache_range_requests; cacheio_stats }
441441-442442- let cacheio_stats_jsont =
443443- Jsont.Object.map ~kind:"CacheioStats" make_cacheio_stats
444444- |> Jsont.Object.mem "total_entries" Jsont.int ~enc:(fun t -> t.total_entries)
445445- |> Jsont.Object.mem "total_bytes" Jsont.int ~enc:(fun t -> t.total_bytes)
446446- |> Jsont.Object.mem "expired_entries" Jsont.int ~enc:(fun t -> t.expired_entries)
447447- |> Jsont.Object.mem "pinned_entries" Jsont.int ~enc:(fun t -> t.pinned_entries)
448448- |> Jsont.Object.mem "temporary_entries" Jsont.int ~enc:(fun t -> t.temporary_entries)
449449- |> Jsont.Object.finish
450450-451451- let t_jsont =
452452- Jsont.Object.map ~kind:"CacheStats" make
453453- |> Jsont.Object.mem "memory_cache_entries" Jsont.int ~enc:(fun t -> t.memory_cache_entries)
454454- |> Jsont.Object.mem "cache_backend" Jsont.string ~enc:(fun t -> t.cache_backend)
455455- |> Jsont.Object.mem "enabled" Jsont.bool ~enc:(fun t -> t.enabled)
456456- |> Jsont.Object.mem "cache_get_requests" Jsont.bool ~enc:(fun t -> t.cache_get_requests)
457457- |> Jsont.Object.mem "cache_range_requests" Jsont.bool ~enc:(fun t -> t.cache_range_requests)
458458- |> Jsont.Object.opt_mem "cacheio_stats" cacheio_stats_jsont ~enc:(fun t -> t.cacheio_stats)
459459- |> Jsont.Object.finish
460460-461461- let to_string t =
462462- match Jsont_bytesrw.encode_string' ~format:Jsont.Indent t_jsont t with
463463- | Ok s -> s
464464- | Error e ->
465465- let msg = Jsont.Error.to_string e in
466466- failwith (Printf.sprintf "Failed to encode stats: %s" msg)
467467-end
468468-469469-let stats t =
470470- let cacheio_stats =
471471- match t.cacheio with
472472- | Some cache ->
473473- let stats = Cacheio.stats cache in
474474- Some (Stats.make_cacheio_stats
475475- (Cacheio.Stats.entry_count stats)
476476- (Int64.to_int (Cacheio.Stats.total_size stats))
477477- (Cacheio.Stats.expired_count stats)
478478- (Cacheio.Stats.pinned_count stats)
479479- (Cacheio.Stats.temporary_count stats))
480480- | None -> None
481481- in
482482- Stats.make
483483- (Hashtbl.length t.memory_cache)
484484- (if Option.is_some t.cacheio then "cacheio" else "memory")
485485- t.enabled
486486- t.cache_get_requests
487487- t.cache_range_requests
488488- cacheio_stats
···1414module Status = Status
1515module Error = Error
1616module Retry = Retry
1717-module Cache = Cache
18171918(* Note: RNG initialization should be done by the application using
2019 Mirage_crypto_rng_unix.initialize before calling Eio_main.run.
···4140 retry : Retry.config option;
4241 persist_cookies : bool;
4342 xdg : Xdge.t option;
4444- cache : Cache.t option;
45434644 (* Statistics - mutable for tracking across all derived sessions *)
4745 mutable requests_made : int;
···6664 ?(connection_lifetime = 300.0)
6765 ?retry
6866 ?(persist_cookies = false)
6969- ?(enable_cache = false)
7067 ?xdg
7168 env =
72697370 let clock = env#clock in
7471 let net = env#net in
75727676- let xdg = match xdg, persist_cookies || enable_cache with
7373+ let xdg = match xdg, persist_cookies with
7774 | Some x, _ -> Some x
7875 | None, true -> Some (Xdge.create env#fs "requests")
7976 | None, false -> None
···135132 Cookeio.create ()
136133 in
137134138138- let cache = match enable_cache, xdg with
139139- | true, Some xdg_ctx ->
140140- let cache_dir = Xdge.cache_dir xdg_ctx in
141141- Some (Cache.create ~sw ~enabled:true ~cache_dir:(Some cache_dir) ())
142142- | true, None ->
143143- (* Memory-only cache when no XDG available *)
144144- Some (Cache.create ~sw ~enabled:true ~cache_dir:None ())
145145- | false, _ -> None
146146- in
147147-148135 {
149136 sw;
150137 clock;
···163150 retry;
164151 persist_cookies;
165152 xdg;
166166- cache;
167153 requests_made = 0;
168154 total_time = 0.0;
169155 retries_count = 0;
···254240 | Some b -> Body.Private.to_string b
255241 in
256242257257- (* Check cache for GET and HEAD requests when body is not present *)
258258- let cached_response = match t.cache, method_, body with
259259- | Some cache, (`GET | `HEAD), None ->
260260- Log.debug (fun m -> m "Checking cache for %s request to %s" method_str url);
261261- let headers_cohttp = Cohttp.Header.of_list (Headers.to_list headers) in
262262- Cache.get cache ~method_ ~url:uri ~headers:headers_cohttp
263263- | _ -> None
264264- in
265265-266266- let response = match cached_response with
267267- | Some cached ->
268268- Log.info (fun m -> m "Cache HIT for %s request to %s" method_str url);
269269- (* Convert cached response to Response.t *)
270270- let status = Cohttp.Code.code_of_status cached.Cache.status in
271271- let resp_headers = Headers.of_list (Cohttp.Header.to_list cached.Cache.headers) in
272272- let body_flow = Eio.Flow.string_source cached.Cache.body in
273273- Response.Private.make ~sw:t.sw ~status ~headers:resp_headers ~body:body_flow ~url ~elapsed:0.0
274274- | None ->
275275- Log.info (fun m -> m "Cache MISS or not applicable for %s request to %s" method_str url);
243243+ let response =
276244277245 (* Execute request with redirect handling *)
278246 let rec make_with_redirects url_to_fetch redirects_left =
···353321354322 let elapsed = Unix.gettimeofday () -. start_time in
355323 Log.info (fun m -> m "Request completed in %.3f seconds" elapsed);
356356-357357- (* Store in cache if successful and caching enabled *)
358358- (match t.cache with
359359- | Some cache when final_status >= 200 && final_status < 300 ->
360360- Log.debug (fun m -> m "Storing response in cache for %s" url);
361361- let status = Cohttp.Code.status_of_code final_status in
362362- let resp_headers_cohttp = Cohttp.Header.of_list (Headers.to_list final_headers) in
363363- let headers_cohttp = Cohttp.Header.of_list (Headers.to_list headers) in
364364- Cache.put cache ~method_ ~url:uri ~request_headers:headers_cohttp
365365- ~status ~headers:resp_headers_cohttp ~body:final_body_str
366366- | _ -> ());
367324368325 (* Create a flow from the body string *)
369326 let body_flow = Eio.Flow.string_source final_body_str in
+4-6
stack/requests/lib/requests.mli
···155155 ?connection_lifetime:float ->
156156 ?retry:Retry.config ->
157157 ?persist_cookies:bool ->
158158- ?enable_cache:bool ->
159158 ?xdg:Xdge.t ->
160159 < clock: 'clock Eio.Resource.t; net: 'net Eio.Resource.t; fs: Eio.Fs.dir_ty Eio.Path.t; .. > ->
161160 ('clock Eio.Resource.t, 'net Eio.Resource.t) t
···178177 @param connection_lifetime Max lifetime of any pooled connection (default: 300s)
179178 @param retry Retry configuration for failed requests
180179 @param persist_cookies Whether to persist cookies to disk (default: false)
181181- @param enable_cache Whether to enable HTTP caching (default: false)
182182- @param xdg XDG directory context for cookies/cache (required if persist_cookies or enable_cache)
180180+ @param xdg XDG directory context for cookies (required if persist_cookies=true)
181181+182182+ {b Note:} HTTP caching has been disabled for simplicity. See CACHEIO.md for integration notes
183183+ if you need to restore caching functionality in the future.
183184*)
184185185186(** {2 Configuration Management} *)
···569570570571(** Timeout configuration for requests *)
571572module Timeout = Timeout
572572-573573-(** HTTP caching with cache control and range request support *)
574574-module Cache = Cache
575573576574(** {2 Logging} *)
577575