roundtripper.go raw

   1  // Copyright (c) HashiCorp, Inc.
   2  // SPDX-License-Identifier: MPL-2.0
   3  
   4  package retryablehttp
   5  
   6  import (
   7  	"errors"
   8  	"net/http"
   9  	"net/url"
  10  	"sync"
  11  )
  12  
  13  // RoundTripper implements the http.RoundTripper interface, using a retrying
  14  // HTTP client to execute requests.
  15  //
  16  // It is important to note that retryablehttp doesn't always act exactly as a
  17  // RoundTripper should. This is highly dependent on the retryable client's
  18  // configuration.
  19  type RoundTripper struct {
  20  	// The client to use during requests. If nil, the default retryablehttp
  21  	// client and settings will be used.
  22  	Client *Client
  23  
  24  	// once ensures that the logic to initialize the default client runs at
  25  	// most once, in a single thread.
  26  	once sync.Once
  27  }
  28  
  29  // init initializes the underlying retryable client.
  30  func (rt *RoundTripper) init() {
  31  	if rt.Client == nil {
  32  		rt.Client = NewClient()
  33  	}
  34  }
  35  
  36  // RoundTrip satisfies the http.RoundTripper interface.
  37  func (rt *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
  38  	rt.once.Do(rt.init)
  39  
  40  	// Convert the request to be retryable.
  41  	retryableReq, err := FromRequest(req)
  42  	if err != nil {
  43  		return nil, err
  44  	}
  45  
  46  	// Execute the request.
  47  	resp, err := rt.Client.Do(retryableReq)
  48  	// If we got an error returned by standard library's `Do` method, unwrap it
  49  	// otherwise we will wind up erroneously re-nesting the error.
  50  	if _, ok := err.(*url.Error); ok {
  51  		return resp, errors.Unwrap(err)
  52  	}
  53  
  54  	return resp, err
  55  }
  56