@@ -46,12 +46,14 @@ package managed
46
46
import (
47
47
"context"
48
48
"crypto/sha256"
49
+ "errors"
49
50
"fmt"
50
51
"io"
51
52
"net"
52
53
"net/url"
53
54
"runtime"
54
55
"strings"
56
+ "sync"
55
57
"time"
56
58
57
59
"golang.org/x/crypto/ssh"
@@ -83,16 +85,22 @@ func sshSmartSubtransportFactory(remote *git2go.Remote, transport *git2go.Transp
83
85
type sshSmartSubtransport struct {
84
86
transport * git2go.Transport
85
87
86
- lastAction git2go.SmartServiceAction
88
+ lastAction git2go.SmartServiceAction
89
+ stdin io.WriteCloser
90
+ stdout io.Reader
91
+ addr string
92
+ ctx context.Context
93
+
94
+ con connection
95
+ }
96
+
97
+ type connection struct {
87
98
conn net.Conn
88
99
client * ssh.Client
89
100
session * ssh.Session
90
- stdin io.WriteCloser
91
- stdout io.Reader
92
101
currentStream * sshSmartSubtransportStream
93
- addr string
94
102
connected bool
95
- ctx context. Context
103
+ m sync. Mutex
96
104
}
97
105
98
106
func (t * sshSmartSubtransport ) Action (transportOptionsURL string , action git2go.SmartServiceAction ) (git2go.SmartSubtransportStream , error ) {
@@ -128,17 +136,17 @@ func (t *sshSmartSubtransport) Action(transportOptionsURL string, action git2go.
128
136
var cmd string
129
137
switch action {
130
138
case git2go .SmartServiceActionUploadpackLs , git2go .SmartServiceActionUploadpack :
131
- if t .currentStream != nil {
139
+ if t .con . currentStream != nil {
132
140
if t .lastAction == git2go .SmartServiceActionUploadpackLs {
133
- return t .currentStream , nil
141
+ return t .con . currentStream , nil
134
142
}
135
143
}
136
144
cmd = fmt .Sprintf ("git-upload-pack '%s'" , uPath )
137
145
138
146
case git2go .SmartServiceActionReceivepackLs , git2go .SmartServiceActionReceivepack :
139
- if t .currentStream != nil {
147
+ if t .con . currentStream != nil {
140
148
if t .lastAction == git2go .SmartServiceActionReceivepackLs {
141
- return t .currentStream , nil
149
+ return t .con . currentStream , nil
142
150
}
143
151
}
144
152
cmd = fmt .Sprintf ("git-receive-pack '%s'" , uPath )
@@ -147,7 +155,7 @@ func (t *sshSmartSubtransport) Action(transportOptionsURL string, action git2go.
147
155
return nil , fmt .Errorf ("unexpected action: %v" , action )
148
156
}
149
157
150
- if t .connected {
158
+ if t .con . connected {
151
159
// Disregard errors from previous stream, futher details inside Close().
152
160
_ = t .Close ()
153
161
}
@@ -185,21 +193,23 @@ func (t *sshSmartSubtransport) Action(transportOptionsURL string, action git2go.
185
193
if err != nil {
186
194
return nil , err
187
195
}
188
- t .connected = true
196
+ t .con .m .Lock ()
197
+ t .con .connected = true
198
+ t .con .m .Unlock ()
189
199
190
200
traceLog .Info ("[ssh]: creating new ssh session" )
191
- if t .session , err = t .client .NewSession (); err != nil {
201
+ if t .con . session , err = t . con .client .NewSession (); err != nil {
192
202
return nil , err
193
203
}
194
204
195
- if t .stdin , err = t .session .StdinPipe (); err != nil {
205
+ if t .stdin , err = t .con . session .StdinPipe (); err != nil {
196
206
return nil , err
197
207
}
198
208
199
209
var w * io.PipeWriter
200
210
var reader io.Reader
201
211
t .stdout , w = io .Pipe ()
202
- if reader , err = t .session .StdoutPipe (); err != nil {
212
+ if reader , err = t .con . session .StdoutPipe (); err != nil {
203
213
return nil , err
204
214
}
205
215
@@ -208,7 +218,15 @@ func (t *sshSmartSubtransport) Action(transportOptionsURL string, action git2go.
208
218
//
209
219
// xref: https://github.com/golang/crypto/blob/eb4f295cb31f7fb5d52810411604a2638c9b19a2/ssh/session.go#L553-L558
210
220
go func () error {
211
- defer w .Close ()
221
+ defer func () {
222
+ w .Close ()
223
+
224
+ // In case this goroutine panics, handle recovery.
225
+ if r := recover (); r != nil {
226
+ traceLog .Error (errors .New (r .(string )),
227
+ "[ssh]: recovered from libgit2 ssh smart subtransport panic" , "address" , t .addr )
228
+ }
229
+ }()
212
230
213
231
var cancel context.CancelFunc
214
232
ctx := t .ctx
@@ -226,9 +244,12 @@ func (t *sshSmartSubtransport) Action(transportOptionsURL string, action git2go.
226
244
return nil
227
245
228
246
default :
229
- if ! t .connected {
247
+ t .con .m .Lock ()
248
+ if ! t .con .connected {
249
+ t .con .m .Unlock ()
230
250
return nil
231
251
}
252
+ t .con .m .Unlock ()
232
253
233
254
_ , err := io .Copy (w , reader )
234
255
if err != nil {
@@ -240,16 +261,16 @@ func (t *sshSmartSubtransport) Action(transportOptionsURL string, action git2go.
240
261
}()
241
262
242
263
traceLog .Info ("[ssh]: run on remote" , "cmd" , cmd )
243
- if err := t .session .Start (cmd ); err != nil {
264
+ if err := t .con . session .Start (cmd ); err != nil {
244
265
return nil , err
245
266
}
246
267
247
268
t .lastAction = action
248
- t .currentStream = & sshSmartSubtransportStream {
269
+ t .con . currentStream = & sshSmartSubtransportStream {
249
270
owner : t ,
250
271
}
251
272
252
- return t .currentStream , nil
273
+ return t .con . currentStream , nil
253
274
}
254
275
255
276
func (t * sshSmartSubtransport ) createConn (addr string , sshConfig * ssh.ClientConfig ) error {
@@ -265,8 +286,8 @@ func (t *sshSmartSubtransport) createConn(addr string, sshConfig *ssh.ClientConf
265
286
return err
266
287
}
267
288
268
- t .conn = conn
269
- t .client = ssh .NewClient (c , chans , reqs )
289
+ t .con . conn = conn
290
+ t .con . client = ssh .NewClient (c , chans , reqs )
270
291
271
292
return nil
272
293
}
@@ -282,31 +303,35 @@ func (t *sshSmartSubtransport) createConn(addr string, sshConfig *ssh.ClientConf
282
303
// SmartSubTransport (i.e. unreleased resources, staled connections).
283
304
func (t * sshSmartSubtransport ) Close () error {
284
305
traceLog .Info ("[ssh]: sshSmartSubtransport.Close()" , "server" , t .addr )
285
- t .currentStream = nil
286
- if t .client != nil && t .stdin != nil {
306
+ t .con .m .Lock ()
307
+ defer t .con .m .Unlock ()
308
+ t .con .currentStream = nil
309
+ if t .con .client != nil && t .stdin != nil {
287
310
_ = t .stdin .Close ()
288
311
}
289
- t .client = nil
312
+ t .con . client = nil
290
313
291
- if t .session != nil {
314
+ if t .con . session != nil {
292
315
traceLog .Info ("[ssh]: session.Close()" , "server" , t .addr )
293
- _ = t .session .Close ()
316
+ _ = t .con . session .Close ()
294
317
}
295
- t .session = nil
318
+ t .con . session = nil
296
319
297
320
return nil
298
321
}
299
322
300
323
func (t * sshSmartSubtransport ) Free () {
301
324
traceLog .Info ("[ssh]: sshSmartSubtransport.Free()" )
302
- if t .client != nil {
303
- _ = t .client .Close ()
325
+ if t .con . client != nil {
326
+ _ = t .con . client .Close ()
304
327
}
305
328
306
- if t .conn != nil {
307
- _ = t .conn .Close ()
329
+ if t .con . conn != nil {
330
+ _ = t .con . conn .Close ()
308
331
}
309
- t .connected = false
332
+ t .con .m .Lock ()
333
+ t .con .connected = false
334
+ t .con .m .Unlock ()
310
335
}
311
336
312
337
type sshSmartSubtransportStream struct {
0 commit comments