-
Notifications
You must be signed in to change notification settings - Fork 525
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
- Loading branch information
Showing
10 changed files
with
183 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# Class: RetryAgent | ||
|
||
Extends: `undici.Dispatcher` | ||
|
||
A `undici.Dispatcher` that allows to automatically retry a request. | ||
Wraps a `undici.RetryHandler`. | ||
|
||
## `new RetryAgent(dispatcher, [options])` | ||
|
||
Arguments: | ||
|
||
* **dispatcher** `undici.Dispatcher` (required) - the dispactgher to wrap | ||
* **options** `RetryHandlerOptions` (optional) - the options | ||
|
||
Returns: `ProxyAgent` | ||
|
||
### Parameter: `RetryHandlerOptions` | ||
|
||
- **retry** `(err: Error, context: RetryContext, callback: (err?: Error | null) => void) => void` (optional) - Function to be called after every retry. It should pass error if no more retries should be performed. | ||
- **maxRetries** `number` (optional) - Maximum number of retries. Default: `5` | ||
- **maxTimeout** `number` (optional) - Maximum number of milliseconds to wait before retrying. Default: `30000` (30 seconds) | ||
- **minTimeout** `number` (optional) - Minimum number of milliseconds to wait before retrying. Default: `500` (half a second) | ||
- **timeoutFactor** `number` (optional) - Factor to multiply the timeout by for each retry attempt. Default: `2` | ||
- **retryAfter** `boolean` (optional) - It enables automatic retry after the `Retry-After` header is received. Default: `true` | ||
- | ||
- **methods** `string[]` (optional) - Array of HTTP methods to retry. Default: `['GET', 'PUT', 'HEAD', 'OPTIONS', 'DELETE']` | ||
- **statusCodes** `number[]` (optional) - Array of HTTP status codes to retry. Default: `[429, 500, 502, 503, 504]` | ||
- **errorCodes** `string[]` (optional) - Array of Error codes to retry. Default: `['ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN','ENETUNREACH', 'EHOSTDOWN', 'UND_ERR_SOCKET']` | ||
|
||
**`RetryContext`** | ||
|
||
- `state`: `RetryState` - Current retry state. It can be mutated. | ||
- `opts`: `Dispatch.DispatchOptions & RetryOptions` - Options passed to the retry handler. | ||
|
||
Example: | ||
|
||
```js | ||
import { Agent, RetryAgent } from 'undici' | ||
|
||
const agent = new RetryAgent(new Agent()) | ||
|
||
const res = await agent.request('http://example.com') | ||
console.log(res.statuCode) | ||
console.log(await res.body.text()) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
'use strict' | ||
|
||
const Dispatcher = require('./dispatcher') | ||
const RetryHandler = require('./handler/RetryHandler') | ||
|
||
class RetryAgent extends Dispatcher { | ||
#agent = null | ||
#options = null | ||
constructor (agent, options = {}) { | ||
super(options) | ||
this.#agent = agent | ||
this.#options = options | ||
} | ||
|
||
dispatch (opts, handler) { | ||
const retry = new RetryHandler({ | ||
...opts, | ||
retryOptions: this.#options | ||
}, { | ||
dispatch: this.#agent.dispatch.bind(this.#agent), | ||
handler | ||
}) | ||
return this.#agent.dispatch(opts, retry) | ||
} | ||
|
||
close () { | ||
return this.#agent.close() | ||
} | ||
|
||
destroy () { | ||
return this.#agent.destroy() | ||
} | ||
} | ||
|
||
module.exports = RetryAgent |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
'use strict' | ||
|
||
const { tspl } = require('@matteo.collina/tspl') | ||
const { test, after } = require('node:test') | ||
const { createServer } = require('node:http') | ||
const { once } = require('node:events') | ||
|
||
const { RetryAgent, Client } = require('..') | ||
test('Should retry status code', async t => { | ||
t = tspl(t, { plan: 2 }) | ||
|
||
let counter = 0 | ||
const server = createServer() | ||
const opts = { | ||
maxRetries: 5, | ||
timeout: 1, | ||
timeoutFactor: 1 | ||
} | ||
|
||
server.on('request', (req, res) => { | ||
switch (counter++) { | ||
case 0: | ||
req.destroy() | ||
return | ||
case 1: | ||
res.writeHead(500) | ||
res.end('failed') | ||
return | ||
case 2: | ||
res.writeHead(200) | ||
res.end('hello world!') | ||
return | ||
default: | ||
t.fail() | ||
} | ||
}) | ||
|
||
server.listen(0, () => { | ||
const client = new Client(`http://localhost:${server.address().port}`) | ||
const agent = new RetryAgent(client, opts) | ||
|
||
after(async () => { | ||
await agent.close() | ||
server.close() | ||
|
||
await once(server, 'close') | ||
}) | ||
|
||
agent.request({ | ||
method: 'GET', | ||
path: '/', | ||
headers: { | ||
'content-type': 'application/json' | ||
} | ||
}).then((res) => { | ||
t.equal(res.statusCode, 200) | ||
res.body.setEncoding('utf8') | ||
let chunks = '' | ||
res.body.on('data', chunk => { chunks += chunk }) | ||
res.body.on('end', () => { | ||
t.equal(chunks, 'hello world!') | ||
}) | ||
}) | ||
}) | ||
|
||
await t.completed | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { expectAssignable } from 'tsd' | ||
import { RetryAgent, Agent } from '../..' | ||
|
||
const dispatcher = new Agent() | ||
|
||
expectAssignable<RetryAgent>(new RetryAgent(dispatcher)) | ||
expectAssignable<RetryAgent>(new RetryAgent(dispatcher, { maxRetries: 5 })) | ||
|
||
{ | ||
const retryAgent = new RetryAgent(dispatcher) | ||
|
||
// close | ||
expectAssignable<Promise<void>>(retryAgent.close()) | ||
|
||
// dispatch | ||
expectAssignable<boolean>(retryAgent.dispatch({ origin: '', path: '', method: 'GET' }, {})) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import Agent from './agent' | ||
import buildConnector from './connector'; | ||
import Dispatcher from './dispatcher' | ||
import { IncomingHttpHeaders } from './header' | ||
import RetryHandler from './retry-handler' | ||
|
||
export default RetryAgent | ||
|
||
declare class RetryAgent extends Dispatcher { | ||
constructor(dispatcher: Dispatcher, options?: RetryHandler.RetryOptions) | ||
} |