let src = Logs.Src.create "requests.response" ~doc:"HTTP Response" module Log = (val Logs.src_log src : Logs.LOG) type t = { status : int; headers : Headers.t; body : Eio.Flow.source_ty Eio.Resource.t; url : string; elapsed : float; mutable closed : bool; } let make ~sw ~status ~headers ~body ~url ~elapsed = Log.debug (fun m -> m "Creating response: status=%d url=%s elapsed=%.3fs" status url elapsed); let response = { status; headers; body; url; elapsed; closed = false } in (* Register cleanup with switch *) Eio.Switch.on_release sw (fun () -> if not response.closed then begin Log.debug (fun m -> m "Auto-closing response for %s via switch" url); response.closed <- true; (* TODO Body cleanup is handled by the underlying HTTP library but test this *) end ); response let status t = Status.of_int t.status let status_code t = t.status let ok t = Status.is_success (Status.of_int t.status) let headers t = t.headers let header name t = Headers.get name t.headers let content_type t = match Headers.get "content-type" t.headers with | None -> None | Some ct -> Some (Mime.of_string ct) let content_length t = match Headers.get "content-length" t.headers with | None -> None | Some len -> try Some (Int64.of_string len) with _ -> None let location t = Headers.get "location" t.headers let url t = t.url let elapsed t = t.elapsed let body t = if t.closed then failwith "Response has been closed" else t.body (* Pretty printers *) let pp ppf t = Format.fprintf ppf "@[Response:@,\ status: %a@,\ url: %s@,\ elapsed: %.3fs@,\ headers: @[%a@]@]" Status.pp (Status.of_int t.status) t.url t.elapsed Headers.pp_brief t.headers let pp_detailed ppf t = Format.fprintf ppf "@[Response:@,\ status: %a@,\ url: %s@,\ elapsed: %.3fs@,\ @[%a@]@]" Status.pp_hum (Status.of_int t.status) t.url t.elapsed Headers.pp t.headers (* Private module *) module Private = struct let make = make end