@@ -343,11 +343,24 @@ type Backoff func(min, max time.Duration, attemptNum int, resp *http.Response) t
343
343
// attempted. If overriding this, be sure to close the body if needed.
344
344
type ErrorHandler func (resp * http.Response , err error , numTries int ) (* http.Response , error )
345
345
346
+ type HTTPClient interface {
347
+ // Do performs an HTTP request and returns an HTTP response.
348
+ Do (* http.Request ) (* http.Response , error )
349
+ // Done is called when the client is no longer needed.
350
+ Done ()
351
+ }
352
+
353
+ type HTTPClientFactory interface {
354
+ // New returns an HTTP client to use for a request, including retries.
355
+ New () HTTPClient
356
+ }
357
+
346
358
// Client is used to make HTTP requests. It adds additional functionality
347
359
// like automatic retries to tolerate minor outages.
348
360
type Client struct {
349
- HTTPClient * http.Client // Internal HTTP client.
350
- Logger interface {} // Customer logger instance. Can be either Logger or LeveledLogger
361
+ HTTPClient * http.Client // Internal HTTP client. This field is used if set, otherwise HTTPClientFactory is used.
362
+ HTTPClientFactory HTTPClientFactory
363
+ Logger interface {} // Customer logger instance. Can be either Logger or LeveledLogger
351
364
352
365
RetryWaitMin time.Duration // Minimum time to wait
353
366
RetryWaitMax time.Duration // Maximum time to wait
@@ -372,19 +385,18 @@ type Client struct {
372
385
ErrorHandler ErrorHandler
373
386
374
387
loggerInit sync.Once
375
- clientInit sync.Once
376
388
}
377
389
378
390
// NewClient creates a new Client with default settings.
379
391
func NewClient () * Client {
380
392
return & Client {
381
- HTTPClient : cleanhttp . DefaultPooledClient () ,
382
- Logger : defaultLogger ,
383
- RetryWaitMin : defaultRetryWaitMin ,
384
- RetryWaitMax : defaultRetryWaitMax ,
385
- RetryMax : defaultRetryMax ,
386
- CheckRetry : DefaultRetryPolicy ,
387
- Backoff : DefaultBackoff ,
393
+ HTTPClientFactory : & CleanPooledClientFactory {} ,
394
+ Logger : defaultLogger ,
395
+ RetryWaitMin : defaultRetryWaitMin ,
396
+ RetryWaitMax : defaultRetryWaitMax ,
397
+ RetryMax : defaultRetryMax ,
398
+ CheckRetry : DefaultRetryPolicy ,
399
+ Backoff : DefaultBackoff ,
388
400
}
389
401
}
390
402
@@ -545,12 +557,6 @@ func PassthroughErrorHandler(resp *http.Response, err error, _ int) (*http.Respo
545
557
546
558
// Do wraps calling an HTTP method with retries.
547
559
func (c * Client ) Do (req * Request ) (* http.Response , error ) {
548
- c .clientInit .Do (func () {
549
- if c .HTTPClient == nil {
550
- c .HTTPClient = cleanhttp .DefaultPooledClient ()
551
- }
552
- })
553
-
554
560
logger := c .logger ()
555
561
556
562
if logger != nil {
@@ -562,6 +568,9 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
562
568
}
563
569
}
564
570
571
+ httpClient := c .getHTTPClient ()
572
+ defer httpClient .Done ()
573
+
565
574
var resp * http.Response
566
575
var attempt int
567
576
var shouldRetry bool
@@ -574,7 +583,6 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
574
583
if req .body != nil {
575
584
body , err := req .body ()
576
585
if err != nil {
577
- c .HTTPClient .CloseIdleConnections ()
578
586
return resp , err
579
587
}
580
588
if c , ok := body .(io.ReadCloser ); ok {
@@ -596,7 +604,8 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
596
604
}
597
605
598
606
// Attempt the request
599
- resp , doErr = c .HTTPClient .Do (req .Request )
607
+
608
+ resp , doErr = httpClient .Do (req .Request )
600
609
601
610
// Check if we should continue with retries.
602
611
shouldRetry , checkErr = c .CheckRetry (req .Context (), resp , doErr )
@@ -657,7 +666,6 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
657
666
select {
658
667
case <- req .Context ().Done ():
659
668
timer .Stop ()
660
- c .HTTPClient .CloseIdleConnections ()
661
669
return nil , req .Context ().Err ()
662
670
case <- timer .C :
663
671
}
@@ -673,8 +681,6 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
673
681
return resp , nil
674
682
}
675
683
676
- defer c .HTTPClient .CloseIdleConnections ()
677
-
678
684
err := doErr
679
685
if checkErr != nil {
680
686
err = checkErr
@@ -717,6 +723,19 @@ func (c *Client) drainBody(body io.ReadCloser) {
717
723
}
718
724
}
719
725
726
+ func (c * Client ) getHTTPClient () HTTPClient {
727
+ if c .HTTPClient != nil {
728
+ return & idleConnectionsClosingClient {
729
+ httpClient : c .HTTPClient ,
730
+ }
731
+ }
732
+ clientFactory := c .HTTPClientFactory
733
+ if clientFactory == nil {
734
+ clientFactory = & CleanPooledClientFactory {}
735
+ }
736
+ return clientFactory .New ()
737
+ }
738
+
720
739
// Get is a shortcut for doing a GET request without making a new client.
721
740
func Get (url string ) (* http.Response , error ) {
722
741
return defaultClient .Get (url )
@@ -779,3 +798,29 @@ func (c *Client) StandardClient() *http.Client {
779
798
Transport : & RoundTripper {Client : c },
780
799
}
781
800
}
801
+
802
+ var (
803
+ _ HTTPClientFactory = & CleanPooledClientFactory {}
804
+ _ HTTPClient = & idleConnectionsClosingClient {}
805
+ )
806
+
807
+ type CleanPooledClientFactory struct {
808
+ }
809
+
810
+ func (f * CleanPooledClientFactory ) New () HTTPClient {
811
+ return & idleConnectionsClosingClient {
812
+ httpClient : cleanhttp .DefaultPooledClient (),
813
+ }
814
+ }
815
+
816
+ type idleConnectionsClosingClient struct {
817
+ httpClient * http.Client
818
+ }
819
+
820
+ func (c * idleConnectionsClosingClient ) Do (req * http.Request ) (* http.Response , error ) {
821
+ return c .httpClient .Do (req )
822
+ }
823
+
824
+ func (c * idleConnectionsClosingClient ) Done () {
825
+ c .httpClient .CloseIdleConnections ()
826
+ }
0 commit comments