Skip to content

Commit 92ad1f8

Browse files
author
Paulo Gomes
committed
Cache SSH connections
The underlying SSH connections are kept open and are reused across several SSH sessions. This is due to upstream issues in which concurrent/parallel SSH connections may lead to instability. golang/go#51926 golang/go#27140 Signed-off-by: Paulo Gomes <[email protected]>
1 parent 017707a commit 92ad1f8

File tree

4 files changed

+321
-94
lines changed

4 files changed

+321
-94
lines changed

pkg/git/libgit2/managed/http.go

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -288,11 +288,13 @@ func (self *httpSmartSubtransportStream) Free() {
288288
if self.resp != nil {
289289
traceLog.Info("[http]: httpSmartSubtransportStream.Free()")
290290

291-
// ensure body is fully processed and closed
292-
// for increased likelihood of transport reuse in HTTP/1.x.
293-
// it should not be a problem to do this more than once.
294-
io.Copy(io.Discard, self.resp.Body)
295-
self.resp.Body.Close()
291+
if self.resp.Body != nil {
292+
// ensure body is fully processed and closed
293+
// for increased likelihood of transport reuse in HTTP/1.x.
294+
// it should not be a problem to do this more than once.
295+
_, _ = io.Copy(io.Discard, self.resp.Body) // errors can be safely ignored
296+
_ = self.resp.Body.Close() // errors can be safely ignored
297+
}
296298
}
297299
}
298300

