(ns ird.p2p.impl.http
"HTTP over Node's global fetch. `fetch-http` is the default :http-fn: a
request map {:method :url :headers :body :timeout-ms} → channel of
{:status <int> :headers {...} :body <string>} or an Error value (network
failure, timeout). Tests inject their own :http-fn instead.
Internal interop namespace."
(:require [clojure.string :as str]
[ird.p2p.impl.bytes :as bytes]
[ird.p2p.impl.node :as node]))
(defn fetch-http
"The default :http-fn. :body may be a string or standard base64 tagged as
{:b64 \"...\"} for raw octets."
[{:keys [method url headers body timeout-ms]}]
(let [init (js-obj "method" (str/upper-case (name (or method :get)))
"headers" (clj->js (or headers {})))]
(when (some? body)
(aset init "body" (if (and (map? body) (:b64 body))
(bytes/b64->u8 (:b64 body))
body)))
(when timeout-ms (aset init "signal" (js/AbortSignal.timeout timeout-ms)))
(node/promise->chan
(.then (js/fetch url init)
(fn [resp]
(.then (.text resp)
(fn [text]
(let [hs (atom {})]
(.forEach (.-headers resp) (fn [v k] (swap! hs assoc (str/lower-case k) v)))
{:status (.-status resp) :headers @hs :body text}))))))))