@@ -343,11 +343,23 @@ 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
+ HTTPClientFactory HTTPClientFactory
362
+ Logger interface {} // Customer logger instance. Can be either Logger or LeveledLogger
351
363
352
364
RetryWaitMin time.Duration // Minimum time to wait
353
365
RetryWaitMax time.Duration // Maximum time to wait
@@ -372,19 +384,18 @@ type Client struct {
372
384
ErrorHandler ErrorHandler
373
385
374
386
loggerInit sync.Once
375
- clientInit sync.Once
376
387
}
377
388
378
389
// NewClient creates a new Client with default settings.
379
390
func NewClient () * Client {
380
391
return & Client {
381
- HTTPClient : cleanhttp . DefaultPooledClient () ,
382
- Logger : defaultLogger ,
383
- RetryWaitMin : defaultRetryWaitMin ,
384
- RetryWaitMax : defaultRetryWaitMax ,
385
- RetryMax : defaultRetryMax ,
386
- CheckRetry : DefaultRetryPolicy ,
387
- Backoff : DefaultBackoff ,
392
+ HTTPClientFactory : & CleanPooledClientFactory {} ,
393
+ Logger : defaultLogger ,
394
+ RetryWaitMin : defaultRetryWaitMin ,
395
+ RetryWaitMax : defaultRetryWaitMax ,
396
+ RetryMax : defaultRetryMax ,
397
+ CheckRetry : DefaultRetryPolicy ,
398
+ Backoff : DefaultBackoff ,
388
399
}
389
400
}
390
401
@@ -545,12 +556,6 @@ func PassthroughErrorHandler(resp *http.Response, err error, _ int) (*http.Respo
545
556
546
557
// Do wraps calling an HTTP method with retries.
547
558
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
559
logger := c .logger ()
555
560
556
561
if logger != nil {
@@ -562,6 +567,13 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
562
567
}
563
568
}
564
569
570
+ clientFactory := c .HTTPClientFactory
571
+ if clientFactory == nil {
572
+ clientFactory = & CleanPooledClientFactory {}
573
+ }
574
+ httpClient := clientFactory .New ()
575
+ defer httpClient .Done ()
576
+
565
577
var resp * http.Response
566
578
var attempt int
567
579
var shouldRetry bool
@@ -574,7 +586,6 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
574
586
if req .body != nil {
575
587
body , err := req .body ()
576
588
if err != nil {
577
- c .HTTPClient .CloseIdleConnections ()
578
589
return resp , err
579
590
}
580
591
if c , ok := body .(io.ReadCloser ); ok {
@@ -596,7 +607,8 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
596
607
}
597
608
598
609
// Attempt the request
599
- resp , doErr = c .HTTPClient .Do (req .Request )
610
+
611
+ resp , doErr = httpClient .Do (req .Request )
600
612
601
613
// Check if we should continue with retries.
602
614
shouldRetry , checkErr = c .CheckRetry (req .Context (), resp , doErr )
@@ -657,7 +669,6 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
657
669
select {
658
670
case <- req .Context ().Done ():
659
671
timer .Stop ()
660
- c .HTTPClient .CloseIdleConnections ()
661
672
return nil , req .Context ().Err ()
662
673
case <- timer .C :
663
674
}
@@ -673,8 +684,6 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
673
684
return resp , nil
674
685
}
675
686
676
- defer c .HTTPClient .CloseIdleConnections ()
677
-
678
687
err := doErr
679
688
if checkErr != nil {
680
689
err = checkErr
@@ -779,3 +788,29 @@ func (c *Client) StandardClient() *http.Client {
779
788
Transport : & RoundTripper {Client : c },
780
789
}
781
790
}
791
+
792
+ var (
793
+ _ HTTPClientFactory = & CleanPooledClientFactory {}
794
+ _ HTTPClient = & cleanClient {}
795
+ )
796
+
797
+ type CleanPooledClientFactory struct {
798
+ }
799
+
800
+ func (f * CleanPooledClientFactory ) New () HTTPClient {
801
+ return & cleanClient {
802
+ httpClient : cleanhttp .DefaultPooledClient (),
803
+ }
804
+ }
805
+
806
+ type cleanClient struct {
807
+ httpClient * http.Client
808
+ }
809
+
810
+ func (c * cleanClient ) Do (req * http.Request ) (* http.Response , error ) {
811
+ return c .httpClient .Do (req )
812
+ }
813
+
814
+ func (c * cleanClient ) Done () {
815
+ c .httpClient .CloseIdleConnections ()
816
+ }
0 commit comments