@@ -50,12 +50,11 @@ import (
50
50
"errors"
51
51
"fmt"
52
52
"io"
53
- "net"
54
53
"net/http"
55
54
"net/url"
56
55
"sync"
57
- "time"
58
56
57
+ pool "github.com/fluxcd/source-controller/internal/transport"
59
58
git2go "github.com/libgit2/git2go/v33"
60
59
)
61
60
@@ -73,15 +72,18 @@ func registerManagedHTTP() error {
73
72
}
74
73
75
74
func httpSmartSubtransportFactory (remote * git2go.Remote , transport * git2go.Transport ) (git2go.SmartSubtransport , error ) {
75
+ traceLog .Info ("[http]: httpSmartSubtransportFactory" )
76
76
sst := & httpSmartSubtransport {
77
- transport : transport ,
77
+ transport : transport ,
78
+ httpTransport : pool .NewOrIdle (nil ),
78
79
}
79
80
80
81
return sst , nil
81
82
}
82
83
83
84
type httpSmartSubtransport struct {
84
- transport * git2go.Transport
85
+ transport * git2go.Transport
86
+ httpTransport * http.Transport
85
87
}
86
88
87
89
func (t * httpSmartSubtransport ) Action (targetUrl string , action git2go.SmartServiceAction ) (git2go.SmartSubtransportStream , error ) {
@@ -104,25 +106,10 @@ func (t *httpSmartSubtransport) Action(targetUrl string, action git2go.SmartServ
104
106
proxyFn = http .ProxyURL (parsedUrl )
105
107
}
106
108
107
- httpTransport := & http.Transport {
108
- // Add the proxy to the http transport.
109
- Proxy : proxyFn ,
110
-
111
- // Set reasonable timeouts to ensure connections are not
112
- // left open in an idle state, nor they hang indefinitely.
113
- //
114
- // These are based on the official go http.DefaultTransport:
115
- DialContext : (& net.Dialer {
116
- Timeout : 30 * time .Second ,
117
- KeepAlive : 30 * time .Second ,
118
- }).DialContext ,
119
- MaxIdleConns : 100 ,
120
- IdleConnTimeout : 90 * time .Second ,
121
- TLSHandshakeTimeout : 10 * time .Second ,
122
- ExpectContinueTimeout : 1 * time .Second ,
123
- }
109
+ t .httpTransport .Proxy = proxyFn
110
+ t .httpTransport .DisableCompression = false
124
111
125
- client , req , err := createClientRequest (targetUrl , action , httpTransport )
112
+ client , req , err := createClientRequest (targetUrl , action , t . httpTransport )
126
113
if err != nil {
127
114
return nil , err
128
115
}
@@ -223,10 +210,18 @@ func createClientRequest(targetUrl string, action git2go.SmartServiceAction, t *
223
210
}
224
211
225
212
func (t * httpSmartSubtransport ) Close () error {
213
+ traceLog .Info ("[http]: httpSmartSubtransport.Close()" )
226
214
return nil
227
215
}
228
216
229
217
func (t * httpSmartSubtransport ) Free () {
218
+ traceLog .Info ("[http]: httpSmartSubtransport.Free()" )
219
+
220
+ if t .httpTransport != nil {
221
+ traceLog .Info ("[http]: release http transport back to pool" )
222
+ pool .Release (t .httpTransport )
223
+ t .httpTransport = nil
224
+ }
230
225
}
231
226
232
227
type httpSmartSubtransportStream struct {
@@ -291,7 +286,15 @@ func (self *httpSmartSubtransportStream) Write(buf []byte) (int, error) {
291
286
292
287
func (self * httpSmartSubtransportStream ) Free () {
293
288
if self .resp != nil {
294
- self .resp .Body .Close ()
289
+ traceLog .Info ("[http]: httpSmartSubtransportStream.Free()" )
290
+
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
+ }
295
298
}
296
299
}
297
300
@@ -354,6 +357,7 @@ func (self *httpSmartSubtransportStream) sendRequest() error {
354
357
}
355
358
356
359
req .SetBasicAuth (userName , password )
360
+ traceLog .Info ("[http]: new request" , "method" , req .Method , "URL" , req .URL )
357
361
resp , err = self .client .Do (req )
358
362
if err != nil {
359
363
return err
@@ -362,21 +366,36 @@ func (self *httpSmartSubtransportStream) sendRequest() error {
362
366
// GET requests will be automatically redirected.
363
367
// POST require the new destination, and also the body content.
364
368
if req .Method == "POST" && resp .StatusCode >= 301 && resp .StatusCode <= 308 {
369
+ // ensure body is fully processed and closed
370
+ // for increased likelihood of transport reuse in HTTP/1.x.
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
+ }
376
+
365
377
// The next try will go against the new destination
366
378
self .req .URL , err = resp .Location ()
367
379
if err != nil {
368
380
return err
369
381
}
370
382
383
+ traceLog .Info ("[http]: POST redirect" , "URL" , self .req .URL )
371
384
continue
372
385
}
373
386
387
+ // for HTTP 200, the response will be cleared up by Free()
374
388
if resp .StatusCode == http .StatusOK {
375
389
break
376
390
}
377
391
378
- io .Copy (io .Discard , resp .Body )
379
- defer resp .Body .Close ()
392
+ // ensure body is fully processed and closed
393
+ // for increased likelihood of transport reuse in HTTP/1.x.
394
+ _ , _ = io .Copy (io .Discard , resp .Body ) // errors can be safely ignored
395
+ if err := resp .Body .Close (); err != nil {
396
+ return err
397
+ }
398
+
380
399
return fmt .Errorf ("Unhandled HTTP error %s" , resp .Status )
381
400
}
382
401
0 commit comments