A Dispatcher handler that automatically retries a request when it fails
with a recoverable network error or an eligible HTTP status code. It wraps an
inner handler and re-dispatches the request, applying an exponential backoff and
honouring the Retry-After response header. When a response is partially
consumed before the failure, the handler resumes the download with a Range
request guarded by the original ETag.
The handler is most often used indirectly through RetryAgent, but it can
also be supplied directly to dispatcher.dispatch() for fine-grained
control over the retry behaviour.
import { RetryHandler } from 'undici'class RetryHandler extends DispatchHandlerImplements the DispatchHandler interface. An instance is consumed by a
single dispatch call and forwards the dispatch lifecycle to the inner handler,
re-issuing the request through the supplied dispatch function whenever a retry
is warranted.
Note: The
RetryHandlerdoes not retry over stateful bodies (for example streams orAsyncIterable), because once consumed they cannot be replayed. In these situations the body is identified as stateful and the request is rejected with theUND_ERR_REQ_RETRYerror instead of being retried.
RetryHandler Constructor
History
Reimplemented on top of the new dispatch lifecycle hooks.
new RetryHandler(options, retryHandlers): RetryHandler<DispatchOptions>retryOptions
field. Type is
DispatchOptions & { retryOptions?: RetryOptions }
.<RetryOptions>RetryOptions
.<RetryHandlers>RetryHandlers
.<RetryHandler><boolean>true
, an error is thrown on the last retry
attempt and propagated to the inner handler; when
false
, the failing response
is passed through instead, which is useful when the error body is needed or a
custom error handler is in place.
Default:
true
.<Function>Error
to stop retrying, or with
null
to
schedule another attempt.
Default:
the built-in retry strategy described
below.<Error><RetryContext>RetryContext
.<Function><number>5
.<number>30000
(30 seconds).<number>500
(half a second).<number>2
.<boolean>true
, the delay before the next retry is inferred
from the
Retry-After
response header when present.
Default:
true
.<string>['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE']
.<number>[500, 502, 503, 504, 429]
.<string>['ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN', 'ENETUNREACH', 'EHOSTDOWN', 'EHOSTUNREACH', 'EPIPE', 'UND_ERR_SOCKET']
.The default retry strategy computes the delay before the next attempt as
minTimeout * timeoutFactor ** (counter - 1), capped at maxTimeout. When a
Retry-After header is present it takes precedence (interpreted as seconds, or as
an HTTP date), still capped at maxTimeout. The default strategy stops retrying
once counter exceeds maxRetries, when the error code is not in errorCodes,
when the method is not in methods, or when the response status code is not in
statusCodes.
<RetryState>RetryState
.<DispatchOptions>retryOptions
. Type is
DispatchOptions & { retryOptions?: RetryOptions }
.The context object passed as the second argument to the retry callback.
<number>1
for the first
retry.Represents the retry state for a given request.
<Function>(options, handler) => boolean
.<DispatchHandler>import { Client, RetryHandler } from 'undici'
const client = new Client(`http://localhost:${server.address().port}`)
const chunks = []
const handler = new RetryHandler(
{
...dispatchOptions,
retryOptions: {
// Custom retry decision function.
retry (err, { state, opts }, callback) {
if (err.code === 'UND_ERR_DESTROYED') {
callback(err)
return
}
if (err.statusCode === 206) {
callback(err)
return
}
setTimeout(() => callback(null), 1000)
}
}
},
{
dispatch (...args) {
return client.dispatch(...args)
},
handler: {
onRequestStart () {},
onResponseStart (controller, status, headers) {
// Do something with the response headers.
},
onResponseData (controller, chunk) {
chunks.push(chunk)
},
onResponseEnd () {},
onResponseError (controller, err) {
// Handle the error.
}
}
}
)
client.dispatch(dispatchOptions, handler)A minimal handler that relies entirely on the default retry options:
import { Client, RetryHandler } from 'undici'
const client = new Client(`http://localhost:${server.address().port}`)
const handler = new RetryHandler(dispatchOptions, {
dispatch: client.dispatch.bind(client),
handler: {
onRequestStart () {},
onResponseStart (controller, status, headers) {},
onResponseData (controller, chunk) {},
onResponseEnd () {},
onResponseError (controller, err) {}
}
})
client.dispatch(dispatchOptions, handler)