My agentic slop goes here. Not intended for anyone else!
1(** JMAP HTTP Client - Eio Implementation *) 2 3type t = { 4 session_url : string; 5 get_request : timeout:Requests.Timeout.t -> string -> Requests.Response.t; 6 post_request : timeout:Requests.Timeout.t -> headers:Requests.Headers.t -> body:Requests.Body.t -> string -> Requests.Response.t; 7 conn : Jmap_connection.t; 8 session : Jmap_core.Session.t option ref; 9} 10 11let create ~sw ~env ~conn ~session_url () = 12 let requests_session = Requests.create ~sw env in 13 14 (* Set authentication if configured *) 15 (match Jmap_connection.auth conn with 16 | Some (Jmap_connection.Bearer token) -> 17 Requests.set_auth requests_session (Requests.Auth.bearer ~token) 18 | Some (Jmap_connection.Basic (user, pass)) -> 19 Requests.set_auth requests_session (Requests.Auth.basic ~username:user ~password:pass) 20 | None -> ()); 21 22 (* Set user agent *) 23 let config = Jmap_connection.config conn in 24 Requests.set_default_header requests_session "User-Agent" 25 (Jmap_connection.user_agent config); 26 27 { session_url; 28 get_request = (fun ~timeout url -> Requests.get requests_session ~timeout url); 29 post_request = (fun ~timeout ~headers ~body url -> Requests.post requests_session ~timeout ~headers ~body url); 30 conn; 31 session = ref None } 32 33let fetch_session t = 34 let config = Jmap_connection.config t.conn in 35 let timeout = Requests.Timeout.create ~total:(Jmap_connection.timeout config) () in 36 37 let response = t.get_request ~timeout t.session_url in 38 39 if not (Requests.Response.ok response) then 40 failwith (Printf.sprintf "Failed to fetch session: HTTP %d" 41 (Requests.Response.status_code response)); 42 43 let body_str = 44 let buf = Buffer.create 4096 in 45 Eio.Flow.copy (Requests.Response.body response) (Eio.Flow.buffer_sink buf); 46 Buffer.contents buf 47 in 48 49 let session = Jmap_core.Session.Parser.of_string body_str in 50 t.session := Some session; 51 session 52 53let get_session t = 54 match !(t.session) with 55 | Some s -> s 56 | None -> fetch_session t 57 58let call t req = 59 let session = get_session t in 60 let api_url = Jmap_core.Session.api_url session in 61 let config = Jmap_connection.config t.conn in 62 let timeout = Requests.Timeout.create ~total:(Jmap_connection.timeout config) () in 63 64 (* Convert request to JSON *) 65 let req_json = Jmap_core.Request.to_json req in 66 67 (* Set up headers *) 68 let headers = Requests.Headers.(empty 69 |> set "Accept" "application/json") in 70 71 (* Make POST request with JSON body *) 72 let body = Requests.Body.json req_json in 73 let response = t.post_request ~timeout ~headers ~body api_url in 74 75 (* Read response body first *) 76 let body_str = 77 let buf = Buffer.create 4096 in 78 Eio.Flow.copy (Requests.Response.body response) (Eio.Flow.buffer_sink buf); 79 Buffer.contents buf 80 in 81 82 if not (Requests.Response.ok response) then ( 83 Printf.eprintf "JMAP API call failed: HTTP %d\n" (Requests.Response.status_code response); 84 Printf.eprintf "Response body: %s\n%!" body_str; 85 failwith (Printf.sprintf "JMAP API call failed: HTTP %d" 86 (Requests.Response.status_code response)) 87 ); 88 89 Jmap_core.Response.Parser.of_string body_str 90 91let upload t ~account_id ~content_type:ct data = 92 let session = get_session t in 93 let upload_url = Jmap_core.Session.upload_url session in 94 let config = Jmap_connection.config t.conn in 95 let timeout = Requests.Timeout.create ~total:(Jmap_connection.timeout config) () in 96 97 (* Replace {accountId} placeholder *) 98 let upload_url = Str.global_replace (Str.regexp_string "{accountId}") 99 account_id upload_url in 100 101 let mime = Requests.Mime.of_string ct in 102 let headers = Requests.Headers.empty in 103 104 let body = Requests.Body.of_string mime data in 105 let response = t.post_request ~timeout ~headers ~body upload_url in 106 107 if not (Requests.Response.ok response) then 108 failwith (Printf.sprintf "Upload failed: HTTP %d" 109 (Requests.Response.status_code response)); 110 111 let body_str = 112 let buf = Buffer.create 4096 in 113 Eio.Flow.copy (Requests.Response.body response) (Eio.Flow.buffer_sink buf); 114 Buffer.contents buf 115 in 116 117 let json = Ezjsonm.value_from_string body_str in 118 Jmap_core.Binary.Upload.of_json json 119 120let download t ~account_id ~blob_id ~name = 121 let session = get_session t in 122 let download_url = Jmap_core.Session.download_url session in 123 let config = Jmap_connection.config t.conn in 124 let timeout = Requests.Timeout.create ~total:(Jmap_connection.timeout config) () in 125 126 (* Replace placeholders *) 127 let download_url = download_url 128 |> Str.global_replace (Str.regexp_string "{accountId}") account_id 129 |> Str.global_replace (Str.regexp_string "{blobId}") blob_id 130 |> Str.global_replace (Str.regexp_string "{name}") name in 131 132 let response = t.get_request ~timeout download_url in 133 134 if not (Requests.Response.ok response) then 135 failwith (Printf.sprintf "Download failed: HTTP %d" 136 (Requests.Response.status_code response)); 137 138 let buf = Buffer.create 4096 in 139 Eio.Flow.copy (Requests.Response.body response) (Eio.Flow.buffer_sink buf); 140 Buffer.contents buf