|
| 1 | +(ns sentry-tiny.impl.http |
| 2 | + (:require [clojure.string :as str]) |
| 3 | + (:import (java.net.http HttpRequest |
| 4 | + HttpResponse |
| 5 | + HttpClient |
| 6 | + HttpClient$Version |
| 7 | + HttpRequest$Builder |
| 8 | + HttpRequest$BodyPublishers |
| 9 | + HttpResponse$BodyHandlers) |
| 10 | + (java.util.function Supplier) |
| 11 | + (java.time Duration) |
| 12 | + (java.net URI) |
| 13 | + (java.io InputStream))) |
| 14 | + |
| 15 | +(def ^:private ^HttpClient default-client |
| 16 | + (delay (HttpClient/newHttpClient))) |
| 17 | + |
| 18 | +(def ^:private bh->string (HttpResponse$BodyHandlers/ofString)) |
| 19 | +(def ^:private bh->istream (HttpResponse$BodyHandlers/ofInputStream)) |
| 20 | +(def ^:private bh->bytes (HttpResponse$BodyHandlers/ofByteArray)) |
| 21 | + |
| 22 | +(def ^:private convert-headers-xf |
| 23 | + (mapcat |
| 24 | + (fn [[k v :as p]] |
| 25 | + (if (sequential? v) |
| 26 | + (interleave (repeat k) v) |
| 27 | + p)))) |
| 28 | + |
| 29 | +(def ^:private bytes-class |
| 30 | + (Class/forName "[B")) |
| 31 | + |
| 32 | +(defn- convert-body-handler [mode] |
| 33 | + (case mode |
| 34 | + nil bh->string |
| 35 | + :string bh->string |
| 36 | + :input-stream bh->istream |
| 37 | + :byte-array bh->bytes)) |
| 38 | + |
| 39 | +(defn- version-enum->version-keyword [^HttpClient$Version version] |
| 40 | + (case (.name version) |
| 41 | + "HTTP_1_1" :http1.1 |
| 42 | + "HTTP_2" :http2)) |
| 43 | + |
| 44 | +(defn- version-keyword->version-enum [version] |
| 45 | + (case version |
| 46 | + :http1.1 HttpClient$Version/HTTP_1_1 |
| 47 | + :http2 HttpClient$Version/HTTP_2)) |
| 48 | + |
| 49 | +(defn- method-keyword->str [method] |
| 50 | + (str/upper-case (name method))) |
| 51 | + |
| 52 | +(defn- convert-timeout [t] |
| 53 | + (if (integer? t) |
| 54 | + (Duration/ofMillis t) |
| 55 | + t)) |
| 56 | + |
| 57 | +(defn- input-stream-supplier [s] |
| 58 | + (reify Supplier |
| 59 | + (get [this] s))) |
| 60 | + |
| 61 | +(defn- convert-body-publisher [body] |
| 62 | + (cond |
| 63 | + (nil? body) |
| 64 | + (HttpRequest$BodyPublishers/noBody) |
| 65 | + |
| 66 | + (string? body) |
| 67 | + (HttpRequest$BodyPublishers/ofString body) |
| 68 | + |
| 69 | + (instance? InputStream body) |
| 70 | + (HttpRequest$BodyPublishers/ofInputStream (input-stream-supplier body)) |
| 71 | + |
| 72 | + (instance? bytes-class body) |
| 73 | + (HttpRequest$BodyPublishers/ofByteArray body))) |
| 74 | + |
| 75 | +(defn request-builder ^HttpRequest$Builder [opts] |
| 76 | + (let [{:keys [expect-continue? |
| 77 | + headers |
| 78 | + method |
| 79 | + timeout |
| 80 | + uri |
| 81 | + version |
| 82 | + body]} opts] |
| 83 | + (cond-> (HttpRequest/newBuilder) |
| 84 | + (some? expect-continue?) (.expectContinue expect-continue?) |
| 85 | + (seq headers) (.headers (into-array String (eduction convert-headers-xf headers))) |
| 86 | + method (.method (method-keyword->str method) (convert-body-publisher body)) |
| 87 | + timeout (.timeout (convert-timeout timeout)) |
| 88 | + uri (.uri (URI/create uri)) |
| 89 | + version (.version (version-keyword->version-enum version))))) |
| 90 | + |
| 91 | +(defn- build-request |
| 92 | + (^HttpRequest [] (.build (request-builder {}))) |
| 93 | + (^HttpRequest [req-map] (.build (request-builder req-map)))) |
| 94 | + |
| 95 | +(defn- response->map [^HttpResponse resp] |
| 96 | + {:status (.statusCode resp) |
| 97 | + :body (.body resp) |
| 98 | + :version (-> resp .version version-enum->version-keyword) |
| 99 | + :headers (into {} |
| 100 | + (map (fn [[k v]] [k (if (> (count v) 1) (vec v) (first v))])) |
| 101 | + (.map (.headers resp)))}) |
| 102 | + |
| 103 | +(defn- convert-request [req] |
| 104 | + (cond |
| 105 | + (map? req) (build-request req) |
| 106 | + (string? req) (build-request {:uri req}) |
| 107 | + (instance? HttpRequest req) req)) |
| 108 | + |
| 109 | +(defn send |
| 110 | + ([req] |
| 111 | + (send req {})) |
| 112 | + ([req {:keys [as client raw?] :as opts}] |
| 113 | + (let [^HttpClient client (or client @default-client) |
| 114 | + req' (convert-request req) |
| 115 | + resp (.send client req' (convert-body-handler as))] |
| 116 | + (if raw? resp (response->map resp))))) |
0 commit comments