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:
import { buildConnector } from 'undici'
const connector = buildConnector({ rejectUnauthorized: false })buildConnector accepts the same TLS options as
tls.connect() in addition to the undici-specific options listed
below, and returns a connector function that
undici invokes for each new connection.
buildConnector(options?): buildConnector.connector<buildConnector.BuildOptions>tls.connect()
option, the following fields
are supported:<boolean>h2
) during TLS ALPN
negotiation.
Default:
true
.<boolean>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
.0
to disable TLS session caching. Must be a non-negative
integer.
Default:
100
.null
.ConnectTimeoutError
is raised when the socket fails to connect within
this period.
Default:
10e3
.<number>443
for
https:
and
80
otherwise.60e3
.<buildConnector.connector>connector
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.
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): Socket | TLSSocket<Object><string><string>Host
header, used to
derive the TLS
servername
when one is not supplied.<string>'https:'
or
'http:'
. A value of
'https:'
establishes a TLS connection.<string>443
for
https:
and
80
otherwise.<string>host
when omitted.<Socket>protocol
of
'https:'
).<Function><Socket>
|
<TLSSocket>
|
<null>null
on failure.<Socket>
|
<TLSSocket>The function returned by buildConnector(). 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.
A custom connector is a convenient place to verify the server certificate before any request is sent over the socket.
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
}