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