• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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
206#### Parameter: `DispatchHandler`
207
208* **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.
209* **onError** `(error: Error) => void` - Invoked when an error has occurred. May not throw.
210* **onUpgrade** `(statusCode: number, headers: Buffer[], socket: Duplex) => void` (optional) - Invoked when request is upgraded. Required if `DispatchOptions.upgrade` is defined or `DispatchOptions.method === 'CONNECT'`.
211* **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.
212* **onData** `(chunk: Buffer) => boolean` - Invoked when response payload data is received. Not required for `upgrade` requests.
213* **onComplete** `(trailers: Buffer[]) => void` - Invoked when response payload and trailers have been received and the request has completed. Not required for `upgrade` requests.
214* **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.
215
216#### Example 1 - Dispatch GET request
217
218```js
219import { createServer } from 'http'
220import { Client } from 'undici'
221import { once } from 'events'
222
223const server = createServer((request, response) => {
224  response.end('Hello, World!')
225}).listen()
226
227await once(server, 'listening')
228
229const client = new Client(`http://localhost:${server.address().port}`)
230
231const data = []
232
233client.dispatch({
234  path: '/',
235  method: 'GET',
236  headers: {
237    'x-foo': 'bar'
238  }
239}, {
240  onConnect: () => {
241    console.log('Connected!')
242  },
243  onError: (error) => {
244    console.error(error)
245  },
246  onHeaders: (statusCode, headers) => {
247    console.log(`onHeaders | statusCode: ${statusCode} | headers: ${headers}`)
248  },
249  onData: (chunk) => {
250    console.log('onData: chunk received')
251    data.push(chunk)
252  },
253  onComplete: (trailers) => {
254    console.log(`onComplete | trailers: ${trailers}`)
255    const res = Buffer.concat(data).toString('utf8')
256    console.log(`Data: ${res}`)
257    client.close()
258    server.close()
259  }
260})
261```
262
263#### Example 2 - Dispatch Upgrade Request
264
265```js
266import { createServer } from 'http'
267import { Client } from 'undici'
268import { once } from 'events'
269
270const server = createServer((request, response) => {
271  response.end()
272}).listen()
273
274await once(server, 'listening')
275
276server.on('upgrade', (request, socket, head) => {
277  console.log('Node.js Server - upgrade event')
278  socket.write('HTTP/1.1 101 Web Socket Protocol Handshake\r\n')
279  socket.write('Upgrade: WebSocket\r\n')
280  socket.write('Connection: Upgrade\r\n')
281  socket.write('\r\n')
282  socket.end()
283})
284
285const client = new Client(`http://localhost:${server.address().port}`)
286
287client.dispatch({
288  path: '/',
289  method: 'GET',
290  upgrade: 'websocket'
291}, {
292  onConnect: () => {
293    console.log('Undici Client - onConnect')
294  },
295  onError: (error) => {
296    console.log('onError') // shouldn't print
297  },
298  onUpgrade: (statusCode, headers, socket) => {
299    console.log('Undici Client - onUpgrade')
300    console.log(`onUpgrade Headers: ${headers}`)
301    socket.on('data', buffer => {
302      console.log(buffer.toString('utf8'))
303    })
304    socket.on('end', () => {
305      client.close()
306      server.close()
307    })
308    socket.end()
309  }
310})
311```
312
313#### Example 3 - Dispatch POST request
314
315```js
316import { createServer } from 'http'
317import { Client } from 'undici'
318import { once } from 'events'
319
320const server = createServer((request, response) => {
321  request.on('data', (data) => {
322    console.log(`Request Data: ${data.toString('utf8')}`)
323    const body = JSON.parse(data)
324    body.message = 'World'
325    response.end(JSON.stringify(body))
326  })
327}).listen()
328
329await once(server, 'listening')
330
331const client = new Client(`http://localhost:${server.address().port}`)
332
333const data = []
334
335client.dispatch({
336  path: '/',
337  method: 'POST',
338  headers: {
339    'content-type': 'application/json'
340  },
341  body: JSON.stringify({ message: 'Hello' })
342}, {
343  onConnect: () => {
344    console.log('Connected!')
345  },
346  onError: (error) => {
347    console.error(error)
348  },
349  onHeaders: (statusCode, headers) => {
350    console.log(`onHeaders | statusCode: ${statusCode} | headers: ${headers}`)
351  },
352  onData: (chunk) => {
353    console.log('onData: chunk received')
354    data.push(chunk)
355  },
356  onComplete: (trailers) => {
357    console.log(`onComplete | trailers: ${trailers}`)
358    const res = Buffer.concat(data).toString('utf8')
359    console.log(`Response Data: ${res}`)
360    client.close()
361    server.close()
362  }
363})
364```
365
366### `Dispatcher.pipeline(options, handler)`
367
368For 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.
369
370Arguments:
371
372* **options** `PipelineOptions`
373* **handler** `(data: PipelineHandlerData) => stream.Readable`
374
375Returns: `stream.Duplex`
376
377#### Parameter: PipelineOptions
378
379Extends: [`RequestOptions`](#parameter-requestoptions)
380
381* **objectMode** `boolean` (optional) - Default: `false` - Set to `true` if the `handler` will return an object stream.
382
383#### Parameter: PipelineHandlerData
384
385* **statusCode** `number`
386* **headers** `Record<string, string | string[] | undefined>`
387* **opaque** `unknown`
388* **body** `stream.Readable`
389* **context** `object`
390* **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.
391
392#### Example 1 - Pipeline Echo
393
394```js
395import { Readable, Writable, PassThrough, pipeline } from 'stream'
396import { createServer } from 'http'
397import { Client } from 'undici'
398import { once } from 'events'
399
400const server = createServer((request, response) => {
401  request.pipe(response)
402}).listen()
403
404await once(server, 'listening')
405
406const client = new Client(`http://localhost:${server.address().port}`)
407
408let res = ''
409
410pipeline(
411  new Readable({
412    read () {
413      this.push(Buffer.from('undici'))
414      this.push(null)
415    }
416  }),
417  client.pipeline({
418    path: '/',
419    method: 'GET'
420  }, ({ statusCode, headers, body }) => {
421    console.log(`response received ${statusCode}`)
422    console.log('headers', headers)
423    return pipeline(body, new PassThrough(), () => {})
424  }),
425  new Writable({
426    write (chunk, _, callback) {
427      res += chunk.toString()
428      callback()
429    },
430    final (callback) {
431      console.log(`Response pipelined to writable: ${res}`)
432      callback()
433    }
434  }),
435  error => {
436    if (error) {
437      console.error(error)
438    }
439
440    client.close()
441    server.close()
442  }
443)
444```
445
446### `Dispatcher.request(options[, callback])`
447
448Performs a HTTP request.
449
450Non-idempotent requests will not be pipelined in order
451to avoid indirect failures.
452
453Idempotent requests will be automatically retried if
454they fail due to indirect failure from the request
455at the head of the pipeline. This does not apply to
456idempotent requests with a stream request body.
457
458All response bodies must always be fully consumed or destroyed.
459
460Arguments:
461
462* **options** `RequestOptions`
463* **callback** `(error: Error | null, data: ResponseData) => void` (optional)
464
465Returns: `void | Promise<ResponseData>` - Only returns a `Promise` if no `callback` argument was passed.
466
467#### Parameter: `RequestOptions`
468
469Extends: [`DispatchOptions`](#parameter-dispatchoptions)
470
471* **opaque** `unknown` (optional) - Default: `null` - Used for passing through context to `ResponseData`.
472* **signal** `AbortSignal | events.EventEmitter | null` (optional) - Default: `null`.
473* **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.
474
475The `RequestOptions.method` property should not be value `'CONNECT'`.
476
477#### Parameter: `ResponseData`
478
479* **statusCode** `number`
480* **headers** `Record<string, string | string[]>` - Note that all header keys are lower-cased, e. g. `content-type`.
481* **body** `stream.Readable` which also implements [the body mixin from the Fetch Standard](https://fetch.spec.whatwg.org/#body-mixin).
482* **trailers** `Record<string, string>` - This object starts out
483  as empty and will be mutated to contain trailers after `body` has emitted `'end'`.
484* **opaque** `unknown`
485* **context** `object`
486
487`body` contains the following additional [body mixin](https://fetch.spec.whatwg.org/#body-mixin) methods and properties:
488
489- `text()`
490- `json()`
491- `arrayBuffer()`
492- `body`
493- `bodyUsed`
494
495`body` can not be consumed twice. For example, calling `text()` after `json()` throws `TypeError`.
496
497`body` contains the following additional extensions:
498
499- `dump({ limit: Integer })`, dump the response by reading up to `limit` bytes without killing the socket (optional) - Default: 262144.
500
501Note 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`.
502
503#### Example 1 - Basic GET Request
504
505```js
506import { createServer } from 'http'
507import { Client } from 'undici'
508import { once } from 'events'
509
510const server = createServer((request, response) => {
511  response.end('Hello, World!')
512}).listen()
513
514await once(server, 'listening')
515
516const client = new Client(`http://localhost:${server.address().port}`)
517
518try {
519  const { body, headers, statusCode, trailers } = await client.request({
520    path: '/',
521    method: 'GET'
522  })
523  console.log(`response received ${statusCode}`)
524  console.log('headers', headers)
525  body.setEncoding('utf8')
526  body.on('data', console.log)
527  body.on('end', () => {
528    console.log('trailers', trailers)
529  })
530
531  client.close()
532  server.close()
533} catch (error) {
534  console.error(error)
535}
536```
537
538#### Example 2 - Aborting a request
539
540> Node.js v15+ is required to run this example
541
542```js
543import { createServer } from 'http'
544import { Client } from 'undici'
545import { once } from 'events'
546
547const server = createServer((request, response) => {
548  response.end('Hello, World!')
549}).listen()
550
551await once(server, 'listening')
552
553const client = new Client(`http://localhost:${server.address().port}`)
554const abortController = new AbortController()
555
556try {
557  client.request({
558    path: '/',
559    method: 'GET',
560    signal: abortController.signal
561  })
562} catch (error) {
563  console.error(error) // should print an RequestAbortedError
564  client.close()
565  server.close()
566}
567
568abortController.abort()
569```
570
571Alternatively, any `EventEmitter` that emits an `'abort'` event may be used as an abort controller:
572
573```js
574import { createServer } from 'http'
575import { Client } from 'undici'
576import EventEmitter, { once } from 'events'
577
578const server = createServer((request, response) => {
579  response.end('Hello, World!')
580}).listen()
581
582await once(server, 'listening')
583
584const client = new Client(`http://localhost:${server.address().port}`)
585const ee = new EventEmitter()
586
587try {
588  client.request({
589    path: '/',
590    method: 'GET',
591    signal: ee
592  })
593} catch (error) {
594  console.error(error) // should print an RequestAbortedError
595  client.close()
596  server.close()
597}
598
599ee.emit('abort')
600```
601
602Destroying the request or response body will have the same effect.
603
604```js
605import { createServer } from 'http'
606import { Client } from 'undici'
607import { once } from 'events'
608
609const server = createServer((request, response) => {
610  response.end('Hello, World!')
611}).listen()
612
613await once(server, 'listening')
614
615const client = new Client(`http://localhost:${server.address().port}`)
616
617try {
618  const { body } = await client.request({
619    path: '/',
620    method: 'GET'
621  })
622  body.destroy()
623} catch (error) {
624  console.error(error) // should print an RequestAbortedError
625  client.close()
626  server.close()
627}
628```
629
630### `Dispatcher.stream(options, factory[, callback])`
631
632A 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.
633
634As 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.
635
636Arguments:
637
638* **options** `RequestOptions`
639* **factory** `(data: StreamFactoryData) => stream.Writable`
640* **callback** `(error: Error | null, data: StreamData) => void` (optional)
641
642Returns: `void | Promise<StreamData>` - Only returns a `Promise` if no `callback` argument was passed
643
644#### Parameter: `StreamFactoryData`
645
646* **statusCode** `number`
647* **headers** `Record<string, string | string[] | undefined>`
648* **opaque** `unknown`
649* **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.
650
651#### Parameter: `StreamData`
652
653* **opaque** `unknown`
654* **trailers** `Record<string, string>`
655* **context** `object`
656
657#### Example 1 - Basic GET stream request
658
659```js
660import { createServer } from 'http'
661import { Client } from 'undici'
662import { once } from 'events'
663import { Writable } from 'stream'
664
665const server = createServer((request, response) => {
666  response.end('Hello, World!')
667}).listen()
668
669await once(server, 'listening')
670
671const client = new Client(`http://localhost:${server.address().port}`)
672
673const bufs = []
674
675try {
676  await client.stream({
677    path: '/',
678    method: 'GET',
679    opaque: { bufs }
680  }, ({ statusCode, headers, opaque: { bufs } }) => {
681    console.log(`response received ${statusCode}`)
682    console.log('headers', headers)
683    return new Writable({
684      write (chunk, encoding, callback) {
685        bufs.push(chunk)
686        callback()
687      }
688    })
689  })
690
691  console.log(Buffer.concat(bufs).toString('utf-8'))
692
693  client.close()
694  server.close()
695} catch (error) {
696  console.error(error)
697}
698```
699
700#### Example 2 - Stream to Fastify Response
701
702In 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.
703
704```js
705import { createServer } from 'http'
706import { Client } from 'undici'
707import { once } from 'events'
708import fastify from 'fastify'
709
710const nodeServer = createServer((request, response) => {
711  response.end('Hello, World! From Node.js HTTP Server')
712}).listen()
713
714await once(nodeServer, 'listening')
715
716console.log('Node Server listening')
717
718const nodeServerUndiciClient = new Client(`http://localhost:${nodeServer.address().port}`)
719
720const fastifyServer = fastify()
721
722fastifyServer.route({
723  url: '/',
724  method: 'GET',
725  handler: (request, response) => {
726    nodeServerUndiciClient.stream({
727      path: '/',
728      method: 'GET',
729      opaque: response
730    }, ({ opaque }) => opaque.raw)
731  }
732})
733
734await fastifyServer.listen()
735
736console.log('Fastify Server listening')
737
738const fastifyServerUndiciClient = new Client(`http://localhost:${fastifyServer.server.address().port}`)
739
740try {
741  const { statusCode, body } = await fastifyServerUndiciClient.request({
742    path: '/',
743    method: 'GET'
744  })
745
746  console.log(`response received ${statusCode}`)
747  body.setEncoding('utf8')
748  body.on('data', console.log)
749
750  nodeServerUndiciClient.close()
751  fastifyServerUndiciClient.close()
752  fastifyServer.close()
753  nodeServer.close()
754} catch (error) { }
755```
756
757### `Dispatcher.upgrade(options[, callback])`
758
759Upgrade 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.
760
761Arguments:
762
763* **options** `UpgradeOptions`
764
765* **callback** `(error: Error | null, data: UpgradeData) => void` (optional)
766
767Returns: `void | Promise<UpgradeData>` - Only returns a `Promise` if no `callback` argument was passed
768
769#### Parameter: `UpgradeOptions`
770
771* **path** `string`
772* **method** `string` (optional) - Default: `'GET'`
773* **headers** `UndiciHeaders` (optional) - Default: `null`
774* **protocol** `string` (optional) - Default: `'Websocket'` - A string of comma separated protocols, in descending preference order.
775* **signal** `AbortSignal | EventEmitter | null` (optional) - Default: `null`
776
777#### Parameter: `UpgradeData`
778
779* **headers** `http.IncomingHeaders`
780* **socket** `stream.Duplex`
781* **opaque** `unknown`
782
783#### Example 1 - Basic Upgrade Request
784
785```js
786import { createServer } from 'http'
787import { Client } from 'undici'
788import { once } from 'events'
789
790const server = createServer((request, response) => {
791  response.statusCode = 101
792  response.setHeader('connection', 'upgrade')
793  response.setHeader('upgrade', request.headers.upgrade)
794  response.end()
795}).listen()
796
797await once(server, 'listening')
798
799const client = new Client(`http://localhost:${server.address().port}`)
800
801try {
802  const { headers, socket } = await client.upgrade({
803    path: '/',
804  })
805  socket.on('end', () => {
806    console.log(`upgrade: ${headers.upgrade}`) // upgrade: Websocket
807    client.close()
808    server.close()
809  })
810  socket.end()
811} catch (error) {
812  console.error(error)
813  client.close()
814  server.close()
815}
816```
817
818## Instance Events
819
820### Event: `'connect'`
821
822Parameters:
823
824* **origin** `URL`
825* **targets** `Array<Dispatcher>`
826
827### Event: `'disconnect'`
828
829Parameters:
830
831* **origin** `URL`
832* **targets** `Array<Dispatcher>`
833* **error** `Error`
834
835### Event: `'connectionError'`
836
837Parameters:
838
839* **origin** `URL`
840* **targets** `Array<Dispatcher>`
841* **error** `Error`
842
843Emitted when dispatcher fails to connect to
844origin.
845
846### Event: `'drain'`
847
848Parameters:
849
850* **origin** `URL`
851
852Emitted when dispatcher is no longer busy.
853
854## Parameter: `UndiciHeaders`
855
856* `Record<string, string | string[] | undefined> | string[] | null`
857
858Header 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.
859
860Keys are lowercase and values are not modified.
861
862Response headers will derive a `host` from the `url` of the [Client](Client.md#class-client) instance if no `host` header was previously specified.
863
864### Example 1 - Object
865
866```js
867{
868  'content-length': '123',
869  'content-type': 'text/plain',
870  connection: 'keep-alive',
871  host: 'mysite.com',
872  accept: '*/*'
873}
874```
875
876### Example 2 - Array
877
878```js
879[
880  'content-length', '123',
881  'content-type', 'text/plain',
882  'connection', 'keep-alive',
883  'host', 'mysite.com',
884  'accept', '*/*'
885]
886```
887