clj-ssh icon indicating copy to clipboard operation
clj-ssh copied to clipboard

Add a timeout option to with-connection

Open hugoduncan opened this issue 12 years ago • 5 comments

with-connection does not currently allow a timeout specification for making a connection.

hugoduncan avatar Apr 23 '12 03:04 hugoduncan

Hi! I've seen this issue in production, and it kills my app permanently when it hangs. Would you be open for a PR fixing this? Are there any specific issues that makes it hard? Thanks! :)

rutchkiwi avatar May 03 '17 14:05 rutchkiwi

If you check how the Util.createSocket is implemented, you will see that the timeout defines an upper limit of the connection only, not a lower limit, because the timeout is strangely not passed to an underlying Socket.Those 20 seconds is probably an OS-level default limit.To override it, try implementing the SocketFactory and attach it to the session using the Session.setSocketFactory.

链接 stackoverflow please close

xingzheone avatar Nov 21 '17 16:11 xingzheone

no timeout and hanging still present in 2019-05

lsh-0 avatar May 08 '19 09:05 lsh-0

Not sure how to reproduce this issue, but I had a similar problem. All of the worker threads in my app were tied up in the with-connection call, rendering my app useless until restart.

Here's a potential workaround, in case it benefits anyone.

I noticed in the source that the Session protocol has a two-arity connect that includes a timeout parameter. https://github.com/clj-commons/clj-ssh/blob/c50b84275cfc5a1a6b4d837686746f3519eaea12/src/clj_ssh/ssh.clj#L119

It seems to work, but is not being used by thewith-connection macro.

We can just define our own alternative macro that uses the timeout parameter:

;; "ssh" here is an alias for clj-ssh.ssh, make sure you are requiring that in your ns declaration
(defmacro with-connection-timeout
    "Creates a context in which the session is connected. Ensures the session is
  disconnected on exit. Will timeout after the provided number of milliseconds if not succesfully connected."
    [session timeout & body]
    `(let [session# ~session timeout# ~timeout]
       (try
         (when-not (ssh/connected? session#)
           (ssh/connect session# timeout#))
         ~@body
         (finally
           (ssh/disconnect session#)))))

You can test this by trying to connect to a non-sftp host:

(let [agent (ssh/ssh-agent {})
        sess (ssh/session agent "google.com" {})]
    (with-connection-timeout sess 5000
      "something!"))

After 5 seconds, it will throw an exception: com.jcraft.jsch.JSchException with message "timeout: socket is not established"

However, do note that this timeout param only controls the time to connect to the socket. If you do some long-running work inside the body, the timeout param will not prevent that.

For example, if this code connects successfully within 3 seconds, it will then execute the 9 second thread sleep before closing the connection and returning:

(with-connection-timeout sess 3000
  (Thread/sleep 9000)
  "this took at least 9 seconds to return")

brianfay avatar Nov 12 '20 23:11 brianfay

Nice work @brianfay. Thank you

horttanainen avatar Nov 27 '23 09:11 horttanainen