|
| 1 | +# Class: H2CClient |
| 2 | + |
| 3 | +Extends: `undici.Dispatcher` |
| 4 | + |
| 5 | +A basic H2C client. |
| 6 | + |
| 7 | +**Example** |
| 8 | + |
| 9 | +```js |
| 10 | +const { createServer } = require('node:http2') |
| 11 | +const { once } = require('node:events') |
| 12 | +const { H2CClient } = require('undici') |
| 13 | + |
| 14 | +const server = createServer((req, res) => { |
| 15 | + res.writeHead(200) |
| 16 | + res.end('Hello, world!') |
| 17 | +}) |
| 18 | + |
| 19 | +server.listen() |
| 20 | +once(server, 'listening').then(() => { |
| 21 | + const client = new H2CClient(`http://localhost:${server.address().port}/`) |
| 22 | + |
| 23 | + const response = await client.request({ path: '/', method: 'GET' }) |
| 24 | + console.log(response.statusCode) // 200 |
| 25 | + response.body.text.then((text) => { |
| 26 | + console.log(text) // Hello, world! |
| 27 | + }) |
| 28 | +}) |
| 29 | +``` |
| 30 | + |
| 31 | +## `new H2CClient(url[, options])` |
| 32 | + |
| 33 | +Arguments: |
| 34 | + |
| 35 | +- **url** `URL | string` - Should only include the **protocol, hostname, and port**. It only supports `http` protocol. |
| 36 | +- **options** `H2CClientOptions` (optional) |
| 37 | + |
| 38 | +Returns: `H2CClient` |
| 39 | + |
| 40 | +### Parameter: `H2CClientOptions` |
| 41 | + |
| 42 | +- **bodyTimeout** `number | null` (optional) - Default: `300e3` - 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. Please note the `timeout` will be reset if you keep writing data to the socket everytime. |
| 43 | +- **headersTimeout** `number | null` (optional) - Default: `300e3` - 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. |
| 44 | +- **keepAliveMaxTimeout** `number | null` (optional) - Default: `600e3` - The maximum allowed `keepAliveTimeout`, in milliseconds, when overridden by _keep-alive_ hints from the server. Defaults to 10 minutes. |
| 45 | +- **keepAliveTimeout** `number | null` (optional) - Default: `4e3` - The timeout, in milliseconds, after which a socket without active requests will time out. Monitors time between activity on a connected socket. This value may be overridden by _keep-alive_ hints from the server. See [MDN: HTTP - Headers - Keep-Alive directives](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive#directives) for more details. Defaults to 4 seconds. |
| 46 | +- **keepAliveTimeoutThreshold** `number | null` (optional) - Default: `2e3` - A number of milliseconds subtracted from server _keep-alive_ hints when overriding `keepAliveTimeout` to account for timing inaccuracies caused by e.g. transport latency. Defaults to 2 seconds. |
| 47 | +- **maxHeaderSize** `number | null` (optional) - Default: `--max-http-header-size` or `16384` - The maximum length of request headers in bytes. Defaults to Node.js' --max-http-header-size or 16KiB. |
| 48 | +- **maxResponseSize** `number | null` (optional) - Default: `-1` - The maximum length of response body in bytes. Set to `-1` to disable. |
| 49 | +- **maxConcurrentStreams**: `number` - Default: `100`. Dictates the maximum number of concurrent streams for a single H2 session. It can be overridden by a SETTINGS remote frame. |
| 50 | +- **pipelining** `number | null` (optional) - Default to `maxConcurrentStreams` - The amount of concurrent requests sent over a single HTTP/2 session in accordance with [RFC-7540](https://httpwg.org/specs/rfc7540.html#StreamsLayer) Stream specification. Streams can be closed up by remote server at any time. |
| 51 | +- **connect** `ConnectOptions | null` (optional) - Default: `null`. |
| 52 | +- **strictContentLength** `Boolean` (optional) - Default: `true` - Whether to treat request content length mismatches as errors. If true, an error is thrown when the request content-length header doesn't match the length of the request body. |
| 53 | +- **autoSelectFamily**: `boolean` (optional) - Default: depends on local Node version, on Node 18.13.0 and above is `false`. Enables a family autodetection algorithm that loosely implements section 5 of [RFC 8305](https://tools.ietf.org/html/rfc8305#section-5). See [here](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) for more details. This option is ignored if not supported by the current Node version. |
| 54 | +- **autoSelectFamilyAttemptTimeout**: `number` - Default: depends on local Node version, on Node 18.13.0 and above is `250`. The amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the `autoSelectFamily` option. See [here](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) for more details. |
| 55 | + |
| 56 | +#### Parameter: `H2CConnectOptions` |
| 57 | + |
| 58 | +- **socketPath** `string | null` (optional) - Default: `null` - An IPC endpoint, either Unix domain socket or Windows named pipe. |
| 59 | +- **timeout** `number | null` (optional) - In milliseconds, Default `10e3`. |
| 60 | +- **servername** `string | null` (optional) |
| 61 | +- **keepAlive** `boolean | null` (optional) - Default: `true` - TCP keep-alive enabled |
| 62 | +- **keepAliveInitialDelay** `number | null` (optional) - Default: `60000` - TCP keep-alive interval for the socket in milliseconds |
| 63 | + |
| 64 | +### Example - Basic Client instantiation |
| 65 | + |
| 66 | +This will instantiate the undici H2CClient, but it will not connect to the origin until something is queued. Consider using `client.connect` to prematurely connect to the origin, or just call `client.request`. |
| 67 | + |
| 68 | +```js |
| 69 | +"use strict"; |
| 70 | +import { H2CClient } from "undici"; |
| 71 | + |
| 72 | +const client = new H2CClient("http://localhost:3000"); |
| 73 | +``` |
| 74 | + |
| 75 | +## Instance Methods |
| 76 | + |
| 77 | +### `H2CClient.close([callback])` |
| 78 | + |
| 79 | +Implements [`Dispatcher.close([callback])`](/docs/docs/api/Dispatcher.md#dispatcherclosecallback-promise). |
| 80 | + |
| 81 | +### `H2CClient.destroy([error, callback])` |
| 82 | + |
| 83 | +Implements [`Dispatcher.destroy([error, callback])`](/docs/docs/api/Dispatcher.md#dispatcherdestroyerror-callback-promise). |
| 84 | + |
| 85 | +Waits until socket is closed before invoking the callback (or returning a promise if no callback is provided). |
| 86 | + |
| 87 | +### `H2CClient.connect(options[, callback])` |
| 88 | + |
| 89 | +See [`Dispatcher.connect(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherconnectoptions-callback). |
| 90 | + |
| 91 | +### `H2CClient.dispatch(options, handlers)` |
| 92 | + |
| 93 | +Implements [`Dispatcher.dispatch(options, handlers)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler). |
| 94 | + |
| 95 | +### `H2CClient.pipeline(options, handler)` |
| 96 | + |
| 97 | +See [`Dispatcher.pipeline(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherpipelineoptions-handler). |
| 98 | + |
| 99 | +### `H2CClient.request(options[, callback])` |
| 100 | + |
| 101 | +See [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback). |
| 102 | + |
| 103 | +### `H2CClient.stream(options, factory[, callback])` |
| 104 | + |
| 105 | +See [`Dispatcher.stream(options, factory[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback). |
| 106 | + |
| 107 | +### `H2CClient.upgrade(options[, callback])` |
| 108 | + |
| 109 | +See [`Dispatcher.upgrade(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherupgradeoptions-callback). |
| 110 | + |
| 111 | +## Instance Properties |
| 112 | + |
| 113 | +### `H2CClient.closed` |
| 114 | + |
| 115 | +- `boolean` |
| 116 | + |
| 117 | +`true` after `H2CClient.close()` has been called. |
| 118 | + |
| 119 | +### `H2CClient.destroyed` |
| 120 | + |
| 121 | +- `boolean` |
| 122 | + |
| 123 | +`true` after `client.destroyed()` has been called or `client.close()` has been called and the client shutdown has completed. |
| 124 | + |
| 125 | +### `H2CClient.pipelining` |
| 126 | + |
| 127 | +- `number` |
| 128 | + |
| 129 | +Property to get and set the pipelining factor. |
| 130 | + |
| 131 | +## Instance Events |
| 132 | + |
| 133 | +### Event: `'connect'` |
| 134 | + |
| 135 | +See [Dispatcher Event: `'connect'`](/docs/docs/api/Dispatcher.md#event-connect). |
| 136 | + |
| 137 | +Parameters: |
| 138 | + |
| 139 | +- **origin** `URL` |
| 140 | +- **targets** `Array<Dispatcher>` |
| 141 | + |
| 142 | +Emitted when a socket has been created and connected. The client will connect once `client.size > 0`. |
| 143 | + |
| 144 | +#### Example - Client connect event |
| 145 | + |
| 146 | +```js |
| 147 | +import { createServer } from "node:http2"; |
| 148 | +import { H2CClient } from "undici"; |
| 149 | +import { once } from "events"; |
| 150 | + |
| 151 | +const server = createServer((request, response) => { |
| 152 | + response.end("Hello, World!"); |
| 153 | +}).listen(); |
| 154 | + |
| 155 | +await once(server, "listening"); |
| 156 | + |
| 157 | +const client = new H2CClient(`http://localhost:${server.address().port}`); |
| 158 | + |
| 159 | +client.on("connect", (origin) => { |
| 160 | + console.log(`Connected to ${origin}`); // should print before the request body statement |
| 161 | +}); |
| 162 | + |
| 163 | +try { |
| 164 | + const { body } = await client.request({ |
| 165 | + path: "/", |
| 166 | + method: "GET", |
| 167 | + }); |
| 168 | + body.setEncoding("utf-8"); |
| 169 | + body.on("data", console.log); |
| 170 | + client.close(); |
| 171 | + server.close(); |
| 172 | +} catch (error) { |
| 173 | + console.error(error); |
| 174 | + client.close(); |
| 175 | + server.close(); |
| 176 | +} |
| 177 | +``` |
| 178 | + |
| 179 | +### Event: `'disconnect'` |
| 180 | + |
| 181 | +See [Dispatcher Event: `'disconnect'`](/docs/docs/api/Dispatcher.md#event-disconnect). |
| 182 | + |
| 183 | +Parameters: |
| 184 | + |
| 185 | +- **origin** `URL` |
| 186 | +- **targets** `Array<Dispatcher>` |
| 187 | +- **error** `Error` |
| 188 | + |
| 189 | +Emitted when socket has disconnected. The error argument of the event is the error which caused the socket to disconnect. The client will reconnect if or once `client.size > 0`. |
| 190 | + |
| 191 | +#### Example - Client disconnect event |
| 192 | + |
| 193 | +```js |
| 194 | +import { createServer } from "node:http2"; |
| 195 | +import { H2CClient } from "undici"; |
| 196 | +import { once } from "events"; |
| 197 | + |
| 198 | +const server = createServer((request, response) => { |
| 199 | + response.destroy(); |
| 200 | +}).listen(); |
| 201 | + |
| 202 | +await once(server, "listening"); |
| 203 | + |
| 204 | +const client = new H2CClient(`http://localhost:${server.address().port}`); |
| 205 | + |
| 206 | +client.on("disconnect", (origin) => { |
| 207 | + console.log(`Disconnected from ${origin}`); |
| 208 | +}); |
| 209 | + |
| 210 | +try { |
| 211 | + await client.request({ |
| 212 | + path: "/", |
| 213 | + method: "GET", |
| 214 | + }); |
| 215 | +} catch (error) { |
| 216 | + console.error(error.message); |
| 217 | + client.close(); |
| 218 | + server.close(); |
| 219 | +} |
| 220 | +``` |
| 221 | + |
| 222 | +### Event: `'drain'` |
| 223 | + |
| 224 | +Emitted when pipeline is no longer busy. |
| 225 | + |
| 226 | +See [Dispatcher Event: `'drain'`](/docs/docs/api/Dispatcher.md#event-drain). |
| 227 | + |
| 228 | +#### Example - Client drain event |
| 229 | + |
| 230 | +```js |
| 231 | +import { createServer } from "node:http2"; |
| 232 | +import { H2CClient } from "undici"; |
| 233 | +import { once } from "events"; |
| 234 | + |
| 235 | +const server = createServer((request, response) => { |
| 236 | + response.end("Hello, World!"); |
| 237 | +}).listen(); |
| 238 | + |
| 239 | +await once(server, "listening"); |
| 240 | + |
| 241 | +const client = new H2CClient(`http://localhost:${server.address().port}`); |
| 242 | + |
| 243 | +client.on("drain", () => { |
| 244 | + console.log("drain event"); |
| 245 | + client.close(); |
| 246 | + server.close(); |
| 247 | +}); |
| 248 | + |
| 249 | +const requests = [ |
| 250 | + client.request({ path: "/", method: "GET" }), |
| 251 | + client.request({ path: "/", method: "GET" }), |
| 252 | + client.request({ path: "/", method: "GET" }), |
| 253 | +]; |
| 254 | + |
| 255 | +await Promise.all(requests); |
| 256 | + |
| 257 | +console.log("requests completed"); |
| 258 | +``` |
| 259 | + |
| 260 | +### Event: `'error'` |
| 261 | + |
| 262 | +Invoked for users errors such as throwing in the `onError` handler. |
0 commit comments