Skip to content

Commit 695d558

Browse files
committed
Dispatch on content type header via multi method in :auto coerce mode.
1 parent 54a9477 commit 695d558

File tree

3 files changed

+68
-36
lines changed

3 files changed

+68
-36
lines changed

src/clj_http/client.clj

Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@
284284

285285
(defn coerce-json-body
286286
[{:keys [coerce]} {:keys [body status] :as resp} keyword? strict? & [charset]]
287-
(let [^String charset (or charset "UTF-8")
287+
(let [^String charset (or charset (-> resp :content-type-params :charset) "UTF-8")
288288
body (util/force-byte-array body)
289289
decode-func (if strict? json-decode-strict json-decode)]
290290
(if json-enabled?
@@ -302,6 +302,36 @@
302302
:else (assoc resp :body (String. ^"[B" body charset)))
303303
(assoc resp :body (String. ^"[B" body charset)))))
304304

305+
(defn coerce-clojure-body
306+
[request {:keys [body] :as resp}]
307+
(let [^String charset (or (-> resp :content-type-params :charset) "UTF-8")
308+
body (util/force-byte-array body)]
309+
(if edn-enabled?
310+
(assoc resp :body (parse-edn (String. ^"[B" body charset)))
311+
(binding [*read-eval* false]
312+
(assoc resp :body (read-string (String. ^"[B" body charset)))))))
313+
314+
(defmulti coerce-content-type (fn [req resp] (:content-type resp)))
315+
316+
(defmethod coerce-content-type :application/clojure [req resp]
317+
(coerce-clojure-body req resp))
318+
319+
(defmethod coerce-content-type :application/edn [req resp]
320+
(coerce-clojure-body req resp))
321+
322+
(defmethod coerce-content-type :application/json [req resp]
323+
(coerce-json-body req resp true false))
324+
325+
(defmethod coerce-content-type :default [req resp]
326+
(if-let [charset (-> resp :content-type-params :charset)]
327+
(coerce-response-body {:as charset} resp)
328+
(coerce-response-body {:as :default} resp)))
329+
330+
(defmethod coerce-response-body :auto [request resp]
331+
(let [header (get-in resp [:headers "content-type"])]
332+
(->> (merge resp (util/parse-content-type header))
333+
(coerce-content-type request))))
334+
305335
(defmethod coerce-response-body :json [req resp]
306336
(coerce-json-body req resp true false))
307337

@@ -314,40 +344,8 @@
314344
(defmethod coerce-response-body :json-string-keys [req resp]
315345
(coerce-json-body req resp false false))
316346

317-
(defmethod coerce-response-body :clojure [_ {:keys [body] :as resp}]
318-
(let [body (util/force-byte-array body)]
319-
(if edn-enabled?
320-
(assoc resp :body (parse-edn (String. ^"[B" body "UTF-8")))
321-
(binding [*read-eval* false]
322-
(assoc resp :body (read-string (String. ^"[B" body "UTF-8")))))))
323-
324-
(defmethod coerce-response-body :auto
325-
[req resp]
326-
(let [typestring (get-in resp [:headers "content-type"])]
327-
(cond
328-
(.startsWith (str typestring) "text/")
329-
(if-let [charset (second (re-find #"charset=(.*)"
330-
(str typestring)))]
331-
(coerce-response-body {:as charset} resp)
332-
(coerce-response-body {:as :default} resp))
333-
334-
(or (.startsWith (str typestring) "application/clojure")
335-
(.startsWith (str typestring) "application/edn"))
336-
(let [charset (or (second (re-find #"charset=(.*)" (str typestring)))
337-
"UTF-8")]
338-
(coerce-response-body {:as :clojure} resp))
339-
340-
(and (.startsWith (str typestring) "application/json")
341-
json-enabled?)
342-
(do
343-
(if-let [charset (second (re-find #"charset=(.*)"
344-
(str typestring)))]
345-
;; Defaulting to lazy parsing w/ symbol keys.
346-
(coerce-json-body req resp true false charset)
347-
(coerce-json-body req resp true false "UTF-8")))
348-
349-
:else
350-
(coerce-response-body {:as :default} resp))))
347+
(defmethod coerce-response-body :clojure [req resp]
348+
(coerce-clojure-body req resp))
351349

352350
(defmethod coerce-response-body :default
353351
[{:keys [as]} {:keys [status body] :as resp}]

src/clj_http/util.clj

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
(ns clj-http.util
22
"Helper functions for the HTTP client."
3-
(:require [clojure.string :refer [lower-case]]
3+
(:require [clojure.string :refer [blank? lower-case split trim]]
44
[clojure.walk :refer [postwalk]])
55
(:import (org.apache.commons.codec.binary Base64)
66
(org.apache.commons.io IOUtils)
@@ -110,3 +110,16 @@
110110
(if (false? v2)
111111
false
112112
(or v1 v2)))))
113+
114+
(defn parse-content-type
115+
"Parse `s` as an RFC 2616 media type."
116+
[s]
117+
(if-let [m (re-matches #"\s*(([^/]+)/([^ ;]+))\s*(\s*;.*)?" (str s))]
118+
{:content-type (keyword (nth m 1))
119+
:content-type-params
120+
(->> (split (str (nth m 4)) #"\s*;\s*")
121+
(identity)
122+
(remove blank?)
123+
(map #(split % #"="))
124+
(mapcat (fn [[k v]] [(keyword (lower-case k)) (trim v)]))
125+
(apply hash-map))}))

test/clj_http/test/util.clj

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,24 @@
2020
(is (= (opt {:thing? nil :thing false} :thing) false))
2121
(is (= (opt {:thing? nil :thing nil} :thing) nil))
2222
(is (= (opt {:thing? :a :thing nil} :thing) :a)))
23+
24+
(deftest test-parse-content-type
25+
(are [s expected]
26+
(is (= expected (parse-content-type s)))
27+
nil nil
28+
"" nil
29+
"application/json"
30+
{:content-type :application/json
31+
:content-type-params {}}
32+
" application/json "
33+
{:content-type :application/json
34+
:content-type-params {}}
35+
"application/json; charset=UTF-8"
36+
{:content-type :application/json
37+
:content-type-params {:charset "UTF-8"}}
38+
" application/json; charset=UTF-8 "
39+
{:content-type :application/json
40+
:content-type-params {:charset "UTF-8"}}
41+
"text/html; charset=ISO-8859-4"
42+
{:content-type :text/html
43+
:content-type-params {:charset "ISO-8859-4"}}))

0 commit comments

Comments
 (0)