func (c *Client) Do(req *Request) (*http.Response, error) {
	if c.HTTPClient == nil {
		c.HTTPClient = cleanhttp.DefaultPooledClient()
	}
	logger := c.logger()
	if logger != nil {
		switch v := logger.(type) {
		case Logger:
			v.Printf("[DEBUG] %s %s", req.Method, req.URL)
		case hclog.Logger:
			v.Debug("performing request", "method", req.Method, "url", req.URL)
		}
	}
	var resp *http.Response
	var err error
	for i := 0; ; i++ {
		var code int  
		if req.body != nil {
			body, err := req.body()
			if err != nil {
				c.HTTPClient.CloseIdleConnections()
				return resp, err
			}
			if c, ok := body.(io.ReadCloser); ok {
				req.Body = c
			} else {
				req.Body = ioutil.NopCloser(body)
			}
		}
		if c.RequestLogHook != nil {
			switch v := logger.(type) {
			case Logger:
				c.RequestLogHook(v, req.Request, i)
			case hclog.Logger:
				c.RequestLogHook(hookLogger{v}, req.Request, i)
			default:
				c.RequestLogHook(nil, req.Request, i)
			}
		}
		resp, err = c.HTTPClient.Do(req.Request)
		if resp != nil {
			code = resp.StatusCode
		}
		checkOK, checkErr := c.CheckRetry(req.Context(), resp, err)
		if err != nil {
			switch v := logger.(type) {
			case Logger:
				v.Printf("[ERR] %s %s request failed: %v", req.Method, req.URL, err)
			case hclog.Logger:
				v.Error("request failed", "error", err, "method", req.Method, "url", req.URL)
			}
		} else {
			if c.ResponseLogHook != nil {
				switch v := logger.(type) {
				case Logger:
					c.ResponseLogHook(v, resp)
				case hclog.Logger:
					c.ResponseLogHook(hookLogger{v}, resp)
				default:
					c.ResponseLogHook(nil, resp)
				}
			}
		}
		if !checkOK {
			if checkErr != nil {
				err = checkErr
			}
			c.HTTPClient.CloseIdleConnections()
			return resp, err
		}
		remain := c.RetryMax - i
		if remain <= 0 {
			break
		}
		if err == nil && resp != nil {
			c.drainBody(resp.Body)
		}
		wait := c.Backoff(c.RetryWaitMin, c.RetryWaitMax, i, resp)
		desc := fmt.Sprintf("%s %s", req.Method, req.URL)
		if code > 0 {
			desc = fmt.Sprintf("%s (status: %d)", desc, code)
		}
		if logger != nil {
			switch v := logger.(type) {
			case Logger:
				v.Printf("[DEBUG] %s: retrying in %s (%d left)", desc, wait, remain)
			case hclog.Logger:
				v.Debug("retrying request", "request", desc, "timeout", wait, "remaining", remain)
			}
		}
		select {
		case <-req.Context().Done():
			c.HTTPClient.CloseIdleConnections()
			return nil, req.Context().Err()
		case <-time.After(wait):
		}
	}
	if c.ErrorHandler != nil {
		c.HTTPClient.CloseIdleConnections()
		return c.ErrorHandler(resp, err, c.RetryMax+1)
	}
	if resp != nil {
		resp.Body.Close()
	}
	c.HTTPClient.CloseIdleConnections()
	return nil, fmt.Errorf("%s %s giving up after %d attempts",
		req.Method, req.URL, c.RetryMax+1)
}
Do wraps calling an HTTP method with retries.
package retryablehttp
import (
	"bytes"
	"context"
	"crypto/x509"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strings"
	"sync"
	"time"
	cleanhttp "github.com/hashicorp/go-cleanhttp"
	"github.com/hashicorp/go-hclog"
)
var (
	defaultRetryWaitMin = 1 * time.Second
	defaultRetryWaitMax = 30 * time.Second
	defaultRetryMax     = 4
	defaultLogger = log.New(os.Stderr, "", log.LstdFlags)
	defaultClient = NewClient()
	respReadLimit = int64(4096)
	redirectsErrorRe = regexp.MustCompile(`stopped after \d+ redirects\z`)
)
type ReaderFunc func() (io.Reader, error)
type LenReader interface {
	Len() int
}
type Request struct {
	body ReaderFunc
	*http.Request
}
func (r *Request) WithContext(ctx context.Context) *Request {
	r.Request = r.Request.WithContext(ctx)
	return r
}
func (r *Request) BodyBytes() ([]byte, error) {
	if r.body == nil {
		return nil, nil
	}
	body, err := r.body()
	if err != nil {
		return nil, err
	}
	buf := new(bytes.Buffer)
	_, err = buf.ReadFrom(body)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}
