1515package  client
1616
1717import  (
18+ 	"crypto/tls" 
19+ 	"errors" 
20+ 	"fmt" 
1821	"net/http" 
22+ 	"net/url" 
23+ 	"regexp" 
24+ 	"strings" 
1925	"time" 
2026
21- 	"github.com/hashicorp/go-retryablehttp " 
27+ 	"github.com/avast/retry-go/v4 " 
2228)
2329
2430// Option is a functional option for customizing static signatures. 
@@ -30,7 +36,6 @@ type options struct {
3036	RetryWaitMin         time.Duration 
3137	RetryWaitMax         time.Duration 
3238	InsecureTLS          bool 
33- 	Logger               interface {}
3439	NoDisableKeepalives  bool 
3540	Headers              map [string ][]string 
3641}
@@ -81,14 +86,9 @@ func WithRetryWaitMax(t time.Duration) Option {
8186	}
8287}
8388
84- // WithLogger sets the logger; it must implement either retryablehttp.Logger or retryablehttp.LeveledLogger; if not, this will not take effect. 
85- func  WithLogger (logger  interface {}) Option  {
86- 	return  func (o  * options ) {
87- 		switch  logger .(type ) {
88- 		case  retryablehttp.Logger , retryablehttp.LeveledLogger :
89- 			o .Logger  =  logger 
90- 		}
91- 	}
89+ // WithLogger sets the logger; this method is deprecated and will not take any effect. 
90+ func  WithLogger (interface {}) Option  {
91+ 	return  func (* options ) {}
9292}
9393
9494// WithInsecureTLS disables TLS verification. 
@@ -113,33 +113,75 @@ func WithHeaders(h map[string][]string) Option {
113113}
114114
115115type  roundTripper  struct  {
116- 	http.RoundTripper 
117- 	UserAgent  string 
118- 	Headers    map [string ][]string 
116+ 	inner  http.RoundTripper 
117+ 	* options 
119118}
120119
121120// RoundTrip implements `http.RoundTripper` 
122- func  (rt  * roundTripper ) RoundTrip (req  * http.Request ) (* http.Response , error ) {
123- 	req .Header .Set ("User-Agent" , rt .UserAgent )
124- 	for  k , v  :=  range  rt .Headers  {
121+ func  (rt  * roundTripper ) RoundTrip (req  * http.Request ) (res   * http.Response ,  err  error ) {
122+ 	req .Header .Set ("User-Agent" , rt .options . UserAgent )
123+ 	for  k , v  :=  range  rt .options . Headers  {
125124		for  _ , h  :=  range  v  {
126125			req .Header .Add (k , h )
127126		}
128127	}
129- 	return  rt .RoundTripper .RoundTrip (req )
128+ 
129+ 	err  =  retry .Do (func () (err  error ) {
130+ 		res , err  =  rt .inner .RoundTrip (req )
131+ 		if  retryErr  :=  shouldRetry (res , err ); retryErr  !=  nil  {
132+ 			return  retryErr 
133+ 		}
134+ 		return  nil 
135+ 	},
136+ 		retry .Attempts (rt .options .RetryCount ),
137+ 		retry .Delay (rt .options .RetryWaitMin ),
138+ 		retry .MaxDelay (rt .options .RetryWaitMax ),
139+ 	)
140+ 
141+ 	return  res , err 
130142}
131143
132- func  createRoundTripper (inner  http.RoundTripper , o  * options ) http.RoundTripper  {
144+ var  tooManyRedirectyRe  =  regexp .MustCompile (`stopped after \d+ redirects\z` )
145+ 
146+ func  shouldRetry (resp  * http.Response , err  error ) error  {
147+ 	if  err  !=  nil  {
148+ 		urlErr  :=  & url.Error {}
149+ 
150+ 		// Filter well known URL errors 
151+ 		if  errors .As (err , & urlErr ) {
152+ 			certVerificationErr  :=  & tls.CertificateVerificationError {}
153+ 
154+ 			if  tooManyRedirectyRe .MatchString (urlErr .Error ()) || 
155+ 				strings .Contains (urlErr .Error (), "unsupported protocol scheme" ) || 
156+ 				strings .Contains (urlErr .Error (), "invalid header" ) || 
157+ 				strings .Contains (urlErr .Error (), "certificate is not trusted" ) || 
158+ 				errors .As (urlErr .Err , & certVerificationErr ) {
159+ 				return  nil 
160+ 			}
161+ 		}
162+ 
163+ 		// Retry any other errror 
164+ 		return  err 
165+ 	}
166+ 
167+ 	if  resp .StatusCode  ==  http .StatusTooManyRequests  {
168+ 		return  fmt .Errorf ("retry %d: %s" , resp .StatusCode , resp .Status )
169+ 	}
170+ 
171+ 	if  resp .StatusCode  ==  0  ||  (resp .StatusCode  >=  500  && 
172+ 		resp .StatusCode  !=  http .StatusNotImplemented ) {
173+ 		return  fmt .Errorf ("retry unexpected HTTP status %d: %s" , resp .StatusCode , resp .Status )
174+ 	}
175+ 
176+ 	return  nil 
177+ }
178+ 
179+ func  wrapRoundTripper (inner  http.RoundTripper , o  * options ) http.RoundTripper  {
133180	if  inner  ==  nil  {
134181		inner  =  http .DefaultTransport 
135182	}
136- 	if  o .UserAgent  ==  ""  &&  o .Headers  ==  nil  {
137- 		// There's nothing to do... 
138- 		return  inner 
139- 	}
140183	return  & roundTripper {
141- 		RoundTripper : inner ,
142- 		UserAgent :    o .UserAgent ,
143- 		Headers :      o .Headers ,
184+ 		inner :   inner ,
185+ 		options : o ,
144186	}
145187}
0 commit comments