Skip to content

Commit e5cb1d8

Browse files
committed
Merge branch 'consolidate-timeouts'
2 parents 5df37e1 + 77829fe commit e5cb1d8

10 files changed

+84
-101
lines changed

src/clj_libssh2/agent.clj

+5-5
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

+2-2
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

+10-10
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

+26-45
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

+2-2
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

+14-8
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

+8-12
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

+1-1
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)