Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calling .close on response http stream will fully read stream contents rather than terminating. #627

Open
phronmophobic opened this issue Dec 13, 2022 · 2 comments

Comments

@phronmophobic
Copy link

phronmophobic commented Dec 13, 2022

Update: See next comment.


I created a minimal repro at https://github.com/phronmophobic/close-hang-repro.

The reproduction is fairly straightforward:

(require '[clj-http.client :as http])
(defn -main [& args]
  (let [url "https://github.com/uscensusbureau/citysdk/archive/bc93425d47508741c8fa8131ac09a53372e1e088.zip"
        response (http/request {:url url
                                :method :get
                                :as :stream})]
    (prn (:body response))
    ;; will hang indefinitely 
    (.close (:body response))))

It doesn't happen for every url, but this url will consistently reproduce the hang.

@phronmophobic
Copy link
Author

I dug into this a bit more. It seems that some requests will have their input stream wrapped with a ContentLengthInputStream.

Per their docs:

Note that this class NEVER closes the underlying stream, even when close gets called. Instead, it will read until the "end" of its limit on close, which allows for the seamless execution of subsequent HTTP 1.1 requests, while not requiring the client to remember to read the entire contents of the response.

What was happening is that calling .close on the inputstream would cause the ContentLengthInputStream to read the rest of the input (aka. downloading the rest of the input). The problem is that the example url is very large and takes quite a while to download. Testing with smaller downloads has shown that the .close will eventually complete after reading the full result.

It seems closing the body inputstream should terminate the connection rather than fully consuming the stream. Is this desired behavior?

A workaround is to call close on the http client before closing the stream, (.close (:http-client response)).

@phronmophobic phronmophobic changed the title Calling .close on response http stream will hang indefinitely for some urls Calling .close on response http stream will fully read stream contents rather than terminating. Dec 13, 2022
@phronmophobic
Copy link
Author

Here's the workaround I ended up with so that closing the streams closes the connection.

(let [body
      (proxy [FilterInputStream]
          [^InputStream (:body response)]
          (close []
            (.close (:http-client response))))]
  (partially-consume body)
  (.close body))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant