1818package fetcher
1919
2020import (
21+ "context"
2122 "fmt"
2223 "io"
2324 "net/http"
2425 "strconv"
26+ "time"
2527
28+ "github.com/cenkalti/backoff/v5"
2629 "github.com/theupdateframework/go-tuf/v2/metadata"
2730)
2831
@@ -39,11 +42,11 @@ type Fetcher interface {
3942
4043// DefaultFetcher implements Fetcher
4144type DefaultFetcher struct {
45+ // httpClient configuration
4246 httpUserAgent string
4347 client httpClient
44- // for implementation of retry logic in a future pull request
45- // retryInterval time.Duration
46- // retryCount int
48+ // retry logic configuration
49+ retryOptions []backoff.RetryOption
4750}
4851
4952func (d * DefaultFetcher ) SetHTTPUserAgent (httpUserAgent string ) {
@@ -61,48 +64,59 @@ func (d *DefaultFetcher) DownloadFile(urlPath string, maxLength int64) ([]byte,
6164 if d .httpUserAgent != "" {
6265 req .Header .Set ("User-Agent" , d .httpUserAgent )
6366 }
64- // Execute the request.
65- res , err := d .client .Do (req )
66- if err != nil {
67- return nil , err
68- }
69- defer res .Body .Close ()
70- // Handle HTTP status codes.
71- if res .StatusCode != http .StatusOK {
72- return nil , & metadata.ErrDownloadHTTP {StatusCode : res .StatusCode , URL : urlPath }
73- }
74- var length int64
75- // Get content length from header (might not be accurate, -1 or not set).
76- if header := res .Header .Get ("Content-Length" ); header != "" {
77- length , err = strconv .ParseInt (header , 10 , 0 )
67+
68+ operation := func () ([]byte , error ) {
69+ // Execute the request.
70+ res , err := d .client .Do (req )
71+ if err != nil {
72+ return nil , err
73+ }
74+ defer res .Body .Close ()
75+ // Handle HTTP status codes.
76+ if res .StatusCode != http .StatusOK {
77+ return nil , & metadata.ErrDownloadHTTP {StatusCode : res .StatusCode , URL : urlPath }
78+ }
79+ var length int64
80+ // Get content length from header (might not be accurate, -1 or not set).
81+ if header := res .Header .Get ("Content-Length" ); header != "" {
82+ length , err = strconv .ParseInt (header , 10 , 0 )
83+ if err != nil {
84+ return nil , err
85+ }
86+ // Error if the reported size is greater than what is expected.
87+ if length > maxLength {
88+ return nil , & metadata.ErrDownloadLengthMismatch {Msg : fmt .Sprintf ("download failed for %s, length %d is larger than expected %d" , urlPath , length , maxLength )}
89+ }
90+ }
91+ // Although the size has been checked above, use a LimitReader in case
92+ // the reported size is inaccurate, or size is -1 which indicates an
93+ // unknown length. We read maxLength + 1 in order to check if the read data
94+ // surpassed our set limit.
95+ data , err := io .ReadAll (io .LimitReader (res .Body , maxLength + 1 ))
7896 if err != nil {
7997 return nil , err
8098 }
8199 // Error if the reported size is greater than what is expected.
100+ length = int64 (len (data ))
82101 if length > maxLength {
83102 return nil , & metadata.ErrDownloadLengthMismatch {Msg : fmt .Sprintf ("download failed for %s, length %d is larger than expected %d" , urlPath , length , maxLength )}
84103 }
104+
105+ return data , nil
85106 }
86- // Although the size has been checked above, use a LimitReader in case
87- // the reported size is inaccurate, or size is -1 which indicates an
88- // unknown length. We read maxLength + 1 in order to check if the read data
89- // surpassed our set limit.
90- data , err := io .ReadAll (io .LimitReader (res .Body , maxLength + 1 ))
107+ data , err := backoff .Retry (context .Background (), operation , d .retryOptions ... )
91108 if err != nil {
92109 return nil , err
93110 }
94- // Error if the reported size is greater than what is expected.
95- length = int64 (len (data ))
96- if length > maxLength {
97- return nil , & metadata.ErrDownloadLengthMismatch {Msg : fmt .Sprintf ("download failed for %s, length %d is larger than expected %d" , urlPath , length , maxLength )}
98- }
99111
100112 return data , nil
101113}
102114
103115func NewDefaultFetcher () * DefaultFetcher {
104116 return & DefaultFetcher {
105117 client : http .DefaultClient ,
118+ // default to attempting the HTTP request once
119+ retryOptions : []backoff.RetryOption {backoff .WithMaxTries (1 )},
106120 }
107121}
108122
@@ -136,3 +150,13 @@ func (f *DefaultFetcher) SetTransport(rt http.RoundTripper) error {
136150 f .client = hc
137151 return nil
138152}
153+
154+ func (f * DefaultFetcher ) SetRetry (retryInterval time.Duration , retryCount uint ) {
155+ constantBackOff := backoff .WithBackOff (backoff .NewConstantBackOff (retryInterval ))
156+ maxTryCount := backoff .WithMaxTries (retryCount )
157+ f .SetRetryOptions (constantBackOff , maxTryCount )
158+ }
159+
160+ func (f * DefaultFetcher ) SetRetryOptions (retryOptions ... backoff.RetryOption ) {
161+ f .retryOptions = retryOptions
162+ }
0 commit comments