source: node_modules/undici/README.md

main
Last change on this file was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 16.5 KB
Line 
1# undici
2
3[![Node CI](https://github.com/nodejs/undici/actions/workflows/nodejs.yml/badge.svg)](https://github.com/nodejs/undici/actions/workflows/nodejs.yml) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/) [![npm version](https://badge.fury.io/js/undici.svg)](https://badge.fury.io/js/undici) [![codecov](https://codecov.io/gh/nodejs/undici/branch/main/graph/badge.svg?token=yZL6LtXkOA)](https://codecov.io/gh/nodejs/undici)
4
5An HTTP/1.1 client, written from scratch for Node.js.
6
7> Undici means eleven in Italian. 1.1 -> 11 -> Eleven -> Undici.
8It is also a Stranger Things reference.
9
10Have a question about using Undici? Open a [Q&A Discussion](https://github.com/nodejs/undici/discussions/new) or join our official OpenJS [Slack](https://openjs-foundation.slack.com/archives/C01QF9Q31QD) channel.
11
12## Install
13
14```
15npm i undici
16```
17
18## Benchmarks
19
20The benchmark is a simple `hello world` [example](benchmarks/benchmark.js) using a
21number of unix sockets (connections) with a pipelining depth of 10 running on Node 20.6.0.
22
23### Connections 1
24
25
26| Tests | Samples | Result | Tolerance | Difference with slowest |
27|---------------------|---------|---------------|-----------|-------------------------|
28| http - no keepalive | 15 | 5.32 req/sec | ± 2.61 % | - |
29| http - keepalive | 10 | 5.35 req/sec | ± 2.47 % | + 0.44 % |
30| undici - fetch | 15 | 41.85 req/sec | ± 2.49 % | + 686.04 % |
31| undici - pipeline | 40 | 50.36 req/sec | ± 2.77 % | + 845.92 % |
32| undici - stream | 15 | 60.58 req/sec | ± 2.75 % | + 1037.72 % |
33| undici - request | 10 | 61.19 req/sec | ± 2.60 % | + 1049.24 % |
34| undici - dispatch | 20 | 64.84 req/sec | ± 2.81 % | + 1117.81 % |
35
36
37### Connections 50
38
39| Tests | Samples | Result | Tolerance | Difference with slowest |
40|---------------------|---------|------------------|-----------|-------------------------|
41| undici - fetch | 30 | 2107.19 req/sec | ± 2.69 % | - |
42| http - no keepalive | 10 | 2698.90 req/sec | ± 2.68 % | + 28.08 % |
43| http - keepalive | 10 | 4639.49 req/sec | ± 2.55 % | + 120.17 % |
44| undici - pipeline | 40 | 6123.33 req/sec | ± 2.97 % | + 190.59 % |
45| undici - stream | 50 | 9426.51 req/sec | ± 2.92 % | + 347.35 % |
46| undici - request | 10 | 10162.88 req/sec | ± 2.13 % | + 382.29 % |
47| undici - dispatch | 50 | 11191.11 req/sec | ± 2.98 % | + 431.09 % |
48
49
50## Quick Start
51
52```js
53import { request } from 'undici'
54
55const {
56 statusCode,
57 headers,
58 trailers,
59 body
60} = await request('http://localhost:3000/foo')
61
62console.log('response received', statusCode)
63console.log('headers', headers)
64
65for await (const data of body) {
66 console.log('data', data)
67}
68
69console.log('trailers', trailers)
70```
71
72## Body Mixins
73
74The `body` mixins are the most common way to format the request/response body. Mixins include:
75
76- [`.formData()`](https://fetch.spec.whatwg.org/#dom-body-formdata)
77- [`.json()`](https://fetch.spec.whatwg.org/#dom-body-json)
78- [`.text()`](https://fetch.spec.whatwg.org/#dom-body-text)
79
80Example usage:
81
82```js
83import { request } from 'undici'
84
85const {
86 statusCode,
87 headers,
88 trailers,
89 body
90} = await request('http://localhost:3000/foo')
91
92console.log('response received', statusCode)
93console.log('headers', headers)
94console.log('data', await body.json())
95console.log('trailers', trailers)
96```
97
98_Note: Once a mixin has been called then the body cannot be reused, thus calling additional mixins on `.body`, e.g. `.body.json(); .body.text()` will result in an error `TypeError: unusable` being thrown and returned through the `Promise` rejection._
99
100Should you need to access the `body` in plain-text after using a mixin, the best practice is to use the `.text()` mixin first and then manually parse the text to the desired format.
101
102For more information about their behavior, please reference the body mixin from the [Fetch Standard](https://fetch.spec.whatwg.org/#body-mixin).
103
104## Common API Methods
105
106This section documents our most commonly used API methods. Additional APIs are documented in their own files within the [docs](./docs/) folder and are accessible via the navigation list on the left side of the docs site.
107
108### `undici.request([url, options]): Promise`
109
110Arguments:
111
112* **url** `string | URL | UrlObject`
113* **options** [`RequestOptions`](./docs/api/Dispatcher.md#parameter-requestoptions)
114 * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
115 * **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`
116 * **maxRedirections** `Integer` - Default: `0`
117
118Returns a promise with the result of the `Dispatcher.request` method.
119
120Calls `options.dispatcher.request(options)`.
121
122See [Dispatcher.request](./docs/api/Dispatcher.md#dispatcherrequestoptions-callback) for more details.
123
124### `undici.stream([url, options, ]factory): Promise`
125
126Arguments:
127
128* **url** `string | URL | UrlObject`
129* **options** [`StreamOptions`](./docs/api/Dispatcher.md#parameter-streamoptions)
130 * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
131 * **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`
132 * **maxRedirections** `Integer` - Default: `0`
133* **factory** `Dispatcher.stream.factory`
134
135Returns a promise with the result of the `Dispatcher.stream` method.
136
137Calls `options.dispatcher.stream(options, factory)`.
138
139See [Dispatcher.stream](docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback) for more details.
140
141### `undici.pipeline([url, options, ]handler): Duplex`
142
143Arguments:
144
145* **url** `string | URL | UrlObject`
146* **options** [`PipelineOptions`](docs/api/Dispatcher.md#parameter-pipelineoptions)
147 * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
148 * **method** `String` - Default: `PUT` if `options.body`, otherwise `GET`
149 * **maxRedirections** `Integer` - Default: `0`
150* **handler** `Dispatcher.pipeline.handler`
151
152Returns: `stream.Duplex`
153
154Calls `options.dispatch.pipeline(options, handler)`.
155
156See [Dispatcher.pipeline](docs/api/Dispatcher.md#dispatcherpipelineoptions-handler) for more details.
157
158### `undici.connect([url, options]): Promise`
159
160Starts two-way communications with the requested resource using [HTTP CONNECT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT).
161
162Arguments:
163
164* **url** `string | URL | UrlObject`
165* **options** [`ConnectOptions`](docs/api/Dispatcher.md#parameter-connectoptions)
166 * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
167 * **maxRedirections** `Integer` - Default: `0`
168* **callback** `(err: Error | null, data: ConnectData | null) => void` (optional)
169
170Returns a promise with the result of the `Dispatcher.connect` method.
171
172Calls `options.dispatch.connect(options)`.
173
174See [Dispatcher.connect](docs/api/Dispatcher.md#dispatcherconnectoptions-callback) for more details.
175
176### `undici.fetch(input[, init]): Promise`
177
178Implements [fetch](https://fetch.spec.whatwg.org/#fetch-method).
179
180* https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch
181* https://fetch.spec.whatwg.org/#fetch-method
182
183Only supported on Node 16.8+.
184
185Basic usage example:
186
187```js
188import { fetch } from 'undici'
189
190
191const res = await fetch('https://example.com')
192const json = await res.json()
193console.log(json)
194```
195
196You can pass an optional dispatcher to `fetch` as:
197
198```js
199import { fetch, Agent } from 'undici'
200
201const res = await fetch('https://example.com', {
202 // Mocks are also supported
203 dispatcher: new Agent({
204 keepAliveTimeout: 10,
205 keepAliveMaxTimeout: 10
206 })
207})
208const json = await res.json()
209console.log(json)
210```
211
212#### `request.body`
213
214A body can be of the following types:
215
216- ArrayBuffer
217- ArrayBufferView
218- AsyncIterables
219- Blob
220- Iterables
221- String
222- URLSearchParams
223- FormData
224
225In this implementation of fetch, ```request.body``` now accepts ```Async Iterables```. It is not present in the [Fetch Standard.](https://fetch.spec.whatwg.org)
226
227```js
228import { fetch } from 'undici'
229
230const data = {
231 async *[Symbol.asyncIterator]() {
232 yield 'hello'
233 yield 'world'
234 },
235}
236
237await fetch('https://example.com', { body: data, method: 'POST', duplex: 'half' })
238```
239
240#### `request.duplex`
241
242- half
243
244In this implementation of fetch, `request.duplex` must be set if `request.body` is `ReadableStream` or `Async Iterables`. And fetch requests are currently always be full duplex. More detail refer to [Fetch Standard.](https://fetch.spec.whatwg.org/#dom-requestinit-duplex)
245
246#### `response.body`
247
248Nodejs 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()`.
249
250```js
251import { fetch } from 'undici'
252import { Readable } from 'node:stream'
253
254const response = await fetch('https://example.com')
255const readableWebStream = response.body
256const readableNodeStream = Readable.fromWeb(readableWebStream)
257```
258
259#### Specification Compliance
260
261This section documents parts of the [Fetch Standard](https://fetch.spec.whatwg.org) that Undici does
262not support or does not fully implement.
263
264##### Garbage Collection
265
266* https://fetch.spec.whatwg.org/#garbage-collection
267
268The [Fetch Standard](https://fetch.spec.whatwg.org) allows users to skip consuming the response body by relying on
269[garbage collection](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management#garbage_collection) to release connection resources. Undici does not do the same. Therefore, it is important to always either consume or cancel the response body.
270
271Garbage collection in Node is less aggressive and deterministic
272(due to the lack of clear idle periods that browsers have through the rendering refresh rate)
273which means that leaving the release of connection resources to the garbage collector can lead
274to excessive connection usage, reduced performance (due to less connection re-use), and even
275stalls or deadlocks when running out of connections.
276
277```js
278// Do
279const headers = await fetch(url)
280 .then(async res => {
281 for await (const chunk of res.body) {
282 // force consumption of body
283 }
284 return res.headers
285 })
286
287// Do not
288const headers = await fetch(url)
289 .then(res => res.headers)
290```
291
292However, if you want to get only headers, it might be better to use `HEAD` request method. Usage of this method will obviate the need for consumption or cancelling of the response body. See [MDN - HTTP - HTTP request methods - HEAD](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD) for more details.
293
294```js
295const headers = await fetch(url, { method: 'HEAD' })
296 .then(res => res.headers)
297```
298
299##### Forbidden and Safelisted Header Names
300
301* https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name
302* https://fetch.spec.whatwg.org/#forbidden-header-name
303* https://fetch.spec.whatwg.org/#forbidden-response-header-name
304* https://github.com/wintercg/fetch/issues/6
305
306The [Fetch Standard](https://fetch.spec.whatwg.org) requires implementations to exclude certain headers from requests and responses. In browser environments, some headers are forbidden so the user agent remains in full control over them. In Undici, these constraints are removed to give more control to the user.
307
308### `undici.upgrade([url, options]): Promise`
309
310Upgrade to a different protocol. See [MDN - HTTP - Protocol upgrade mechanism](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism) for more details.
311
312Arguments:
313
314* **url** `string | URL | UrlObject`
315* **options** [`UpgradeOptions`](docs/api/Dispatcher.md#parameter-upgradeoptions)
316 * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher)
317 * **maxRedirections** `Integer` - Default: `0`
318* **callback** `(error: Error | null, data: UpgradeData) => void` (optional)
319
320Returns a promise with the result of the `Dispatcher.upgrade` method.
321
322Calls `options.dispatcher.upgrade(options)`.
323
324See [Dispatcher.upgrade](docs/api/Dispatcher.md#dispatcherupgradeoptions-callback) for more details.
325
326### `undici.setGlobalDispatcher(dispatcher)`
327
328* dispatcher `Dispatcher`
329
330Sets the global dispatcher used by Common API Methods.
331
332### `undici.getGlobalDispatcher()`
333
334Gets the global dispatcher used by Common API Methods.
335
336Returns: `Dispatcher`
337
338### `undici.setGlobalOrigin(origin)`
339
340* origin `string | URL | undefined`
341
342Sets the global origin used in `fetch`.
343
344If `undefined` is passed, the global origin will be reset. This will cause `Response.redirect`, `new Request()`, and `fetch` to throw an error when a relative path is passed.
345
346```js
347setGlobalOrigin('http://localhost:3000')
348
349const response = await fetch('/api/ping')
350
351console.log(response.url) // http://localhost:3000/api/ping
352```
353
354### `undici.getGlobalOrigin()`
355
356Gets the global origin used in `fetch`.
357
358Returns: `URL`
359
360### `UrlObject`
361
362* **port** `string | number` (optional)
363* **path** `string` (optional)
364* **pathname** `string` (optional)
365* **hostname** `string` (optional)
366* **origin** `string` (optional)
367* **protocol** `string` (optional)
368* **search** `string` (optional)
369
370## Specification Compliance
371
372This section documents parts of the HTTP/1.1 specification that Undici does
373not support or does not fully implement.
374
375### Expect
376
377Undici does not support the `Expect` request header field. The request
378body is always immediately sent and the `100 Continue` response will be
379ignored.
380
381Refs: https://tools.ietf.org/html/rfc7231#section-5.1.1
382
383### Pipelining
384
385Undici will only use pipelining if configured with a `pipelining` factor
386greater than `1`.
387
388Undici always assumes that connections are persistent and will immediately
389pipeline requests, without checking whether the connection is persistent.
390Hence, automatic fallback to HTTP/1.0 or HTTP/1.1 without pipelining is
391not supported.
392
393Undici will immediately pipeline when retrying requests after a failed
394connection. However, Undici will not retry the first remaining requests in
395the prior pipeline and instead error the corresponding callback/promise/stream.
396
397Undici will abort all running requests in the pipeline when any of them are
398aborted.
399
400* Refs: https://tools.ietf.org/html/rfc2616#section-8.1.2.2
401* Refs: https://tools.ietf.org/html/rfc7230#section-6.3.2
402
403### Manual Redirect
404
405Since it is not possible to manually follow an HTTP redirect on the server-side,
406Undici returns the actual response instead of an `opaqueredirect` filtered one
407when invoked with a `manual` redirect. This aligns `fetch()` with the other
408implementations in Deno and Cloudflare Workers.
409
410Refs: https://fetch.spec.whatwg.org/#atomic-http-redirect-handling
411
412## Workarounds
413
414### Network address family autoselection.
415
416If you experience problem when connecting to a remote server that is resolved by your DNS servers to a IPv6 (AAAA record)
417first, there are chances that your local router or ISP might have problem connecting to IPv6 networks. In that case
418undici will throw an error with code `UND_ERR_CONNECT_TIMEOUT`.
419
420If the target server resolves to both a IPv6 and IPv4 (A records) address and you are using a compatible Node version
421(18.3.0 and above), you can fix the problem by providing the `autoSelectFamily` option (support by both `undici.request`
422and `undici.Agent`) which will enable the family autoselection algorithm when establishing the connection.
423
424## Collaborators
425
426* [__Daniele Belardi__](https://github.com/dnlup), <https://www.npmjs.com/~dnlup>
427* [__Ethan Arrowood__](https://github.com/ethan-arrowood), <https://www.npmjs.com/~ethan_arrowood>
428* [__Matteo Collina__](https://github.com/mcollina), <https://www.npmjs.com/~matteo.collina>
429* [__Matthew Aitken__](https://github.com/KhafraDev), <https://www.npmjs.com/~khaf>
430* [__Robert Nagy__](https://github.com/ronag), <https://www.npmjs.com/~ronag>
431* [__Szymon Marczak__](https://github.com/szmarczak), <https://www.npmjs.com/~szmarczak>
432* [__Tomas Della Vedova__](https://github.com/delvedor), <https://www.npmjs.com/~delvedor>
433
434### Releasers
435
436* [__Ethan Arrowood__](https://github.com/ethan-arrowood), <https://www.npmjs.com/~ethan_arrowood>
437* [__Matteo Collina__](https://github.com/mcollina), <https://www.npmjs.com/~matteo.collina>
438* [__Robert Nagy__](https://github.com/ronag), <https://www.npmjs.com/~ronag>
439* [__Matthew Aitken__](https://github.com/KhafraDev), <https://www.npmjs.com/~khaf>
440
441## License
442
443MIT
Note: See TracBrowser for help on using the repository browser.