1# Dispatcher
2
3Extends: `events.EventEmitter`
4
5Dispatcher is the core API used to dispatch requests.
6
7Requests are not guaranteed to be dispatched in order of invocation.
8
9## Instance Methods
10
11### `Dispatcher.close([callback]): Promise`
12
13Closes the dispatcher and gracefully waits for enqueued requests to complete before resolving.
14
15Arguments:
16
17* **callback** `(error: Error | null, data: null) => void` (optional)
18
19Returns: `void | Promise<null>` - Only returns a `Promise` if no `callback` argument was passed
20
21```js
22dispatcher.close() // -> Promise
23dispatcher.close(() => {}) // -> void
24```
25
26#### Example - Request resolves before Client closes
27
28```js
29import { createServer } from 'http'
30import { Client } from 'undici'
31import { once } from 'events'
32
33const server = createServer((request, response) => {
34  response.end('undici')
35}).listen()
36
37await once(server, 'listening')
38
39const client = new Client(`http://localhost:${server.address().port}`)
40
41try {
42  const { body } = await client.request({
43      path: '/',
44      method: 'GET'
45  })
46  body.setEncoding('utf8')
47  body.on('data', console.log)
48} catch (error) {}
49
50await client.close()
51
52console.log('Client closed')
53server.close()
54```
55
56### `Dispatcher.connect(options[, callback])`
57
58Starts two-way communications with the requested resource using [HTTP CONNECT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT).
59
60Arguments:
61
62* **options** `ConnectOptions`
63* **callback** `(err: Error | null, data: ConnectData | null) => void` (optional)
64
65Returns: `void | Promise<ConnectData>` - Only returns a `Promise` if no `callback` argument was passed
66
67#### Parameter: `ConnectOptions`
68
69* **path** `string`
70* **headers** `UndiciHeaders` (optional) - Default: `null`
71* **signal** `AbortSignal | events.EventEmitter | null` (optional) - Default: `null`
72* **opaque** `unknown` (optional) - This argument parameter is passed through to `ConnectData`
73
74#### Parameter: `ConnectData`
75
76* **statusCode** `number`
77* **headers** `Record<string, string | string[] | undefined>`
78* **socket** `stream.Duplex`
79* **opaque** `unknown`
80
81#### Example - Connect request with echo
82
83```js
84import { createServer } from 'http'
85import { Client } from 'undici'
86import { once } from 'events'
87
88const server = createServer((request, response) => {
89  throw Error('should never get here')
90}).listen()
91
92server.on('connect', (req, socket, head) => {
93  socket.write('HTTP/1.1 200 Connection established\r\n\r\n')
94
95  let data = head.toString()
96  socket.on('data', (buf) => {
97    data += buf.toString()
98  })
99
100  socket.on('end', () => {
101    socket.end(data)
102  })
103})
104
105await once(server, 'listening')
106
107const client = new Client(`http://localhost:${server.address().port}`)
108
109try {
110  const { socket } = await client.connect({
111    path: '/'
112  })
113  const wanted = 'Body'
114  let data = ''
115  socket.on('data', d => { data += d })
116  socket.on('end', () => {
117    console.log(`Data received: ${data.toString()} | Data wanted: ${wanted}`)
118    client.close()
119    server.close()
120  })
121  socket.write(wanted)
122  socket.end()
123} catch (error) { }
124```
125
126### `Dispatcher.destroy([error, callback]): Promise`
127
128Destroy 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.
129
130Both arguments are optional; the method can be called in four different ways:
131
132Arguments:
133
134* **error** `Error | null` (optional)
135* **callback** `(error: Error | null, data: null) => void` (optional)
136
137Returns: `void | Promise<void>` - Only returns a `Promise` if no `callback` argument was passed
138
139```js
140dispatcher.destroy() // -> Promise
141dispatcher.destroy(new Error()) // -> Promise
142dispatcher.destroy(() => {}) // -> void
143dispatcher.destroy(new Error(), () => {}) // -> void
144```
145
146#### Example - Request is aborted when Client is destroyed
147
148```js
149import { createServer } from 'http'
150import { Client } from 'undici'
151import { once } from 'events'
152
153const server = createServer((request, response) => {
154  response.end()
155}).listen()
156
157await once(server, 'listening')
158
159const client = new Client(`http://localhost:${server.address().port}`)
160
161try {
162  const request = client.request({
163    path: '/',
164    method: 'GET'
165  })
166  client.destroy()
167    .then(() => {
168      console.log('Client destroyed')
169      server.close()
170    })
171  await request
172} catch (error) {
173  console.error(error)
174}
175```
176
177### `Dispatcher.dispatch(options, handler)`
178
179This is the low level API which all the preceding APIs are implemented on top of.
180This API is expected to evolve through semver-major versions and is less stable than the preceding higher level APIs.
181It is primarily intended for library developers who implement higher level APIs on top of this.
182
183Arguments:
184
185* **options** `DispatchOptions`
186* **handler** `DispatchHandler`
187
188Returns: `Boolean` - `false` if dispatcher is busy and further dispatch calls won't make any progress until the `'drain'` event has been emitted.
189
190#### Parameter: `DispatchOptions`
191
192* **origin** `string | URL`
193* **path** `string`
194* **method** `string`
195* **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.
196* **body** `string | Buffer | Uint8Array | stream.Readable | Iterable | AsyncIterable | null` (optional) - Default: `null`
197* **headers** `UndiciHeaders | string[]` (optional) - Default: `null`.
198* **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.
199* **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.
200* **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.
201* **upgrade** `string | null` (optional) - Default: `null` - Upgrade the request. Should be used to specify the kind of upgrade i.e. `'Websocket'`.
202* **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.
203* **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.
204* **throwOnError** `boolean` (optional) - Default: `false` - Whether Undici should throw an error upon receiving a 4xx or 5xx response from the server.
205* **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
206
207#### Parameter: `DispatchHandler`
208
209* **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.
210* **onError** `(error: Error) => void` - Invoked when an error has occurred. May not throw.
211* **onUpgrade** `(statusCode: number, headers: Buffer[], socket: Duplex) => void` (optional) - Invoked when request is upgraded. Required if `DispatchOptions.upgrade` is defined or `DispatchOptions.method === 'CONNECT'`.
212* **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.
213* **onData** `(chunk: Buffer) => boolean` - Invoked when response payload data is received. Not required for `upgrade` requests.
214* **onComplete** `(trailers: Buffer[]) => void` - Invoked when response payload and trailers have been received and the request has completed. Not required for `upgrade` requests.
215* **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.
216
217#### Example 1 - Dispatch GET request
218
219```js
220import { createServer } from 'http'
221import { Client } from 'undici'
222import { once } from 'events'
223
224const server = createServer((request, response) => {
225  response.end('Hello, World!')
226}).listen()
227
228await once(server, 'listening')
229
230const client = new Client(`http://localhost:${server.address().port}`)
231
232const data = []
233
234client.dispatch({
235  path: '/',
236  method: 'GET',
237  headers: {
238    'x-foo': 'bar'
239  }
240}, {
241  onConnect: () => {
242    console.log('Connected!')
243  },
244  onError: (error) => {
245    console.error(error)
246  },
247  onHeaders: (statusCode, headers) => {
248    console.log(`onHeaders | statusCode: ${statusCode} | headers: ${headers}`)
249  },
250  onData: (chunk) => {
251    console.log('onData: chunk received')
252    data.push(chunk)
253  },
254  onComplete: (trailers) => {
255    console.log(`onComplete | trailers: ${trailers}`)
256    const res = Buffer.concat(data).toString('utf8')
257    console.log(`Data: ${res}`)
258    client.close()
259    server.close()
260  }
261})
262```
263
264#### Example 2 - Dispatch Upgrade Request
265
266```js
267import { createServer } from 'http'
268import { Client } from 'undici'
269import { once } from 'events'
270
271const server = createServer((request, response) => {
272  response.end()
273}).listen()
274
275await once(server, 'listening')
276
277server.on('upgrade', (request, socket, head) => {
278  console.log('Node.js Server - upgrade event')
279  socket.write('HTTP/1.1 101 Web Socket Protocol Handshake\r\n')
280  socket.write('Upgrade: WebSocket\r\n')
281  socket.write('Connection: Upgrade\r\n')
282  socket.write('\r\n')
283  socket.end()
284})
285
286const client = new Client(`http://localhost:${server.address().port}`)
287
288client.dispatch({
289  path: '/',
290  method: 'GET',
291  upgrade: 'websocket'
292}, {
293  onConnect: () => {
294    console.log('Undici Client - onConnect')
295  },
296  onError: (error) => {
297    console.log('onError') // shouldn't print
298  },
299  onUpgrade: (statusCode, headers, socket) => {
300    console.log('Undici Client - onUpgrade')
301    console.log(`onUpgrade Headers: ${headers}`)
302    socket.on('data', buffer => {
303      console.log(buffer.toString('utf8'))
304    })
305    socket.on('end', () => {
306      client.close()
307      server.close()
308    })
309    socket.end()
310  }
311})
312```
313
314#### Example 3 - Dispatch POST request
315
316```js
317import { createServer } from 'http'
318import { Client } from 'undici'
319import { once } from 'events'
320
321const server = createServer((request, response) => {
322  request.on('data', (data) => {
323    console.log(`Request Data: ${data.toString('utf8')}`)
324    const body = JSON.parse(data)
325    body.message = 'World'
326    response.end(JSON.stringify(body))
327  })
328}).listen()
329
330await once(server, 'listening')
331
332const client = new Client(`http://localhost:${server.address().port}`)
333
334const data = []
335
336client.dispatch({
337  path: '/',
338  method: 'POST',
339  headers: {
340    'content-type': 'application/json'
341  },
342  body: JSON.stringify({ message: 'Hello' })
343}, {
344  onConnect: () => {
345    console.log('Connected!')
346  },
347  onError: (error) => {
348    console.error(error)
349  },
350  onHeaders: (statusCode, headers) => {
351    console.log(`onHeaders | statusCode: ${statusCode} | headers: ${headers}`)
352  },
353  onData: (chunk) => {
354    console.log('onData: chunk received')
355    data.push(chunk)
356  },
357  onComplete: (trailers) => {
358    console.log(`onComplete | trailers: ${trailers}`)
359    const res = Buffer.concat(data).toString('utf8')
360    console.log(`Response Data: ${res}`)
361    client.close()
362    server.close()
363  }
364})
365```
366
367### `Dispatcher.pipeline(options, handler)`
368
369For 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.
370
371Arguments:
372
373* **options** `PipelineOptions`
374* **handler** `(data: PipelineHandlerData) => stream.Readable`
375
376Returns: `stream.Duplex`
377
378#### Parameter: PipelineOptions
379
380Extends: [`RequestOptions`](#parameter-requestoptions)
381
382* **objectMode** `boolean` (optional) - Default: `false` - Set to `true` if the `handler` will return an object stream.
383
384#### Parameter: PipelineHandlerData
385
386* **statusCode** `number`
387* **headers** `Record<string, string | string[] | undefined>`
388* **opaque** `unknown`
389* **body** `stream.Readable`
390* **context** `object`
391* **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.
392
393#### Example 1 - Pipeline Echo
394
395```js
396import { Readable, Writable, PassThrough, pipeline } from 'stream'
397import { createServer } from 'http'
398import { Client } from 'undici'
399import { once } from 'events'
400
401const server = createServer((request, response) => {
402  request.pipe(response)
403}).listen()
404
405await once(server, 'listening')
406
407const client = new Client(`http://localhost:${server.address().port}`)
408
409let res = ''
410
411pipeline(
412  new Readable({
413    read () {
414      this.push(Buffer.from('undici'))
415      this.push(null)
416    }
417  }),
418  client.pipeline({
419    path: '/',
420    method: 'GET'
421  }, ({ statusCode, headers, body }) => {
422    console.log(`response received ${statusCode}`)
423    console.log('headers', headers)
424    return pipeline(body, new PassThrough(), () => {})
425  }),
426  new Writable({
427    write (chunk, _, callback) {
428      res += chunk.toString()
429      callback()
430    },
431    final (callback) {
432      console.log(`Response pipelined to writable: ${res}`)
433      callback()
434    }
435  }),
436  error => {
437    if (error) {
438      console.error(error)
439    }
440
441    client.close()
442    server.close()
443  }
444)
445```
446
447### `Dispatcher.request(options[, callback])`
448
449Performs a HTTP request.
450
451Non-idempotent requests will not be pipelined in order
452to avoid indirect failures.
453
454Idempotent requests will be automatically retried if
455they fail due to indirect failure from the request
456at the head of the pipeline. This does not apply to
457idempotent requests with a stream request body.
458
459All response bodies must always be fully consumed or destroyed.
460
461Arguments:
462
463* **options** `RequestOptions`
464* **callback** `(error: Error | null, data: ResponseData) => void` (optional)
465
466Returns: `void | Promise<ResponseData>` - Only returns a `Promise` if no `callback` argument was passed.
467
468#### Parameter: `RequestOptions`
469
470Extends: [`DispatchOptions`](#parameter-dispatchoptions)
471
472* **opaque** `unknown` (optional) - Default: `null` - Used for passing through context to `ResponseData`.
473* **signal** `AbortSignal | events.EventEmitter | null` (optional) - Default: `null`.
474* **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.
475
476The `RequestOptions.method` property should not be value `'CONNECT'`.
477
478#### Parameter: `ResponseData`
479
480* **statusCode** `number`
481* **headers** `Record<string, string | string[]>` - Note that all header keys are lower-cased, e. g. `content-type`.
482* **body** `stream.Readable` which also implements [the body mixin from the Fetch Standard](https://fetch.spec.whatwg.org/#body-mixin).
483* **trailers** `Record<string, string>` - This object starts out
484  as empty and will be mutated to contain trailers after `body` has emitted `'end'`.
485* **opaque** `unknown`
486* **context** `object`
487
488`body` contains the following additional [body mixin](https://fetch.spec.whatwg.org/#body-mixin) methods and properties:
489
490- `text()`
491- `json()`
492- `arrayBuffer()`
493- `body`
494- `bodyUsed`
495
496`body` can not be consumed twice. For example, calling `text()` after `json()` throws `TypeError`.
497
498`body` contains the following additional extensions:
499
500- `dump({ limit: Integer })`, dump the response by reading up to `limit` bytes without killing the socket (optional) - Default: 262144.
501
502Note 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`.
503
504#### Example 1 - Basic GET Request
505
506```js
507import { createServer } from 'http'
508import { Client } from 'undici'
509import { once } from 'events'
510
511const server = createServer((request, response) => {
512  response.end('Hello, World!')
513}).listen()
514
515await once(server, 'listening')
516
517const client = new Client(`http://localhost:${server.address().port}`)
518
519try {
520  const { body, headers, statusCode, trailers } = await client.request({
521    path: '/',
522    method: 'GET'
523  })
524  console.log(`response received ${statusCode}`)
525  console.log('headers', headers)
526  body.setEncoding('utf8')
527  body.on('data', console.log)
528  body.on('end', () => {
529    console.log('trailers', trailers)
530  })
531
532  client.close()
533  server.close()
534} catch (error) {
535  console.error(error)
536}
537```
538
539#### Example 2 - Aborting a request
540
541> Node.js v15+ is required to run this example
542
543```js
544import { createServer } from 'http'
545import { Client } from 'undici'
546import { once } from 'events'
547
548const server = createServer((request, response) => {
549  response.end('Hello, World!')
550}).listen()
551
552await once(server, 'listening')
553
554const client = new Client(`http://localhost:${server.address().port}`)
555const abortController = new AbortController()
556
557try {
558  client.request({
559    path: '/',
560    method: 'GET',
561    signal: abortController.signal
562  })
563} catch (error) {
564  console.error(error) // should print an RequestAbortedError
565  client.close()
566  server.close()
567}
568
569abortController.abort()
570```
571
572Alternatively, any `EventEmitter` that emits an `'abort'` event may be used as an abort controller:
573
574```js
575import { createServer } from 'http'
576import { Client } from 'undici'
577import EventEmitter, { once } from 'events'
578
579const server = createServer((request, response) => {
580  response.end('Hello, World!')
581}).listen()
582
583await once(server, 'listening')
584
585const client = new Client(`http://localhost:${server.address().port}`)
586const ee = new EventEmitter()
587
588try {
589  client.request({
590    path: '/',
591    method: 'GET',
592    signal: ee
593  })
594} catch (error) {
595  console.error(error) // should print an RequestAbortedError
596  client.close()
597  server.close()
598}
599
600ee.emit('abort')
601```
602
603Destroying the request or response body will have the same effect.
604
605```js
606import { createServer } from 'http'
607import { Client } from 'undici'
608import { once } from 'events'
609
610const server = createServer((request, response) => {
611  response.end('Hello, World!')
612}).listen()
613
614await once(server, 'listening')
615
616const client = new Client(`http://localhost:${server.address().port}`)
617
618try {
619  const { body } = await client.request({
620    path: '/',
621    method: 'GET'
622  })
623  body.destroy()
624} catch (error) {
625  console.error(error) // should print an RequestAbortedError
626  client.close()
627  server.close()
628}
629```
630
631### `Dispatcher.stream(options, factory[, callback])`
632
633A 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.
634
635As 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.
636
637Arguments:
638
639* **options** `RequestOptions`
640* **factory** `(data: StreamFactoryData) => stream.Writable`
641* **callback** `(error: Error | null, data: StreamData) => void` (optional)
642
643Returns: `void | Promise<StreamData>` - Only returns a `Promise` if no `callback` argument was passed
644
645#### Parameter: `StreamFactoryData`
646
647* **statusCode** `number`
648* **headers** `Record<string, string | string[] | undefined>`
649* **opaque** `unknown`
650* **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.
651
652#### Parameter: `StreamData`
653
654* **opaque** `unknown`
655* **trailers** `Record<string, string>`
656* **context** `object`
657
658#### Example 1 - Basic GET stream request
659
660```js
661import { createServer } from 'http'
662import { Client } from 'undici'
663import { once } from 'events'
664import { Writable } from 'stream'
665
666const server = createServer((request, response) => {
667  response.end('Hello, World!')
668}).listen()
669
670await once(server, 'listening')
671
672const client = new Client(`http://localhost:${server.address().port}`)
673
674const bufs = []
675
676try {
677  await client.stream({
678    path: '/',
679    method: 'GET',
680    opaque: { bufs }
681  }, ({ statusCode, headers, opaque: { bufs } }) => {
682    console.log(`response received ${statusCode}`)
683    console.log('headers', headers)
684    return new Writable({
685      write (chunk, encoding, callback) {
686        bufs.push(chunk)
687        callback()
688      }
689    })
690  })
691
692  console.log(Buffer.concat(bufs).toString('utf-8'))
693
694  client.close()
695  server.close()
696} catch (error) {
697  console.error(error)
698}
699```
700
701#### Example 2 - Stream to Fastify Response
702
703In 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.
704
705```js
706import { createServer } from 'http'
707import { Client } from 'undici'
708import { once } from 'events'
709import fastify from 'fastify'
710
711const nodeServer = createServer((request, response) => {
712  response.end('Hello, World! From Node.js HTTP Server')
713}).listen()
714
715await once(nodeServer, 'listening')
716
717console.log('Node Server listening')
718
719const nodeServerUndiciClient = new Client(`http://localhost:${nodeServer.address().port}`)
720
721const fastifyServer = fastify()
722
723fastifyServer.route({
724  url: '/',
725  method: 'GET',
726  handler: (request, response) => {
727    nodeServerUndiciClient.stream({
728      path: '/',
729      method: 'GET',
730      opaque: response
731    }, ({ opaque }) => opaque.raw)
732  }
733})
734
735await fastifyServer.listen()
736
737console.log('Fastify Server listening')
738
739const fastifyServerUndiciClient = new Client(`http://localhost:${fastifyServer.server.address().port}`)
740
741try {
742  const { statusCode, body } = await fastifyServerUndiciClient.request({
743    path: '/',
744    method: 'GET'
745  })
746
747  console.log(`response received ${statusCode}`)
748  body.setEncoding('utf8')
749  body.on('data', console.log)
750
751  nodeServerUndiciClient.close()
752  fastifyServerUndiciClient.close()
753  fastifyServer.close()
754  nodeServer.close()
755} catch (error) { }
756```
757
758### `Dispatcher.upgrade(options[, callback])`
759
760Upgrade 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.
761
762Arguments:
763
764* **options** `UpgradeOptions`
765
766* **callback** `(error: Error | null, data: UpgradeData) => void` (optional)
767
768Returns: `void | Promise<UpgradeData>` - Only returns a `Promise` if no `callback` argument was passed
769
770#### Parameter: `UpgradeOptions`
771
772* **path** `string`
773* **method** `string` (optional) - Default: `'GET'`
774* **headers** `UndiciHeaders` (optional) - Default: `null`
775* **protocol** `string` (optional) - Default: `'Websocket'` - A string of comma separated protocols, in descending preference order.
776* **signal** `AbortSignal | EventEmitter | null` (optional) - Default: `null`
777
778#### Parameter: `UpgradeData`
779
780* **headers** `http.IncomingHeaders`
781* **socket** `stream.Duplex`
782* **opaque** `unknown`
783
784#### Example 1 - Basic Upgrade Request
785
786```js
787import { createServer } from 'http'
788import { Client } from 'undici'
789import { once } from 'events'
790
791const server = createServer((request, response) => {
792  response.statusCode = 101
793  response.setHeader('connection', 'upgrade')
794  response.setHeader('upgrade', request.headers.upgrade)
795  response.end()
796}).listen()
797
798await once(server, 'listening')
799
800const client = new Client(`http://localhost:${server.address().port}`)
801
802try {
803  const { headers, socket } = await client.upgrade({
804    path: '/',
805  })
806  socket.on('end', () => {
807    console.log(`upgrade: ${headers.upgrade}`) // upgrade: Websocket
808    client.close()
809    server.close()
810  })
811  socket.end()
812} catch (error) {
813  console.error(error)
814  client.close()
815  server.close()
816}
817```
818
819## Instance Events
820
821### Event: `'connect'`
822
823Parameters:
824
825* **origin** `URL`
826* **targets** `Array<Dispatcher>`
827
828### Event: `'disconnect'`
829
830Parameters:
831
832* **origin** `URL`
833* **targets** `Array<Dispatcher>`
834* **error** `Error`
835
836### Event: `'connectionError'`
837
838Parameters:
839
840* **origin** `URL`
841* **targets** `Array<Dispatcher>`
842* **error** `Error`
843
844Emitted when dispatcher fails to connect to
845origin.
846
847### Event: `'drain'`
848
849Parameters:
850
851* **origin** `URL`
852
853Emitted when dispatcher is no longer busy.
854
855## Parameter: `UndiciHeaders`
856
857* `Record<string, string | string[] | undefined> | string[] | null`
858
859Header 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.
860
861Keys are lowercase and values are not modified.
862
863Response headers will derive a `host` from the `url` of the [Client](Client.md#class-client) instance if no `host` header was previously specified.
864
865### Example 1 - Object
866
867```js
868{
869  'content-length': '123',
870  'content-type': 'text/plain',
871  connection: 'keep-alive',
872  host: 'mysite.com',
873  accept: '*/*'
874}
875```
876
877### Example 2 - Array
878
879```js
880[
881  'content-length', '123',
882  'content-type', 'text/plain',
883  'connection', 'keep-alive',
884  'host', 'mysite.com',
885  'accept', '*/*'
886]
887```
888