@@ -4,8 +4,9 @@ import util from 'util';
44
55import { DownloadHTTPError , DownloadLengthMismatchError } from './error' ;
66import { withTempFile } from './utils/tmpfile' ;
7+ import { promiseRetry } from '@gar/promise-retry' ;
78
8- import type { TimeoutsOptions } from 'retry' ;
9+ import type { OperationOptions , TimeoutsOptions } from 'retry' ;
910
1011const log = debug ( 'tuf:fetch' ) ;
1112
@@ -95,31 +96,69 @@ interface FetcherOptions {
9596export class DefaultFetcher extends BaseFetcher {
9697 private userAgent ?: string ;
9798 private timeout ?: number ;
98- private retry ?: Retry ;
99+ private retry ?: OperationOptions ;
99100
100101 constructor ( options : FetcherOptions = { } ) {
101102 super ( ) ;
102103 this . userAgent = options . userAgent ;
103104 this . timeout = options . timeout ;
104- this . retry = options . retry ;
105+ // Map retry to OperationOptions
106+ if ( options . retry === true ) {
107+ this . retry = { forever : true } ;
108+ } else if ( options . retry === false || options . retry === undefined ) {
109+ this . retry = undefined ;
110+ } else if ( typeof options . retry === 'number' ) {
111+ if ( options . retry < 0 ) {
112+ throw new Error ( 'Retry count must be non-negative number' ) ;
113+ }
114+ this . retry = { retries : options . retry } ;
115+ } else {
116+ this . retry = options . retry ;
117+ }
105118 }
106119
107120 public override async fetch (
108121 url : string
109122 ) : Promise < ReadableStream < Uint8Array < ArrayBuffer > > > {
110- log ( 'GET %s' , url ) ;
111- const response = await fetch ( url , {
112- headers : {
113- [ USER_AGENT_HEADER ] : this . userAgent || '' ,
114- } ,
115- signal : this . timeout ? AbortSignal . timeout ( this . timeout ) : undefined ,
116- } ) ;
123+ const shouldRetry = this . retry !== undefined ;
124+
125+ return promiseRetry (
126+ async ( retry : ( err : Error ) => never , number : number ) => {
127+ log ( 'GET %s (attempt %d)' , url , number ) ;
128+
129+ let response : Response ;
130+ try {
131+ response = await fetch ( url , {
132+ headers : {
133+ [ USER_AGENT_HEADER ] : this . userAgent || '' ,
134+ } ,
135+ signal : this . timeout
136+ ? AbortSignal . timeout ( this . timeout )
137+ : undefined ,
138+ } ) ;
139+ } catch ( error ) {
140+ const err = error instanceof Error ? error : new Error ( String ( error ) ) ;
141+ if ( shouldRetry ) {
142+ return retry ( err ) ;
143+ }
144+ throw err ;
145+ }
117146
118- if ( ! response . ok || ! response ?. body ) {
119- throw new DownloadHTTPError ( 'Failed to download' , response . status ) ;
120- }
147+ if ( ! response . ok || ! response . body ) {
148+ const err = new DownloadHTTPError (
149+ 'Failed to download' ,
150+ response . status
151+ ) ;
152+ if ( shouldRetry && response . status >= 500 && response . status < 600 ) {
153+ return retry ( err ) ;
154+ }
155+ throw err ;
156+ }
121157
122- return response . body ;
158+ return response . body ;
159+ } ,
160+ this . retry
161+ ) ;
123162 }
124163}
125164
0 commit comments