@@ -50,12 +50,11 @@ import (
5050 "errors"
5151 "fmt"
5252 "io"
53- "net"
5453 "net/http"
5554 "net/url"
5655 "sync"
57- "time"
5856
57+ "github.com/fluxcd/source-controller/internal/transport"
5958 git2go "github.com/libgit2/git2go/v33"
6059)
6160
@@ -81,7 +80,8 @@ func httpSmartSubtransportFactory(remote *git2go.Remote, transport *git2go.Trans
8180}
8281
8382type httpSmartSubtransport struct {
84- transport * git2go.Transport
83+ transport * git2go.Transport
84+ httpTransport * http.Transport
8585}
8686
8787func (t * httpSmartSubtransport ) Action (targetUrl string , action git2go.SmartServiceAction ) (git2go.SmartSubtransportStream , error ) {
@@ -104,25 +104,11 @@ func (t *httpSmartSubtransport) Action(targetUrl string, action git2go.SmartServ
104104 proxyFn = http .ProxyURL (parsedUrl )
105105 }
106106
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- }
107+ // reuses the http transport from a pool, or create new one on demand.
108+ t .httpTransport = transport .NewOrIdle (nil )
109+ t .httpTransport .Proxy = proxyFn
124110
125- client , req , err := createClientRequest (targetUrl , action , httpTransport )
111+ client , req , err := createClientRequest (targetUrl , action , t . httpTransport )
126112 if err != nil {
127113 return nil , err
128114 }
@@ -291,6 +277,10 @@ func (self *httpSmartSubtransportStream) Write(buf []byte) (int, error) {
291277
292278func (self * httpSmartSubtransportStream ) Free () {
293279 if self .resp != nil {
280+ // ensure body is fully processed and closed
281+ // for increased likelihood of transport reuse in HTTP/1.x.
282+ // it should not be a problem to do this more than once.
283+ io .Copy (io .Discard , self .resp .Body )
294284 self .resp .Body .Close ()
295285 }
296286}
@@ -362,6 +352,11 @@ func (self *httpSmartSubtransportStream) sendRequest() error {
362352 // GET requests will be automatically redirected.
363353 // POST require the new destination, and also the body content.
364354 if req .Method == "POST" && resp .StatusCode >= 301 && resp .StatusCode <= 308 {
355+ // ensure body is fully processed and closed
356+ // for increased likelihood of transport reuse in HTTP/1.x.
357+ io .Copy (io .Discard , resp .Body )
358+ resp .Body .Close ()
359+
365360 // The next try will go against the new destination
366361 self .req .URL , err = resp .Location ()
367362 if err != nil {
@@ -371,15 +366,24 @@ func (self *httpSmartSubtransportStream) sendRequest() error {
371366 continue
372367 }
373368
369+ // for HTTP 200, the response will be cleared up by Free()
374370 if resp .StatusCode == http .StatusOK {
375371 break
376372 }
377373
374+ // ensure body is fully processed and closed
375+ // for increased likelihood of transport reuse in HTTP/1.x.
378376 io .Copy (io .Discard , resp .Body )
379- defer resp .Body .Close ()
377+ resp .Body .Close ()
378+
380379 return fmt .Errorf ("Unhandled HTTP error %s" , resp .Status )
381380 }
382381
382+ if self .owner .httpTransport != nil {
383+ transport .Release (self .owner .httpTransport )
384+ self .owner .httpTransport = nil
385+ }
386+
383387 self .resp = resp
384388 self .sentRequest = true
385389 return nil
0 commit comments