func getBodyReaderAndContentLength(rawBody interface{}) (ReaderFunc, int64, error) {
	var bodyReader ReaderFunc
	var contentLength int64
	if rawBody != nil {
		switch body := rawBody.(type) {
		case ReaderFunc:
			bodyReader = body
			tmp, err := body()
			if err != nil {
				return nil, 0, err
			}
			if lr, ok := tmp.(LenReader); ok {
				contentLength = int64(lr.Len())
			}
			if c, ok := tmp.(io.Closer); ok {
				c.Close()
			}
		case func() (io.Reader, error):
			bodyReader = body
			tmp, err := body()
			if err != nil {
				return nil, 0, err
			}
			if lr, ok := tmp.(LenReader); ok {
				contentLength = int64(lr.Len())
			}
			if c, ok := tmp.(io.Closer); ok {
				c.Close()
			}
		case []byte:
			buf := body
			bodyReader = func() (io.Reader, error) {
				return bytes.NewReader(buf), nil
			}
			contentLength = int64(len(buf))
		case *bytes.Buffer:
			buf := body
			bodyReader = func() (io.Reader, error) {
				return bytes.NewReader(buf.Bytes()), nil
			}
			contentLength = int64(buf.Len())
		case *bytes.Reader:
			buf, err := ioutil.ReadAll(body)
			if err != nil {
				return nil, 0, err
			}
			bodyReader = func() (io.Reader, error) {
				return bytes.NewReader(buf), nil
			}
			contentLength = int64(len(buf))
		case io.ReadSeeker:
			raw := body
			bodyReader = func() (io.Reader, error) {
				_, err := raw.Seek(0, 0)
				return ioutil.NopCloser(raw), err
			}
			if lr, ok := raw.(LenReader); ok {
				contentLength = int64(lr.Len())
			}
		case io.Reader:
			buf, err := ioutil.ReadAll(body)
			if err != nil {
				return nil, 0, err
			}
			bodyReader = func() (io.Reader, error) {
				return bytes.NewReader(buf), nil
			}
			contentLength = int64(len(buf))
		default:
			return nil, 0, fmt.Errorf("cannot handle type %T", rawBody)
		}
	}
	return bodyReader, contentLength, nil
}
func FromRequest(r *http.Request) (*Request, error) {
	bodyReader, _, err := getBodyReaderAndContentLength(r.Body)
	if err != nil {
		return nil, err
	}
	return &Request{bodyReader, r}, nil
}
func NewRequest(method, url string, rawBody interface{}) (*Request, error) {
	bodyReader, contentLength, err := getBodyReaderAndContentLength(rawBody)
	if err != nil {
		return nil, err
	}
	httpReq, err := http.NewRequest(method, url, nil)
	if err != nil {
		return nil, err
	}
	httpReq.ContentLength = contentLength
	return &Request{bodyReader, httpReq}, nil
}
type Logger interface {
	Printf(string, ...interface{})
}
type hookLogger struct {
	logger hclog.Logger
}
func (h hookLogger) Printf(s string, args ...interface{}) {
	h.logger.Info(fmt.Sprintf(s, args...))
}
type RequestLogHook func(Logger, *http.Request, int)
type ResponseLogHook func(Logger, *http.Response)
type CheckRetry func(ctx context.Context, resp *http.Response, err error) (bool, error)
type Backoff func(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration
type ErrorHandler func(resp *http.Response, err error, numTries int) (*http.Response, error)
type Client struct {
	HTTPClient *http.Client  
	Logger     interface{}   
	RetryWaitMin time.Duration  
	RetryWaitMax time.Duration  
	RetryMax     int            
	RequestLogHook RequestLogHook
	ResponseLogHook ResponseLogHook
	CheckRetry CheckRetry
	Backoff Backoff
	ErrorHandler ErrorHandler
	loggerInit sync.Once
}
func NewClient() *Client {
	return &Client{
		HTTPClient:   cleanhttp.DefaultPooledClient(),
		Logger:       defaultLogger,
		RetryWaitMin: defaultRetryWaitMin,
		RetryWaitMax: defaultRetryWaitMax,
		RetryMax:     defaultRetryMax,
		CheckRetry:   DefaultRetryPolicy,
		Backoff:      DefaultBackoff,
	}
}
func (c *Client) logger() interface{} {
	c.loggerInit.Do(func() {
		if c.Logger == nil {
			return
		}
		switch c.Logger.(type) {
		case Logger:
		case hclog.Logger:
		default:
			panic(fmt.Sprintf("invalid logger type passed, must be Logger or hclog.Logger, was %T", c.Logger))
		}
	})
	return c.Logger
}
func DefaultRetryPolicy(ctx context.Context, resp *http.Response, err error) (bool, error) {
	if ctx.Err() != nil {
		return false, ctx.Err()
	}
	if err != nil {
		if v, ok := err.(*url.Error); ok {
			if redirectsErrorRe.MatchString(v.Error()) {
				return false, nil
			}
			if _, ok := v.Err.(x509.UnknownAuthorityError); ok {
				return false, nil
			}
		}
		return true, nil
	}
	if resp.StatusCode == 0 || (resp.StatusCode >= 500 && resp.StatusCode != 501) {
		return true, nil
	}
	return false, nil
}
func DefaultBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
	mult := math.Pow(2, float64(attemptNum)) * float64(min)
	sleep := time.Duration(mult)
	if float64(sleep) != mult || sleep > max {
		sleep = max
	}
	return sleep
}
func LinearJitterBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
	attemptNum++
	if max <= min {
		return min * time.Duration(attemptNum)
	}
	rand := rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
	jitter := rand.Float64() * float64(max-min)
	jitterMin := int64(jitter) + int64(min)
	return time.Duration(jitterMin * int64(attemptNum))
}
func PassthroughErrorHandler(resp *http.Response, err error, _ int) (*http.Response, error) {
	return resp, err
}