|
1 | 1 | (ns aleph.http.clj-http.util
|
2 | 2 | (:require
|
3 | 3 | [aleph.http :as http]
|
| 4 | + [aleph.http.client-middleware :as aleph.mid] |
4 | 5 | [clj-commons.byte-streams :as bs]
|
5 | 6 | [clj-http.core :as clj-http]
|
| 7 | + [clj-http.client] |
6 | 8 | [clojure.set :as set]
|
7 |
| - [clojure.test :refer :all]) |
| 9 | + [clojure.string :as str] |
| 10 | + [clojure.test :refer :all] |
| 11 | + [clojure.tools.logging :as log]) |
8 | 12 | (:import
|
9 | 13 | (java.io ByteArrayInputStream
|
10 | 14 | ByteArrayOutputStream
|
|
41 | 45 | aleph-common-headers (select-keys aleph-headers ks-intersection)]
|
42 | 46 | (is (= clj-http-common-headers aleph-common-headers)))))
|
43 | 47 |
|
44 |
| -(defn is-input-stream= |
45 |
| - "Are the two body InputStreams equal? |
| 48 | +(defn bodies= |
| 49 | + "Are the two bodies equal? clj-http's client/request fn coerces to strings by default, |
| 50 | + while the core/request leaves the body an InputStream. |
| 51 | + Aleph, in keeping with it's stream-based nature, leaves as an InputStream by default. |
46 | 52 |
|
47 |
| - Returns a new ByteArrayInputStream based on the consumed original" |
48 |
| - [^InputStream clj-http-body ^InputStream aleph-body] |
| 53 | + If an InputStream, returns a new ByteArrayInputStream based on the consumed original" |
| 54 | + [clj-http-body ^InputStream aleph-body] |
49 | 55 | (if clj-http-body
|
50 |
| - (do |
51 |
| - (is (some? aleph-body) "Why is aleph body nil? It should be an empty InputStream for now...") |
52 |
| - (let [baos (ByteArrayOutputStream.)] |
53 |
| - (.transferTo clj-http-body baos) ; not avail until JDK 9 |
54 |
| - |
55 |
| - (let [clj-http-body-bytes (.toByteArray baos)] |
56 |
| - (is (= (count clj-http-body-bytes) |
57 |
| - (.available aleph-body))) |
58 |
| - (is (bs/bytes= clj-http-body-bytes aleph-body)) |
59 |
| - |
60 |
| - (proxy [FilterInputStream] |
61 |
| - [^InputStream (ByteArrayInputStream. clj-http-body-bytes)] |
62 |
| - (close [] |
63 |
| - (.close clj-http-body) |
64 |
| - (proxy-super close)))))) |
| 56 | + (condp instance? clj-http-body |
| 57 | + InputStream |
| 58 | + (do |
| 59 | + (is (some? aleph-body) "Why is aleph body nil? It should be an empty InputStream for now...") |
| 60 | + (let [baos (ByteArrayOutputStream.)] |
| 61 | + (.transferTo clj-http-body baos) ; not avail until JDK 9 |
| 62 | + |
| 63 | + (let [clj-http-body-bytes (.toByteArray baos)] |
| 64 | + (is (= (count clj-http-body-bytes) |
| 65 | + (.available aleph-body))) |
| 66 | + (is (bs/bytes= clj-http-body-bytes aleph-body)) |
| 67 | + |
| 68 | + (proxy [FilterInputStream] |
| 69 | + [^InputStream (ByteArrayInputStream. clj-http-body-bytes)] |
| 70 | + (close [] |
| 71 | + (.close clj-http-body) |
| 72 | + (proxy-super close)))))) |
| 73 | + |
| 74 | + (do |
| 75 | + (is (bs/bytes= clj-http-body aleph-body)) |
| 76 | + clj-http-body)) |
65 | 77 | (do
|
66 | 78 | (is (= clj-http-body aleph-body))
|
67 | 79 | clj-http-body)))
|
68 | 80 |
|
69 |
| -(defn request |
70 |
| - "Modified version of original, that also sends request via Aleph, and |
71 |
| - tests the responses for equality." |
72 |
| - ([req] |
73 |
| - (request req nil nil)) |
74 |
| - ([req respond raise] |
75 |
| - (if (or respond raise) |
76 |
| - ;; do not attempt to compare when using async clj-http...for now |
77 |
| - (let [ring-map (merge base-req req)] |
78 |
| - (clj-http/request ring-map respond raise)) |
79 |
| - |
80 |
| - (let [clj-http-ring-map (merge base-req req) |
81 |
| - aleph-ring-map (merge base-req req {:pool no-middleware-pool}) |
82 |
| - clj-http-resp (clj-http/request clj-http-ring-map) |
83 |
| - aleph-resp @(http/request aleph-ring-map)] |
84 |
| - (is (= (:status clj-http-resp) (:status aleph-resp))) |
85 |
| - (is-headers= (:headers clj-http-resp) (:headers aleph-resp)) |
86 |
| - (let [new-clj-http-body (is-input-stream= (:body clj-http-resp) (:body aleph-resp))] |
87 |
| - (assoc clj-http-resp :body new-clj-http-body)))))) |
| 81 | + |
| 82 | +(defn- defined-middleware |
| 83 | + "Returns a set of symbols beginning with `wrap-` in the ns" |
| 84 | + [ns] |
| 85 | + (->> (ns-publics ns) |
| 86 | + keys |
| 87 | + (map str) |
| 88 | + (filter #(str/starts-with? % "wrap-")) |
| 89 | + (map symbol) |
| 90 | + set)) |
| 91 | + |
| 92 | +(defn- aleph-test-conn-pool |
| 93 | + "clj-http middleware is traditional fn-based middleware, using a 3-arity version to handle async. |
| 94 | +
|
| 95 | + Aleph usually uses a more async-friendly interceptor-style, where the middleware transforms the request maps, |
| 96 | + but does nothing about calling the next fn in the chain. |
| 97 | +
|
| 98 | + Unfortunately, a couple middleware cannot be converted to interceptor-style, complicating things." |
| 99 | + [middleware-list] |
| 100 | + (let [missing-midw (set/difference |
| 101 | + (defined-middleware 'clj-http.client) |
| 102 | + (defined-middleware 'aleph.http.client-middleware))] |
| 103 | + (when-not (seq missing-midw) |
| 104 | + (println "clj-http is using middleware that aleph lacks:") |
| 105 | + (prn missing-midw) |
| 106 | + (log/warn "clj-http is using middleware that aleph lacks" |
| 107 | + :missing-middleware missing-midw))) |
| 108 | + (let [non-interceptor-middleware (set aleph.mid/default-client-middleware) |
| 109 | + client-middleware (cond-> [] |
| 110 | + (some #{clj-http.client/wrap-exceptions} middleware-list) |
| 111 | + (conj aleph.mid/wrap-exceptions) |
| 112 | + |
| 113 | + (some #{clj-http.client/wrap-request-timing} middleware-list) |
| 114 | + (conj aleph.mid/wrap-request-timing)) |
| 115 | + middleware-list' (->> middleware-list |
| 116 | + (map (fn [midw] |
| 117 | + (-> midw |
| 118 | + class |
| 119 | + str |
| 120 | + (str/split #"\$") |
| 121 | + peek |
| 122 | + (str/replace "_" "-") |
| 123 | + (->> (symbol "aleph.http.client-middleware")) |
| 124 | + requiring-resolve))) |
| 125 | + (filter some?) |
| 126 | + (map var-get) |
| 127 | + (remove non-interceptor-middleware) |
| 128 | + vec)] |
| 129 | + ;;(println "Client-based middleware:") |
| 130 | + ;;(prn client-middleware) |
| 131 | + ;;(println "Regular middleware:") |
| 132 | + ;;(prn middleware-list') |
| 133 | + (http/connection-pool {:middleware #(aleph.mid/wrap-request % client-middleware middleware-list')}))) |
| 134 | + |
| 135 | + |
| 136 | +(defn- print-middleware-list |
| 137 | + [middleware-list] |
| 138 | + (prn (mapv (fn [midw] |
| 139 | + (-> midw |
| 140 | + class |
| 141 | + str |
| 142 | + (str/split #"\$") |
| 143 | + peek |
| 144 | + (str/replace "_" "-") |
| 145 | + symbol)) |
| 146 | + middleware-list))) |
| 147 | + |
| 148 | +(defn make-request |
| 149 | + "Need to switch between clj-http's core/request and client/request. |
| 150 | +
|
| 151 | + Modified version of original request fns, that also sends requests |
| 152 | + via Aleph, and tests the responses for equality." |
| 153 | + [clj-http-request {:keys [using-middleware?]}] |
| 154 | + (fn compare-request |
| 155 | + ([req] |
| 156 | + (compare-request req nil nil)) |
| 157 | + ([req respond raise] |
| 158 | + (if (or respond raise) |
| 159 | + ;; do not attempt to compare when using async clj-http...for now |
| 160 | + (let [ring-map (merge base-req req)] |
| 161 | + (clj-http-request ring-map respond raise)) |
| 162 | + |
| 163 | + (let [clj-http-ring-map (merge base-req req) |
| 164 | + ;;_ (prn clj-http-ring-map) |
| 165 | + clj-http-middleware (if using-middleware? clj-http.client/*current-middleware* []) |
| 166 | + ;;_ (print-middleware-list clj-http.client/*current-middleware*) |
| 167 | + aleph-ring-map (merge base-req req {:pool (aleph-test-conn-pool clj-http-middleware)}) |
| 168 | + ;;_ (prn aleph-ring-map) |
| 169 | + clj-http-resp (clj-http-request clj-http-ring-map) |
| 170 | + aleph-resp @(http/request aleph-ring-map)] |
| 171 | + (is (= (:status clj-http-resp) (:status aleph-resp))) |
| 172 | + |
| 173 | + |
| 174 | + #_(when (not= (:status clj-http-resp) (:status aleph-resp)) |
| 175 | + (println "clj-http req:") |
| 176 | + (prn clj-http-ring-map) |
| 177 | + (println) |
| 178 | + (println "clj-http resp:") |
| 179 | + (prn clj-http-resp) |
| 180 | + (println) |
| 181 | + (println) |
| 182 | + (println "aleph req:") |
| 183 | + (prn aleph-ring-map) |
| 184 | + (println) |
| 185 | + (println "aleph resp:") |
| 186 | + (prn aleph-resp)) |
| 187 | + |
| 188 | + (is-headers= (:headers clj-http-resp) (:headers aleph-resp)) |
| 189 | + (is (instance? InputStream (:body aleph-resp))) |
| 190 | + (let [new-clj-http-body (bodies= (:body clj-http-resp) (:body aleph-resp))] |
| 191 | + (assoc clj-http-resp :body new-clj-http-body))))))) |
0 commit comments