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' });