Skip to content

Commit 1d601b2

Browse files
committed
Add :raw-headers option to preserve response headers verbatim
Closes dakrone#155.
1 parent 45fc4f5 commit 1d601b2

File tree

3 files changed

+46
-14
lines changed

3 files changed

+46
-14
lines changed

Readme.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,28 @@ response map. This is particularly useful for paging RESTful APIs:
466466
:last {:href "https://api.github.com/gists?page=22884"}}
467467
```
468468

469+
### Raw headers
470+
By default clj-http forces lowercase header names when parsing the
471+
response. If you want to preserve the exact response headers (with their
472+
original casing), you can use the `:raw-headers` option on your request.
473+
When you add this option you'll receive both the usual downcased headers
474+
_and_ an additional map of raw headers in your response.
475+
476+
```clojure
477+
(client/get "http://google.com" {:raw-headers true})
478+
=> {:status 200
479+
:headers {"date" "Sun, 01 Aug 2010 07:03:49 GMT"
480+
"cache-control" "private, max-age=0"
481+
"content-type" "text/html; charset=ISO-8859-1"
482+
...}
483+
:raw-headers {"Date" "Sun, 01 Aug 2010 07:03:49 GMT"
484+
"Cache-Control" "private, max-age=0"
485+
"Content-Type" "text/html; charset=ISO-8859-1"
486+
...}
487+
...
488+
489+
```
490+
469491
### Using persistent connections
470492
clj-http can use persistent connections to speed up connections if
471493
multiple connections are being used:

src/clj_http/core.clj

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,17 @@
3535
If a name appears more than once (like `set-cookie`) then the value
3636
will be a vector containing the values in the order they appeared
3737
in the headers."
38-
[#^HeaderIterator headers]
39-
(->> (iterator-seq headers)
40-
(map (fn [#^Header h] [(.toLowerCase (.getName h)) (.getValue h)]))
41-
(group-by first)
42-
(map (fn [[name headers]]
43-
(let [values (map second headers)]
44-
[name (let [[value & tail] values]
45-
(if tail values value))])))
46-
(into {})))
38+
([#^HeaderIterator headers]
39+
(parse-headers headers #(.toLowerCase %)))
40+
([#^HeaderIterator headers name-transform]
41+
(->> (iterator-seq headers)
42+
(map (fn [#^Header h] [(name-transform (.getName h)) (.getValue h)]))
43+
(group-by first)
44+
(map (fn [[name headers]]
45+
(let [values (map second headers)]
46+
[name (let [[value & tail] values]
47+
(if tail values value))])))
48+
(into {}))))
4749

4850
(defn set-client-param [#^HttpClient client key val]
4951
(when-not (nil? val)
@@ -202,7 +204,8 @@
202204
[{:keys [request-method scheme server-name server-port uri query-string
203205
headers body multipart debug debug-body socket-timeout conn-timeout
204206
save-request? proxy-host proxy-port as cookie-store retry-handler
205-
response-interceptor digest-auth connection-manager client-params]
207+
response-interceptor digest-auth connection-manager client-params
208+
raw-headers]
206209
:as req}]
207210
(let [^ClientConnectionManager conn-mgr
208211
(or connection-manager
@@ -270,9 +273,10 @@
270273
(try
271274
(let [http-resp (.execute http-client http-req)
272275
http-entity (.getEntity http-resp)
273-
resp {:status (.getStatusCode (.getStatusLine http-resp))
274-
:headers (parse-headers (.headerIterator http-resp))
275-
:body (coerce-body-entity req http-entity conn-mgr)}]
276+
resp (merge {:status (.getStatusCode (.getStatusLine http-resp))
277+
:headers (parse-headers (.headerIterator http-resp))
278+
:body (coerce-body-entity req http-entity conn-mgr)}
279+
(if raw-headers {:raw-headers (parse-headers (.headerIterator http-resp) identity)}))]
276280
(if save-request?
277281
(-> resp
278282
(assoc :request req)

test/clj_http/test/core.clj

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,13 @@
144144
(deftest ^{:integration true} returns-arbitrary-headers
145145
(run-server)
146146
(let [resp (request {:request-method :get :uri "/get"})]
147-
(is (string? (get-in resp [:headers "date"])))))
147+
(is (string? (get-in resp [:headers "date"])))
148+
(is (nil? (get-in resp [:headers "Date"])))))
149+
150+
(deftest ^{:integration true} returns-raw-headers
151+
(run-server)
152+
(let [resp (request {:request-method :get :uri "/get" :raw-headers true})]
153+
(is (string? (get-in resp [:raw-headers "Date"])))))
148154

149155
(deftest ^{:integration true} returns-status-on-exceptional-responses
150156
(run-server)

0 commit comments

Comments
 (0)