undici 4.9.2 → 4.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -93,7 +93,7 @@ Arguments:
93
93
 
94
94
  * **url** `string | URL | UrlObject`
95
95
  * **options** [`RequestOptions`](./docs/api/Dispatcher.md#parameter-requestoptions)
96
- * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcherdispatcher)
96
+ * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
97
97
  * **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`
98
98
  * **maxRedirections** `Integer` - Default: `0`
99
99
 
@@ -109,7 +109,7 @@ Arguments:
109
109
 
110
110
  * **url** `string | URL | UrlObject`
111
111
  * **options** [`StreamOptions`](./docs/api/Dispatcher.md#parameter-streamoptions)
112
- * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcherdispatcher)
112
+ * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
113
113
  * **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`
114
114
  * **maxRedirections** `Integer` - Default: `0`
115
115
  * **factory** `Dispatcher.stream.factory`
@@ -118,7 +118,7 @@ Returns a promise with the result of the `Dispatcher.stream` method.
118
118
 
119
119
  Calls `options.dispatcher.stream(options, factory)`.
120
120
 
121
- See [Dispatcher.stream](docs/api/Dispatcher.md#dispatcherstream) for more details.
121
+ See [Dispatcher.stream](docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback) for more details.
122
122
 
123
123
  ### `undici.pipeline([url, options, ]handler): Duplex`
124
124
 
@@ -126,7 +126,7 @@ Arguments:
126
126
 
127
127
  * **url** `string | URL | UrlObject`
128
128
  * **options** [`PipelineOptions`](docs/api/Dispatcher.md#parameter-pipelineoptions)
129
- * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcherdispatcher)
129
+ * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
130
130
  * **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`
131
131
  * **maxRedirections** `Integer` - Default: `0`
132
132
  * **handler** `Dispatcher.pipeline.handler`
@@ -135,7 +135,7 @@ Returns: `stream.Duplex`
135
135
 
136
136
  Calls `options.dispatch.pipeline(options, handler)`.
137
137
 
138
- See [Dispatcher.pipeline](docs/api/Dispatcher.md#dispatcherpipeline) for more details.
138
+ See [Dispatcher.pipeline](docs/api/Dispatcher.md#dispatcherpipelineoptions-handler) for more details.
139
139
 
140
140
  ### `undici.connect([url, options]): Promise`
141
141
 
@@ -145,7 +145,7 @@ Arguments:
145
145
 
146
146
  * **url** `string | URL | UrlObject`
147
147
  * **options** [`ConnectOptions`](docs/api/Dispatcher.md#parameter-connectoptions)
148
- * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcherdispatcher)
148
+ * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
149
149
  * **maxRedirections** `Integer` - Default: `0`
150
150
  * **callback** `(err: Error | null, data: ConnectData | null) => void` (optional)
151
151
 
@@ -153,7 +153,7 @@ Returns a promise with the result of the `Dispatcher.connect` method.
153
153
 
154
154
  Calls `options.dispatch.connect(options)`.
155
155
 
156
- See [Dispatcher.connect](docs/api/Dispatcher.md#dispatcherconnect) for more details.
156
+ See [Dispatcher.connect](docs/api/Dispatcher.md#dispatcherconnectoptions-callback) for more details.
157
157
 
158
158
  ### `undici.fetch(input[, init]): Promise`
159
159
 
@@ -170,7 +170,7 @@ Basic usage example:
170
170
 
171
171
  ```js
172
172
  import {fetch} from 'undici';
173
-
173
+
174
174
  async function fetchJson() {
175
175
  const res = await fetch('https://example.com')
176
176
  const json = await res.json()
@@ -180,7 +180,7 @@ Basic usage example:
180
180
 
181
181
  #### `response.body`
182
182
 
183
- Nodejs has two kinds of streams: [web streams](https://nodejs.org/dist/latest-v16.x/docs/api/webstreams.html) which follow the API of the WHATWG web standard found in browsers, and an older Node-specific [streams API](https://nodejs.org/api/stream.html). `response.body` returns a readable web stream. If you would prefer to work with a Node stream you can convert a web stream using `.fromWeb()`.
183
+ Nodejs has two kinds of streams: [web streams](https://nodejs.org/dist/latest-v16.x/docs/api/webstreams.html) which follow the API of the WHATWG web standard found in browsers, and an older Node-specific [streams API](https://nodejs.org/api/stream.html). `response.body` returns a readable web stream. If you would prefer to work with a Node stream you can convert a web stream using `.fromWeb()`.
184
184
 
185
185
  ```js
186
186
  import {fetch} from 'undici';
@@ -208,7 +208,7 @@ garbage collection in Node is less aggressive and deterministic (due to the lack
208
208
  of clear idle periods that browser have through the rendering refresh rate)
209
209
  which means that leaving the release of connection resources to the garbage collector
210
210
  can lead to excessive connection usage, reduced performance (due to less connection re-use),
211
- and even stalls or deadlocks when running out of connections. Therefore, it is highly
211
+ and even stalls or deadlocks when running out of connections. Therefore, it is highly
212
212
  recommended to always either consume or cancel the response body.
213
213
 
214
214
  ```js
@@ -234,7 +234,7 @@ Arguments:
234
234
 
235
235
  * **url** `string | URL | UrlObject`
236
236
  * **options** [`UpgradeOptions`](docs/api/Dispatcher.md#parameter-upgradeoptions)
237
- * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcherdispatcher)
237
+ * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
238
238
  * **maxRedirections** `Integer` - Default: `0`
239
239
  * **callback** `(error: Error | null, data: UpgradeData) => void` (optional)
240
240
 
package/docs/api/Agent.md CHANGED
@@ -16,64 +16,64 @@ Returns: `Agent`
16
16
 
17
17
  ### Parameter: `AgentOptions`
18
18
 
19
- Extends: [`ClientOptions`](docs/api/Pool.md#parameter-pooloptions)
19
+ Extends: [`ClientOptions`](Pool.md#parameter-pooloptions)
20
20
 
21
- * **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Pool(origin, opts)`
21
+ * **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Pool(origin, opts)`
22
22
  * **maxRedirections** `Integer` - Default: `0`. The number of HTTP redirection to follow unless otherwise specified in `DispatchOptions`.
23
23
 
24
24
  ## Instance Properties
25
25
 
26
26
  ### `Agent.closed`
27
27
 
28
- Implements [Client.closed](docs/api/Client.md#clientclosed)
28
+ Implements [Client.closed](Client.md#clientclosed)
29
29
 
30
30
  ### `Agent.destroyed`
31
31
 
32
- Implements [Client.destroyed](docs/api/Client.md#clientdestroyed)
32
+ Implements [Client.destroyed](Client.md#clientdestroyed)
33
33
 
34
34
  ## Instance Methods
35
35
 
36
36
  ### `Agent.close([callback])`
37
37
 
38
- Implements [`Dispatcher.close([callback])`](docs/api/Dispatcher.md#clientclose-callback-).
38
+ Implements [`Dispatcher.close([callback])`](Dispatcher.md#dispatcherclosecallback-promise).
39
39
 
40
40
  ### `Agent.destroy([error, callback])`
41
41
 
42
- Implements [`Dispatcher.destroy([error, callback])`](docs/api/Dispatcher.md#dispatcher-callback-).
42
+ Implements [`Dispatcher.destroy([error, callback])`](Dispatcher.md#dispatcherdestroyerror-callback-promise).
43
43
 
44
- ### `Agent.dispatch(options, handlers: AgentDispatchOptions)`
44
+ ### `Agent.dispatch(options, handler: AgentDispatchOptions)`
45
45
 
46
- Implements [`Dispatcher.dispatch(options, handlers)`](docs/api/Dispatcher.md#clientdispatchoptions-handlers).
46
+ Implements [`Dispatcher.dispatch(options, handler)`](Dispatcher.md#dispatcherdispatchoptions-handler).
47
47
 
48
48
  #### Parameter: `AgentDispatchOptions`
49
49
 
50
- Extends: [`DispatchOptions``](docs/api/Dispatcher.md#parameter-dispatchoptions)
50
+ Extends: [`DispatchOptions`](Dispatcher.md#parameter-dispatchoptions)
51
51
 
52
52
  * **origin** `string | URL`
53
53
  * **maxRedirections** `Integer`.
54
54
 
55
- Implements [`Dispatcher.destroy([error, callback])`](docs/api/Dispatcher.md#dispatcher-callback-).
55
+ Implements [`Dispatcher.destroy([error, callback])`](Dispatcher.md#dispatcherdestroyerror-callback-promise).
56
56
 
57
57
  ### `Agent.connect(options[, callback])`
58
58
 
59
- See [`Dispatcher.connect(options[, callback])`](docs/api/Dispatcher.md#clientconnectoptions--callback).
59
+ See [`Dispatcher.connect(options[, callback])`](Dispatcher.md#dispatcherconnectoptions-callback).
60
60
 
61
- ### `Agent.dispatch(options, handlers)`
61
+ ### `Agent.dispatch(options, handler)`
62
62
 
63
- Implements [`Dispatcher.dispatch(options, handlers)`](docs/api/Dispatcher.md#clientdispatchoptions-handlers).
63
+ Implements [`Dispatcher.dispatch(options, handler)`](Dispatcher.md#dispatcherdispatchoptions-handler).
64
64
 
65
65
  ### `Agent.pipeline(options, handler)`
66
66
 
67
- See [`Dispatcher.pipeline(options, handler)`](docs/api/Dispatcher.md#clientpipelineoptions-handler).
67
+ See [`Dispatcher.pipeline(options, handler)`](Dispatcher.md#dispatcherpipelineoptions-handler).
68
68
 
69
69
  ### `Agent.request(options[, callback])`
70
70
 
71
- See [`Dispatcher.request(options [, callback])`](docs/api/Dispatcher.md#clientrequestoptions--callback).
71
+ See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
72
72
 
73
73
  ### `Agent.stream(options, factory[, callback])`
74
74
 
75
- See [`Dispatcher.stream(options, factory[, callback])`](docs/api/Dispatcher.md#clientstreamoptions-factory--callback).
75
+ See [`Dispatcher.stream(options, factory[, callback])`](Dispatcher.md#dispatcherstreamoptions-factory-callback).
76
76
 
77
77
  ### `Agent.upgrade(options[, callback])`
78
78
 
79
- See [`Dispatcher.upgrade(options[, callback])`](docs/api/Dispatcher.md#clientupgradeoptions-callback).
79
+ See [`Dispatcher.upgrade(options[, callback])`](Dispatcher.md#dispatcherupgradeoptions-callback).
@@ -2,7 +2,7 @@
2
2
 
3
3
  Extends: `undici.Dispatcher`
4
4
 
5
- A pool of [Pool](docs/api/Pool.md) instances connected to multiple upstreams.
5
+ A pool of [Pool](Pool.md) instances connected to multiple upstreams.
6
6
 
7
7
  Requests are not guaranteed to be dispatched in order of invocation.
8
8
 
@@ -17,7 +17,7 @@ Arguments:
17
17
 
18
18
  The `PoolOptions` are passed to each of the `Pool` instances being created.
19
19
 
20
- See: [`PoolOptions`](docs/api/Pool.md#parameter-pooloptions)
20
+ See: [`PoolOptions`](Pool.md#parameter-pooloptions)
21
21
 
22
22
  ## Instance Properties
23
23
 
@@ -25,17 +25,13 @@ See: [`PoolOptions`](docs/api/Pool.md#parameter-pooloptions)
25
25
 
26
26
  Returns an array of upstreams that were previously added.
27
27
 
28
- ### `BalancedPool.busy`
29
-
30
- Implements [Client.busy](docs/api/Client.md#clientbusy)
31
-
32
28
  ### `BalancedPool.closed`
33
29
 
34
- Implements [Client.closed](docs/api/Client.md#clientclosed)
30
+ Implements [Client.closed](Client.md#clientclosed)
35
31
 
36
32
  ### `BalancedPool.destroyed`
37
33
 
38
- Implements [Client.destroyed](docs/api/Client.md#clientdestroyed)
34
+ Implements [Client.destroyed](Client.md#clientdestroyed)
39
35
 
40
36
  ## Instance Methods
41
37
 
@@ -53,46 +49,46 @@ Removes an upstream that was previously addded.
53
49
 
54
50
  ### `BalancedPool.close([callback])`
55
51
 
56
- Implements [`Dispatcher.close([callback])`](docs/api/Dispatcher.md#clientclose-callback-).
52
+ Implements [`Dispatcher.close([callback])`](Dispatcher.md#dispatcherclosecallback-promise).
57
53
 
58
54
  ### `BalancedPool.destroy([error, callback])`
59
55
 
60
- Implements [`Dispatcher.destroy([error, callback])`](docs/api/Dispatcher.md#dispatcher-callback-).
56
+ Implements [`Dispatcher.destroy([error, callback])`](Dispatcher.md#dispatcherdestroyerror-callback-promise).
61
57
 
62
58
  ### `BalancedPool.connect(options[, callback])`
63
59
 
64
- See [`Dispatcher.connect(options[, callback])`](docs/api/Dispatcher.md#clientconnectoptions--callback).
60
+ See [`Dispatcher.connect(options[, callback])`](Dispatcher.md#dispatcherconnectoptions-callback).
65
61
 
66
62
  ### `BalancedPool.dispatch(options, handlers)`
67
63
 
68
- Implements [`Dispatcher.dispatch(options, handlers)`](docs/api/Dispatcher.md#clientdispatchoptions-handlers).
64
+ Implements [`Dispatcher.dispatch(options, handlers)`](Dispatcher.md#dispatcherdispatchoptions-handler).
69
65
 
70
66
  ### `BalancedPool.pipeline(options, handler)`
71
67
 
72
- See [`Dispatcher.pipeline(options, handler)`](docs/api/Dispatcher.md#clientpipelineoptions-handler).
68
+ See [`Dispatcher.pipeline(options, handler)`](Dispatcher.md#dispatcherpipelineoptions-handler).
73
69
 
74
70
  ### `BalancedPool.request(options[, callback])`
75
71
 
76
- See [`Dispatcher.request(options [, callback])`](docs/api/Dispatcher.md#clientrequestoptions--callback).
72
+ See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
77
73
 
78
74
  ### `BalancedPool.stream(options, factory[, callback])`
79
75
 
80
- See [`Dispatcher.stream(options, factory[, callback])`](docs/api/Dispatcher.md#clientstreamoptions-factory--callback).
76
+ See [`Dispatcher.stream(options, factory[, callback])`](Dispatcher.md#dispatcherstreamoptions-factory-callback).
81
77
 
82
78
  ### `BalancedPool.upgrade(options[, callback])`
83
79
 
84
- See [`Dispatcher.upgrade(options[, callback])`](docs/api/Dispatcher.md#clientupgradeoptions-callback).
80
+ See [`Dispatcher.upgrade(options[, callback])`](Dispatcher.md#dispatcherupgradeoptions-callback).
85
81
 
86
82
  ## Instance Events
87
83
 
88
84
  ### Event: `'connect'`
89
85
 
90
- See [Dispatcher Event: `'connect'`](docs/api/Dispatcher.md#event-connect).
86
+ See [Dispatcher Event: `'connect'`](Dispatcher.md#event-connect).
91
87
 
92
88
  ### Event: `'disconnect'`
93
89
 
94
- See [Dispatcher Event: `'disconnect'`](docs/api/Dispatcher.md#event-connect).
90
+ See [Dispatcher Event: `'disconnect'`](Dispatcher.md#event-disconnect).
95
91
 
96
92
  ### Event: `'drain'`
97
93
 
98
- See [Dispatcher Event: `'drain'`](docs/api/Dispatcher.md#event-connect).
94
+ See [Dispatcher Event: `'drain'`](Dispatcher.md#event-drain).
@@ -77,48 +77,40 @@ const client = new Client('https://localhost:3000', {
77
77
 
78
78
  ### `Client.close([callback])`
79
79
 
80
- Implements [`Dispatcher.close([callback])`](docs/api/Dispatcher.md#clientclose-callback-).
80
+ Implements [`Dispatcher.close([callback])`](Dispatcher.md#dispatcherclosecallback-promise).
81
81
 
82
82
  ### `Client.destroy([error, callback])`
83
83
 
84
- Implements [`Dispatcher.destroy([error, callback])`](docs/api/Dispatcher.md#dispatcher-callback-).
84
+ Implements [`Dispatcher.destroy([error, callback])`](Dispatcher.md#dispatcherdestroyerror-callback-promise).
85
85
 
86
86
  Waits until socket is closed before invoking the callback (or returning a promise if no callback is provided).
87
87
 
88
- Implements [`Dispatcher.destroy([error, callback])`](docs/api/Dispatcher.md#dispatcher-callback-).
89
-
90
88
  ### `Client.connect(options[, callback])`
91
89
 
92
- See [`Dispatcher.connect(options[, callback])`](docs/api/Dispatcher.md#clientconnectoptions--callback).
90
+ See [`Dispatcher.connect(options[, callback])`](Dispatcher.md#dispatcherconnectoptions-callback).
93
91
 
94
92
  ### `Client.dispatch(options, handlers)`
95
93
 
96
- Implements [`Dispatcher.dispatch(options, handlers)`](docs/api/Dispatcher.md#clientdispatchoptions-handlers).
94
+ Implements [`Dispatcher.dispatch(options, handlers)`](Dispatcher.md#dispatcherdispatchoptions-handler).
97
95
 
98
96
  ### `Client.pipeline(options, handler)`
99
97
 
100
- See [`Dispatcher.pipeline(options, handler)`](docs/api/Dispatcher.md#clientpipelineoptions-handler).
98
+ See [`Dispatcher.pipeline(options, handler)`](Dispatcher.md#dispatcherpipelineoptions-handler).
101
99
 
102
100
  ### `Client.request(options[, callback])`
103
101
 
104
- See [`Dispatcher.request(options [, callback])`](docs/api/Dispatcher.md#clientrequestoptions--callback).
102
+ See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
105
103
 
106
104
  ### `Client.stream(options, factory[, callback])`
107
105
 
108
- See [`Dispatcher.stream(options, factory[, callback])`](docs/api/Dispatcher.md#clientstreamoptions-factory--callback).
106
+ See [`Dispatcher.stream(options, factory[, callback])`](Dispatcher.md#dispatcherstreamoptions-factory-callback).
109
107
 
110
108
  ### `Client.upgrade(options[, callback])`
111
109
 
112
- See [`Dispatcher.upgrade(options[, callback])`](docs/api/Dispatcher.md#clientupgradeoptions-callback).
110
+ See [`Dispatcher.upgrade(options[, callback])`](Dispatcher.md#dispatcherupgradeoptions-callback).
113
111
 
114
112
  ## Instance Properties
115
113
 
116
- ### `Client.busy`
117
-
118
- * `boolean`
119
-
120
- `true` if pipeline is saturated or blocked. Indicates whether dispatching further requests is meaningful.
121
-
122
114
  ### `Client.closed`
123
115
 
124
116
  * `boolean`
@@ -141,7 +133,7 @@ Property to get and set the pipelining factor.
141
133
 
142
134
  ### Event: `'connect'`
143
135
 
144
- See [Dispatcher Event: `'connect'`](docs/api/Dispatcher.md#event-connect).
136
+ See [Dispatcher Event: `'connect'`](Dispatcher.md#event-connect).
145
137
 
146
138
  Parameters:
147
139
 
@@ -161,7 +153,7 @@ const server = createServer((request, response) => {
161
153
  response.end('Hello, World!')
162
154
  }).listen()
163
155
 
164
- await once(server, 'listening')
156
+ await once(server, 'listening')
165
157
 
166
158
  const client = new Client(`http://localhost:${server.address().port}`)
167
159
 
@@ -187,7 +179,7 @@ try {
187
179
 
188
180
  ### Event: `'disconnect'`
189
181
 
190
- See [Dispatcher Event: `'disconnect'`](docs/api/Dispatcher.md#event-disconnect).
182
+ See [Dispatcher Event: `'disconnect'`](Dispatcher.md#event-disconnect).
191
183
 
192
184
  Parameters:
193
185
 
@@ -230,9 +222,9 @@ try {
230
222
 
231
223
  ### Event: `'drain'`
232
224
 
233
- Emitted when pipeline is no longer [`busy`](#clientbusy).
225
+ Emitted when pipeline is no longer busy.
234
226
 
235
- See [Dispatcher Event: `'drain'`](docs/api/Dispatcher.md#event-drain).
227
+ See [Dispatcher Event: `'drain'`](Dispatcher.md#event-drain).
236
228
 
237
229
  #### Example - Client drain event
238
230
 
@@ -251,7 +243,6 @@ const client = new Client(`http://localhost:${server.address().port}`)
251
243
 
252
244
  client.on('drain', () => {
253
245
  console.log('drain event')
254
- console.log(`Is Client busy: ${client.busy}`)
255
246
  client.close()
256
247
  server.close()
257
248
  })
@@ -262,8 +253,6 @@ const requests = [
262
253
  client.request({ path: '/', method: 'GET' })
263
254
  ]
264
255
 
265
- console.log(`Is Client busy: ${client.busy}`)
266
-
267
256
  await Promise.all(requests)
268
257
 
269
258
  console.log('requests completed')
@@ -177,14 +177,15 @@ try {
177
177
  ### `Dispatcher.dispatch(options, handler)`
178
178
 
179
179
  This is the low level API which all the preceding APIs are implemented on top of.
180
- This API is expected to evolve through semver-major versions and is less stable than the preceding higher level APIs. It is primarily intended for library developers who implement higher level APIs on top of this.
180
+ This API is expected to evolve through semver-major versions and is less stable than the preceding higher level APIs.
181
+ It is primarily intended for library developers who implement higher level APIs on top of this.
181
182
 
182
183
  Arguments:
183
184
 
184
185
  * **options** `DispatchOptions`
185
186
  * **handler** `DispatchHandler`
186
187
 
187
- Returns: `void`
188
+ Returns: `Boolean` - `false` if dispatcher is busy and further dispatch calls won't make any progress until the `'drain'` event has been emitted.
188
189
 
189
190
  #### Parameter: `DispatchOptions`
190
191
 
@@ -330,7 +331,7 @@ Extends: [`RequestOptions`](#parameter-requestoptions)
330
331
  * **opaque** `unknown`
331
332
  * **body** `stream.Readable`
332
333
  * **context** `object`
333
- * **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.
334
+ * **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.
334
335
 
335
336
  #### Example 1 - Pipeline Echo
336
337
 
@@ -413,7 +414,7 @@ Extends: [`DispatchOptions`](#parameter-dispatchoptions)
413
414
 
414
415
  * **opaque** `unknown` (optional) - Default: `null` - Used for passing through context to `ResponseData`
415
416
  * **signal** `AbortSignal | events.EventEmitter | null` (optional) - Default: `null`
416
- * **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.
417
+ * **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.
417
418
 
418
419
  The `RequestOptions.method` property should not be value `'CONNECT'`.
419
420
 
@@ -581,7 +582,7 @@ Returns: `void | Promise<StreamData>` - Only returns a `Promise` if no `callback
581
582
  * **statusCode** `number`
582
583
  * **headers** `http.IncomingHttpHeaders`
583
584
  * **opaque** `unknown`
584
- * **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.
585
+ * **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.
585
586
 
586
587
  #### Parameter: `StreamData`
587
588
 
@@ -790,11 +791,11 @@ Emitted when dispatcher is no longer busy.
790
791
 
791
792
  * `http.IncomingHttpHeaders | string[] | null`
792
793
 
793
- Header arguments such as `options.headers` in [`Client.dispatch`](./Client.md#client-dispatchoptions-handlers) can be specified in two forms; either as an object specified by the `http.IncomingHttpHeaders` type, or an array of strings. An array representation of a header list must have an even length or an `InvalidArgumentError` will be thrown.
794
+ Header arguments such as `options.headers` in [`Client.dispatch`](Client.md#clientdispatchoptions-handlers) can be specified in two forms; either as an object specified by the `http.IncomingHttpHeaders` type, or an array of strings. An array representation of a header list must have an even length or an `InvalidArgumentError` will be thrown.
794
795
 
795
796
  Keys are lowercase and values are not modified.
796
797
 
797
- Response headers will derive a `host` from the `url` of the [Client](#class-client) instance if no `host` header was previously specified.
798
+ Response headers will derive a `host` from the `url` of the [Client](Client.md#class-client) instance if no `host` header was previously specified.
798
799
 
799
800
  ### Example 1 - Object
800
801
 
@@ -14,7 +14,7 @@ Returns: `MockAgent`
14
14
 
15
15
  ### Parameter: `MockAgentOptions`
16
16
 
17
- Extends: [`AgentOptions`](docs/api/Agent.md#parameter-agentoptions)
17
+ Extends: [`AgentOptions`](Agent.md#parameter-agentoptions)
18
18
 
19
19
  * **agent** `Agent` (optional) - Default: `new Agent([options])` - a custom agent encapsulated by the MockAgent.
20
20
 
@@ -314,11 +314,11 @@ await mockAgent.close()
314
314
 
315
315
  ### `MockAgent.dispatch(options, handlers)`
316
316
 
317
- Implements [`Agent.dispatch(options, handlers)`](docs/api/Agent.md#parameter-agentdispatchoptions).
317
+ Implements [`Agent.dispatch(options, handlers)`](Agent.md#parameter-agentdispatchoptions).
318
318
 
319
319
  ### `MockAgent.request(options[, callback])`
320
320
 
321
- See [`Dispatcher.request(options [, callback])`](docs/api/Dispatcher.md#clientrequestoptions--callback).
321
+ See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
322
322
 
323
323
  #### Example - MockAgent request
324
324
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Extends: `undici.Client`
4
4
 
5
- A mock client class that implements the same api as [MockPool](docs/api/MockPool.md).
5
+ A mock client class that implements the same api as [MockPool](MockPool.md).
6
6
 
7
7
  ## `new MockClient(origin, [options])`
8
8
 
@@ -36,19 +36,19 @@ const mockClient = mockAgent.get('http://localhost:3000')
36
36
 
37
37
  ### `MockClient.intercept(options)`
38
38
 
39
- Implements: [`MockPool.intercept(options)`](docs/api/MockPool.md#mockpoolinterceptoptions)
39
+ Implements: [`MockPool.intercept(options)`](MockPool.md#mockpoolinterceptoptions)
40
40
 
41
41
  ### `MockClient.close()`
42
42
 
43
- Implements: [`MockPool.close()`](docs/api/MockPool.md#mockpoolclose)
43
+ Implements: [`MockPool.close()`](MockPool.md#mockpoolclose)
44
44
 
45
45
  ### `MockClient.dispatch(options, handlers)`
46
46
 
47
- Implements [`Dispatcher.dispatch(options, handlers)`](docs/api/Dispatcher.md#clientdispatchoptions-handlers).
47
+ Implements [`Dispatcher.dispatch(options, handlers)`](Dispatcher.md#dispatcherdispatchoptions-handler).
48
48
 
49
49
  ### `MockClient.request(options[, callback])`
50
50
 
51
- See [`Dispatcher.request(options [, callback])`](docs/api/Dispatcher.md#clientrequestoptions--callback).
51
+ See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
52
52
 
53
53
  #### Example - MockClient request
54
54
 
@@ -62,12 +62,14 @@ Returns: `MockInterceptor` corresponding to the input options.
62
62
 
63
63
  We can define the behaviour of an intercepted request with the following options.
64
64
 
65
- * **reply** `(statusCode: number, replyData: string | object, responseOptions?: MockResponseOptions) => MockScope` - define a reply for a matching request. Default for `responseOptions` is `{}`.
65
+ * **reply** `(statusCode: number, replyData: string | Buffer | object, responseOptions?: MockResponseOptions) => MockScope` - define a reply for a matching request. Default for `responseOptions` is `{}`.
66
66
  * **replyWithError** `(error: Error) => MockScope` - define an error for a matching request to throw.
67
67
  * **defaultReplyHeaders** `(headers: Record<string, string>) => MockInterceptor` - define default headers to be included in subsequent replies. These are in addition to headers on a specific reply.
68
68
  * **defaultReplyTrailers** `(trailers: Record<string, string>) => MockInterceptor` - define default trailers to be included in subsequent replies. These are in addition to trailers on a specific reply.
69
69
  * **replyContentLength** `() => MockInterceptor` - define automatically calculated `content-length` headers to be included in subsequent replies.
70
70
 
71
+ The reply data of an intercepted request may either be a string, buffer, or JavaScript object. Objects are converted to JSON while strings and buffers are sent as-is.
72
+
71
73
  By default, `reply` and `replyWithError` define the behaviour for the first matching request only. Subsequent requests will not be affected (this can be changed using the returned `MockScope`).
72
74
 
73
75
  ### Parameter: `MockResponseOptions`
@@ -297,7 +299,7 @@ mockPool.intercept({
297
299
  }).defaultReplyTrailers({ foo: 'bar' })
298
300
  .reply(200, 'foo')
299
301
 
300
- const { trailers } = await request('http://localhost:3000/foo')
302
+ const { trailers } = await request('http://localhost:3000/foo')
301
303
 
302
304
  console.log('trailers', trailers) // trailers { foo: 'bar' }
303
305
  ```
@@ -410,11 +412,11 @@ await mockPool.close()
410
412
 
411
413
  ### `MockPool.dispatch(options, handlers)`
412
414
 
413
- Implements [`Dispatcher.dispatch(options, handlers)`](docs/api/Dispatcher.md#clientdispatchoptions-handlers).
415
+ Implements [`Dispatcher.dispatch(options, handlers)`](Dispatcher.md#dispatcherdispatchoptions-handler).
414
416
 
415
417
  ### `MockPool.request(options[, callback])`
416
418
 
417
- See [`Dispatcher.request(options [, callback])`](docs/api/Dispatcher.md#clientrequestoptions--callback).
419
+ See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
418
420
 
419
421
  #### Example - MockPool request
420
422
 
package/docs/api/Pool.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Extends: `undici.Dispatcher`
4
4
 
5
- A pool of [Client](docs/api/Client.md) instances connected to the same upstream target.
5
+ A pool of [Client](Client.md) instances connected to the same upstream target.
6
6
 
7
7
  Requests are not guaranteed to be dispatched in order of invocation.
8
8
 
@@ -15,69 +15,65 @@ Arguments:
15
15
 
16
16
  ### Parameter: `PoolOptions`
17
17
 
18
- Extends: [`ClientOptions`](docs/api/Client.md#parameter-clientoptions)
18
+ Extends: [`ClientOptions`](Client.md#parameter-clientoptions)
19
19
 
20
20
  * **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Client(origin, opts)`
21
21
  * **connections** `number | null` (optional) - Default: `null` - The number of `Client` instances to create. When set to `null`, the `Pool` instance will create an unlimited amount of `Client` instances.
22
22
 
23
23
  ## Instance Properties
24
24
 
25
- ### `Pool.busy`
26
-
27
- Implements [Client.busy](docs/api/Client.md#clientbusy)
28
-
29
25
  ### `Pool.closed`
30
26
 
31
- Implements [Client.closed](docs/api/Client.md#clientclosed)
27
+ Implements [Client.closed](Client.md#clientclosed)
32
28
 
33
29
  ### `Pool.destroyed`
34
30
 
35
- Implements [Client.destroyed](docs/api/Client.md#clientdestroyed)
31
+ Implements [Client.destroyed](Client.md#clientdestroyed)
36
32
 
37
33
  ## Instance Methods
38
34
 
39
35
  ### `Pool.close([callback])`
40
36
 
41
- Implements [`Dispatcher.close([callback])`](docs/api/Dispatcher.md#clientclose-callback-).
37
+ Implements [`Dispatcher.close([callback])`](Dispatcher.md#dispatcherclosecallback-promise).
42
38
 
43
39
  ### `Pool.destroy([error, callback])`
44
40
 
45
- Implements [`Dispatcher.destroy([error, callback])`](docs/api/Dispatcher.md#dispatcher-callback-).
41
+ Implements [`Dispatcher.destroy([error, callback])`](Dispatcher.md#dispatcherdestroyerror-callback-promise).
46
42
 
47
43
  ### `Pool.connect(options[, callback])`
48
44
 
49
- See [`Dispatcher.connect(options[, callback])`](docs/api/Dispatcher.md#clientconnectoptions--callback).
45
+ See [`Dispatcher.connect(options[, callback])`](Dispatcher.md#dispatcherconnectoptions-callback).
50
46
 
51
- ### `Pool.dispatch(options, handlers)`
47
+ ### `Pool.dispatch(options, handler)`
52
48
 
53
- Implements [`Dispatcher.dispatch(options, handlers)`](docs/api/Dispatcher.md#clientdispatchoptions-handlers).
49
+ Implements [`Dispatcher.dispatch(options, handler)`](Dispatcher.md#dispatcherdispatchoptions-handler).
54
50
 
55
51
  ### `Pool.pipeline(options, handler)`
56
52
 
57
- See [`Dispatcher.pipeline(options, handler)`](docs/api/Dispatcher.md#clientpipelineoptions-handler).
53
+ See [`Dispatcher.pipeline(options, handler)`](Dispatcher.md#dispatcherpipelineoptions-handler).
58
54
 
59
55
  ### `Pool.request(options[, callback])`
60
56
 
61
- See [`Dispatcher.request(options [, callback])`](docs/api/Dispatcher.md#clientrequestoptions--callback).
57
+ See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
62
58
 
63
59
  ### `Pool.stream(options, factory[, callback])`
64
60
 
65
- See [`Dispatcher.stream(options, factory[, callback])`](docs/api/Dispatcher.md#clientstreamoptions-factory--callback).
61
+ See [`Dispatcher.stream(options, factory[, callback])`](Dispatcher.md#dispatcherstreamoptions-factory-callback).
66
62
 
67
63
  ### `Pool.upgrade(options[, callback])`
68
64
 
69
- See [`Dispatcher.upgrade(options[, callback])`](docs/api/Dispatcher.md#clientupgradeoptions-callback).
65
+ See [`Dispatcher.upgrade(options[, callback])`](Dispatcher.md#dispatcherupgradeoptions-callback).
70
66
 
71
67
  ## Instance Events
72
68
 
73
69
  ### Event: `'connect'`
74
70
 
75
- See [Dispatcher Event: `'connect'`](docs/api/Dispatcher.md#event-connect).
71
+ See [Dispatcher Event: `'connect'`](Dispatcher.md#event-connect).
76
72
 
77
73
  ### Event: `'disconnect'`
78
74
 
79
- See [Dispatcher Event: `'disconnect'`](docs/api/Dispatcher.md#event-connect).
75
+ See [Dispatcher Event: `'disconnect'`](Dispatcher.md#event-disconnect).
80
76
 
81
77
  ### Event: `'drain'`
82
78
 
83
- See [Dispatcher Event: `'drain'`](docs/api/Dispatcher.md#event-connect).
79
+ See [Dispatcher Event: `'drain'`](Dispatcher.md#event-drain).
@@ -14,7 +14,7 @@ Returns: `ProxyAgent`
14
14
 
15
15
  ### Parameter: `ProxyAgentOptions`
16
16
 
17
- Extends: [`AgentOptions`](docs/api/Agent.md#parameter-agentoptions)
17
+ Extends: [`AgentOptions`](Agent.md#parameter-agentoptions)
18
18
 
19
19
  * **uri** `string` (required) - It can be passed either by a string or a object containing `uri` as string.
20
20
 
@@ -93,8 +93,8 @@ await proxyAgent.close()
93
93
 
94
94
  ### `ProxyAgent.dispatch(options, handlers)`
95
95
 
96
- Implements [`Agent.dispatch(options, handlers)`](docs/api/Agent.md#parameter-agentdispatchoptions).
96
+ Implements [`Agent.dispatch(options, handlers)`](Agent.md#parameter-agentdispatchoptions).
97
97
 
98
98
  ### `ProxyAgent.request(options[, callback])`
99
99
 
100
- See [`Dispatcher.request(options [, callback])`](docs/api/Dispatcher.md#clientrequestoptions--callback).
100
+ See [`Dispatcher.request(options [, callback])`](Dispatcher.md#dispatcherrequestoptions-callback).
@@ -1,6 +1,6 @@
1
1
  # Client Lifecycle
2
2
 
3
- An Undici [Client](docs/api/Client.md) can be best described as a state machine. The following list is a summary of the various state transitions the `Client` will go through in its lifecycle. This document also contains detailed breakdowns of each state.
3
+ An Undici [Client](Client.md) can be best described as a state machine. The following list is a summary of the various state transitions the `Client` will go through in its lifecycle. This document also contains detailed breakdowns of each state.
4
4
 
5
5
  > This diagram is not a perfect representation of the undici Client. Since the Client class is not actually implemented as a state-machine, actual execution may deviate slightly from what is described below. Consider this as a general resource for understanding the inner workings of the Undici client rather than some kind of formal specification.
6
6
 
@@ -29,25 +29,25 @@ An Undici [Client](docs/api/Client.md) can be best described as a state machine.
29
29
 
30
30
  ### idle
31
31
 
32
- The **idle** state is the initial state of a `Client` instance. While an `origin` is required for instantiating a `Client` instance, the underlying socket connection will not be established until a request is queued using [`Client.dispatch()`](docs/api/Client.md#clientdispatchoptions-handlers). By calling `Client.dispatch()` directly or using one of the multiple implementations ([`Client.connect()`](docs/api/Client.md#clientconnectoptions--callback), [`Client.pipeline()`](docs/api/Client.md#clientpipelineoptions-handler), [`Client.request()`](docs/api/Client.md#clientrequestoptions--callback), [`Client.stream()`](docs/api/Client.md#clientstreamoptions-factory--callback), and [`Client.upgrade()`](docs/api/Client.md#clientupgradeoptions-callback)), the `Client` instance will transition from **idle** to [**pending**](#pending) and then most likely directly to [**processing**](#processing).
32
+ The **idle** state is the initial state of a `Client` instance. While an `origin` is required for instantiating a `Client` instance, the underlying socket connection will not be established until a request is queued using [`Client.dispatch()`](Client.md#clientdispatchoptions-handlers). By calling `Client.dispatch()` directly or using one of the multiple implementations ([`Client.connect()`](Client.md#clientconnectoptions-callback), [`Client.pipeline()`](Client.md#clientpipelineoptions-handler), [`Client.request()`](Client.md#clientrequestoptions-callback), [`Client.stream()`](Client.md#clientstreamoptions-factory-callback), and [`Client.upgrade()`](Client.md#clientupgradeoptions-callback)), the `Client` instance will transition from **idle** to [**pending**](#pending) and then most likely directly to [**processing**](#processing).
33
33
 
34
- Calling [`Client.close()`](docs/api/Client.md#clientclose-callback-) or [`Client.destroy()`](docs/api/Client.md#clientdestroyerror) transitions directly to the [**destroyed**](#destroyed) state since the `Client` instance will have no queued requests in this state.
34
+ Calling [`Client.close()`](Client.md#clientclosecallback) or [`Client.destroy()`](Client.md#clientdestroyerror-callback) transitions directly to the [**destroyed**](#destroyed) state since the `Client` instance will have no queued requests in this state.
35
35
 
36
36
  ### pending
37
37
 
38
- The **pending** state signifies a non-processing `Client`. Upon entering this state, the `Client` establishes a socket connection and emits the [`'connect'`](docs/api/Client.md#event-connect) event signalling a connection was successfully established with the `origin` provided during `Client` instantiation. The internal queue is initially empty, and requests can start queueing.
38
+ The **pending** state signifies a non-processing `Client`. Upon entering this state, the `Client` establishes a socket connection and emits the [`'connect'`](Client.md#event-connect) event signalling a connection was successfully established with the `origin` provided during `Client` instantiation. The internal queue is initially empty, and requests can start queueing.
39
39
 
40
- Calling [`Client.close()`](docs/api/Client.md#clientclose-callback-) with queued requests, transitions the `Client` to the [**processing**](#processing) state. Without queued requests, it transitions to the [**destroyed**](#destroyed) state.
40
+ Calling [`Client.close()`](Client.md#clientclosecallback) with queued requests, transitions the `Client` to the [**processing**](#processing) state. Without queued requests, it transitions to the [**destroyed**](#destroyed) state.
41
41
 
42
- Calling [`Client.destroy()`](docs/api/Client.md#clientdestroyerror) transitions directly to the [**destroyed**](#destroyed) state regardless of existing requests.
42
+ Calling [`Client.destroy()`](Client.md#clientdestroyerror-callback) transitions directly to the [**destroyed**](#destroyed) state regardless of existing requests.
43
43
 
44
44
  ### processing
45
45
 
46
- The **processing** state is a state machine within itself. It initializes to the [**processing.running**](#running) state. The [`Client.dispatch()`](docs/api/Client.md#clientdispatchoptions-handlers), [`Client.close()`](docs/api/Client.md#clientclose-callback-), and [`Client.destroy()`](docs/api/Client.md#clientdestroyerror) can be called at any time while the `Client` is in this state. `Client.dispatch()` will add more requests to the queue while existing requests continue to be processed. `Client.close()` will transition to the [**processing.closing**](#closing) state. And `Client.destroy()` will transition to [**destroyed**](#destroyed).
46
+ The **processing** state is a state machine within itself. It initializes to the [**processing.running**](#running) state. The [`Client.dispatch()`](Client.md#clientdispatchoptions-handlers), [`Client.close()`](Client.md#clientclosecallback), and [`Client.destroy()`](Client.md#clientdestroyerror-callback) can be called at any time while the `Client` is in this state. `Client.dispatch()` will add more requests to the queue while existing requests continue to be processed. `Client.close()` will transition to the [**processing.closing**](#closing) state. And `Client.destroy()` will transition to [**destroyed**](#destroyed).
47
47
 
48
48
  #### running
49
49
 
50
- In the **processing.running** sub-state, queued requests are being processed in a FIFO order. If a request body requires draining, the *needDrain* event transitions to the [**processing.busy**](#busy) sub-state. The *close* event transitions the Client to the [**process.closing**](#closing) sub-state. If all queued requests are processed and neither [`Client.close()`](docs/api/Client.md#clientclose-callback-) nor [`Client.destroy()`](docs/api/Client.md#clientdestroyerror) are called, then the [**processing**](#processing) machine will trigger a *keepalive* event transitioning the `Client` back to the [**pending**](#pending) state. During this time, the `Client` is waiting for the socket connection to timeout, and once it does, it triggers the *timeout* event and transitions to the [**idle**](#idle) state.
50
+ In the **processing.running** sub-state, queued requests are being processed in a FIFO order. If a request body requires draining, the *needDrain* event transitions to the [**processing.busy**](#busy) sub-state. The *close* event transitions the Client to the [**process.closing**](#closing) sub-state. If all queued requests are processed and neither [`Client.close()`](Client.md#clientclosecallback) nor [`Client.destroy()`](Client.md#clientdestroyerror-callback) are called, then the [**processing**](#processing) machine will trigger a *keepalive* event transitioning the `Client` back to the [**pending**](#pending) state. During this time, the `Client` is waiting for the socket connection to timeout, and once it does, it triggers the *timeout* event and transitions to the [**idle**](#idle) state.
51
51
 
52
52
  #### busy
53
53
 
@@ -55,8 +55,8 @@ This sub-state is only entered when a request body is an instance of [Stream](ht
55
55
 
56
56
  #### closing
57
57
 
58
- This sub-state is only entered when a `Client` instance has queued requests and the [`Client.close()`](docs/api/Client.md#clientclose-callback-) method is called. In this state, the `Client` instance continues to process requests as usual, with the one exception that no additional requests can be queued. Once all of the queued requests are processed, the `Client` will trigger the *done* event gracefully entering the [**destroyed**](#destroyed) state without an error.
58
+ This sub-state is only entered when a `Client` instance has queued requests and the [`Client.close()`](Client.md#clientclosecallback) method is called. In this state, the `Client` instance continues to process requests as usual, with the one exception that no additional requests can be queued. Once all of the queued requests are processed, the `Client` will trigger the *done* event gracefully entering the [**destroyed**](#destroyed) state without an error.
59
59
 
60
60
  ### destroyed
61
61
 
62
- The **destroyed** state is a final state for the `Client` instance. Once in this state, a `Client` is nonfunctional. Calling any other `Client` methods will result in an `ClientDestroyedError`.
62
+ The **destroyed** state is a final state for the `Client` instance. Once in this state, a `Client` is nonfunctional. Calling any other `Client` methods will result in an `ClientDestroyedError`.
package/index.d.ts CHANGED
@@ -41,4 +41,5 @@ declare namespace Undici {
41
41
  var MockPool: typeof import('./types/mock-pool');
42
42
  var MockAgent: typeof import('./types/mock-agent');
43
43
  var mockErrors: typeof import('./types/mock-errors');
44
+ var fetch: typeof import('./types/fetch').fetch;
44
45
  }
@@ -44,6 +44,8 @@ class BalancedPool extends Dispatcher {
44
44
  })
45
45
 
46
46
  pool.on('drain', (...args) => {
47
+ pool[kNeedDrain] = false
48
+
47
49
  if (this[kNeedDrain]) {
48
50
  this[kNeedDrain] = false
49
51
  this.emit('drain', ...args)
@@ -62,12 +64,16 @@ class BalancedPool extends Dispatcher {
62
64
  throw new BalancedPoolMissingUpstreamError()
63
65
  }
64
66
 
65
- let pool = this[kPools].shift()
66
- this[kPools].push(pool)
67
- if (pool.busy) {
68
- pool = this[kPools].find(pool => !pool.busy) || pool
67
+ const pool = this[kPools].find(pool => !pool[kNeedDrain]) || this[kPools][0]
68
+
69
+ if (!pool.dispatch(opts, handler)) {
70
+ pool[kNeedDrain] = true
71
+ this[kNeedDrain] = true
69
72
  }
70
- this[kNeedDrain] = !pool.dispatch(opts, handler)
73
+
74
+ this[kPools].splice(this[kPools].indexOf(pool), 1)
75
+ this[kPools].push(pool)
76
+
71
77
  return !this[kNeedDrain]
72
78
  }
73
79
 
@@ -87,10 +93,6 @@ class BalancedPool extends Dispatcher {
87
93
  return this[kPools].reduce((acc, pool) => acc && pool.destroyed, true)
88
94
  }
89
95
 
90
- get busy () {
91
- return this[kPools].reduce((acc, pool) => acc && pool.busy, true) || false
92
- }
93
-
94
96
  get closed () {
95
97
  return this[kPools].reduce((acc, pool) => acc && pool.closed, true)
96
98
  }
@@ -24,9 +24,12 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) {
24
24
  return function connect ({ hostname, host, protocol, port, servername }, callback) {
25
25
  let socket
26
26
  if (protocol === 'https:') {
27
- servername = servername || options.servername || util.getServerName(host)
27
+ servername = servername || options.servername || util.getServerName(host) || null
28
28
 
29
- const session = sessionCache.get(servername) || null
29
+ const sessionKey = servername || hostname
30
+ const session = sessionCache.get(sessionKey) || null
31
+
32
+ assert(sessionKey)
30
33
 
31
34
  socket = tls.connect({
32
35
  highWaterMark: 16384, // TLS in node can't have bigger HWM anyway...
@@ -39,8 +42,6 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) {
39
42
 
40
43
  socket
41
44
  .on('session', function (session) {
42
- assert(this.servername)
43
-
44
45
  // cache is disabled
45
46
  if (maxCachedSessions === 0) {
46
47
  return
@@ -52,12 +53,12 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) {
52
53
  sessionCache.delete(oldestKey)
53
54
  }
54
55
 
55
- sessionCache.set(this.servername, session)
56
+ sessionCache.set(sessionKey, session)
56
57
  })
57
58
  .on('error', function (err) {
58
- if (this.servername && err.code !== 'UND_ERR_INFO') {
59
+ if (sessionKey && err.code !== 'UND_ERR_INFO') {
59
60
  // TODO (fix): Only delete for session related errors.
60
- sessionCache.delete(this.servername)
61
+ sessionCache.delete(sessionKey)
61
62
  }
62
63
  })
63
64
  } else {
@@ -3,11 +3,11 @@
3
3
  const { Blob } = require('buffer')
4
4
  const { kState } = require('./symbols')
5
5
  const { File } = require('./file')
6
- const { HTMLFormElement, toUSVString } = require('./util')
6
+ const { toUSVString } = require('./util')
7
7
 
8
8
  class FormData {
9
9
  constructor (...args) {
10
- if (args.length > 0 && !(args[0] instanceof HTMLFormElement)) {
10
+ if (args.length > 0 && !(args[0]?.constructor?.name === 'HTMLFormElement')) {
11
11
  throw new TypeError(
12
12
  "Failed to construct 'FormData': parameter 1 is not of type 'HTMLFormElement'"
13
13
  )
@@ -168,6 +168,28 @@ class FormData {
168
168
  }
169
169
  }
170
170
 
171
+ get [Symbol.toStringTag] () {
172
+ return 'FormData'
173
+ }
174
+
175
+ * entries () {
176
+ for (const pair of this) {
177
+ yield pair
178
+ }
179
+ }
180
+
181
+ * keys () {
182
+ for (const [key] of this) {
183
+ yield key
184
+ }
185
+ }
186
+
187
+ * values () {
188
+ for (const [, value] of this) {
189
+ yield value
190
+ }
191
+ }
192
+
171
193
  * [Symbol.iterator] () {
172
194
  // The value pairs to iterate over are this’s entry list’s entries with
173
195
  // the key being the name and the value being the value.
@@ -313,20 +313,23 @@ class Headers {
313
313
  }
314
314
 
315
315
  * keys () {
316
- for (let index = 0; index < this[kHeadersList].length; index += 2) {
317
- yield this[kHeadersList][index]
316
+ const clone = this[kHeadersList].slice()
317
+ for (let index = 0; index < clone.length; index += 2) {
318
+ yield clone[index]
318
319
  }
319
320
  }
320
321
 
321
322
  * values () {
322
- for (let index = 1; index < this[kHeadersList].length; index += 2) {
323
- yield this[kHeadersList][index]
323
+ const clone = this[kHeadersList].slice()
324
+ for (let index = 1; index < clone.length; index += 2) {
325
+ yield clone[index]
324
326
  }
325
327
  }
326
328
 
327
329
  * entries () {
328
- for (let index = 0; index < this[kHeadersList].length; index += 2) {
329
- yield [this[kHeadersList][index], this[kHeadersList][index + 1]]
330
+ const clone = this[kHeadersList].slice()
331
+ for (let index = 0; index < clone.length; index += 2) {
332
+ yield [clone[index], clone[index + 1]]
330
333
  }
331
334
  }
332
335
 
@@ -737,7 +737,7 @@ function fetchFinale (fetchParams, response) {
737
737
 
738
738
  // TODO (spec): The spec doesn't specify this but we need to
739
739
  // terminate fetch if we have an error response.
740
- if (response.status === 0) {
740
+ if (response.type === 'error') {
741
741
  context.terminate({ reason: response.error })
742
742
  }
743
743
  }
package/lib/fetch/util.js CHANGED
@@ -229,7 +229,47 @@ function appendFetchMetadata (httpRequest) {
229
229
 
230
230
  // https://fetch.spec.whatwg.org/#append-a-request-origin-header
231
231
  function appendRequestOriginHeader (request) {
232
- // TODO
232
+ // 1. Let serializedOrigin be the result of byte-serializing a request origin with request.
233
+ let serializedOrigin = request.origin
234
+
235
+ // 2. If request’s response tainting is "cors" or request’s mode is "websocket", then append (`Origin`, serializedOrigin) to request’s header list.
236
+ if (request.responseTainting === 'cors' || request.mode === 'websocket') {
237
+ if (serializedOrigin) {
238
+ request.headersList.append('Origin', serializedOrigin)
239
+ }
240
+ }
241
+
242
+ // 3. Otherwise, if request’s method is neither `GET` nor `HEAD`, then:
243
+ else if (request.method !== 'GET' && request.method !== 'HEAD') {
244
+ // 1. Switch on request’s referrer policy:
245
+ switch (request.referrerPolicy) {
246
+ case 'no-referrer':
247
+ // Set serializedOrigin to `null`.
248
+ serializedOrigin = null
249
+ break
250
+ case 'no-referrer-when-downgrade':
251
+ case 'strict-origin':
252
+ case 'strict-origin-when-cross-origin':
253
+ // If request’s origin is a tuple origin, its scheme is "https", and request’s current URL’s scheme is not "https", then set serializedOrigin to `null`.
254
+ if (/^https:/.test(request.origin) && !/^https:/.test(requestCurrentURL(request))) {
255
+ serializedOrigin = null
256
+ }
257
+ break
258
+ case 'same-origin':
259
+ // If request’s origin is not same origin with request’s current URL’s origin, then set serializedOrigin to `null`.
260
+ if (request.origin !== requestCurrentURL(request).origin) {
261
+ serializedOrigin = null
262
+ }
263
+ break
264
+ default:
265
+ // Do nothing.
266
+ }
267
+
268
+ if (serializedOrigin) {
269
+ // 2. Append (`Origin`, serializedOrigin) to request’s header list.
270
+ request.headersList.append('Origin', serializedOrigin)
271
+ }
272
+ }
233
273
  }
234
274
 
235
275
  function coarsenedSharedCurrentTime (crossOriginIsolatedCapability) {
@@ -284,10 +324,8 @@ function tryUpgradeRequestToAPotentiallyTrustworthyURL (request) {
284
324
  class ServiceWorkerGlobalScope {} // dummy
285
325
  class Window {} // dummy
286
326
  class EnvironmentSettingsObject {} // dummy
287
- class HTMLFormElement {} // dummy
288
327
 
289
328
  module.exports = {
290
- HTMLFormElement,
291
329
  ServiceWorkerGlobalScope,
292
330
  Window,
293
331
  EnvironmentSettingsObject,
@@ -47,7 +47,13 @@ function matchKey (mockDispatch, { path, method, body, headers }) {
47
47
  }
48
48
 
49
49
  function getResponseData (data) {
50
- return typeof data === 'object' ? JSON.stringify(data) : data.toString()
50
+ if (Buffer.isBuffer(data)) {
51
+ return data
52
+ } else if (typeof data === 'object') {
53
+ return JSON.stringify(data)
54
+ } else {
55
+ return data.toString()
56
+ }
51
57
  }
52
58
 
53
59
  function getMockDispatch (mockDispatches, key) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "undici",
3
- "version": "4.9.2",
3
+ "version": "4.10.0",
4
4
  "description": "An HTTP/1.1 client, written from scratch for Node.js",
5
5
  "homepage": "https://undici.nodejs.org",
6
6
  "bugs": {
package/types/agent.d.ts CHANGED
@@ -11,7 +11,7 @@ declare class Agent extends Dispatcher{
11
11
  /** `true` after `dispatcher.destroyed()` has been called or `dispatcher.close()` has been called and the dispatcher shutdown has completed. */
12
12
  destroyed: boolean;
13
13
  /** Dispatches a request. */
14
- dispatch(options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandlers): void;
14
+ dispatch(options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandlers): boolean;
15
15
  }
16
16
 
17
17
  declare namespace Agent {
@@ -11,7 +11,7 @@ export = Dispatcher;
11
11
  /** Dispatcher is the core API used to dispatch requests. */
12
12
  declare class Dispatcher extends EventEmitter {
13
13
  /** Dispatches a request. This API is expected to evolve through semver-major versions and is less stable than the preceding higher level APIs. It is primarily intended for library developers who implement higher level APIs on top of this. */
14
- dispatch(options: Dispatcher.DispatchOptions, handler: Dispatcher.DispatchHandlers): void;
14
+ dispatch(options: Dispatcher.DispatchOptions, handler: Dispatcher.DispatchHandlers): boolean;
15
15
  /** Starts two-way communications with the requested resource. */
16
16
  connect(options: Dispatcher.ConnectOptions): Promise<Dispatcher.ConnectData>;
17
17
  connect(options: Dispatcher.ConnectOptions, callback: (err: Error | null, data: Dispatcher.ConnectData) => void): void;
@@ -148,7 +148,7 @@ declare namespace Dispatcher {
148
148
  export type PipelineHandler = (data: PipelineHandlerData) => Readable;
149
149
  export type HttpMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'CONNECT' | 'OPTIONS' | 'TRACE' | 'PATCH';
150
150
 
151
- /**
151
+ /**
152
152
  * @link https://fetch.spec.whatwg.org/#body-mixin
153
153
  */
154
154
  interface BodyMixin {
@@ -12,7 +12,7 @@ declare class MockAgent<TMockAgentOptions extends MockAgent.Options = MockAgent.
12
12
  get<TInterceptable extends Interceptable>(origin: RegExp): TInterceptable;
13
13
  get<TInterceptable extends Interceptable>(origin: ((origin: string) => boolean)): TInterceptable;
14
14
  /** Dispatches a mocked request. */
15
- dispatch(options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandlers): void;
15
+ dispatch(options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandlers): boolean;
16
16
  /** Closes the mock agent and waits for registered mock pools and clients to also close before resolving. */
17
17
  close(): Promise<void>;
18
18
  /** Disables mocking in MockAgent. */
@@ -11,7 +11,7 @@ declare class MockClient extends Client implements Interceptable {
11
11
  /** Intercepts any matching requests that use the same origin as this mock client. */
12
12
  intercept(options: MockInterceptor.Options): MockInterceptor;
13
13
  /** Dispatches a mocked request. */
14
- dispatch(options: Dispatcher.DispatchOptions, handlers: Dispatcher.DispatchHandlers): void;
14
+ dispatch(options: Dispatcher.DispatchOptions, handlers: Dispatcher.DispatchHandlers): boolean;
15
15
  /** Closes the mock client and gracefully waits for enqueued requests to complete. */
16
16
  close(): Promise<void>;
17
17
  }
@@ -21,7 +21,7 @@ declare class MockScope<TData extends object = object> {
21
21
  declare class MockInterceptor {
22
22
  constructor(options: MockInterceptor.Options, mockDispatches: MockInterceptor.MockDispatch[]);
23
23
  /** Mock an undici request with the defined reply. */
24
- reply<TData extends object = object>(statusCode: number, data: TData | string, responseOptions?: MockInterceptor.MockResponseOptions): MockScope<TData>;
24
+ reply<TData extends object = object>(statusCode: number, data: TData | Buffer| string , responseOptions?: MockInterceptor.MockResponseOptions): MockScope<TData>;
25
25
  /** Mock an undici request by throwing the defined reply error. */
26
26
  replyWithError<TError extends Error = Error>(error: TError): MockScope;
27
27
  /** Set default reply headers on the interceptor for subsequent mocked replies. */
@@ -11,7 +11,7 @@ declare class MockPool extends Pool implements Interceptable {
11
11
  /** Intercepts any matching requests that use the same origin as this mock pool. */
12
12
  intercept(options: MockInterceptor.Options): MockInterceptor;
13
13
  /** Dispatches a mocked request. */
14
- dispatch(options: Dispatcher.DispatchOptions, handlers: Dispatcher.DispatchHandlers): void;
14
+ dispatch(options: Dispatcher.DispatchOptions, handlers: Dispatcher.DispatchHandlers): boolean;
15
15
  /** Closes the mock pool and gracefully waits for enqueued requests to complete. */
16
16
  close(): Promise<void>;
17
17
  }
@@ -6,7 +6,7 @@ export = ProxyAgent
6
6
  declare class ProxyAgent extends Dispatcher {
7
7
  constructor(options: ProxyAgent.Options | string)
8
8
 
9
- dispatch(options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandlers): void;
9
+ dispatch(options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandlers): boolean;
10
10
  close(): Promise<void>;
11
11
  }
12
12