Skip to content

Commit c960a6a

Browse files
authored
Fix #609: Fix reading Transit response stream if data isn't available… (#611)
… right-away InputStream .available only checks if data is available to read right now. It is possible that data becomes available leter. Read method (used by Transit internally) will block untill data is available or the stream is closed. It is best to let Transit read the stream. Empty streams throws an exception with Transit, but we can catch that specific case and return nil like previously. Two test cases test the existing functionality, and third new test case checks that Transit data is read correctly when the data becomes available a bit later.
1 parent 3ff32c3 commit c960a6a

File tree

2 files changed

+42
-2
lines changed

2 files changed

+42
-2
lines changed

src/clj_http/client.clj

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,15 @@
101101
"Resolve and apply Transit's JSON/MessagePack decoding."
102102
[^InputStream in type & [opts]]
103103
{:pre [transit-enabled?]}
104-
(when (pos? (.available in))
104+
(try
105105
(let [reader (ns-resolve 'cognitect.transit 'reader)
106106
read (ns-resolve 'cognitect.transit 'read)]
107-
(read (reader in type (transit-read-opts opts))))))
107+
(read (reader in type (transit-read-opts opts))))
108+
(catch RuntimeException e
109+
;; Ignore exceptions from trying to read an empty stream.
110+
(if (instance? EOFException (.getCause e))
111+
nil
112+
(throw e)))))
108113

109114
(defn ^:dynamic transit-encode
110115
"Resolve and apply Transit's JSON/MessagePack encoding."

test/clj_http/test/client_test.clj

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
[ring.util.codec :refer [form-decode-str]]
1313
[slingshot.slingshot :refer [try+]])
1414
(:import java.io.ByteArrayInputStream
15+
java.io.PipedInputStream
16+
java.io.PipedOutputStream
1517
java.net.UnknownHostException
1618
org.apache.http.HttpEntity
1719
org.apache.logging.log4j.LogManager))
@@ -1754,3 +1756,36 @@
17541756
(is (= (.getMessage e)
17551757
(str "only :flatten-nested-keys or :ignore-nested-query-string/"
17561758
":flatten-nested-keys may be specified, not both"))))))
1759+
1760+
(defn transit-resp [body]
1761+
{:body body
1762+
:status 200
1763+
:headers {"content-type" "application/transit-json"}})
1764+
1765+
(deftest issue-609-empty-transit-response
1766+
(testing "Body is available right away"
1767+
(is (= {:foo "bar"}
1768+
(:body (client/coerce-response-body
1769+
{:as :transit+json}
1770+
(transit-resp (ByteArrayInputStream.
1771+
(.getBytes "[\"^ \",\"~:foo\",\"bar\"]"))))))))
1772+
1773+
(testing "Empty body is read as nil"
1774+
(is (nil? (:body (client/coerce-response-body
1775+
{:as :transit+json}
1776+
(transit-resp (ByteArrayInputStream. (.getBytes ""))))))))
1777+
1778+
(testing "Body is read correctly even if the data becomes available later"
1779+
;; Ensure both streams are closed (normally done inside future).
1780+
(with-open [o (PipedOutputStream.)
1781+
i (PipedInputStream.)]
1782+
(.connect i o)
1783+
(future
1784+
(Thread/sleep 10)
1785+
(.write o (.getBytes "[\"^ \",\"~:foo\",\"bar\"]"))
1786+
;; Close right now, with-open will wait until test is done.
1787+
(.close o))
1788+
(is (= {:foo "bar"}
1789+
(:body (client/coerce-response-body
1790+
{:as :transit+json}
1791+
(transit-resp i))))))))

0 commit comments

Comments
 (0)