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