js/net/quic/endpoint
js/net/quic/endpoint.ts
fino:net/quic/endpoint — QUIC endpoint ownership and routing.
This module exposes the endpoint object that owns UDP listeners, outbound
connection setup, and connection ID routing. Import
fino:net/quic/connection, fino:net/quic/listener, or
fino:net/quic/stream when code needs those class-specific public modules.
import { QuicEndpoint } from 'fino:net/quic/endpoint';
const endpoint = new QuicEndpoint({ alpnProtocols: ['h3'] });
const listener = await endpoint.listen({
address: { family: 'ipv4', ip: '127.0.0.1', port: 4433 },
});
await listener.close();
await endpoint.close();
Classes
class CidRoutingTable<T = QuicConnection> {
Routes incoming QUIC packets to a connection by destination connection ID.
A QUIC connection is identified not by its 4-tuple but by the connection IDs
it advertises, so a single endpoint maps every active (and retiring) CID to
its owning connection here. Keys are normalized from either a raw CID byte
array or its hex string form, so callers may look up with whichever they
hold. The endpoint owns one instance (endpoint.cidTable); the default type
parameter is QuicConnection.
import { CidRoutingTable } from 'internal:net/quic/endpoint';
const table = new CidRoutingTable<{ name: string }>();
table.add(new Uint8Array([1, 2, 3, 4]), { name: 'conn-a' });
table.get('01020304'); // → { name: 'conn-a' }
table.delete('01020304');
Methods
add(cid: Uint8Array | string, value: T): void
Associate a connection ID (bytes or hex) with a value.
get(cid: Uint8Array | string): T | undefined
Look up the value routed to by a connection ID, or undefined.
delete(cid: Uint8Array | string): boolean
Remove a connection ID's route; returns whether an entry existed.
clear(): void
Drop every route, for example when the endpoint closes.
class QuicEndpoint extends EventTarget {
A QUIC endpoint: the owner of UDP sockets, listeners, and connections.
One endpoint can act as both a server and a client. listen() binds a socket
and returns a QuicListener; accepted connections arrive through accept()
or the 'connection' event. connect() binds an ephemeral socket and dials
a peer, returning a QuicConnection. The constructor takes endpoint-wide
defaults (QuicEndpointOptions) that every listener and connection inherits,
plus an optional second internals argument used only by deterministic tests
to inject a clock and transport factory.
The endpoint holds the shared server-side machinery: the connection-ID
routing table (cidTable), the Retry / Version Negotiation / stateless-reset
/ immediate-close / session-creation rate limiters, source-address filtering,
and address-validation caches. It extends EventTarget and emits
'connection', 'error', and 'close'. It also implements
Symbol.asyncDispose, so an await using endpoint closes automatically.
Call close() for an immediate teardown or closeGracefully() to let
in-flight connections drain first.
import { QuicEndpoint } from 'internal:net/quic/endpoint';
await using endpoint = new QuicEndpoint({ alpnProtocols: ['h3'] });
const listener = await endpoint.listen({
address: { family: 'ipv4', ip: '127.0.0.1', port: 0 },
certificateFile: '/etc/tls/cert.pem',
privateKeyFile: '/etc/tls/key.pem',
});
for (;;) {
const conn = await endpoint.accept();
handleConnection(conn);
}
Readonly Properties
readonly cidTable
Connection-ID routing table mapping every active CID to its connection.
readonly alpnProtocols: string[]
Default ALPN protocol list inherited by listeners and connections.
readonly versions: QuicVersion[]
Enabled QUIC wire versions, in preference order.
readonly tlsCipherSuites: QuicTlsCipherSuite[] | null
Configured TLS cipher-suite restriction, or null for the default set.
readonly tlsGroups: string[] | null
Configured key-exchange groups, or null for the default set.
readonly retry: ResolvedRetryOptions
Resolved Retry / address-validation policy.
readonly sessionStore?: QuicSessionStore
Session store used for resumption and 0-RTT, when configured.
readonly earlyData: false | QuicEarlyDataPolicy
Resolved 0-RTT early-data policy, or false when disabled.
readonly migration: ResolvedMigrationOptions
Resolved migration and preferred-address policy.
readonly datagrams: ResolvedDatagramOptions
Resolved DATAGRAM negotiation and queueing settings.
readonly connection: QuicResolvedConnectionOptions
Frozen snapshot of the resolved per-connection transport tuning.
readonly qlog: QuicQlogOptions
Resolved qlog diagnostics configuration.
readonly keylog: QuicKeylogOptions
Resolved TLS keylog diagnostics configuration.
Constructors
constructor(options: QuicEndpointOptions = {}, internals: QuicEndpointInternals = {})
Create an endpoint from endpoint-wide defaults and optional test internals.
Getters
get listeners(): ReadonlyArray<QuicListener>
Snapshot array of the listeners currently open on this endpoint.
get busy(): boolean
Whether the endpoint is refusing new server connections (busy mode).
get transport(): QuicResolvedTransportOptions
Frozen snapshot of the resolved server-transport controls and packet defenses.
get stats(): QuicEndpointStats
Frozen snapshot of the endpoint's aggregate counters and live gauges.
Methods
setBusy(busy: boolean): void
Toggle server busy mode.
In busy mode the endpoint refuses new incoming Initial packets without
creating sessions, letting a server shed load while keeping existing
connections alive. Entering busy mode bumps the serverBusyCount stat.
async listen(options: QuicListenOptions = {}): Promise<QuicListener>
Bind a server socket and start accepting connections on it.
Resolves the given QuicListenOptions against the endpoint defaults, binds
the listen address (and any advertised preferred addresses), builds the TLS
server context plus any SNI contexts, and begins the receive loop. The
returned QuicListener is registered on the endpoint; accepted connections
surface through accept() / the 'connection' event.
Throws if the endpoint is closed, if QUIC is unavailable, or if
certificateFile/privateKeyFile are missing (TypeError). If binding a
preferred address or building a TLS context fails, every socket bound for
this call is released before the error propagates.
const listener = await endpoint.listen({
address: { family: 'ipv4', ip: '0.0.0.0', port: 4433 },
certificateFile: '/etc/tls/cert.pem',
privateKeyFile: '/etc/tls/key.pem',
});
async connect(options: QuicConnectOptions): Promise<QuicConnection>
Dial a QUIC server and return the client connection.
Binds an ephemeral client socket in the remote address family (or the
injected clientBindAddress), builds the client TLS context and session,
consults sessionStore for resumption/0-RTT state and any cached address
token, then starts the handshake. The returned QuicConnection may still be
in its 'connecting' state — await openBidirectionalStream() or the
connection's readiness before relying on it.
Throws if the endpoint is closed or QUIC is unavailable. Peer verification
is governed by verifyPeer (default false).
const conn = await endpoint.connect({
address: { family: 'ipv4', ip: '203.0.113.10', port: 4433 },
serverName: 'example.com',
verifyPeer: true,
});
accept(): Promise<QuicConnection>
Wait for and return the next accepted server connection.
Resolves as soon as a connection has completed enough of the handshake to be handed to the application, in FIFO order. The returned promise rejects if the endpoint is closed while waiting.
for (;;) {
const conn = await endpoint.accept();
handleConnection(conn);
}
async close(): Promise<void>
Immediately close the endpoint and destroy every connection.
Closes all listeners, destroys active connections (sending CONNECTION_CLOSE
where possible), waits for them to settle, releases every socket, and
rejects any pending accept(). Idempotent. Use closeGracefully() instead
to let in-flight work drain first. Also invoked by Symbol.asyncDispose.
async closeGracefully(options: QuicCloseOptions = {}): Promise<void>
Close the endpoint after letting active connections drain.
Closes all listeners so no new connections are accepted, then asks every
active connection to close gracefully with the given QuicCloseOptions
(flushing pending stream data and exchanging CONNECTION_CLOSE) before
releasing sockets. Idempotent.
await endpoint.closeGracefully({ errorCode: 0, reason: 'shutdown' });