Files
SnowLuma/.pnpm-store/v11/files/c3/4f106950f5f2c544fb53929654b2e641ba40e82942e5881a45d06df3b4d7425350afbf9a1115ada94b5359b8ead2e284bf59565e7ee77728aa8b7a2ea67e5a
T
TanTan 2a69b23b47
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
docs: 仓库链接指向本镜像仓库
2026-11-13 09:27:07 +08:00

199 lines
7.3 KiB
Plaintext

# Connector
<!--introduced_in=v4.3.0-->
<!--type=module-->
<!-- source_link=lib/core/connect.js -->
> Stability: 2 - Stable
undici opens the underlying socket for every request through a *connector*. By
default this is handled internally, so most applications never interact with the
connector directly. When a request needs additional control over the socket —
for example to inspect a TLS certificate, pin a CA fingerprint, or tunnel the
connection — a custom connector can be supplied through the `connect` option of
a [`Dispatcher`][].
Use the `buildConnector` helper to construct a connector with sensible defaults
and then wrap it:
```mjs
import { buildConnector } from 'undici'
const connector = buildConnector({ rejectUnauthorized: false })
```
`buildConnector` accepts the same TLS options as
[`tls.connect()`][tls.connect] in addition to the undici-specific options listed
below, and returns a [`connector`](#connectoroptions-callback) function that
undici invokes for each new connection.
## `buildConnector([options])`
<!-- YAML
added: v5.23.4
-->
* `options` {buildConnector.BuildOptions} (optional) Connection options. In
addition to every [`tls.connect()`][tls.connect] option, the following fields
are supported:
* `allowH2` {boolean} Whether to offer HTTP/2 (`h2`) during TLS ALPN
negotiation. **Default:** `true`.
* `preferH2` {boolean} Only effective together with `allowH2`. When `true`,
ALPN is offered as `['h2', 'http/1.1']` (HTTP/2 first) instead of the default
`['http/1.1', 'h2']`. Use this when the server selects the ALPN protocol by
*client* preference (for example, some load balancers) so that HTTP/2 is
negotiated whenever the server supports it. If the server does not support
HTTP/2, ALPN transparently falls back to `http/1.1`. **Default:** `false`.
* `maxCachedSessions` {number|null} Maximum number of TLS sessions to cache for
reuse. Use `0` to disable TLS session caching. Must be a non-negative
integer. **Default:** `100`.
* `socketPath` {string|null} An IPC endpoint, either a Unix domain socket or a
Windows named pipe. **Default:** `null`.
* `timeout` {number|null} The connection timeout in milliseconds. A
[`ConnectTimeoutError`][] is raised when the socket fails to connect within
this period. **Default:** `10e3`.
* `port` {number} The port to connect to when one is not provided to the
connector. Defaults to `443` for `https:` and `80` otherwise.
* `keepAlive` {boolean|null} Whether to enable TCP keep-alive on the socket.
**Default:** `true`.
* `keepAliveInitialDelay` {number|null} The delay in milliseconds before the
first TCP keep-alive probe is sent on an idle socket. **Default:** `60e3`.
* `typeOfService` {number|null} The IP Type of Service (ToS) value to set on
the socket. **Default:** `null`.
* Returns: {buildConnector.connector} A
[`connector`](#connectoroptions-callback) function bound to the supplied
options.
Builds a connector function. `maxCachedSessions` must be a non-negative integer
or an [`InvalidArgumentError`][] is thrown.
The returned function may be passed directly as the `connect` option of a
[`Dispatcher`][], or wrapped to perform extra validation on each socket before it
is handed back to undici.
```mjs
import { Client, buildConnector } from 'undici'
const connector = buildConnector({ rejectUnauthorized: false })
const client = new Client('https://localhost:3000', {
connect (opts, cb) {
connector(opts, (err, socket) => {
if (err) {
cb(err)
} else if (/* assertion */ false) {
socket.destroy()
cb(new Error('kaboom'))
} else {
cb(null, socket)
}
})
}
})
```
## `connector(options, callback)`
<!-- YAML
added: v4.3.0
-->
* `options` {Object} Per-connection options provided by undici.
* `hostname` {string} The host to connect to.
* `host` {string} (optional) The value of the request `Host` header, used to
derive the TLS `servername` when one is not supplied.
* `protocol` {string} The request protocol, for example `'https:'` or
`'http:'`. A value of `'https:'` establishes a TLS connection.
* `port` {string} The port to connect to. Defaults to `443` for `https:` and
`80` otherwise.
* `servername` {string} (optional) The TLS Server Name Indication (SNI) value.
Derived from `host` when omitted.
* `localAddress` {string|null} (optional) The local address the socket should
connect from.
* `httpSocket` {Socket} (optional) An existing socket on which to establish the
secure connection rather than creating a new one. May only be supplied for a
TLS upgrade (`protocol` of `'https:'`).
* `callback` {Function} Called once the socket has connected or failed.
* `err` {Error|null} The connection error, or `null` on success.
* `socket` {Socket|TLSSocket|null} The connected socket, or `null` on failure.
* Returns: {Socket|TLSSocket} The socket being established.
The function returned by [`buildConnector()`](#buildconnectoroptions). undici
calls it for every new connection. It opens a [`net.Socket`][] for `http:`
targets or a [`tls.TLSSocket`][] for `https:` targets, applies TCP keep-alive and
the connect timeout, and invokes `callback` once the socket connects or errors.
The socket is also returned synchronously.
When connecting over TLS, established sessions are cached (subject to
`maxCachedSessions`) and reused for subsequent connections to the same
`servername` or `hostname`.
### Example: validate the CA fingerprint
A custom connector is a convenient place to verify the server certificate before
any request is sent over the socket.
```mjs
import { Client, buildConnector } from 'undici'
const caFingerprint = 'FO:OB:AR'
const connector = buildConnector({ rejectUnauthorized: false })
const client = new Client('https://localhost:3000', {
connect (opts, cb) {
connector(opts, (err, socket) => {
if (err) {
cb(err)
} else if (getIssuerCertificate(socket).fingerprint256 !== caFingerprint) {
socket.destroy()
cb(new Error('Fingerprint does not match or malformed certificate'))
} else {
cb(null, socket)
}
})
}
})
client.request({
path: '/',
method: 'GET'
}, (err, data) => {
if (err) throw err
const bufs = []
data.body.on('data', (buf) => {
bufs.push(buf)
})
data.body.on('end', () => {
console.log(Buffer.concat(bufs).toString('utf8'))
client.close()
})
})
function getIssuerCertificate (socket) {
let certificate = socket.getPeerCertificate(true)
while (certificate && Object.keys(certificate).length > 0) {
// Invalid certificate.
if (certificate.issuerCertificate == null) {
return null
}
// We have reached the root certificate. In case of self-signed
// certificates, `issuerCertificate` may be a circular reference.
if (certificate.fingerprint256 === certificate.issuerCertificate.fingerprint256) {
break
}
certificate = certificate.issuerCertificate
}
return certificate
}
```
[`ConnectTimeoutError`]: Errors.md#class-connecttimeouterror
[`Dispatcher`]: Dispatcher.md#class-dispatcher
[`InvalidArgumentError`]: Errors.md#class-invalidargumenterror
[`net.Socket`]: https://nodejs.org/api/net.html#class-netsocket
[`tls.TLSSocket`]: https://nodejs.org/api/tls.html#class-tlstlssocket
[tls.connect]: https://nodejs.org/api/tls.html#tlsconnectoptions-callback