From e6ba7286e6b3c8f0f5a6470b71a4815af640fd19 Mon Sep 17 00:00:00 2001 From: Matt Parker Date: Sat, 16 Apr 2016 10:36:09 -0700 Subject: [PATCH] Fix issue #257 This commit checks if the body is empty during clojure coercion. If it's empty, the body is set to nil. Previously, the test assumed the body would return as an empty string but the expected behavior per the convo in the issue is that nil should be returned. --- src/clj_http/client.clj | 173 +++++++++---------- test/clj_http/test/core_test.clj | 281 +++++++++++++++---------------- 2 files changed, 227 insertions(+), 227 deletions(-) diff --git a/src/clj_http/client.clj b/src/clj_http/client.clj index 3de215f0..a1ff1b6f 100644 --- a/src/clj_http/client.clj +++ b/src/clj_http/client.clj @@ -71,7 +71,7 @@ [in type & [opts]] {:pre [transit-enabled?]} (let [reader (ns-resolve 'cognitect.transit 'reader) - read (ns-resolve 'cognitect.transit 'read)] + read (ns-resolve 'cognitect.transit 'read)] (read (reader in type opts)))) (defn ^:dynamic transit-encode @@ -80,7 +80,7 @@ {:pre [transit-enabled?]} (let [output (ByteArrayOutputStream.) writer (ns-resolve 'cognitect.transit 'writer) - write (ns-resolve 'cognitect.transit 'write)] + write (ns-resolve 'cognitect.transit 'write)] (write (writer output type opts) out) (.toByteArray output))) @@ -143,12 +143,12 @@ "Parse a URL string into a map of interesting parts." [url] (let [url-parsed (URL. url)] - {:scheme (keyword (.getProtocol url-parsed)) - :server-name (.getHost url-parsed) - :server-port (when-pos (.getPort url-parsed)) - :uri (url-encode-illegal-characters (.getPath url-parsed)) - :user-info (if-let [user-info (.getUserInfo url-parsed)] - (util/url-decode user-info)) + {:scheme (keyword (.getProtocol url-parsed)) + :server-name (.getHost url-parsed) + :server-port (when-pos (.getPort url-parsed)) + :uri (url-encode-illegal-characters (.getPath url-parsed)) + :user-info (if-let [user-info (.getUserInfo url-parsed)] + (util/url-decode user-info)) :query-string (url-encode-illegal-characters (.getQuery url-parsed))})) ;; Statuses for which clj-http will not throw an exception @@ -230,15 +230,15 @@ :trace-redirects - vector of sites the request was redirected from" [client] (fn [{:keys [request-method max-redirects redirects-count trace-redirects url] - :or {redirects-count 1 trace-redirects [] - ;; max-redirects default taken from Firefox - max-redirects 20} - :as req}] + :or {redirects-count 1 trace-redirects [] + ;; max-redirects default taken from Firefox + max-redirects 20} + :as req}] (let [{:keys [status] :as resp} (client req) resp-r (assoc resp :trace-redirects - (if url - (conj trace-redirects url) - trace-redirects))] + (if url + (conj trace-redirects url) + trace-redirects))] (cond (false? (opt req :follow-redirects)) resp @@ -250,17 +250,17 @@ resp-r) (= 303 status) (follow-redirect client (assoc req :request-method :get - :redirects-count (inc redirects-count)) + :redirects-count (inc redirects-count)) resp-r) (#{301 302} status) (cond (#{:get :head} request-method) (follow-redirect client (assoc req :redirects-count - (inc redirects-count)) resp-r) + (inc redirects-count)) resp-r) (opt req :force-redirects) (follow-redirect client (assoc req - :request-method :get - :redirects-count (inc redirects-count)) + :request-method :get + :redirects-count (inc redirects-count)) resp-r) :else resp-r) @@ -268,7 +268,7 @@ (if (or (#{:get :head} request-method) (opt req :force-redirects)) (follow-redirect client (assoc req :redirects-count - (inc redirects-count)) resp-r) + (inc redirects-count)) resp-r) resp-r) :else resp-r)))) @@ -276,7 +276,7 @@ ;; Multimethods for Content-Encoding dispatch automatically ;; decompressing response bodies (defmulti decompress-body - (fn [resp] (get-in resp [:headers "content-encoding"]))) + (fn [resp] (get-in resp [:headers "content-encoding"]))) (defmethod decompress-body "gzip" [resp] @@ -294,8 +294,8 @@ (defmethod decompress-body :default [resp] (assoc resp - :orig-content-encoding - (get-in resp [:headers "content-encoding"]))) + :orig-content-encoding + (get-in resp [:headers "content-encoding"]))) (defn wrap-decompression "Middleware handling automatic decompression of responses from web servers. If @@ -305,7 +305,7 @@ (fn [req] (if (false? (opt req :decompress-body)) (client req) - (let [req-c (update req :headers assoc "accept-encoding" "gzip, deflate") + (let [req-c (update req :headers assoc "accept-encoding" "gzip, deflate") resp-c (client req-c)] (decompress-body resp-c))))) @@ -326,8 +326,8 @@ [{:keys [coerce]} {:keys [body status] :as resp} keyword? strict? & [charset]] (let [^String charset (or charset (-> resp :content-type-params :charset) "UTF-8") - body (util/force-byte-array body) - decode-func (if strict? json-decode-strict json-decode)] + body (util/force-byte-array body) + decode-func (if strict? json-decode-strict json-decode)] (if json-enabled? (cond (= coerce :always) @@ -346,11 +346,12 @@ (defn coerce-clojure-body [request {:keys [body] :as resp}] (let [^String charset (or (-> resp :content-type-params :charset) "UTF-8") - body (util/force-byte-array body)] - (if edn-enabled? - (assoc resp :body (parse-edn (String. ^"[B" body charset))) - (binding [*read-eval* false] - (assoc resp :body (read-string (String. ^"[B" body charset))))))) + body (util/force-byte-array body)] + (assoc resp :body (cond + (empty? body) nil + edn-enabled? (binding [*read-eval* false] + (read-string (String. ^"[B" body charset))) + :else (parse-edn (String. ^"[B" body charset)))))) (defn coerce-transit-body [{:keys [transit-opts] :as request} {:keys [body] :as resp} type] @@ -361,7 +362,7 @@ (defn coerce-form-urlencoded-body [request {:keys [body] :as resp}] (let [^String charset (or (-> resp :content-type-params :charset) "UTF-8") - body-bytes (util/force-byte-array body)] + body-bytes (util/force-byte-array body)] (if ring-codec-enabled? (assoc resp :body (-> (String. ^"[B" body-bytes charset) form-decode keywordize-keys)) @@ -425,7 +426,7 @@ [{:keys [as]} {:keys [body] :as resp}] (let [body-bytes (util/force-byte-array body)] (cond - (string? as) (assoc resp :body (String. ^"[B" body-bytes ^String as)) + (string? as) (assoc resp :body (String. ^"[B" body-bytes ^String as)) :else (assoc resp :body (String. ^"[B" body-bytes "UTF-8"))))) (defn wrap-output-coercion @@ -453,20 +454,20 @@ and byte-arrays." [client] (fn [{:keys [body body-encoding length] - :or {^String body-encoding "UTF-8"} :as req}] + :or {^String body-encoding "UTF-8"} :as req}] (if body (cond (string? body) (client (-> req (assoc :body (maybe-wrap-entity - req (StringEntity. ^String body - ^String body-encoding)) + req (StringEntity. ^String body + ^String body-encoding)) :character-encoding (or body-encoding "UTF-8")))) (instance? File body) (client (-> req (assoc :body (maybe-wrap-entity - req (FileEntity. ^File body - ^String body-encoding))))) + req (FileEntity. ^File body + ^String body-encoding))))) ;; A length of -1 instructs HttpClient to use chunked encoding. (instance? InputStream body) @@ -474,14 +475,14 @@ (assoc :body (if length (InputStreamEntity. - ^InputStream body (long length)) + ^InputStream body (long length)) (maybe-wrap-entity - req - (InputStreamEntity. ^InputStream body -1)))))) + req + (InputStreamEntity. ^InputStream body -1)))))) (instance? (Class/forName "[B") body) (client (-> req (assoc :body (maybe-wrap-entity - req (ByteArrayEntity. body))))) + req (ByteArrayEntity. body))))) :else (client req)) @@ -491,24 +492,24 @@ "Given a map of body content, return a map of header-name to header-value." [body-map] (let [;; parse out HTML content - h (or (:content body-map) - (:content (first (filter #(= (:tag %) :html) body-map)))) + h (or (:content body-map) + (:content (first (filter #(= (:tag %) :html) body-map)))) ;; parse out tags - heads (:content (first (filter #(= (:tag %) :head) h))) + heads (:content (first (filter #(= (:tag %) :head) h))) ;; parse out attributes of 'meta' head tags - attrs (map :attrs (filter #(= (:tag %) :meta) heads)) + attrs (map :attrs (filter #(= (:tag %) :meta) heads)) ;; parse out the 'http-equiv' meta head tags - http-attrs (filter :http-equiv attrs) + http-attrs (filter :http-equiv attrs) ;; parse out HTML5 charset meta tags html5-charset (filter :charset attrs) ;; convert http-attributes into map of headers (lowercased) - headers (apply merge (map (fn [{:keys [http-equiv content]}] - {(.toLowerCase ^String http-equiv) content}) - http-attrs)) + headers (apply merge (map (fn [{:keys [http-equiv content]}] + {(.toLowerCase ^String http-equiv) content}) + http-attrs)) ;; merge in html5 charset setting - headers (merge headers - (when-let [cs (:charset (first html5-charset))] - {"content-type" (str "text/html; charset=" cs)}))] + headers (merge headers + (when-let [cs (:charset (first html5-charset))] + {"content-type" (str "text/html; charset=" cs)}))] headers)) (defn wrap-additional-header-parsing @@ -526,11 +527,11 @@ (let [^String content-type (get-in resp [:headers "content-type"])] (or (str/blank? content-type) (.startsWith content-type "text")))) - (let [body-bytes (util/force-byte-array (:body resp)) - body-stream1 (java.io.ByteArrayInputStream. body-bytes) - body-map (parse-html body-stream1) + (let [body-bytes (util/force-byte-array (:body resp)) + body-stream1 (java.io.ByteArrayInputStream. body-bytes) + body-map (parse-html body-stream1) additional-headers (get-headers-from-body body-map) - body-stream2 (java.io.ByteArrayInputStream. body-bytes)] + body-stream2 (java.io.ByteArrayInputStream. body-bytes)] (assoc resp :headers (merge (:headers resp) additional-headers) :body body-stream2)) @@ -548,9 +549,9 @@ (fn [{:keys [content-type character-encoding] :as req}] (if content-type (let [ctv (content-type-value content-type) - ct (if character-encoding - (str ctv "; charset=" character-encoding) - ctv)] + ct (if character-encoding + (str ctv "; charset=" character-encoding) + ctv)] (client (update-in req [:headers] assoc "content-type" ct))) (client req)))) @@ -582,10 +583,10 @@ "Given a charset header, detect the charset, returns UTF-8 if not found." [content-type] (or - (when-let [found (when content-type - (re-find #"(?i)charset\s*=\s*([^\s]+)" content-type))] - (second found)) - "UTF-8")) + (when-let [found (when content-type + (re-find #"(?i)charset\s*=\s*([^\s]+)" content-type))] + (second found)) + "UTF-8")) (defn generate-query-string-with-encoding [params encoding] (str/join "&" @@ -609,8 +610,8 @@ the request." [client] (fn [{:keys [query-params content-type] - :or {content-type :x-www-form-urlencoded} - :as req}] + :or {content-type :x-www-form-urlencoded} + :as req}] (if query-params (client (-> req (dissoc :query-params) (update-in [:query-string] @@ -619,8 +620,8 @@ (str old-query-string "&" new-query-string) new-query-string)) (generate-query-string - query-params - (content-type-value content-type))))) + query-params + (content-type-value content-type))))) (client req)))) (defn basic-auth-value [basic-auth] @@ -672,7 +673,7 @@ (client req)))) (defmulti coerce-form-params - (fn [req] (keyword (content-type-value (:content-type req))))) + (fn [req] (keyword (content-type-value (:content-type req))))) (defmethod coerce-form-params :application/edn [{:keys [form-params]}] @@ -684,8 +685,8 @@ "\"application/transit+%s\". " "Transit dependency not loaded.") (name type)) - {:type :transit-not-loaded - :form-params form-params + {:type :transit-not-loaded + :form-params form-params :transit-opts transit-opts :transit-type type}))) (transit-encode form-params type transit-opts)) @@ -701,9 +702,9 @@ (when-not json-enabled? (throw (ex-info (str "Can't encode form params as \"application/json\". " "Cheshire dependency not loaded.") - {:type :cheshire-not-loaded + {:type :cheshire-not-loaded :form-params form-params - :json-opts json-opts}))) + :json-opts json-opts}))) (json-encode form-params json-opts)) (defmethod coerce-form-params :default [{:keys [content-type form-params @@ -716,8 +717,8 @@ "Middleware wrapping the submission or form parameters." [client] (fn [{:keys [form-params content-type request-method] - :or {content-type :x-www-form-urlencoded} - :as req}] + :or {content-type :x-www-form-urlencoded} + :as req}] (if (and form-params (#{:post :put :patch} request-method)) (client (-> req (dissoc :form-params) @@ -750,13 +751,13 @@ "Middleware wrapping nested parameters for query strings." [client] (fn [{:keys [content-type] - :as req}] + :as req}] (if (or (nil? content-type) (= content-type :x-www-form-urlencoded)) (client (reduce - nest-params - req - [:query-params :form-params])) + nest-params + req + [:query-params :form-params])) (client req)))) (defn wrap-url @@ -786,8 +787,8 @@ [client] (let [lower-case-headers #(if-let [headers (:headers %1)] - (assoc %1 :headers (util/lower-case-keys headers)) - %1)] + (assoc %1 :headers (util/lower-case-keys headers)) + %1)] (fn [req] (-> (client (lower-case-headers req)) (lower-case-headers))))) @@ -798,7 +799,7 @@ [client] (fn [req] (let [start (System/currentTimeMillis) - resp (client req)] + resp (client req)] (assoc resp :request-time (- (System/currentTimeMillis) start))))) (def default-middleware @@ -829,7 +830,7 @@ wrap-unknown-host]) (def ^:dynamic - *current-middleware* +*current-middleware* "Available at any time to retrieve the middleware being used. Automatically bound when `with-middleware` is used." default-middleware) @@ -949,7 +950,7 @@ `*current-middleware*' sequence." [middleware-seq & body] `(with-middleware (concat ~middleware-seq *current-middleware*) - ~@body)) + ~@body)) (defmacro with-connection-pool "Macro to execute the body using a connection manager. Creates a @@ -989,5 +990,5 @@ ~@body (finally (.shutdown - ^PoolingHttpClientConnectionManager - conn/*connection-manager*)))))) + ^PoolingHttpClientConnectionManager + conn/*connection-manager*)))))) diff --git a/test/clj_http/test/core_test.clj b/test/clj_http/test/core_test.clj index 66388280..6d2c5bc8 100644 --- a/test/clj_http/test/core_test.clj +++ b/test/clj_http/test/core_test.clj @@ -27,44 +27,44 @@ [:get "/empty"] {:status 200 :body nil} [:get "/clojure"] - {:status 200 :body "{:foo \"bar\" :baz 7M :eggplant {:quux #{1 2 3}}}" + {:status 200 :body "{:foo \"bar\" :baz 7M :eggplant {:quux #{1 2 3}}}" :headers {"content-type" "application/clojure"}} [:get "/edn"] - {:status 200 :body "{:foo \"bar\" :baz 7M :eggplant {:quux #{1 2 3}}}" + {:status 200 :body "{:foo \"bar\" :baz 7M :eggplant {:quux #{1 2 3}}}" :headers {"content-type" "application/edn"}} [:get "/clojure-bad"] - {:status 200 :body "{:foo \"bar\" :baz #=(+ 1 1)}" + {:status 200 :body "{:foo \"bar\" :baz #=(+ 1 1)}" :headers {"content-type" "application/clojure"}} [:get "/json"] - {:status 200 :body "{\"foo\":\"bar\"}" + {:status 200 :body "{\"foo\":\"bar\"}" :headers {"content-type" "application/json"}} [:get "/json-array"] - {:status 200 :body "[\"foo\", \"bar\"]" + {:status 200 :body "[\"foo\", \"bar\"]" :headers {"content-type" "application/json"}} [:get "/json-bad"] {:status 400 :body "{\"foo\":\"bar\"}"} [:get "/redirect"] - {:status 302 + {:status 302 :headers {"location" "http://localhost:18080/redirect"}} [:get "/redirect-to-get"] - {:status 302 + {:status 302 :headers {"location" "http://localhost:18080/get"}} [:get "/unmodified-resource"] {:status 304} [:get "/transit-json"] - {:status 200 :body (str "[\"^ \",\"~:eggplant\",[\"^ \",\"~:quux\"," - "[\"~#set\",[1,3,2]]],\"~:baz\",\"~f7\"," - "\"~:foo\",\"bar\"]") + {:status 200 :body (str "[\"^ \",\"~:eggplant\",[\"^ \",\"~:quux\"," + "[\"~#set\",[1,3,2]]],\"~:baz\",\"~f7\"," + "\"~:foo\",\"bar\"]") :headers {"content-type" "application/transit+json"}} [:get "/transit-msgpack"] - {:status 200 - :body (->> [-125 -86 126 58 101 103 103 112 108 97 110 116 -127 -90 126 - 58 113 117 117 120 -110 -91 126 35 115 101 116 -109 1 3 2 - -91 126 58 98 97 122 -93 126 102 55 -91 126 58 102 111 111 - -93 98 97 114] - (map byte) - (byte-array) - (ByteArrayInputStream.)) + {:status 200 + :body (->> [-125 -86 126 58 101 103 103 112 108 97 110 116 -127 -90 126 + 58 113 117 117 120 -110 -91 126 35 115 101 116 -109 1 3 2 + -91 126 58 98 97 122 -93 126 102 55 -91 126 58 102 111 111 + -93 98 97 114] + (map byte) + (byte-array) + (ByteArrayInputStream.)) :headers {"content-type" "application/transit+msgpack"}} [:head "/head"] {:status 200} @@ -104,13 +104,13 @@ (defn run-server [] (defonce server - (ring/run-jetty #'handler {:port 18080 :join? false}))) + (ring/run-jetty #'handler {:port 18080 :join? false}))) (defn localhost [path] (str "http://localhost:18080" path)) (def base-req - {:scheme :http + {:scheme :http :server-name "localhost" :server-port 18080}) @@ -134,33 +134,33 @@ (deftest ^:integration sets-content-type-with-charset (run-server) - (let [resp (client/request {:scheme :http - :server-name "localhost" - :server-port 18080 - :request-method :get :uri "/content-type" - :content-type "text/plain" + (let [resp (client/request {:scheme :http + :server-name "localhost" + :server-port 18080 + :request-method :get :uri "/content-type" + :content-type "text/plain" :character-encoding "UTF-8"})] (is (= "text/plain; charset=UTF-8" (:body resp))))) (deftest ^:integration sets-content-type-without-charset (run-server) - (let [resp (client/request {:scheme :http - :server-name "localhost" - :server-port 18080 + (let [resp (client/request {:scheme :http + :server-name "localhost" + :server-port 18080 :request-method :get :uri "/content-type" - :content-type "text/plain"})] + :content-type "text/plain"})] (is (= "text/plain" (:body resp))))) (deftest ^:integration sets-arbitrary-headers (run-server) (let [resp (request {:request-method :get :uri "/header" - :headers {"x-my-header" "header-val"}})] + :headers {"x-my-header" "header-val"}})] (is (= "header-val" (slurp-body resp))))) (deftest ^:integration sends-and-returns-byte-array-body (run-server) (let [resp (request {:request-method :post :uri "/post" - :body (util/utf8-bytes "contents")})] + :body (util/utf8-bytes "contents")})] (is (= 200 (:status resp))) (is (= "contents" (slurp-body resp))))) @@ -179,33 +179,33 @@ (run-server) (try (is (thrown? SocketTimeoutException - (client/request {:scheme :http - :server-name "localhost" - :server-port 18080 + (client/request {:scheme :http + :server-name "localhost" + :server-port 18080 :request-method :get :uri "/timeout" :socket-timeout 1}))))) (deftest ^:integration delete-with-body (run-server) (let [resp (request {:request-method :delete :uri "/delete-with-body" - :body (.getBytes "foo bar")})] + :body (.getBytes "foo bar")})] (is (= 200 (:status resp))))) (deftest ^:integration self-signed-ssl-get (let [server (ring/run-jetty handler - {:port 8081 :ssl-port 18082 - :ssl? true - :join? false - :keystore "test-resources/keystore" + {:port 8081 :ssl-port 18082 + :ssl? true + :join? false + :keystore "test-resources/keystore" :key-password "keykey"})] (try (is (thrown? SunCertPathBuilderException - (client/request {:scheme :https - :server-name "localhost" - :server-port 18082 + (client/request {:scheme :https + :server-name "localhost" + :server-port 18082 :request-method :get :uri "/get"}))) (let [resp (request {:request-method :get :uri "/get" :server-port 18082 - :scheme :https :insecure? true})] + :scheme :https :insecure? true})] (is (= 200 (:status resp))) (is (= "get" (String. (util/force-byte-array (:body resp)))))) (finally @@ -213,23 +213,23 @@ (deftest ^:integration multipart-form-uploads (run-server) - (let [bytes (util/utf8-bytes "byte-test") - stream (ByteArrayInputStream. bytes) - resp (request {:request-method :post :uri "/multipart" - :multipart [{:name "a" :content "testFINDMEtest" - :encoding "UTF-8" - :mime-type "application/text"} - {:name "b" :content bytes - :mime-type "application/json"} - {:name "d" - :content (file "test-resources/keystore") - :encoding "UTF-8" - :mime-type "application/binary"} - {:name "c" :content stream - :mime-type "application/json"} - {:name "e" :part-name "eggplant" - :content "content" - :mime-type "application/text"}]}) + (let [bytes (util/utf8-bytes "byte-test") + stream (ByteArrayInputStream. bytes) + resp (request {:request-method :post :uri "/multipart" + :multipart [{:name "a" :content "testFINDMEtest" + :encoding "UTF-8" + :mime-type "application/text"} + {:name "b" :content bytes + :mime-type "application/json"} + {:name "d" + :content (file "test-resources/keystore") + :encoding "UTF-8" + :mime-type "application/binary"} + {:name "c" :content stream + :mime-type "application/json"} + {:name "e" :part-name "eggplant" + :content "content" + :mime-type "application/text"}]}) resp-body (apply str (map #(try (char %) (catch Exception _ "")) (util/force-byte-array (:body resp))))] (is (= 200 (:status resp))) @@ -245,11 +245,11 @@ (deftest ^:integration multipart-inputstream-length (run-server) - (let [bytes (util/utf8-bytes "byte-test") - stream (ByteArrayInputStream. bytes) - resp (request {:request-method :post :uri "/multipart" - :multipart [{:name "c" :content stream :length 9 - :mime-type "application/json"}]}) + (let [bytes (util/utf8-bytes "byte-test") + stream (ByteArrayInputStream. bytes) + resp (request {:request-method :post :uri "/multipart" + :multipart [{:name "c" :content stream :length 9 + :mime-type "application/json"}]}) resp-body (apply str (map #(try (char %) (catch Exception _ "")) (util/force-byte-array (:body resp))))] (is (= 200 (:status resp))) @@ -257,12 +257,12 @@ (deftest parse-headers (are [headers expected] - (let [iterator (BasicHeaderIterator. - (into-array BasicHeader - (map (fn [[name value]] - (BasicHeader. name value)) - headers)) nil)] - (is (= (core/parse-headers iterator) expected))) + (let [iterator (BasicHeaderIterator. + (into-array BasicHeader + (map (fn [[name value]] + (BasicHeader. name value)) + headers)) nil)] + (is (= (core/parse-headers iterator) expected))) [] {} @@ -277,33 +277,33 @@ (deftest ^:integration t-streaming-response (run-server) (let [stream (:body (request {:request-method :get :uri "/get" :as :stream})) - body (slurp stream)] + body (slurp stream)] (is (= "get" body)))) (deftest ^:integration throw-on-too-many-redirects (run-server) (let [resp (client/get (localhost "/redirect") - {:max-redirects 2 :throw-exceptions false - :redirect-strategy :none + {:max-redirects 2 :throw-exceptions false + :redirect-strategy :none :allow-circular-redirects true})] (is (= 302 (:status resp))) #_(is (= (apply vector (repeat 3 "http://localhost:18080/redirect")) (:trace-redirects resp)))) (is (thrown-with-msg? Exception #"Maximum redirects \(2\) exceeded" (client/get (localhost "/redirect") - {:max-redirects 2 - :throw-exceptions true + {:max-redirects 2 + :throw-exceptions true :allow-circular-redirects true}))) (is (thrown-with-msg? Exception #"Maximum redirects \(50\) exceeded" (client/get (localhost "/redirect") - {:throw-exceptions true + {:throw-exceptions true :allow-circular-redirects true})))) (deftest ^:integration get-with-body (run-server) (let [resp (request {:request-method :get :uri "/get-with-body" - :body (.getBytes "foo bar")})] + :body (.getBytes "foo bar")})] (is (= 200 (:status resp))) (is (= "foo bar" (String. (util/force-byte-array (:body resp))))))) @@ -326,7 +326,7 @@ (deftest ^:integration t-transit-output-coercion (run-server) - (let [transit-json-resp (client/get (localhost "/transit-json") {:as :auto}) + (let [transit-json-resp (client/get (localhost "/transit-json") {:as :auto}) transit-msgpack-resp (client/get (localhost "/transit-msgpack") {:as :auto})] (is (= 200 @@ -338,22 +338,22 @@ (deftest ^:integration t-json-output-coercion (run-server) - (let [resp (client/get (localhost "/json") {:as :json}) - resp-array (client/get (localhost "/json-array") {:as :json-strict}) - resp-str (client/get (localhost "/json") - {:as :json :coerce :exceptional}) - resp-str-keys (client/get (localhost "/json") {:as :json-string-keys}) + (let [resp (client/get (localhost "/json") {:as :json}) + resp-array (client/get (localhost "/json-array") {:as :json-strict}) + resp-str (client/get (localhost "/json") + {:as :json :coerce :exceptional}) + resp-str-keys (client/get (localhost "/json") {:as :json-string-keys}) resp-strict-str-keys (client/get (localhost "/json") {:as :json-strict-string-keys}) - resp-auto (client/get (localhost "/json") {:as :auto}) - bad-resp (client/get (localhost "/json-bad") - {:throw-exceptions false :as :json}) - bad-resp-json (client/get (localhost "/json-bad") - {:throw-exceptions false :as :json - :coerce :always}) - bad-resp-json2 (client/get (localhost "/json-bad") - {:throw-exceptions false :as :json - :coerce :unexceptional})] + resp-auto (client/get (localhost "/json") {:as :auto}) + bad-resp (client/get (localhost "/json-bad") + {:throw-exceptions false :as :json}) + bad-resp-json (client/get (localhost "/json-bad") + {:throw-exceptions false :as :json + :coerce :always}) + bad-resp-json2 (client/get (localhost "/json-bad") + {:throw-exceptions false :as :json + :coerce :unexceptional})] (is (= 200 (:status resp) (:status resp-array) @@ -391,11 +391,11 @@ (let [called? (atom false)] (is (thrown? Exception (client/post "http://localhost" - {:multipart [{:name "title" :content "Foo"} - {:name "Content/type" - :content "text/plain"} - {:name "file" - :content (file "/tmp/missingfile")}] + {:multipart [{:name "title" :content "Foo"} + {:name "Content/type" + :content "text/plain"} + {:name "file" + :content (file "/tmp/missingfile")}] :retry-handler (fn [ex try-count http-context] (reset! called? true) false)}))) @@ -417,8 +417,8 @@ (deftest ^:integration t-json-encoded-form-params (run-server) (let [params {:param1 "value1" :param2 {:foo "bar"}} - resp (client/post (localhost "/post") {:content-type :json - :form-params params})] + resp (client/post (localhost "/post") {:content-type :json + :form-params params})] (is (= 200 (:status resp))) (is (= (json/encode params) (:body resp))))) @@ -427,10 +427,10 @@ (let [req-ctx (atom []) {:keys [status trace-redirects] :as resp} (client/get - (localhost "/get") - {:request-interceptor - (fn [^HttpRequest req ^HttpContext ctx] - (reset! req-ctx {:method (.getMethod req) :uri (.getURI req)}))})] + (localhost "/get") + {:request-interceptor + (fn [^HttpRequest req ^HttpContext ctx] + (reset! req-ctx {:method (.getMethod req) :uri (.getURI req)}))})] (is (= 200 status)) (is (= "GET" (:method @req-ctx))) (is (= "/get" (.getPath (:uri @req-ctx)))))) @@ -440,13 +440,13 @@ (let [saved-ctx (atom []) {:keys [status trace-redirects] :as resp} (client/get - (localhost "/redirect-to-get") - {:response-interceptor - (fn [^HttpResponse resp ^HttpContext ctx] - (let [^HttpInetConnection conn - (.getAttribute ctx ExecutionContext/HTTP_CONNECTION)] - (swap! saved-ctx conj {:remote-port (.getRemotePort conn) - :http-conn conn})))})] + (localhost "/redirect-to-get") + {:response-interceptor + (fn [^HttpResponse resp ^HttpContext ctx] + (let [^HttpInetConnection conn + (.getAttribute ctx ExecutionContext/HTTP_CONNECTION)] + (swap! saved-ctx conj {:remote-port (.getRemotePort conn) + :http-conn conn})))})] (is (= 200 status)) (is (= 2 (count @saved-ctx))) #_(is (= (count trace-redirects) (count @saved-ctx))) @@ -456,14 +456,14 @@ (deftest ^:integration t-send-input-stream-body (run-server) (let [b1 (:body (client/post "http://localhost:18080/post" - {:body (ByteArrayInputStream. (.getBytes "foo")) + {:body (ByteArrayInputStream. (.getBytes "foo")) :length 3})) b2 (:body (client/post "http://localhost:18080/post" {:body (ByteArrayInputStream. - (.getBytes "foo"))})) + (.getBytes "foo"))})) b3 (:body (client/post "http://localhost:18080/post" - {:body (ByteArrayInputStream. - (.getBytes "apple")) + {:body (ByteArrayInputStream. + (.getBytes "apple")) :length 2}))] (is (= b1 "foo")) (is (= b2 "foo")) @@ -520,36 +520,36 @@ (run-server) (try (is (thrown? SocketTimeoutException - (client/request {:scheme :http - :server-name "www.writequit.org" - :server-port 80 + (client/request {:scheme :http + :server-name "www.writequit.org" + :server-port 80 :request-method :get :uri "/" - :conn-timeout 10}))))) + :conn-timeout 10}))))) (deftest ^:integration connection-pool-timeout (run-server) (client/with-connection-pool {:threads 1 :default-per-route 1} - (let [async-request #(future (client/request {:scheme :http - :server-name "localhost" - :server-port 18080 - :request-method :get - :conn-timeout 1 - :conn-request-timeout 1 - :uri "/timeout"})) - is-pool-timeout-error? - (fn [req-fut] - (instance? org.apache.http.conn.ConnectionPoolTimeoutException - (try @req-fut (catch Exception e (.getCause e))))) - req1 (async-request) - req2 (async-request) - timeout-error1 (is-pool-timeout-error? req1) - timeout-error2 (is-pool-timeout-error? req2)] - (is (or timeout-error1 timeout-error2))))) + (let [async-request #(future (client/request {:scheme :http + :server-name "localhost" + :server-port 18080 + :request-method :get + :conn-timeout 1 + :conn-request-timeout 1 + :uri "/timeout"})) + is-pool-timeout-error? + (fn [req-fut] + (instance? org.apache.http.conn.ConnectionPoolTimeoutException + (try @req-fut (catch Exception e (.getCause e))))) + req1 (async-request) + req2 (async-request) + timeout-error1 (is-pool-timeout-error? req1) + timeout-error2 (is-pool-timeout-error? req2)] + (is (or timeout-error1 timeout-error2))))) (deftest ^:integration t-header-collections (run-server) (let [headers (-> (client/get "http://localhost:18080/headers" - {:headers {"foo" ["bar" "baz"] + {:headers {"foo" ["bar" "baz"] "eggplant" "quux"}}) :body json/decode)] @@ -565,8 +565,7 @@ (run-server) (client/request {:method :get :url (localhost "/get") :headers {"foo" 2}})) -;; Currently failing, see: https://github.com/dakrone/clj-http/issues/257 -;; (deftest ^:integration t-empty-response-coercion -;; (run-server) -;; (let [resp (client/get (localhost "/empty") {:as :clojure})] -;; (is (= (:body resp) "")))) +(deftest ^:integration t-empty-response-coercion + (run-server) + (let [resp (client/get (localhost "/empty") {:as :clojure})] + (is (= (:body resp) nil))))