Skip to content

Commit 77829fe

Browse files
committed
Consolidate the timeout handling.
Previously there were two sources of timeout values. Now there is one, in the session options. In the process of consolidating these timeouts we've made it a bit easier to manage the timeouts on a per-session basis.
1 parent 5df37e1 commit 77829fe

File tree

10 files changed

+84
-101
lines changed

10 files changed

+84
-101
lines changed

src/clj_libssh2/agent.clj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@
2525
[session ssh-agent previous]
2626
(when (nil? previous)
2727
(handle-errors session
28-
(with-timeout :agent
28+
(with-timeout session :agent
2929
(libssh2-agent/list-identities ssh-agent))))
3030
(let [id (PointerByReference.)
3131
ret (handle-errors session
32-
(with-timeout :agent
32+
(with-timeout session :agent
3333
(libssh2-agent/get-identity ssh-agent id previous)))]
3434
(case ret
3535
0 (.getValue id)
@@ -59,12 +59,12 @@
5959
(try
6060
(log/info "Connecting to the SSH agent...")
6161
(handle-errors session
62-
(with-timeout :agent
62+
(with-timeout session :agent
6363
(libssh2-agent/connect ssh-agent)))
6464
(when (loop [previous nil]
6565
(log/info "Fetching an ID to authenticate with...")
6666
(if-let [id (get-identity session ssh-agent previous)]
67-
(when-not (= 0 (with-timeout :agent
67+
(when-not (= 0 (with-timeout session :agent
6868
(libssh2-agent/userauth ssh-agent username id)))
6969
(recur id))
7070
:fail))
@@ -75,7 +75,7 @@
7575
true
7676
(finally
7777
(handle-errors session
78-
(with-timeout :agent
78+
(with-timeout session :agent
7979
(log/info "Disconnecting from the agent...")
8080
(libssh2-agent/disconnect ssh-agent)))
8181
(libssh2-agent/free ssh-agent)))))

src/clj_libssh2/authentication.clj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
(when-not (.exists (file keyfile))
5959
(error/raise (FileNotFoundException. keyfile))))
6060
(handle-errors session
61-
(with-timeout :auth
61+
(with-timeout session :auth
6262
(libssh2-userauth/publickey-fromfile (:session session)
6363
(:username credentials)
6464
(:public-key credentials)
@@ -70,7 +70,7 @@
7070
[session credentials]
7171
(log/info "Authenticating using a username and password.")
7272
(handle-errors session
73-
(with-timeout :auth
73+
(with-timeout session :auth
7474
(libssh2-userauth/password (:session session)
7575
(:username credentials)
7676
(:password credentials))))

src/clj_libssh2/channel.clj

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@
2525
0 on success. An exception will be thrown if an error occurs."
2626
[session channel]
2727
(log/info "Closing channel.")
28-
(block session
29-
(handle-errors session (libssh2-channel/close channel))))
28+
(block session :lib (handle-errors session (libssh2-channel/close channel))))
3029

3130
(defn exec
3231
"Execute a command on the remote host. This merely starts the execution of
@@ -44,7 +43,7 @@
4443
0 on success. An exception will be thrown if an error occurs."
4544
[session channel commandline]
4645
(log/info "Executing a command on the remote host.")
47-
(block session
46+
(block session :request
4847
(handle-errors session (libssh2-channel/exec channel commandline))))
4948

5049
(defn exit-signal
@@ -117,7 +116,7 @@
117116
118117
0 on success or throws an exception on failure."
119118
[session channel]
120-
(block session (handle-errors session (libssh2-channel/free channel))))
119+
(block session :lib (handle-errors session (libssh2-channel/free channel))))
121120

122121
(defn open
123122
"Create a new channel for a session.
@@ -131,7 +130,7 @@
131130
A newly-allocated channel object, or throws exception on failure."
132131
[session]
133132
(log/info "Opening a new channel.")
134-
(block-return session (libssh2-channel/open-session (:session session))))
133+
(block-return session :lib (libssh2-channel/open-session (:session session))))
135134

136135
(defn open-scp-recv
137136
"Create a new channel dedicated to receiving a file using SCP.
@@ -149,7 +148,7 @@
149148
[session remote-path]
150149
(log/info "Opening a new channel to receive a file using SCP.")
151150
(let [fileinfo (Stat/newInstance)]
152-
{:channel (block-return session
151+
{:channel (block-return session :request
153152
(libssh2-scp/recv2 (:session session) remote-path fileinfo))
154153
:fileinfo fileinfo}))
155154

@@ -175,7 +174,7 @@
175174
(let [mode (or mode 0644)
176175
mtime (or mtime 0)
177176
atime (or atime 0)]
178-
(block-return session
177+
(block-return session :request
179178
(libssh2-scp/send64 (:session session) remote-path mode size mtime atime))))
180179

181180
(defn send-eof
@@ -191,7 +190,8 @@
191190
0 on success, throws an exception on failure."
192191
[session channel]
193192
(log/info "Notifying the remote host of EOF")
194-
(block session (handle-errors session (libssh2-channel/send-eof channel))))
193+
(block session :request
194+
(handle-errors session (libssh2-channel/send-eof channel))))
195195

196196
(defn setenv
197197
"Set a selection of environment variables on the channel. These will be
@@ -224,7 +224,7 @@
224224
{:session session})
225225
ret))]
226226
(doseq [[k v] env]
227-
(block session
227+
(block session :request
228228
(handle-errors session
229229
(fail-if-forbidden
230230
(libssh2-channel/setenv channel (->str k) (->str v))))))))
@@ -419,7 +419,7 @@
419419
nil, or throw an exception if the timeout is exceeded on any of the streams
420420
given."
421421
[session channel streams]
422-
(let [read-timeout (-> session :options :read-timeout)
422+
(let [read-timeout (-> session :options :timeout :read)
423423
last-read-time (->> streams
424424
(remove #(= :input (:direction %)))
425425
(map :last-read-time)

src/clj_libssh2/error.clj

Lines changed: 26 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,6 @@
66
[clj-libssh2.libssh2.session :as libssh2-session])
77
(:import [com.sun.jna.ptr IntByReference PointerByReference]))
88

9-
(def timeouts
10-
"An atom map where the keys are keywords being the symbolic names of named
11-
timeouts (see get-timeout/set-timeout) and the values are the number of
12-
milliseconds for that timeout."
13-
(atom {:agent 10000
14-
:auth 10000
15-
:known-hosts 10000}))
16-
179
(def error-messages
1810
"All of the error codes that are documented for libssh2 except for
1911
LIBSSH2_ERROR_SOCKET_NONE which despite its name is a generic error and
@@ -172,33 +164,20 @@
172164
(maybe-throw-error (:session session#) res#)
173165
res#))
174166

175-
(defn get-timeout
176-
"Get a timeout value.
167+
(defn enforce-timeout
168+
"Throw an error if a timeout has been exceeded.
177169
178170
Arguments:
179171
180-
name-or-value Either the name of a symbolic timeout (e.g. :session) or a
181-
number of milliseconds.
182-
183-
Return:
184-
185-
A number of milliseconds for the timeout."
186-
[name-or-value]
187-
(or (get @timeouts name-or-value) name-or-value 1000))
188-
189-
(defn set-timeout
190-
"Update a timeout value.
191-
192-
Arguments:
193-
194-
timeout-name The name of a symbolic timeout.
195-
millis The number of milliseconds for that timeout.
196-
197-
Return:
198-
199-
A map of all the current symbolic timeouts."
200-
[timeout-name millis]
201-
(swap! timeouts assoc timeout-name millis))
172+
session The clj-libssh2.session.Session object for the current session.
173+
start-time The time the timeout is relative to.
174+
timeout The number of milliseconds describing the timeout value."
175+
[session start-time timeout]
176+
(when (< (if (keyword? timeout)
177+
(-> session :options :timeout timeout)
178+
timeout)
179+
(- (System/currentTimeMillis) start-time))
180+
(handle-errors session libssh2/ERROR_TIMEOUT)))
202181

203182
(defmacro with-timeout
204183
"Run some code that could return libssh2/ERROR_EAGAIN and if it does, retry
@@ -213,22 +192,24 @@
213192
214193
Arguments:
215194
216-
timeout Either a number of milliseconds or a keyword referring to a named
217-
timeout set using set-timeout.
195+
session The clj-libssh2.session.Session object for the current session.
196+
timeout A number of milliseconds specifying the timeout. This macro will
197+
wait for at least that number of milliseconds before failing with a
198+
timeout error. It may return successfully sooner, but this value is
199+
the minimum time you will wait for failure. The argument can also be
200+
passed a keyword which will be looked up in the :timeout section of
201+
the session options.
218202
219203
Return:
220204
221205
Either the return value of the code being wrapped or an exception if the
222206
timeout is exceeded."
223-
[timeout & body]
207+
[session timeout & body]
224208
`(let [start# (System/currentTimeMillis)
225-
timeout# (get-timeout ~timeout)]
226-
(loop [timedout# false]
227-
(if timedout#
228-
(raise (format "Timeout of %d ms exceeded" timeout#)
229-
{:timeout ~timeout
230-
:timeout-length timeout#})
231-
(let [r# (do ~@body)]
232-
(if (= r# libssh2/ERROR_EAGAIN)
233-
(recur (< timeout# (- (System/currentTimeMillis) start#)))
234-
r#))))))
209+
session# ~session
210+
timeout# ~timeout]
211+
(loop [result# (do ~@body)]
212+
(enforce-timeout session# start# timeout#)
213+
(if (= result# libssh2/ERROR_EAGAIN)
214+
(recur (do ~@body))
215+
result#))))

src/clj_libssh2/known_hosts.clj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
0 on success or an exception if the key does not validate."
8585
[session known-hosts host port host-key fail-on-missing fail-on-mismatch]
8686
(handle-errors session
87-
(with-timeout :known-hosts
87+
(with-timeout session :known-hosts
8888
(checkp-result fail-on-mismatch fail-on-missing
8989
(libssh2-knownhost/checkp known-hosts
9090
host
@@ -112,7 +112,7 @@
112112
[session known-hosts known-hosts-file]
113113
(when (.exists (file known-hosts-file))
114114
(handle-errors session
115-
(with-timeout :known-hosts
115+
(with-timeout session :known-hosts
116116
(libssh2-knownhost/readfile known-hosts
117117
known-hosts-file
118118
libssh2/KNOWNHOST_FILE_OPENSSH)))))

src/clj_libssh2/session.clj

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,20 @@
1515
protect against attempting to close sessions twice."
1616
(atom #{}))
1717

18-
(def default-opts
18+
(def ^:private default-opts
1919
"The default options for a session. These are not only the defaults, but an
2020
exhaustive list of the legal options."
21-
{:blocking-timeout 60000
22-
:character-set "UTF-8"
21+
{:character-set "UTF-8"
2322
:fail-if-not-in-known-hosts false
2423
:fail-unless-known-hosts-matches true
2524
:known-hosts-file nil
2625
:read-chunk-size (* 1024 1024)
27-
:read-timeout 60000
26+
:timeout {:agent 5000 ; All agent operations
27+
:auth 5000 ; All authentication calls
28+
:known-hosts 5000 ; All interactions with the known hosts API
29+
:lib 1000 ; Operations that are 100% local library calls
30+
:read 60000 ; Reads from the remote host
31+
:request 5000} ; Simple calls to the remote host
2832
:write-chunk-size (* 1024 1024)})
2933

3034
(defrecord Session [session socket host port options])
@@ -56,7 +60,9 @@
5660
keys in default-opts."
5761
[opts]
5862
{:pre [(map? opts) (every? (set (keys default-opts)) (keys opts))]}
59-
(merge default-opts opts))
63+
(merge default-opts
64+
(assoc opts
65+
:timeout (merge (:timeout default-opts) (:timeout opts)))))
6066

6167
(defn- destroy-session
6268
"Free a libssh2 session object from a Session and optionally raise an
@@ -77,10 +83,10 @@
7783
(destroy-session session "Shutting down normally." false))
7884
([session reason raise]
7985
(log/info "Tearing down the session.")
80-
(socket/block session
86+
(socket/block session :request
8187
(handle-errors session
8288
(libssh2-session/disconnect (:session session) reason)))
83-
(socket/block session
89+
(socket/block session :lib
8490
(handle-errors session
8591
(libssh2-session/free (:session session))))
8692
(when raise
@@ -98,7 +104,7 @@
98104
0 on success. Throws an exception on failure."
99105
[session]
100106
(log/info "Handshaking with the remote host.")
101-
(socket/block session
107+
(socket/block session :request
102108
(handle-errors session
103109
(libssh2-session/handshake (:session session) (:socket session)))))
104110

src/clj_libssh2/socket.clj

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -95,33 +95,29 @@
9595
(when (>= 0 select-result)
9696
(handle-errors session libssh2/ERROR_TIMEOUT))))))
9797

98-
(defn enforce-blocking-timeout
99-
[session start-time]
100-
(when (< (-> session :options :blocking-timeout)
101-
(- (System/currentTimeMillis) start-time))
102-
(handle-errors session libssh2/ERROR_TIMEOUT)))
103-
10498
(defmacro block
10599
"Turn a non-blocking call that returns EAGAIN into a blocking one."
106-
[session & body]
100+
[session timeout & body]
107101
`(let [session# ~session
108-
start-time# (System/currentTimeMillis)]
102+
start-time# (System/currentTimeMillis)
103+
timeout# ~timeout]
109104
(while (= libssh2/ERROR_EAGAIN (do ~@body))
110105
(handle-errors session#
111106
(wait session# start-time#))
112-
(enforce-blocking-timeout session# start-time#))))
107+
(error/enforce-timeout session# start-time# timeout#))))
113108

114109
(defmacro block-return
115110
"Similar to block, but for functions that return a pointer"
116-
[session & body]
111+
[session timeout & body]
117112
`(let [session# ~session
118-
start-time# (System/currentTimeMillis)]
113+
start-time# (System/currentTimeMillis)
114+
timeout# ~timeout]
119115
(loop [result# (do ~@body)]
120116
(if (nil? result#)
121117
(let [errno# (libssh2-session/last-errno (:session session#))]
122118
(handle-errors session# errno#)
123119
(when (= libssh2/ERROR_EAGAIN errno#)
124120
(wait session# start-time#))
125-
(enforce-blocking-timeout session# start-time#)
121+
(error/enforce-timeout session# start-time# timeout#)
126122
(recur (do ~@body)))
127123
result#))))

src/clj_libssh2/ssh.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@
149149
(let [output (FileOutputStream. local-path)
150150
file-size (.getSize fileinfo)
151151
read-chunk-size (-> session :options :read-chunk-size)
152-
read-timeout (-> session :options :read-timeout)
152+
read-timeout (-> session :options :timeout :read)
153153
finish (fn [bytes-read]
154154
(.close output)
155155
{:local-path local-path

0 commit comments

Comments
 (0)