@@ -366,8 +368,11 @@ func (self *httpSmartSubtransportStream) sendRequest() error {
366368
if req.Method == "POST" && resp.StatusCode >= 301 && resp.StatusCode <= 308 {
367369
// ensure body is fully processed and closed
368370
// for increased likelihood of transport reuse in HTTP/1.x.
369-
io.Copy(io.Discard, resp.Body)
370-
resp.Body.Close()
371+
_, _ = io.Copy(io.Discard, resp.Body) // errors can be safely ignored
372+
373+
if err := resp.Body.Close(); err != nil {
374+
return err
375+
}
371376

372377
// The next try will go against the new destination
373378
self.req.URL, err = resp.Location()
@@ -386,8 +391,10 @@ func (self *httpSmartSubtransportStream) sendRequest() error {
386391

387392
// ensure body is fully processed and closed
388393
// for increased likelihood of transport reuse in HTTP/1.x.
389-
io.Copy(io.Discard, resp.Body)
390-
resp.Body.Close()
394+
_, _ = io.Copy(io.Discard, resp.Body) // errors can be safely ignored
395+
if err := resp.Body.Close(); err != nil {
396+
return err
397+
}
391398

392399
return fmt.Errorf("Unhandled HTTP error %s", resp.Status)
393400
}

pkg/git/libgit2/managed/managed_test.go

Lines changed: 50 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import (
3636

3737
func TestHttpAction_CreateClientRequest(t *testing.T) {
3838
tests := []struct {
39-
description string
39+
name string
4040
url string
4141
expectedUrl string
4242
expectedMethod string
@@ -46,7 +46,7 @@ func TestHttpAction_CreateClientRequest(t *testing.T) {
4646
wantedErr error
4747
}{
4848
{
49-
description: "Uploadpack: no changes when no options found",
49+
name: "Uploadpack: no changes when no options found",
5050
url: "https://sometarget/abc",
5151
expectedUrl: "https://sometarget/abc/git-upload-pack",
5252
expectedMethod: "POST",
@@ -56,7 +56,7 @@ func TestHttpAction_CreateClientRequest(t *testing.T) {
5656
wantedErr: nil,
5757
},
5858
{
59-
description: "UploadpackLs: no changes when no options found",
59+
name: "UploadpackLs: no changes when no options found",
6060
url: "https://sometarget/abc",
6161
expectedUrl: "https://sometarget/abc/info/refs?service=git-upload-pack",
6262
expectedMethod: "GET",
@@ -66,7 +66,7 @@ func TestHttpAction_CreateClientRequest(t *testing.T) {
6666
wantedErr: nil,
6767
},
6868
{
69-
description: "Receivepack: no changes when no options found",
69+
name: "Receivepack: no changes when no options found",
7070
url: "https://sometarget/abc",
7171
expectedUrl: "https://sometarget/abc/git-receive-pack",
7272
expectedMethod: "POST",
@@ -76,7 +76,7 @@ func TestHttpAction_CreateClientRequest(t *testing.T) {
7676
wantedErr: nil,
7777
},
7878
{
79-
description: "ReceivepackLs: no changes when no options found",
79+
name: "ReceivepackLs: no changes when no options found",
8080
url: "https://sometarget/abc",
8181
expectedUrl: "https://sometarget/abc/info/refs?service=git-receive-pack",
8282
expectedMethod: "GET",
@@ -86,7 +86,7 @@ func TestHttpAction_CreateClientRequest(t *testing.T) {
8686
wantedErr: nil,
8787
},
8888
{
89-
description: "override URL via options",
89+
name: "override URL via options",
9090
url: "https://initial-target/abc",
9191
expectedUrl: "https://final-target/git-upload-pack",
9292
expectedMethod: "POST",
@@ -98,7 +98,7 @@ func TestHttpAction_CreateClientRequest(t *testing.T) {
9898
wantedErr: nil,
9999
},
100100
{
101-
description: "error when no http.transport provided",
101+
name: "error when no http.transport provided",
102102
url: "https://initial-target/abc",
103103
expectedUrl: "",
104104
expectedMethod: "",
@@ -110,45 +110,47 @@ func TestHttpAction_CreateClientRequest(t *testing.T) {
110110
}
111111

112112
for _, tt := range tests {
113-
if tt.opts != nil {
114-
AddTransportOptions(tt.url, *tt.opts)
115-
}
116-
117-
_, req, err := createClientRequest(tt.url, tt.action, tt.transport)
118-
if tt.wantedErr != nil {
119-
if tt.wantedErr.Error() != err.Error() {
120-
t.Errorf("%s: wanted: %v got: %v", tt.description, tt.wantedErr, err)
113+
t.Run(tt.name, func(t *testing.T) {
114+
if tt.opts != nil {
115+
AddTransportOptions(tt.url, *tt.opts)
121116
}
122-
} else {
123-
assert.Equal(t, req.URL.String(), tt.expectedUrl)
124-
assert.Equal(t, req.Method, tt.expectedMethod)
125-
}
126-
127-
if tt.opts != nil {
128-
RemoveTransportOptions(tt.url)
129-
}
117+
118+
_, req, err := createClientRequest(tt.url, tt.action, tt.transport)
119+
if tt.wantedErr != nil {
120+
if tt.wantedErr.Error() != err.Error() {
121+
t.Errorf("wanted: %v got: %v", tt.wantedErr, err)
122+
}
123+
} else {
124+
assert.Equal(t, req.URL.String(), tt.expectedUrl)
125+
assert.Equal(t, req.Method, tt.expectedMethod)
126+
}
127+
128+
if tt.opts != nil {
129+
RemoveTransportOptions(tt.url)
130+
}
131+
})
130132
}
131133
}
132134

133135
func TestOptions(t *testing.T) {
134136
tests := []struct {
135-
description string
137+
name string
136138
registerOpts bool
137139
url string
138140
opts TransportOptions
139141
expectOpts bool
140142
expectedOpts *TransportOptions
141143
}{
142144
{
143-
description: "return registered option",
145+
name: "return registered option",
144146
registerOpts: true,
145147
url: "https://target/?123",
146148
opts: TransportOptions{},
147149
expectOpts: true,
148150
expectedOpts: &TransportOptions{},
149151
},
150152
{
151-
description: "match registered options",
153+
name: "match registered options",
152154
registerOpts: true,
153155
url: "https://target/?876",
154156
opts: TransportOptions{
@@ -162,7 +164,7 @@ func TestOptions(t *testing.T) {
162164
},
163165
},
164166
{
165-
description: "ignore when options not registered",
167+
name: "ignore when options not registered",
166168
registerOpts: false,
167169
url: "",
168170
opts: TransportOptions{},
@@ -172,28 +174,30 @@ func TestOptions(t *testing.T) {
172174
}
173175

174176
for _, tt := range tests {
175-
if tt.registerOpts {
176-
AddTransportOptions(tt.url, tt.opts)
177-
}
178-
179-
opts, found := transportOptions(tt.url)
180-
if tt.expectOpts != found {
181-
t.Errorf("%s: wanted %v got %v", tt.description, tt.expectOpts, found)
182-
}
183-
184-
if tt.expectOpts {
185-
if reflect.DeepEqual(opts, *tt.expectedOpts) {
186-
t.Errorf("%s: wanted %v got %v", tt.description, *tt.expectedOpts, opts)
177+
t.Run(tt.name, func(t *testing.T) {
178+
if tt.registerOpts {
179+
AddTransportOptions(tt.url, tt.opts)
180+
}
181+
182+
opts, found := transportOptions(tt.url)
183+
if tt.expectOpts != found {
184+
t.Errorf("%s: wanted %v got %v", tt.name, tt.expectOpts, found)
187185
}
188-
}
189186

190-
if tt.registerOpts {
191-
RemoveTransportOptions(tt.url)
192-
}
187+
if tt.expectOpts {
188+
if reflect.DeepEqual(opts, *tt.expectedOpts) {
189+
t.Errorf("%s: wanted %v got %v", tt.name, *tt.expectedOpts, opts)
190+
}
191+
}
193192

194-
if _, found = transportOptions(tt.url); found {
195-
t.Errorf("%s: option for %s was not removed", tt.description, tt.url)
196-
}
193+
if tt.registerOpts {
194+
RemoveTransportOptions(tt.url)
195+
}
196+
197+
if _, found = transportOptions(tt.url); found {
198+
t.Errorf("%s: option for %s was not removed", tt.name, tt.url)
199+
}
200+
})
197201
}
198202
}
199203

0 commit comments

Comments
 (0)