Skip to content

Commit 9d5f8ab

Browse files
committed
Rip out the session pooling.
It was open to accidentally prematurely closing sessions just because they happened to connect to the same host, port, etc. While we're at it, make the fourth arugment to open mandatory. Fixes: #6
1 parent 181be43 commit 9d5f8ab

File tree

4 files changed

+35
-73
lines changed

4 files changed

+35
-73
lines changed

src/clj_libssh2/session.clj

Lines changed: 30 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
(ns clj-libssh2.session
22
"Functions for creating and managing sessions."
3-
(:require [clj-libssh2.libssh2 :as libssh2]
3+
(:require [clojure.set :as set]
4+
[clj-libssh2.libssh2 :as libssh2]
45
[clj-libssh2.libssh2.session :as libssh2-session]
56
[clj-libssh2.authentication :refer [authenticate]]
67
[clj-libssh2.error :refer [handle-errors with-timeout]]
78
[clj-libssh2.known-hosts :as known-hosts]
89
[clj-libssh2.socket :as socket]))
910

1011
(def sessions
11-
"A pool of currently running sessions. This is an atomic map where the keys
12-
are session IDs and the values are running session objects."
13-
(atom {}))
12+
"An atomic set of currently active sessions. This is used to trigger calls to
13+
libssh2/init and libssh2/exit at appropriate times. It's also used to
14+
protect against attempting to close sessions twice."
15+
(atom #{}))
1416

1517
(def default-opts
1618
"The default options for a session. These are not only the defaults, but an
@@ -23,7 +25,7 @@
2325
:read-timeout 60000
2426
:write-chunk-size (* 1024 1024)})
2527

26-
(defrecord Session [id session socket host port options])
28+
(defrecord Session [session socket host port options])
2729

2830
(defn- create-session
2931
"Make a native libssh2 session object.
@@ -96,27 +98,6 @@
9698
(with-timeout :session
9799
(libssh2-session/handshake session socket))))
98100

99-
(defn- session-id
100-
"Generate the session ID that will be used to pool sessions.
101-
102-
Arguments:
103-
104-
host The hostname or IP of the remote host.
105-
port The port we're connecting to.
106-
credentials The credentials to be used to authenticate the session.
107-
opts The session options.
108-
109-
Return:
110-
111-
A string that will uniquely identify a session."
112-
[host port credentials opts]
113-
(format "%s@%s:%d-%d-%d"
114-
(:username credentials)
115-
host
116-
port
117-
(.hashCode credentials)
118-
(.hashCode opts)))
119-
120101
(defn close
121102
"Disconnect an SSH session and discard the session.
122103
@@ -127,11 +108,11 @@
127108
Return:
128109
129110
nil."
130-
[{id :id}]
131-
(when-let [session (get @sessions id)]
111+
[session]
112+
(when (contains? @sessions session)
132113
(destroy-session session)
133114
(socket/close (:socket session))
134-
(swap! sessions dissoc id)
115+
(swap! sessions set/difference #{session})
135116
(when (empty? @sessions)
136117
(libssh2/exit))))
137118

@@ -149,33 +130,26 @@
149130
Return:
150131
151132
A Session object for the connected and authenticated session."
152-
([host port credentials]
153-
(open host port credentials {}))
154-
([host port credentials opts]
155-
(when (empty? @sessions)
156-
(handle-errors nil (libssh2/init 0)))
157-
(let [session-options (create-session-options opts)
158-
id (session-id host port credentials session-options)]
159-
(if (contains? @sessions id)
160-
(get @sessions id)
161-
(let [session (Session. id
162-
(create-session)
163-
(socket/connect host port)
164-
host
165-
port
166-
session-options)]
167-
(when (> 0 (:socket session))
168-
(destroy-session session "Shutting down due to bad socket." true))
169-
(try
170-
(libssh2-session/set-blocking (:session session) 0)
171-
(handshake session)
172-
(known-hosts/check session)
173-
(authenticate session credentials)
174-
(swap! sessions assoc (:id session) session)
175-
(get @sessions (:id session))
176-
(catch Exception e
177-
(close session)
178-
(throw e))))))))
133+
[host port credentials opts]
134+
(when (empty? @sessions)
135+
(handle-errors nil (libssh2/init 0)))
136+
(let [session (Session. (create-session)
137+
(socket/connect host port)
138+
host
139+
port
140+
(create-session-options opts))]
141+
(when (> 0 (:socket session))
142+
(destroy-session session "Shutting down due to bad socket." true))
143+
(try
144+
(libssh2-session/set-blocking (:session session) 0)
145+
(handshake session)
146+
(known-hosts/check session)
147+
(authenticate session credentials)
148+
(swap! sessions conj session)
149+
session
150+
(catch Exception e
151+
(close session)
152+
(throw e)))))
179153

180154
(defmacro with-session
181155
"A convenience macro for running some code with a particular session.

test/clj_libssh2/test_agent.clj

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@
99

1010
(defn agent-session
1111
[]
12-
(session/open test/ssh-host
13-
test/ssh-port
14-
{:username (test/ssh-user)}))
12+
(session/open test/ssh-host test/ssh-port {:username (test/ssh-user)} {}))
1513

1614
(defn open-and-close
1715
[]

test/clj_libssh2/test_authentication.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
(defn auth
1212
[creds]
1313
(is (= 0 (count @session/sessions)))
14-
(let [session (session/open test/ssh-host test/ssh-port creds)]
14+
(let [session (session/open test/ssh-host test/ssh-port creds {})]
1515
(is (= 1 (count @session/sessions)))
1616
(session/close session))
1717
(is (= 0 (count @session/sessions))))

test/clj_libssh2/test_session.clj

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
(session/open test/ssh-host
1313
test/ssh-port
1414
(merge {:username (test/ssh-user)}
15-
(apply hash-map creds))))
15+
(apply hash-map creds))
16+
{}))
1617

1718
(deftest close-is-robust
1819
(testing "Closing a good, open connection does not fail."
@@ -31,16 +32,6 @@
3132
(session/close nil)))
3233

3334
(deftest open-works
34-
(testing "sessions are pooled"
35-
(is (= 0 (count @session/sessions)))
36-
(let [session1 (open)]
37-
(is (= 1 (count @session/sessions)))
38-
(let [session2 (open)]
39-
(is (= 1 (count @session/sessions)))
40-
(is (= session1 session2))
41-
(session/close session1)
42-
(session/close session2)
43-
(is (= 0 (count @session/sessions))))))
4435
(testing "throws but doesn't crash on handshake failure"
4536
(with-redefs [libssh2-session/handshake (constantly libssh2/ERROR_PROTO)]
4637
(is (= 0 (count @session/sessions)))
@@ -56,5 +47,4 @@
5647
test/ssh-port
5748
{:username (test/ssh-user)}
5849
{:bad-option :bad-option-value})))
59-
(is (= 0 (count @session/sessions)))
60-
))
50+
(is (= 0 (count @session/sessions)))))

0 commit comments

Comments
 (0)