@@ -368,11 +368,24 @@ type Backoff func(min, max time.Duration, attemptNum int, resp *http.Response) t
368
368
// attempted. If overriding this, be sure to close the body if needed.
369
369
type ErrorHandler func (resp * http.Response , err error , numTries int ) (* http.Response , error )
370
370
371
+ type HTTPClient interface {
372
+ // Do performs an HTTP request and returns an HTTP response.
373
+ Do (* http.Request ) (* http.Response , error )
374
+ // Done is called when the client is no longer needed.
375
+ Done ()
376
+ }
377
+
378
+ type HTTPClientFactory interface {
379
+ // New returns an HTTP client to use for a request, including retries.
380
+ New () HTTPClient
381
+ }
382
+
371
383
// Client is used to make HTTP requests. It adds additional functionality
372
384
// like automatic retries to tolerate minor outages.
373
385
type Client struct {
374
- HTTPClient * http.Client // Internal HTTP client.
375
- Logger interface {} // Customer logger instance. Can be either Logger or LeveledLogger
386
+ HTTPClient * http.Client // Internal HTTP client. This field is used if set, otherwise HTTPClientFactory is used.
387
+ HTTPClientFactory HTTPClientFactory
388
+ Logger interface {} // Customer logger instance. Can be either Logger or LeveledLogger
376
389
377
390
RetryWaitMin time.Duration // Minimum time to wait
378
391
RetryWaitMax time.Duration // Maximum time to wait
@@ -397,19 +410,18 @@ type Client struct {
397
410
ErrorHandler ErrorHandler
398
411
399
412
loggerInit sync.Once
400
- clientInit sync.Once
401
413
}
402
414
403
415
// NewClient creates a new Client with default settings.
404
416
func NewClient () * Client {
405
417
return & Client {
406
- HTTPClient : cleanhttp . DefaultPooledClient () ,
407
- Logger : defaultLogger ,
408
- RetryWaitMin : defaultRetryWaitMin ,
409
- RetryWaitMax : defaultRetryWaitMax ,
410
- RetryMax : defaultRetryMax ,
411
- CheckRetry : DefaultRetryPolicy ,
412
- Backoff : DefaultBackoff ,
418
+ HTTPClientFactory : & CleanPooledClientFactory {} ,
419
+ Logger : defaultLogger ,
420
+ RetryWaitMin : defaultRetryWaitMin ,
421
+ RetryWaitMax : defaultRetryWaitMax ,
422
+ RetryMax : defaultRetryMax ,
423
+ CheckRetry : DefaultRetryPolicy ,
424
+ Backoff : DefaultBackoff ,
413
425
}
414
426
}
415
427
@@ -573,12 +585,6 @@ func PassthroughErrorHandler(resp *http.Response, err error, _ int) (*http.Respo
573
585
574
586
// Do wraps calling an HTTP method with retries.
575
587
func (c * Client ) Do (req * Request ) (* http.Response , error ) {
576
- c .clientInit .Do (func () {
577
- if c .HTTPClient == nil {
578
- c .HTTPClient = cleanhttp .DefaultPooledClient ()
579
- }
580
- })
581
-
582
588
logger := c .logger ()
583
589
584
590
if logger != nil {
@@ -590,6 +596,9 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
590
596
}
591
597
}
592
598
599
+ httpClient := c .getHTTPClient ()
600
+ defer httpClient .Done ()
601
+
593
602
var resp * http.Response
594
603
var attempt int
595
604
var shouldRetry bool
@@ -603,7 +612,6 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
603
612
if req .body != nil {
604
613
body , err := req .body ()
605
614
if err != nil {
606
- c .HTTPClient .CloseIdleConnections ()
607
615
return resp , err
608
616
}
609
617
if c , ok := body .(io.ReadCloser ); ok {
@@ -625,7 +633,8 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
625
633
}
626
634
627
635
// Attempt the request
628
- resp , doErr = c .HTTPClient .Do (req .Request )
636
+
637
+ resp , doErr = httpClient .Do (req .Request )
629
638
630
639
// Check if we should continue with retries.
631
640
shouldRetry , checkErr = c .CheckRetry (req .Context (), resp , doErr )
@@ -694,7 +703,6 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
694
703
select {
695
704
case <- req .Context ().Done ():
696
705
timer .Stop ()
697
- c .HTTPClient .CloseIdleConnections ()
698
706
return nil , req .Context ().Err ()
699
707
case <- timer .C :
700
708
}
@@ -710,8 +718,6 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
710
718
return resp , nil
711
719
}
712
720
713
- defer c .HTTPClient .CloseIdleConnections ()
714
-
715
721
var err error
716
722
if checkErr != nil {
717
723
err = checkErr
@@ -758,6 +764,19 @@ func (c *Client) drainBody(body io.ReadCloser) {
758
764
}
759
765
}
760
766
767
+ func (c * Client ) getHTTPClient () HTTPClient {
768
+ if c .HTTPClient != nil {
769
+ return & idleConnectionsClosingClient {
770
+ httpClient : c .HTTPClient ,
771
+ }
772
+ }
773
+ clientFactory := c .HTTPClientFactory
774
+ if clientFactory == nil {
775
+ clientFactory = & CleanPooledClientFactory {}
776
+ }
777
+ return clientFactory .New ()
778
+ }
779
+
761
780
// Get is a shortcut for doing a GET request without making a new client.
762
781
func Get (url string ) (* http.Response , error ) {
763
782
return defaultClient .Get (url )
@@ -820,3 +839,29 @@ func (c *Client) StandardClient() *http.Client {
820
839
Transport : & RoundTripper {Client : c },
821
840
}
822
841
}
842
+
843
+ var (
844
+ _ HTTPClientFactory = & CleanPooledClientFactory {}
845
+ _ HTTPClient = & idleConnectionsClosingClient {}
846
+ )
847
+
848
+ type CleanPooledClientFactory struct {
849
+ }
850
+
851
+ func (f * CleanPooledClientFactory ) New () HTTPClient {
852
+ return & idleConnectionsClosingClient {
853
+ httpClient : cleanhttp .DefaultPooledClient (),
854
+ }
855
+ }
856
+
857
+ type idleConnectionsClosingClient struct {
858
+ httpClient * http.Client
859
+ }
860
+
861
+ func (c * idleConnectionsClosingClient ) Do (req * http.Request ) (* http.Response , error ) {
862
+ return c .httpClient .Do (req )
863
+ }
864
+
865
+ func (c * idleConnectionsClosingClient ) Done () {
866
+ c .httpClient .CloseIdleConnections ()
867
+ }
0 commit comments