A batteries included HTTP/1.1 client in OCaml

tidy

+28 -36
+16 -11
lib/body.ml
··· 29 29 | None -> 30 30 (* Guess MIME type from filename if available *) 31 31 let path = Eio.Path.native_exn file in 32 + let mime_map = [ 33 + (".json", Mime.json); 34 + (".html", Mime.html); 35 + (".xml", Mime.xml); 36 + (".txt", Mime.text); 37 + ] in 32 38 let guessed = 33 - if String.ends_with ~suffix:".json" path then Mime.json 34 - else if String.ends_with ~suffix:".html" path then Mime.html 35 - else if String.ends_with ~suffix:".xml" path then Mime.xml 36 - else if String.ends_with ~suffix:".txt" path then Mime.text 37 - else Mime.octet_stream 39 + List.find_map (fun (suffix, mime_type) -> 40 + if String.ends_with ~suffix path then Some mime_type else None 41 + ) mime_map 42 + |> Option.value ~default:Mime.octet_stream 38 43 in 39 44 Log.debug (fun m -> m "Guessed MIME type %s for file %s" (Mime.to_string guessed) path); 40 45 guessed ··· 42 47 Log.debug (fun m -> m "Creating file body from %s with MIME type %s" 43 48 (Eio.Path.native_exn file) (Mime.to_string mime)); 44 49 File { file; mime } 50 + 51 + let json_encoding_error e = 52 + let msg = Jsont.Error.to_string e in 53 + failwith (Printf.sprintf "Failed to encode JSON: %s" msg) 45 54 46 55 (* For simple JSON encoding, we just take a Jsont.json value and encode it *) 47 56 let json (json_value : Jsont.json) = 48 57 let content = match Jsont_bytesrw.encode_string' ~format:Jsont.Minify Jsont.json json_value with 49 58 | Ok s -> s 50 - | Error e -> 51 - let msg = Jsont.Error.to_string e in 52 - failwith (Printf.sprintf "Failed to encode JSON: %s" msg) 59 + | Error e -> json_encoding_error e 53 60 in 54 61 String { content; mime = Mime.json } 55 62 ··· 78 85 (* Encode the entire JSON value to string with minified format *) 79 86 let content = match Jsont_bytesrw.encode_string' ~format:Jsont.Minify Jsont.json json_value with 80 87 | Ok s -> s 81 - | Error e -> 82 - let msg = Jsont.Error.to_string e in 83 - failwith (Printf.sprintf "Failed to encode JSON stream: %s" msg) 88 + | Error e -> json_encoding_error e 84 89 in 85 90 let t = { Json_stream_source.content; offset = 0 } in 86 91 let ops = Eio.Flow.Pi.source (module Json_stream_source) in
+10 -22
lib/one.ml
··· 78 78 (tcp_flow :> [`Close | `Flow | `R | `Shutdown | `W] Eio.Resource.t) 79 79 in 80 80 81 - match timeout with 82 - | Some t -> 83 - let timeout_seconds = Timeout.total t in 84 - (match timeout_seconds with 85 - | Some seconds -> 86 - Log.debug (fun m -> m "Setting connection timeout: %.2f seconds" seconds); 87 - Eio.Time.with_timeout_exn clock seconds connect_fn 88 - | None -> connect_fn ()) 81 + match Option.bind timeout Timeout.total with 82 + | Some seconds -> 83 + Log.debug (fun m -> m "Setting connection timeout: %.2f seconds" seconds); 84 + Eio.Time.with_timeout_exn clock seconds connect_fn 89 85 | None -> connect_fn () 90 86 91 87 (* Main request implementation - completely stateless *) ··· 101 97 let headers = Option.value headers ~default:Headers.empty in 102 98 103 99 (* Apply auth *) 104 - let headers = match auth with 105 - | Some a -> 106 - Log.debug (fun m -> m "Applying authentication"); 107 - Auth.apply a headers 108 - | None -> headers 100 + let headers = Option.fold ~none:headers auth ~some:(fun a -> 101 + Log.debug (fun m -> m "Applying authentication"); 102 + Auth.apply a headers) 109 103 in 110 104 111 105 (* Add content type from body *) 112 - let headers = match body with 113 - | Some b -> (match Body.content_type b with 114 - | Some mime -> Headers.content_type mime headers 115 - | None -> headers) 116 - | None -> headers 106 + let headers = Option.bind body Body.content_type 107 + |> Option.fold ~none:headers ~some:(fun mime -> Headers.content_type mime headers) 117 108 in 118 109 119 110 (* Convert body to string for sending *) 120 - let request_body_str = match body with 121 - | None -> "" 122 - | Some b -> Body.Private.to_string b 123 - in 111 + let request_body_str = Option.fold ~none:"" ~some:Body.Private.to_string body in 124 112 125 113 (* Execute request with redirects *) 126 114 let rec make_with_redirects url_to_fetch redirects_left =
+2 -3
lib/response.ml
··· 39 39 let header name t = Headers.get name t.headers 40 40 41 41 let content_type t = 42 - match Headers.get "content-type" t.headers with 43 - | None -> None 44 - | Some ct -> Some (Mime.of_string ct) 42 + Headers.get "content-type" t.headers 43 + |> Option.map Mime.of_string 45 44 46 45 let content_length t = 47 46 match Headers.get "content-length" t.headers with