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