-
Notifications
You must be signed in to change notification settings - Fork 0
/
request.clj
74 lines (65 loc) · 2.72 KB
/
request.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
(ns scroll.request
(:require [clojure.tools.logging :as log]
[clojure.string :as str]
[org.httpkit.client :as http]
[jsonista.core :as json])
(:import (javax.net.ssl SSLEngine SSLParameters SNIHostName)
(java.net URI)
(java.util Base64)))
(def default-exponential-backoff-params
{:time 1000
:rate 2
:max 60000
:p? identity})
(defn exponential-backoff
([f] (exponential-backoff f default-exponential-backoff-params))
([f {:keys [time rate max p?] :as opts}]
(if (>= time max) ;; we're over budget, just call f
(f)
(try
(f)
(catch Throwable t
(log/errorf "Exponential backoff failed with '%s'" t)
(if (p? t)
(do
(Thread/sleep time)
(exponential-backoff f (assoc opts :time (* time rate))))
(throw t)))))))
(defn sni-configure
[^SSLEngine ssl-engine ^URI uri]
(let [^SSLParameters ssl-params (.getSSLParameters ssl-engine)]
(.setServerNames ssl-params [(SNIHostName. (.getHost uri))])
(.setSSLParameters ssl-engine ssl-params)))
(def client (delay (http/make-client {:ssl-configurer sni-configure})))
(def default-headers {"Content-Type" "application/json"})
(defn authorization-token []
(let [username (System/getenv "ELASTIC_USERNAME")
password (System/getenv "ELASTIC_PASSWORD")]
(when (and username password)
(.encodeToString (Base64/getEncoder) (.getBytes (str username ":" password))))))
(defn prepare-headers [headers _]
; basic authorization
; https://www.elastic.co/guide/en/elasticsearch/reference/current/http-clients.html
(if-let [auth-token (authorization-token)]
(assoc headers "Authorization" (str "Basic " auth-token))
headers))
(defn execute-request [{:keys [url body opts method headers]}]
(exponential-backoff
(fn []
(let [{:keys [status body error] :as resp}
@(http/request
(cond-> {:method (or method :get)
:client @client
:url url
:headers (prepare-headers (or headers default-headers) opts)}
(not (nil? body)) (assoc :body (json/write-value-as-string body))))]
(when error (throw (Exception. (str error))))
(when-not (str/blank? body)
(let [{:keys [error] :as decoded-body}
(json/read-value body (json/object-mapper
{:decode-key-fn (get opts :keywordize?)}))]
(when error (throw (Exception. (str error))))
(if (<= 200 status 299)
decoded-body
(throw (Exception. (format "Response exception: %s" resp))))))))
(or opts default-exponential-backoff-params)))