11cb0ef41Sopenharmony_ci# Dispatcher
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ciExtends: `events.EventEmitter`
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ciDispatcher is the core API used to dispatch requests.
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ciRequests are not guaranteed to be dispatched in order of invocation.
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ci## Instance Methods
101cb0ef41Sopenharmony_ci
111cb0ef41Sopenharmony_ci### `Dispatcher.close([callback]): Promise`
121cb0ef41Sopenharmony_ci
131cb0ef41Sopenharmony_ciCloses the dispatcher and gracefully waits for enqueued requests to complete before resolving.
141cb0ef41Sopenharmony_ci
151cb0ef41Sopenharmony_ciArguments:
161cb0ef41Sopenharmony_ci
171cb0ef41Sopenharmony_ci* **callback** `(error: Error | null, data: null) => void` (optional)
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_ciReturns: `void | Promise<null>` - Only returns a `Promise` if no `callback` argument was passed
201cb0ef41Sopenharmony_ci
211cb0ef41Sopenharmony_ci```js
221cb0ef41Sopenharmony_cidispatcher.close() // -> Promise
231cb0ef41Sopenharmony_cidispatcher.close(() => {}) // -> void
241cb0ef41Sopenharmony_ci```
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_ci#### Example - Request resolves before Client closes
271cb0ef41Sopenharmony_ci
281cb0ef41Sopenharmony_ci```js
291cb0ef41Sopenharmony_ciimport { createServer } from 'http'
301cb0ef41Sopenharmony_ciimport { Client } from 'undici'
311cb0ef41Sopenharmony_ciimport { once } from 'events'
321cb0ef41Sopenharmony_ci
331cb0ef41Sopenharmony_ciconst server = createServer((request, response) => {
341cb0ef41Sopenharmony_ci  response.end('undici')
351cb0ef41Sopenharmony_ci}).listen()
361cb0ef41Sopenharmony_ci
371cb0ef41Sopenharmony_ciawait once(server, 'listening')
381cb0ef41Sopenharmony_ci
391cb0ef41Sopenharmony_ciconst client = new Client(`http://localhost:${server.address().port}`)
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_citry {
421cb0ef41Sopenharmony_ci  const { body } = await client.request({
431cb0ef41Sopenharmony_ci      path: '/',
441cb0ef41Sopenharmony_ci      method: 'GET'
451cb0ef41Sopenharmony_ci  })
461cb0ef41Sopenharmony_ci  body.setEncoding('utf8')
471cb0ef41Sopenharmony_ci  body.on('data', console.log)
481cb0ef41Sopenharmony_ci} catch (error) {}
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_ciawait client.close()
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_ciconsole.log('Client closed')
531cb0ef41Sopenharmony_ciserver.close()
541cb0ef41Sopenharmony_ci```
551cb0ef41Sopenharmony_ci
561cb0ef41Sopenharmony_ci### `Dispatcher.connect(options[, callback])`
571cb0ef41Sopenharmony_ci
581cb0ef41Sopenharmony_ciStarts two-way communications with the requested resource using [HTTP CONNECT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT).
591cb0ef41Sopenharmony_ci
601cb0ef41Sopenharmony_ciArguments:
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_ci* **options** `ConnectOptions`
631cb0ef41Sopenharmony_ci* **callback** `(err: Error | null, data: ConnectData | null) => void` (optional)
641cb0ef41Sopenharmony_ci
651cb0ef41Sopenharmony_ciReturns: `void | Promise<ConnectData>` - Only returns a `Promise` if no `callback` argument was passed
661cb0ef41Sopenharmony_ci
671cb0ef41Sopenharmony_ci#### Parameter: `ConnectOptions`
681cb0ef41Sopenharmony_ci
691cb0ef41Sopenharmony_ci* **path** `string`
701cb0ef41Sopenharmony_ci* **headers** `UndiciHeaders` (optional) - Default: `null`
711cb0ef41Sopenharmony_ci* **signal** `AbortSignal | events.EventEmitter | null` (optional) - Default: `null`
721cb0ef41Sopenharmony_ci* **opaque** `unknown` (optional) - This argument parameter is passed through to `ConnectData`
731cb0ef41Sopenharmony_ci
741cb0ef41Sopenharmony_ci#### Parameter: `ConnectData`
751cb0ef41Sopenharmony_ci
761cb0ef41Sopenharmony_ci* **statusCode** `number`
771cb0ef41Sopenharmony_ci* **headers** `Record<string, string | string[] | undefined>`
781cb0ef41Sopenharmony_ci* **socket** `stream.Duplex`
791cb0ef41Sopenharmony_ci* **opaque** `unknown`
801cb0ef41Sopenharmony_ci
811cb0ef41Sopenharmony_ci#### Example - Connect request with echo
821cb0ef41Sopenharmony_ci
831cb0ef41Sopenharmony_ci```js
841cb0ef41Sopenharmony_ciimport { createServer } from 'http'
851cb0ef41Sopenharmony_ciimport { Client } from 'undici'
861cb0ef41Sopenharmony_ciimport { once } from 'events'
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_ciconst server = createServer((request, response) => {
891cb0ef41Sopenharmony_ci  throw Error('should never get here')
901cb0ef41Sopenharmony_ci}).listen()
911cb0ef41Sopenharmony_ci
921cb0ef41Sopenharmony_ciserver.on('connect', (req, socket, head) => {
931cb0ef41Sopenharmony_ci  socket.write('HTTP/1.1 200 Connection established\r\n\r\n')
941cb0ef41Sopenharmony_ci
951cb0ef41Sopenharmony_ci  let data = head.toString()
961cb0ef41Sopenharmony_ci  socket.on('data', (buf) => {
971cb0ef41Sopenharmony_ci    data += buf.toString()
981cb0ef41Sopenharmony_ci  })
991cb0ef41Sopenharmony_ci
1001cb0ef41Sopenharmony_ci  socket.on('end', () => {
1011cb0ef41Sopenharmony_ci    socket.end(data)
1021cb0ef41Sopenharmony_ci  })
1031cb0ef41Sopenharmony_ci})
1041cb0ef41Sopenharmony_ci
1051cb0ef41Sopenharmony_ciawait once(server, 'listening')
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ciconst client = new Client(`http://localhost:${server.address().port}`)
1081cb0ef41Sopenharmony_ci
1091cb0ef41Sopenharmony_citry {
1101cb0ef41Sopenharmony_ci  const { socket } = await client.connect({
1111cb0ef41Sopenharmony_ci    path: '/'
1121cb0ef41Sopenharmony_ci  })
1131cb0ef41Sopenharmony_ci  const wanted = 'Body'
1141cb0ef41Sopenharmony_ci  let data = ''
1151cb0ef41Sopenharmony_ci  socket.on('data', d => { data += d })
1161cb0ef41Sopenharmony_ci  socket.on('end', () => {
1171cb0ef41Sopenharmony_ci    console.log(`Data received: ${data.toString()} | Data wanted: ${wanted}`)
1181cb0ef41Sopenharmony_ci    client.close()
1191cb0ef41Sopenharmony_ci    server.close()
1201cb0ef41Sopenharmony_ci  })
1211cb0ef41Sopenharmony_ci  socket.write(wanted)
1221cb0ef41Sopenharmony_ci  socket.end()
1231cb0ef41Sopenharmony_ci} catch (error) { }
1241cb0ef41Sopenharmony_ci```
1251cb0ef41Sopenharmony_ci
1261cb0ef41Sopenharmony_ci### `Dispatcher.destroy([error, callback]): Promise`
1271cb0ef41Sopenharmony_ci
1281cb0ef41Sopenharmony_ciDestroy the dispatcher abruptly with the given error. All the pending and running requests will be asynchronously aborted and error. Since this operation is asynchronously dispatched there might still be some progress on dispatched requests.
1291cb0ef41Sopenharmony_ci
1301cb0ef41Sopenharmony_ciBoth arguments are optional; the method can be called in four different ways:
1311cb0ef41Sopenharmony_ci
1321cb0ef41Sopenharmony_ciArguments:
1331cb0ef41Sopenharmony_ci
1341cb0ef41Sopenharmony_ci* **error** `Error | null` (optional)
1351cb0ef41Sopenharmony_ci* **callback** `(error: Error | null, data: null) => void` (optional)
1361cb0ef41Sopenharmony_ci
1371cb0ef41Sopenharmony_ciReturns: `void | Promise<void>` - Only returns a `Promise` if no `callback` argument was passed
1381cb0ef41Sopenharmony_ci
1391cb0ef41Sopenharmony_ci```js
1401cb0ef41Sopenharmony_cidispatcher.destroy() // -> Promise
1411cb0ef41Sopenharmony_cidispatcher.destroy(new Error()) // -> Promise
1421cb0ef41Sopenharmony_cidispatcher.destroy(() => {}) // -> void
1431cb0ef41Sopenharmony_cidispatcher.destroy(new Error(), () => {}) // -> void
1441cb0ef41Sopenharmony_ci```
1451cb0ef41Sopenharmony_ci
1461cb0ef41Sopenharmony_ci#### Example - Request is aborted when Client is destroyed
1471cb0ef41Sopenharmony_ci
1481cb0ef41Sopenharmony_ci```js
1491cb0ef41Sopenharmony_ciimport { createServer } from 'http'
1501cb0ef41Sopenharmony_ciimport { Client } from 'undici'
1511cb0ef41Sopenharmony_ciimport { once } from 'events'
1521cb0ef41Sopenharmony_ci
1531cb0ef41Sopenharmony_ciconst server = createServer((request, response) => {
1541cb0ef41Sopenharmony_ci  response.end()
1551cb0ef41Sopenharmony_ci}).listen()
1561cb0ef41Sopenharmony_ci
1571cb0ef41Sopenharmony_ciawait once(server, 'listening')
1581cb0ef41Sopenharmony_ci
1591cb0ef41Sopenharmony_ciconst client = new Client(`http://localhost:${server.address().port}`)
1601cb0ef41Sopenharmony_ci
1611cb0ef41Sopenharmony_citry {
1621cb0ef41Sopenharmony_ci  const request = client.request({
1631cb0ef41Sopenharmony_ci    path: '/',
1641cb0ef41Sopenharmony_ci    method: 'GET'
1651cb0ef41Sopenharmony_ci  })
1661cb0ef41Sopenharmony_ci  client.destroy()
1671cb0ef41Sopenharmony_ci    .then(() => {
1681cb0ef41Sopenharmony_ci      console.log('Client destroyed')
1691cb0ef41Sopenharmony_ci      server.close()
1701cb0ef41Sopenharmony_ci    })
1711cb0ef41Sopenharmony_ci  await request
1721cb0ef41Sopenharmony_ci} catch (error) {
1731cb0ef41Sopenharmony_ci  console.error(error)
1741cb0ef41Sopenharmony_ci}
1751cb0ef41Sopenharmony_ci```
1761cb0ef41Sopenharmony_ci
1771cb0ef41Sopenharmony_ci### `Dispatcher.dispatch(options, handler)`
1781cb0ef41Sopenharmony_ci
1791cb0ef41Sopenharmony_ciThis is the low level API which all the preceding APIs are implemented on top of.
1801cb0ef41Sopenharmony_ciThis API is expected to evolve through semver-major versions and is less stable than the preceding higher level APIs.
1811cb0ef41Sopenharmony_ciIt is primarily intended for library developers who implement higher level APIs on top of this.
1821cb0ef41Sopenharmony_ci
1831cb0ef41Sopenharmony_ciArguments:
1841cb0ef41Sopenharmony_ci
1851cb0ef41Sopenharmony_ci* **options** `DispatchOptions`
1861cb0ef41Sopenharmony_ci* **handler** `DispatchHandler`
1871cb0ef41Sopenharmony_ci
1881cb0ef41Sopenharmony_ciReturns: `Boolean` - `false` if dispatcher is busy and further dispatch calls won't make any progress until the `'drain'` event has been emitted.
1891cb0ef41Sopenharmony_ci
1901cb0ef41Sopenharmony_ci#### Parameter: `DispatchOptions`
1911cb0ef41Sopenharmony_ci
1921cb0ef41Sopenharmony_ci* **origin** `string | URL`
1931cb0ef41Sopenharmony_ci* **path** `string`
1941cb0ef41Sopenharmony_ci* **method** `string`
1951cb0ef41Sopenharmony_ci* **reset** `boolean` (optional) - Default: `false` - If `false`, the request will attempt to create a long-living connection by sending the `connection: keep-alive` header,otherwise will attempt to close it immediately after response by sending `connection: close` within the request and closing the socket afterwards.
1961cb0ef41Sopenharmony_ci* **body** `string | Buffer | Uint8Array | stream.Readable | Iterable | AsyncIterable | null` (optional) - Default: `null`
1971cb0ef41Sopenharmony_ci* **headers** `UndiciHeaders | string[]` (optional) - Default: `null`.
1981cb0ef41Sopenharmony_ci* **query** `Record<string, any> | null` (optional) - Default: `null` - Query string params to be embedded in the request URL. Note that both keys and values of query are encoded using `encodeURIComponent`. If for some reason you need to send them unencoded, embed query params into path directly instead.
1991cb0ef41Sopenharmony_ci* **idempotent** `boolean` (optional) - Default: `true` if `method` is `'HEAD'` or `'GET'` - Whether the requests can be safely retried or not. If `false` the request won't be sent until all preceding requests in the pipeline has completed.
2001cb0ef41Sopenharmony_ci* **blocking** `boolean` (optional) - Default: `false` - Whether the response is expected to take a long time and would end up blocking the pipeline. When this is set to `true` further pipelining will be avoided on the same connection until headers have been received.
2011cb0ef41Sopenharmony_ci* **upgrade** `string | null` (optional) - Default: `null` - Upgrade the request. Should be used to specify the kind of upgrade i.e. `'Websocket'`.
2021cb0ef41Sopenharmony_ci* **bodyTimeout** `number | null` (optional) - The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Defaults to 300 seconds.
2031cb0ef41Sopenharmony_ci* **headersTimeout** `number | null` (optional) - The amount of time, in milliseconds, the parser will wait to receive the complete HTTP headers while not sending the request. Defaults to 300 seconds.
2041cb0ef41Sopenharmony_ci* **throwOnError** `boolean` (optional) - Default: `false` - Whether Undici should throw an error upon receiving a 4xx or 5xx response from the server.
2051cb0ef41Sopenharmony_ci* **expectContinue** `boolean` (optional) - Default: `false` - For H2, it appends the expect: 100-continue header, and halts the request body until a 100-continue is received from the remote server
2061cb0ef41Sopenharmony_ci
2071cb0ef41Sopenharmony_ci#### Parameter: `DispatchHandler`
2081cb0ef41Sopenharmony_ci
2091cb0ef41Sopenharmony_ci* **onConnect** `(abort: () => void, context: object) => void` - Invoked before request is dispatched on socket. May be invoked multiple times when a request is retried when the request at the head of the pipeline fails.
2101cb0ef41Sopenharmony_ci* **onError** `(error: Error) => void` - Invoked when an error has occurred. May not throw.
2111cb0ef41Sopenharmony_ci* **onUpgrade** `(statusCode: number, headers: Buffer[], socket: Duplex) => void` (optional) - Invoked when request is upgraded. Required if `DispatchOptions.upgrade` is defined or `DispatchOptions.method === 'CONNECT'`.
2121cb0ef41Sopenharmony_ci* **onHeaders** `(statusCode: number, headers: Buffer[], resume: () => void, statusText: string) => boolean` - Invoked when statusCode and headers have been received. May be invoked multiple times due to 1xx informational headers. Not required for `upgrade` requests.
2131cb0ef41Sopenharmony_ci* **onData** `(chunk: Buffer) => boolean` - Invoked when response payload data is received. Not required for `upgrade` requests.
2141cb0ef41Sopenharmony_ci* **onComplete** `(trailers: Buffer[]) => void` - Invoked when response payload and trailers have been received and the request has completed. Not required for `upgrade` requests.
2151cb0ef41Sopenharmony_ci* **onBodySent** `(chunk: string | Buffer | Uint8Array) => void` - Invoked when a body chunk is sent to the server. Not required. For a stream or iterable body this will be invoked for every chunk. For other body types, it will be invoked once after the body is sent.
2161cb0ef41Sopenharmony_ci
2171cb0ef41Sopenharmony_ci#### Example 1 - Dispatch GET request
2181cb0ef41Sopenharmony_ci
2191cb0ef41Sopenharmony_ci```js
2201cb0ef41Sopenharmony_ciimport { createServer } from 'http'
2211cb0ef41Sopenharmony_ciimport { Client } from 'undici'
2221cb0ef41Sopenharmony_ciimport { once } from 'events'
2231cb0ef41Sopenharmony_ci
2241cb0ef41Sopenharmony_ciconst server = createServer((request, response) => {
2251cb0ef41Sopenharmony_ci  response.end('Hello, World!')
2261cb0ef41Sopenharmony_ci}).listen()
2271cb0ef41Sopenharmony_ci
2281cb0ef41Sopenharmony_ciawait once(server, 'listening')
2291cb0ef41Sopenharmony_ci
2301cb0ef41Sopenharmony_ciconst client = new Client(`http://localhost:${server.address().port}`)
2311cb0ef41Sopenharmony_ci
2321cb0ef41Sopenharmony_ciconst data = []
2331cb0ef41Sopenharmony_ci
2341cb0ef41Sopenharmony_ciclient.dispatch({
2351cb0ef41Sopenharmony_ci  path: '/',
2361cb0ef41Sopenharmony_ci  method: 'GET',
2371cb0ef41Sopenharmony_ci  headers: {
2381cb0ef41Sopenharmony_ci    'x-foo': 'bar'
2391cb0ef41Sopenharmony_ci  }
2401cb0ef41Sopenharmony_ci}, {
2411cb0ef41Sopenharmony_ci  onConnect: () => {
2421cb0ef41Sopenharmony_ci    console.log('Connected!')
2431cb0ef41Sopenharmony_ci  },
2441cb0ef41Sopenharmony_ci  onError: (error) => {
2451cb0ef41Sopenharmony_ci    console.error(error)
2461cb0ef41Sopenharmony_ci  },
2471cb0ef41Sopenharmony_ci  onHeaders: (statusCode, headers) => {
2481cb0ef41Sopenharmony_ci    console.log(`onHeaders | statusCode: ${statusCode} | headers: ${headers}`)
2491cb0ef41Sopenharmony_ci  },
2501cb0ef41Sopenharmony_ci  onData: (chunk) => {
2511cb0ef41Sopenharmony_ci    console.log('onData: chunk received')
2521cb0ef41Sopenharmony_ci    data.push(chunk)
2531cb0ef41Sopenharmony_ci  },
2541cb0ef41Sopenharmony_ci  onComplete: (trailers) => {
2551cb0ef41Sopenharmony_ci    console.log(`onComplete | trailers: ${trailers}`)
2561cb0ef41Sopenharmony_ci    const res = Buffer.concat(data).toString('utf8')
2571cb0ef41Sopenharmony_ci    console.log(`Data: ${res}`)
2581cb0ef41Sopenharmony_ci    client.close()
2591cb0ef41Sopenharmony_ci    server.close()
2601cb0ef41Sopenharmony_ci  }
2611cb0ef41Sopenharmony_ci})
2621cb0ef41Sopenharmony_ci```
2631cb0ef41Sopenharmony_ci
2641cb0ef41Sopenharmony_ci#### Example 2 - Dispatch Upgrade Request
2651cb0ef41Sopenharmony_ci
2661cb0ef41Sopenharmony_ci```js
2671cb0ef41Sopenharmony_ciimport { createServer } from 'http'
2681cb0ef41Sopenharmony_ciimport { Client } from 'undici'
2691cb0ef41Sopenharmony_ciimport { once } from 'events'
2701cb0ef41Sopenharmony_ci
2711cb0ef41Sopenharmony_ciconst server = createServer((request, response) => {
2721cb0ef41Sopenharmony_ci  response.end()
2731cb0ef41Sopenharmony_ci}).listen()
2741cb0ef41Sopenharmony_ci
2751cb0ef41Sopenharmony_ciawait once(server, 'listening')
2761cb0ef41Sopenharmony_ci
2771cb0ef41Sopenharmony_ciserver.on('upgrade', (request, socket, head) => {
2781cb0ef41Sopenharmony_ci  console.log('Node.js Server - upgrade event')
2791cb0ef41Sopenharmony_ci  socket.write('HTTP/1.1 101 Web Socket Protocol Handshake\r\n')
2801cb0ef41Sopenharmony_ci  socket.write('Upgrade: WebSocket\r\n')
2811cb0ef41Sopenharmony_ci  socket.write('Connection: Upgrade\r\n')
2821cb0ef41Sopenharmony_ci  socket.write('\r\n')
2831cb0ef41Sopenharmony_ci  socket.end()
2841cb0ef41Sopenharmony_ci})
2851cb0ef41Sopenharmony_ci
2861cb0ef41Sopenharmony_ciconst client = new Client(`http://localhost:${server.address().port}`)
2871cb0ef41Sopenharmony_ci
2881cb0ef41Sopenharmony_ciclient.dispatch({
2891cb0ef41Sopenharmony_ci  path: '/',
2901cb0ef41Sopenharmony_ci  method: 'GET',
2911cb0ef41Sopenharmony_ci  upgrade: 'websocket'
2921cb0ef41Sopenharmony_ci}, {
2931cb0ef41Sopenharmony_ci  onConnect: () => {
2941cb0ef41Sopenharmony_ci    console.log('Undici Client - onConnect')
2951cb0ef41Sopenharmony_ci  },
2961cb0ef41Sopenharmony_ci  onError: (error) => {
2971cb0ef41Sopenharmony_ci    console.log('onError') // shouldn't print
2981cb0ef41Sopenharmony_ci  },
2991cb0ef41Sopenharmony_ci  onUpgrade: (statusCode, headers, socket) => {
3001cb0ef41Sopenharmony_ci    console.log('Undici Client - onUpgrade')
3011cb0ef41Sopenharmony_ci    console.log(`onUpgrade Headers: ${headers}`)
3021cb0ef41Sopenharmony_ci    socket.on('data', buffer => {
3031cb0ef41Sopenharmony_ci      console.log(buffer.toString('utf8'))
3041cb0ef41Sopenharmony_ci    })
3051cb0ef41Sopenharmony_ci    socket.on('end', () => {
3061cb0ef41Sopenharmony_ci      client.close()
3071cb0ef41Sopenharmony_ci      server.close()
3081cb0ef41Sopenharmony_ci    })
3091cb0ef41Sopenharmony_ci    socket.end()
3101cb0ef41Sopenharmony_ci  }
3111cb0ef41Sopenharmony_ci})
3121cb0ef41Sopenharmony_ci```
3131cb0ef41Sopenharmony_ci
3141cb0ef41Sopenharmony_ci#### Example 3 - Dispatch POST request
3151cb0ef41Sopenharmony_ci
3161cb0ef41Sopenharmony_ci```js
3171cb0ef41Sopenharmony_ciimport { createServer } from 'http'
3181cb0ef41Sopenharmony_ciimport { Client } from 'undici'
3191cb0ef41Sopenharmony_ciimport { once } from 'events'
3201cb0ef41Sopenharmony_ci
3211cb0ef41Sopenharmony_ciconst server = createServer((request, response) => {
3221cb0ef41Sopenharmony_ci  request.on('data', (data) => {
3231cb0ef41Sopenharmony_ci    console.log(`Request Data: ${data.toString('utf8')}`)
3241cb0ef41Sopenharmony_ci    const body = JSON.parse(data)
3251cb0ef41Sopenharmony_ci    body.message = 'World'
3261cb0ef41Sopenharmony_ci    response.end(JSON.stringify(body))
3271cb0ef41Sopenharmony_ci  })
3281cb0ef41Sopenharmony_ci}).listen()
3291cb0ef41Sopenharmony_ci
3301cb0ef41Sopenharmony_ciawait once(server, 'listening')
3311cb0ef41Sopenharmony_ci
3321cb0ef41Sopenharmony_ciconst client = new Client(`http://localhost:${server.address().port}`)
3331cb0ef41Sopenharmony_ci
3341cb0ef41Sopenharmony_ciconst data = []
3351cb0ef41Sopenharmony_ci
3361cb0ef41Sopenharmony_ciclient.dispatch({
3371cb0ef41Sopenharmony_ci  path: '/',
3381cb0ef41Sopenharmony_ci  method: 'POST',
3391cb0ef41Sopenharmony_ci  headers: {
3401cb0ef41Sopenharmony_ci    'content-type': 'application/json'
3411cb0ef41Sopenharmony_ci  },
3421cb0ef41Sopenharmony_ci  body: JSON.stringify({ message: 'Hello' })
3431cb0ef41Sopenharmony_ci}, {
3441cb0ef41Sopenharmony_ci  onConnect: () => {
3451cb0ef41Sopenharmony_ci    console.log('Connected!')
3461cb0ef41Sopenharmony_ci  },
3471cb0ef41Sopenharmony_ci  onError: (error) => {
3481cb0ef41Sopenharmony_ci    console.error(error)
3491cb0ef41Sopenharmony_ci  },
3501cb0ef41Sopenharmony_ci  onHeaders: (statusCode, headers) => {
3511cb0ef41Sopenharmony_ci    console.log(`onHeaders | statusCode: ${statusCode} | headers: ${headers}`)
3521cb0ef41Sopenharmony_ci  },
3531cb0ef41Sopenharmony_ci  onData: (chunk) => {
3541cb0ef41Sopenharmony_ci    console.log('onData: chunk received')
3551cb0ef41Sopenharmony_ci    data.push(chunk)
3561cb0ef41Sopenharmony_ci  },
3571cb0ef41Sopenharmony_ci  onComplete: (trailers) => {
3581cb0ef41Sopenharmony_ci    console.log(`onComplete | trailers: ${trailers}`)
3591cb0ef41Sopenharmony_ci    const res = Buffer.concat(data).toString('utf8')
3601cb0ef41Sopenharmony_ci    console.log(`Response Data: ${res}`)
3611cb0ef41Sopenharmony_ci    client.close()
3621cb0ef41Sopenharmony_ci    server.close()
3631cb0ef41Sopenharmony_ci  }
3641cb0ef41Sopenharmony_ci})
3651cb0ef41Sopenharmony_ci```
3661cb0ef41Sopenharmony_ci
3671cb0ef41Sopenharmony_ci### `Dispatcher.pipeline(options, handler)`
3681cb0ef41Sopenharmony_ci
3691cb0ef41Sopenharmony_ciFor easy use with [stream.pipeline](https://nodejs.org/api/stream.html#stream_stream_pipeline_source_transforms_destination_callback). The `handler` argument should return a `Readable` from which the result will be read. Usually it should just return the `body` argument unless some kind of transformation needs to be performed based on e.g. `headers` or `statusCode`. The `handler` should validate the response and save any required state. If there is an error, it should be thrown. The function returns a `Duplex` which writes to the request and reads from the response.
3701cb0ef41Sopenharmony_ci
3711cb0ef41Sopenharmony_ciArguments:
3721cb0ef41Sopenharmony_ci
3731cb0ef41Sopenharmony_ci* **options** `PipelineOptions`
3741cb0ef41Sopenharmony_ci* **handler** `(data: PipelineHandlerData) => stream.Readable`
3751cb0ef41Sopenharmony_ci
3761cb0ef41Sopenharmony_ciReturns: `stream.Duplex`
3771cb0ef41Sopenharmony_ci
3781cb0ef41Sopenharmony_ci#### Parameter: PipelineOptions
3791cb0ef41Sopenharmony_ci
3801cb0ef41Sopenharmony_ciExtends: [`RequestOptions`](#parameter-requestoptions)
3811cb0ef41Sopenharmony_ci
3821cb0ef41Sopenharmony_ci* **objectMode** `boolean` (optional) - Default: `false` - Set to `true` if the `handler` will return an object stream.
3831cb0ef41Sopenharmony_ci
3841cb0ef41Sopenharmony_ci#### Parameter: PipelineHandlerData
3851cb0ef41Sopenharmony_ci
3861cb0ef41Sopenharmony_ci* **statusCode** `number`
3871cb0ef41Sopenharmony_ci* **headers** `Record<string, string | string[] | undefined>`
3881cb0ef41Sopenharmony_ci* **opaque** `unknown`
3891cb0ef41Sopenharmony_ci* **body** `stream.Readable`
3901cb0ef41Sopenharmony_ci* **context** `object`
3911cb0ef41Sopenharmony_ci* **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.
3921cb0ef41Sopenharmony_ci
3931cb0ef41Sopenharmony_ci#### Example 1 - Pipeline Echo
3941cb0ef41Sopenharmony_ci
3951cb0ef41Sopenharmony_ci```js
3961cb0ef41Sopenharmony_ciimport { Readable, Writable, PassThrough, pipeline } from 'stream'
3971cb0ef41Sopenharmony_ciimport { createServer } from 'http'
3981cb0ef41Sopenharmony_ciimport { Client } from 'undici'
3991cb0ef41Sopenharmony_ciimport { once } from 'events'
4001cb0ef41Sopenharmony_ci
4011cb0ef41Sopenharmony_ciconst server = createServer((request, response) => {
4021cb0ef41Sopenharmony_ci  request.pipe(response)
4031cb0ef41Sopenharmony_ci}).listen()
4041cb0ef41Sopenharmony_ci
4051cb0ef41Sopenharmony_ciawait once(server, 'listening')
4061cb0ef41Sopenharmony_ci
4071cb0ef41Sopenharmony_ciconst client = new Client(`http://localhost:${server.address().port}`)
4081cb0ef41Sopenharmony_ci
4091cb0ef41Sopenharmony_cilet res = ''
4101cb0ef41Sopenharmony_ci
4111cb0ef41Sopenharmony_cipipeline(
4121cb0ef41Sopenharmony_ci  new Readable({
4131cb0ef41Sopenharmony_ci    read () {
4141cb0ef41Sopenharmony_ci      this.push(Buffer.from('undici'))
4151cb0ef41Sopenharmony_ci      this.push(null)
4161cb0ef41Sopenharmony_ci    }
4171cb0ef41Sopenharmony_ci  }),
4181cb0ef41Sopenharmony_ci  client.pipeline({
4191cb0ef41Sopenharmony_ci    path: '/',
4201cb0ef41Sopenharmony_ci    method: 'GET'
4211cb0ef41Sopenharmony_ci  }, ({ statusCode, headers, body }) => {
4221cb0ef41Sopenharmony_ci    console.log(`response received ${statusCode}`)
4231cb0ef41Sopenharmony_ci    console.log('headers', headers)
4241cb0ef41Sopenharmony_ci    return pipeline(body, new PassThrough(), () => {})
4251cb0ef41Sopenharmony_ci  }),
4261cb0ef41Sopenharmony_ci  new Writable({
4271cb0ef41Sopenharmony_ci    write (chunk, _, callback) {
4281cb0ef41Sopenharmony_ci      res += chunk.toString()
4291cb0ef41Sopenharmony_ci      callback()
4301cb0ef41Sopenharmony_ci    },
4311cb0ef41Sopenharmony_ci    final (callback) {
4321cb0ef41Sopenharmony_ci      console.log(`Response pipelined to writable: ${res}`)
4331cb0ef41Sopenharmony_ci      callback()
4341cb0ef41Sopenharmony_ci    }
4351cb0ef41Sopenharmony_ci  }),
4361cb0ef41Sopenharmony_ci  error => {
4371cb0ef41Sopenharmony_ci    if (error) {
4381cb0ef41Sopenharmony_ci      console.error(error)
4391cb0ef41Sopenharmony_ci    }
4401cb0ef41Sopenharmony_ci
4411cb0ef41Sopenharmony_ci    client.close()
4421cb0ef41Sopenharmony_ci    server.close()
4431cb0ef41Sopenharmony_ci  }
4441cb0ef41Sopenharmony_ci)
4451cb0ef41Sopenharmony_ci```
4461cb0ef41Sopenharmony_ci
4471cb0ef41Sopenharmony_ci### `Dispatcher.request(options[, callback])`
4481cb0ef41Sopenharmony_ci
4491cb0ef41Sopenharmony_ciPerforms a HTTP request.
4501cb0ef41Sopenharmony_ci
4511cb0ef41Sopenharmony_ciNon-idempotent requests will not be pipelined in order
4521cb0ef41Sopenharmony_cito avoid indirect failures.
4531cb0ef41Sopenharmony_ci
4541cb0ef41Sopenharmony_ciIdempotent requests will be automatically retried if
4551cb0ef41Sopenharmony_cithey fail due to indirect failure from the request
4561cb0ef41Sopenharmony_ciat the head of the pipeline. This does not apply to
4571cb0ef41Sopenharmony_ciidempotent requests with a stream request body.
4581cb0ef41Sopenharmony_ci
4591cb0ef41Sopenharmony_ciAll response bodies must always be fully consumed or destroyed.
4601cb0ef41Sopenharmony_ci
4611cb0ef41Sopenharmony_ciArguments:
4621cb0ef41Sopenharmony_ci
4631cb0ef41Sopenharmony_ci* **options** `RequestOptions`
4641cb0ef41Sopenharmony_ci* **callback** `(error: Error | null, data: ResponseData) => void` (optional)
4651cb0ef41Sopenharmony_ci
4661cb0ef41Sopenharmony_ciReturns: `void | Promise<ResponseData>` - Only returns a `Promise` if no `callback` argument was passed.
4671cb0ef41Sopenharmony_ci
4681cb0ef41Sopenharmony_ci#### Parameter: `RequestOptions`
4691cb0ef41Sopenharmony_ci
4701cb0ef41Sopenharmony_ciExtends: [`DispatchOptions`](#parameter-dispatchoptions)
4711cb0ef41Sopenharmony_ci
4721cb0ef41Sopenharmony_ci* **opaque** `unknown` (optional) - Default: `null` - Used for passing through context to `ResponseData`.
4731cb0ef41Sopenharmony_ci* **signal** `AbortSignal | events.EventEmitter | null` (optional) - Default: `null`.
4741cb0ef41Sopenharmony_ci* **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.
4751cb0ef41Sopenharmony_ci
4761cb0ef41Sopenharmony_ciThe `RequestOptions.method` property should not be value `'CONNECT'`.
4771cb0ef41Sopenharmony_ci
4781cb0ef41Sopenharmony_ci#### Parameter: `ResponseData`
4791cb0ef41Sopenharmony_ci
4801cb0ef41Sopenharmony_ci* **statusCode** `number`
4811cb0ef41Sopenharmony_ci* **headers** `Record<string, string | string[]>` - Note that all header keys are lower-cased, e. g. `content-type`.
4821cb0ef41Sopenharmony_ci* **body** `stream.Readable` which also implements [the body mixin from the Fetch Standard](https://fetch.spec.whatwg.org/#body-mixin).
4831cb0ef41Sopenharmony_ci* **trailers** `Record<string, string>` - This object starts out
4841cb0ef41Sopenharmony_ci  as empty and will be mutated to contain trailers after `body` has emitted `'end'`.
4851cb0ef41Sopenharmony_ci* **opaque** `unknown`
4861cb0ef41Sopenharmony_ci* **context** `object`
4871cb0ef41Sopenharmony_ci
4881cb0ef41Sopenharmony_ci`body` contains the following additional [body mixin](https://fetch.spec.whatwg.org/#body-mixin) methods and properties:
4891cb0ef41Sopenharmony_ci
4901cb0ef41Sopenharmony_ci- `text()`
4911cb0ef41Sopenharmony_ci- `json()`
4921cb0ef41Sopenharmony_ci- `arrayBuffer()`
4931cb0ef41Sopenharmony_ci- `body`
4941cb0ef41Sopenharmony_ci- `bodyUsed`
4951cb0ef41Sopenharmony_ci
4961cb0ef41Sopenharmony_ci`body` can not be consumed twice. For example, calling `text()` after `json()` throws `TypeError`.
4971cb0ef41Sopenharmony_ci
4981cb0ef41Sopenharmony_ci`body` contains the following additional extensions:
4991cb0ef41Sopenharmony_ci
5001cb0ef41Sopenharmony_ci- `dump({ limit: Integer })`, dump the response by reading up to `limit` bytes without killing the socket (optional) - Default: 262144.
5011cb0ef41Sopenharmony_ci
5021cb0ef41Sopenharmony_ciNote that body will still be a `Readable` even if it is empty, but attempting to deserialize it with `json()` will result in an exception. Recommended way to ensure there is a body to deserialize is to check if status code is not 204, and `content-type` header starts with `application/json`.
5031cb0ef41Sopenharmony_ci
5041cb0ef41Sopenharmony_ci#### Example 1 - Basic GET Request
5051cb0ef41Sopenharmony_ci
5061cb0ef41Sopenharmony_ci```js
5071cb0ef41Sopenharmony_ciimport { createServer } from 'http'
5081cb0ef41Sopenharmony_ciimport { Client } from 'undici'
5091cb0ef41Sopenharmony_ciimport { once } from 'events'
5101cb0ef41Sopenharmony_ci
5111cb0ef41Sopenharmony_ciconst server = createServer((request, response) => {
5121cb0ef41Sopenharmony_ci  response.end('Hello, World!')
5131cb0ef41Sopenharmony_ci}).listen()
5141cb0ef41Sopenharmony_ci
5151cb0ef41Sopenharmony_ciawait once(server, 'listening')
5161cb0ef41Sopenharmony_ci
5171cb0ef41Sopenharmony_ciconst client = new Client(`http://localhost:${server.address().port}`)
5181cb0ef41Sopenharmony_ci
5191cb0ef41Sopenharmony_citry {
5201cb0ef41Sopenharmony_ci  const { body, headers, statusCode, trailers } = await client.request({
5211cb0ef41Sopenharmony_ci    path: '/',
5221cb0ef41Sopenharmony_ci    method: 'GET'
5231cb0ef41Sopenharmony_ci  })
5241cb0ef41Sopenharmony_ci  console.log(`response received ${statusCode}`)
5251cb0ef41Sopenharmony_ci  console.log('headers', headers)
5261cb0ef41Sopenharmony_ci  body.setEncoding('utf8')
5271cb0ef41Sopenharmony_ci  body.on('data', console.log)
5281cb0ef41Sopenharmony_ci  body.on('end', () => {
5291cb0ef41Sopenharmony_ci    console.log('trailers', trailers)
5301cb0ef41Sopenharmony_ci  })
5311cb0ef41Sopenharmony_ci
5321cb0ef41Sopenharmony_ci  client.close()
5331cb0ef41Sopenharmony_ci  server.close()
5341cb0ef41Sopenharmony_ci} catch (error) {
5351cb0ef41Sopenharmony_ci  console.error(error)
5361cb0ef41Sopenharmony_ci}
5371cb0ef41Sopenharmony_ci```
5381cb0ef41Sopenharmony_ci
5391cb0ef41Sopenharmony_ci#### Example 2 - Aborting a request
5401cb0ef41Sopenharmony_ci
5411cb0ef41Sopenharmony_ci> Node.js v15+ is required to run this example
5421cb0ef41Sopenharmony_ci
5431cb0ef41Sopenharmony_ci```js
5441cb0ef41Sopenharmony_ciimport { createServer } from 'http'
5451cb0ef41Sopenharmony_ciimport { Client } from 'undici'
5461cb0ef41Sopenharmony_ciimport { once } from 'events'
5471cb0ef41Sopenharmony_ci
5481cb0ef41Sopenharmony_ciconst server = createServer((request, response) => {
5491cb0ef41Sopenharmony_ci  response.end('Hello, World!')
5501cb0ef41Sopenharmony_ci}).listen()
5511cb0ef41Sopenharmony_ci
5521cb0ef41Sopenharmony_ciawait once(server, 'listening')
5531cb0ef41Sopenharmony_ci
5541cb0ef41Sopenharmony_ciconst client = new Client(`http://localhost:${server.address().port}`)
5551cb0ef41Sopenharmony_ciconst abortController = new AbortController()
5561cb0ef41Sopenharmony_ci
5571cb0ef41Sopenharmony_citry {
5581cb0ef41Sopenharmony_ci  client.request({
5591cb0ef41Sopenharmony_ci    path: '/',
5601cb0ef41Sopenharmony_ci    method: 'GET',
5611cb0ef41Sopenharmony_ci    signal: abortController.signal
5621cb0ef41Sopenharmony_ci  })
5631cb0ef41Sopenharmony_ci} catch (error) {
5641cb0ef41Sopenharmony_ci  console.error(error) // should print an RequestAbortedError
5651cb0ef41Sopenharmony_ci  client.close()
5661cb0ef41Sopenharmony_ci  server.close()
5671cb0ef41Sopenharmony_ci}
5681cb0ef41Sopenharmony_ci
5691cb0ef41Sopenharmony_ciabortController.abort()
5701cb0ef41Sopenharmony_ci```
5711cb0ef41Sopenharmony_ci
5721cb0ef41Sopenharmony_ciAlternatively, any `EventEmitter` that emits an `'abort'` event may be used as an abort controller:
5731cb0ef41Sopenharmony_ci
5741cb0ef41Sopenharmony_ci```js
5751cb0ef41Sopenharmony_ciimport { createServer } from 'http'
5761cb0ef41Sopenharmony_ciimport { Client } from 'undici'
5771cb0ef41Sopenharmony_ciimport EventEmitter, { once } from 'events'
5781cb0ef41Sopenharmony_ci
5791cb0ef41Sopenharmony_ciconst server = createServer((request, response) => {
5801cb0ef41Sopenharmony_ci  response.end('Hello, World!')
5811cb0ef41Sopenharmony_ci}).listen()
5821cb0ef41Sopenharmony_ci
5831cb0ef41Sopenharmony_ciawait once(server, 'listening')
5841cb0ef41Sopenharmony_ci
5851cb0ef41Sopenharmony_ciconst client = new Client(`http://localhost:${server.address().port}`)
5861cb0ef41Sopenharmony_ciconst ee = new EventEmitter()
5871cb0ef41Sopenharmony_ci
5881cb0ef41Sopenharmony_citry {
5891cb0ef41Sopenharmony_ci  client.request({
5901cb0ef41Sopenharmony_ci    path: '/',
5911cb0ef41Sopenharmony_ci    method: 'GET',
5921cb0ef41Sopenharmony_ci    signal: ee
5931cb0ef41Sopenharmony_ci  })
5941cb0ef41Sopenharmony_ci} catch (error) {
5951cb0ef41Sopenharmony_ci  console.error(error) // should print an RequestAbortedError
5961cb0ef41Sopenharmony_ci  client.close()
5971cb0ef41Sopenharmony_ci  server.close()
5981cb0ef41Sopenharmony_ci}
5991cb0ef41Sopenharmony_ci
6001cb0ef41Sopenharmony_ciee.emit('abort')
6011cb0ef41Sopenharmony_ci```
6021cb0ef41Sopenharmony_ci
6031cb0ef41Sopenharmony_ciDestroying the request or response body will have the same effect.
6041cb0ef41Sopenharmony_ci
6051cb0ef41Sopenharmony_ci```js
6061cb0ef41Sopenharmony_ciimport { createServer } from 'http'
6071cb0ef41Sopenharmony_ciimport { Client } from 'undici'
6081cb0ef41Sopenharmony_ciimport { once } from 'events'
6091cb0ef41Sopenharmony_ci
6101cb0ef41Sopenharmony_ciconst server = createServer((request, response) => {
6111cb0ef41Sopenharmony_ci  response.end('Hello, World!')
6121cb0ef41Sopenharmony_ci}).listen()
6131cb0ef41Sopenharmony_ci
6141cb0ef41Sopenharmony_ciawait once(server, 'listening')
6151cb0ef41Sopenharmony_ci
6161cb0ef41Sopenharmony_ciconst client = new Client(`http://localhost:${server.address().port}`)
6171cb0ef41Sopenharmony_ci
6181cb0ef41Sopenharmony_citry {
6191cb0ef41Sopenharmony_ci  const { body } = await client.request({
6201cb0ef41Sopenharmony_ci    path: '/',
6211cb0ef41Sopenharmony_ci    method: 'GET'
6221cb0ef41Sopenharmony_ci  })
6231cb0ef41Sopenharmony_ci  body.destroy()
6241cb0ef41Sopenharmony_ci} catch (error) {
6251cb0ef41Sopenharmony_ci  console.error(error) // should print an RequestAbortedError
6261cb0ef41Sopenharmony_ci  client.close()
6271cb0ef41Sopenharmony_ci  server.close()
6281cb0ef41Sopenharmony_ci}
6291cb0ef41Sopenharmony_ci```
6301cb0ef41Sopenharmony_ci
6311cb0ef41Sopenharmony_ci### `Dispatcher.stream(options, factory[, callback])`
6321cb0ef41Sopenharmony_ci
6331cb0ef41Sopenharmony_ciA faster version of `Dispatcher.request`. This method expects the second argument `factory` to return a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable) stream which the response will be written to. This improves performance by avoiding creating an intermediate [`stream.Readable`](https://nodejs.org/api/stream.html#stream_readable_streams) stream when the user expects to directly pipe the response body to a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable) stream.
6341cb0ef41Sopenharmony_ci
6351cb0ef41Sopenharmony_ciAs demonstrated in [Example 1 - Basic GET stream request](#example-1---basic-get-stream-request), it is recommended to use the `option.opaque` property to avoid creating a closure for the `factory` method. This pattern works well with Node.js Web Frameworks such as [Fastify](https://fastify.io). See [Example 2 - Stream to Fastify Response](#example-2---stream-to-fastify-response) for more details.
6361cb0ef41Sopenharmony_ci
6371cb0ef41Sopenharmony_ciArguments:
6381cb0ef41Sopenharmony_ci
6391cb0ef41Sopenharmony_ci* **options** `RequestOptions`
6401cb0ef41Sopenharmony_ci* **factory** `(data: StreamFactoryData) => stream.Writable`
6411cb0ef41Sopenharmony_ci* **callback** `(error: Error | null, data: StreamData) => void` (optional)
6421cb0ef41Sopenharmony_ci
6431cb0ef41Sopenharmony_ciReturns: `void | Promise<StreamData>` - Only returns a `Promise` if no `callback` argument was passed
6441cb0ef41Sopenharmony_ci
6451cb0ef41Sopenharmony_ci#### Parameter: `StreamFactoryData`
6461cb0ef41Sopenharmony_ci
6471cb0ef41Sopenharmony_ci* **statusCode** `number`
6481cb0ef41Sopenharmony_ci* **headers** `Record<string, string | string[] | undefined>`
6491cb0ef41Sopenharmony_ci* **opaque** `unknown`
6501cb0ef41Sopenharmony_ci* **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.
6511cb0ef41Sopenharmony_ci
6521cb0ef41Sopenharmony_ci#### Parameter: `StreamData`
6531cb0ef41Sopenharmony_ci
6541cb0ef41Sopenharmony_ci* **opaque** `unknown`
6551cb0ef41Sopenharmony_ci* **trailers** `Record<string, string>`
6561cb0ef41Sopenharmony_ci* **context** `object`
6571cb0ef41Sopenharmony_ci
6581cb0ef41Sopenharmony_ci#### Example 1 - Basic GET stream request
6591cb0ef41Sopenharmony_ci
6601cb0ef41Sopenharmony_ci```js
6611cb0ef41Sopenharmony_ciimport { createServer } from 'http'
6621cb0ef41Sopenharmony_ciimport { Client } from 'undici'
6631cb0ef41Sopenharmony_ciimport { once } from 'events'
6641cb0ef41Sopenharmony_ciimport { Writable } from 'stream'
6651cb0ef41Sopenharmony_ci
6661cb0ef41Sopenharmony_ciconst server = createServer((request, response) => {
6671cb0ef41Sopenharmony_ci  response.end('Hello, World!')
6681cb0ef41Sopenharmony_ci}).listen()
6691cb0ef41Sopenharmony_ci
6701cb0ef41Sopenharmony_ciawait once(server, 'listening')
6711cb0ef41Sopenharmony_ci
6721cb0ef41Sopenharmony_ciconst client = new Client(`http://localhost:${server.address().port}`)
6731cb0ef41Sopenharmony_ci
6741cb0ef41Sopenharmony_ciconst bufs = []
6751cb0ef41Sopenharmony_ci
6761cb0ef41Sopenharmony_citry {
6771cb0ef41Sopenharmony_ci  await client.stream({
6781cb0ef41Sopenharmony_ci    path: '/',
6791cb0ef41Sopenharmony_ci    method: 'GET',
6801cb0ef41Sopenharmony_ci    opaque: { bufs }
6811cb0ef41Sopenharmony_ci  }, ({ statusCode, headers, opaque: { bufs } }) => {
6821cb0ef41Sopenharmony_ci    console.log(`response received ${statusCode}`)
6831cb0ef41Sopenharmony_ci    console.log('headers', headers)
6841cb0ef41Sopenharmony_ci    return new Writable({
6851cb0ef41Sopenharmony_ci      write (chunk, encoding, callback) {
6861cb0ef41Sopenharmony_ci        bufs.push(chunk)
6871cb0ef41Sopenharmony_ci        callback()
6881cb0ef41Sopenharmony_ci      }
6891cb0ef41Sopenharmony_ci    })
6901cb0ef41Sopenharmony_ci  })
6911cb0ef41Sopenharmony_ci
6921cb0ef41Sopenharmony_ci  console.log(Buffer.concat(bufs).toString('utf-8'))
6931cb0ef41Sopenharmony_ci
6941cb0ef41Sopenharmony_ci  client.close()
6951cb0ef41Sopenharmony_ci  server.close()
6961cb0ef41Sopenharmony_ci} catch (error) {
6971cb0ef41Sopenharmony_ci  console.error(error)
6981cb0ef41Sopenharmony_ci}
6991cb0ef41Sopenharmony_ci```
7001cb0ef41Sopenharmony_ci
7011cb0ef41Sopenharmony_ci#### Example 2 - Stream to Fastify Response
7021cb0ef41Sopenharmony_ci
7031cb0ef41Sopenharmony_ciIn this example, a (fake) request is made to the fastify server using `fastify.inject()`. This request then executes the fastify route handler which makes a subsequent request to the raw Node.js http server using `undici.dispatcher.stream()`. The fastify response is passed to the `opaque` option so that undici can tap into the underlying writable stream using `response.raw`. This methodology demonstrates how one could use undici and fastify together to create fast-as-possible requests from one backend server to another.
7041cb0ef41Sopenharmony_ci
7051cb0ef41Sopenharmony_ci```js
7061cb0ef41Sopenharmony_ciimport { createServer } from 'http'
7071cb0ef41Sopenharmony_ciimport { Client } from 'undici'
7081cb0ef41Sopenharmony_ciimport { once } from 'events'
7091cb0ef41Sopenharmony_ciimport fastify from 'fastify'
7101cb0ef41Sopenharmony_ci
7111cb0ef41Sopenharmony_ciconst nodeServer = createServer((request, response) => {
7121cb0ef41Sopenharmony_ci  response.end('Hello, World! From Node.js HTTP Server')
7131cb0ef41Sopenharmony_ci}).listen()
7141cb0ef41Sopenharmony_ci
7151cb0ef41Sopenharmony_ciawait once(nodeServer, 'listening')
7161cb0ef41Sopenharmony_ci
7171cb0ef41Sopenharmony_ciconsole.log('Node Server listening')
7181cb0ef41Sopenharmony_ci
7191cb0ef41Sopenharmony_ciconst nodeServerUndiciClient = new Client(`http://localhost:${nodeServer.address().port}`)
7201cb0ef41Sopenharmony_ci
7211cb0ef41Sopenharmony_ciconst fastifyServer = fastify()
7221cb0ef41Sopenharmony_ci
7231cb0ef41Sopenharmony_cifastifyServer.route({
7241cb0ef41Sopenharmony_ci  url: '/',
7251cb0ef41Sopenharmony_ci  method: 'GET',
7261cb0ef41Sopenharmony_ci  handler: (request, response) => {
7271cb0ef41Sopenharmony_ci    nodeServerUndiciClient.stream({
7281cb0ef41Sopenharmony_ci      path: '/',
7291cb0ef41Sopenharmony_ci      method: 'GET',
7301cb0ef41Sopenharmony_ci      opaque: response
7311cb0ef41Sopenharmony_ci    }, ({ opaque }) => opaque.raw)
7321cb0ef41Sopenharmony_ci  }
7331cb0ef41Sopenharmony_ci})
7341cb0ef41Sopenharmony_ci
7351cb0ef41Sopenharmony_ciawait fastifyServer.listen()
7361cb0ef41Sopenharmony_ci
7371cb0ef41Sopenharmony_ciconsole.log('Fastify Server listening')
7381cb0ef41Sopenharmony_ci
7391cb0ef41Sopenharmony_ciconst fastifyServerUndiciClient = new Client(`http://localhost:${fastifyServer.server.address().port}`)
7401cb0ef41Sopenharmony_ci
7411cb0ef41Sopenharmony_citry {
7421cb0ef41Sopenharmony_ci  const { statusCode, body } = await fastifyServerUndiciClient.request({
7431cb0ef41Sopenharmony_ci    path: '/',
7441cb0ef41Sopenharmony_ci    method: 'GET'
7451cb0ef41Sopenharmony_ci  })
7461cb0ef41Sopenharmony_ci
7471cb0ef41Sopenharmony_ci  console.log(`response received ${statusCode}`)
7481cb0ef41Sopenharmony_ci  body.setEncoding('utf8')
7491cb0ef41Sopenharmony_ci  body.on('data', console.log)
7501cb0ef41Sopenharmony_ci
7511cb0ef41Sopenharmony_ci  nodeServerUndiciClient.close()
7521cb0ef41Sopenharmony_ci  fastifyServerUndiciClient.close()
7531cb0ef41Sopenharmony_ci  fastifyServer.close()
7541cb0ef41Sopenharmony_ci  nodeServer.close()
7551cb0ef41Sopenharmony_ci} catch (error) { }
7561cb0ef41Sopenharmony_ci```
7571cb0ef41Sopenharmony_ci
7581cb0ef41Sopenharmony_ci### `Dispatcher.upgrade(options[, callback])`
7591cb0ef41Sopenharmony_ci
7601cb0ef41Sopenharmony_ciUpgrade to a different protocol. Visit [MDN - HTTP - Protocol upgrade mechanism](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism) for more details.
7611cb0ef41Sopenharmony_ci
7621cb0ef41Sopenharmony_ciArguments:
7631cb0ef41Sopenharmony_ci
7641cb0ef41Sopenharmony_ci* **options** `UpgradeOptions`
7651cb0ef41Sopenharmony_ci
7661cb0ef41Sopenharmony_ci* **callback** `(error: Error | null, data: UpgradeData) => void` (optional)
7671cb0ef41Sopenharmony_ci
7681cb0ef41Sopenharmony_ciReturns: `void | Promise<UpgradeData>` - Only returns a `Promise` if no `callback` argument was passed
7691cb0ef41Sopenharmony_ci
7701cb0ef41Sopenharmony_ci#### Parameter: `UpgradeOptions`
7711cb0ef41Sopenharmony_ci
7721cb0ef41Sopenharmony_ci* **path** `string`
7731cb0ef41Sopenharmony_ci* **method** `string` (optional) - Default: `'GET'`
7741cb0ef41Sopenharmony_ci* **headers** `UndiciHeaders` (optional) - Default: `null`
7751cb0ef41Sopenharmony_ci* **protocol** `string` (optional) - Default: `'Websocket'` - A string of comma separated protocols, in descending preference order.
7761cb0ef41Sopenharmony_ci* **signal** `AbortSignal | EventEmitter | null` (optional) - Default: `null`
7771cb0ef41Sopenharmony_ci
7781cb0ef41Sopenharmony_ci#### Parameter: `UpgradeData`
7791cb0ef41Sopenharmony_ci
7801cb0ef41Sopenharmony_ci* **headers** `http.IncomingHeaders`
7811cb0ef41Sopenharmony_ci* **socket** `stream.Duplex`
7821cb0ef41Sopenharmony_ci* **opaque** `unknown`
7831cb0ef41Sopenharmony_ci
7841cb0ef41Sopenharmony_ci#### Example 1 - Basic Upgrade Request
7851cb0ef41Sopenharmony_ci
7861cb0ef41Sopenharmony_ci```js
7871cb0ef41Sopenharmony_ciimport { createServer } from 'http'
7881cb0ef41Sopenharmony_ciimport { Client } from 'undici'
7891cb0ef41Sopenharmony_ciimport { once } from 'events'
7901cb0ef41Sopenharmony_ci
7911cb0ef41Sopenharmony_ciconst server = createServer((request, response) => {
7921cb0ef41Sopenharmony_ci  response.statusCode = 101
7931cb0ef41Sopenharmony_ci  response.setHeader('connection', 'upgrade')
7941cb0ef41Sopenharmony_ci  response.setHeader('upgrade', request.headers.upgrade)
7951cb0ef41Sopenharmony_ci  response.end()
7961cb0ef41Sopenharmony_ci}).listen()
7971cb0ef41Sopenharmony_ci
7981cb0ef41Sopenharmony_ciawait once(server, 'listening')
7991cb0ef41Sopenharmony_ci
8001cb0ef41Sopenharmony_ciconst client = new Client(`http://localhost:${server.address().port}`)
8011cb0ef41Sopenharmony_ci
8021cb0ef41Sopenharmony_citry {
8031cb0ef41Sopenharmony_ci  const { headers, socket } = await client.upgrade({
8041cb0ef41Sopenharmony_ci    path: '/',
8051cb0ef41Sopenharmony_ci  })
8061cb0ef41Sopenharmony_ci  socket.on('end', () => {
8071cb0ef41Sopenharmony_ci    console.log(`upgrade: ${headers.upgrade}`) // upgrade: Websocket
8081cb0ef41Sopenharmony_ci    client.close()
8091cb0ef41Sopenharmony_ci    server.close()
8101cb0ef41Sopenharmony_ci  })
8111cb0ef41Sopenharmony_ci  socket.end()
8121cb0ef41Sopenharmony_ci} catch (error) {
8131cb0ef41Sopenharmony_ci  console.error(error)
8141cb0ef41Sopenharmony_ci  client.close()
8151cb0ef41Sopenharmony_ci  server.close()
8161cb0ef41Sopenharmony_ci}
8171cb0ef41Sopenharmony_ci```
8181cb0ef41Sopenharmony_ci
8191cb0ef41Sopenharmony_ci## Instance Events
8201cb0ef41Sopenharmony_ci
8211cb0ef41Sopenharmony_ci### Event: `'connect'`
8221cb0ef41Sopenharmony_ci
8231cb0ef41Sopenharmony_ciParameters:
8241cb0ef41Sopenharmony_ci
8251cb0ef41Sopenharmony_ci* **origin** `URL`
8261cb0ef41Sopenharmony_ci* **targets** `Array<Dispatcher>`
8271cb0ef41Sopenharmony_ci
8281cb0ef41Sopenharmony_ci### Event: `'disconnect'`
8291cb0ef41Sopenharmony_ci
8301cb0ef41Sopenharmony_ciParameters:
8311cb0ef41Sopenharmony_ci
8321cb0ef41Sopenharmony_ci* **origin** `URL`
8331cb0ef41Sopenharmony_ci* **targets** `Array<Dispatcher>`
8341cb0ef41Sopenharmony_ci* **error** `Error`
8351cb0ef41Sopenharmony_ci
8361cb0ef41Sopenharmony_ci### Event: `'connectionError'`
8371cb0ef41Sopenharmony_ci
8381cb0ef41Sopenharmony_ciParameters:
8391cb0ef41Sopenharmony_ci
8401cb0ef41Sopenharmony_ci* **origin** `URL`
8411cb0ef41Sopenharmony_ci* **targets** `Array<Dispatcher>`
8421cb0ef41Sopenharmony_ci* **error** `Error`
8431cb0ef41Sopenharmony_ci
8441cb0ef41Sopenharmony_ciEmitted when dispatcher fails to connect to
8451cb0ef41Sopenharmony_ciorigin.
8461cb0ef41Sopenharmony_ci
8471cb0ef41Sopenharmony_ci### Event: `'drain'`
8481cb0ef41Sopenharmony_ci
8491cb0ef41Sopenharmony_ciParameters:
8501cb0ef41Sopenharmony_ci
8511cb0ef41Sopenharmony_ci* **origin** `URL`
8521cb0ef41Sopenharmony_ci
8531cb0ef41Sopenharmony_ciEmitted when dispatcher is no longer busy.
8541cb0ef41Sopenharmony_ci
8551cb0ef41Sopenharmony_ci## Parameter: `UndiciHeaders`
8561cb0ef41Sopenharmony_ci
8571cb0ef41Sopenharmony_ci* `Record<string, string | string[] | undefined> | string[] | null`
8581cb0ef41Sopenharmony_ci
8591cb0ef41Sopenharmony_ciHeader 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 `Record<string, string | string[] | undefined>` (`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.
8601cb0ef41Sopenharmony_ci
8611cb0ef41Sopenharmony_ciKeys are lowercase and values are not modified.
8621cb0ef41Sopenharmony_ci
8631cb0ef41Sopenharmony_ciResponse headers will derive a `host` from the `url` of the [Client](Client.md#class-client) instance if no `host` header was previously specified.
8641cb0ef41Sopenharmony_ci
8651cb0ef41Sopenharmony_ci### Example 1 - Object
8661cb0ef41Sopenharmony_ci
8671cb0ef41Sopenharmony_ci```js
8681cb0ef41Sopenharmony_ci{
8691cb0ef41Sopenharmony_ci  'content-length': '123',
8701cb0ef41Sopenharmony_ci  'content-type': 'text/plain',
8711cb0ef41Sopenharmony_ci  connection: 'keep-alive',
8721cb0ef41Sopenharmony_ci  host: 'mysite.com',
8731cb0ef41Sopenharmony_ci  accept: '*/*'
8741cb0ef41Sopenharmony_ci}
8751cb0ef41Sopenharmony_ci```
8761cb0ef41Sopenharmony_ci
8771cb0ef41Sopenharmony_ci### Example 2 - Array
8781cb0ef41Sopenharmony_ci
8791cb0ef41Sopenharmony_ci```js
8801cb0ef41Sopenharmony_ci[
8811cb0ef41Sopenharmony_ci  'content-length', '123',
8821cb0ef41Sopenharmony_ci  'content-type', 'text/plain',
8831cb0ef41Sopenharmony_ci  'connection', 'keep-alive',
8841cb0ef41Sopenharmony_ci  'host', 'mysite.com',
8851cb0ef41Sopenharmony_ci  'accept', '*/*'
8861cb0ef41Sopenharmony_ci]
8871cb0ef41Sopenharmony_ci```
888