/* * quico — TypeScript declarations * A full HTTP stack in JavaScript: QUIC, HTTP/3, HTTP/2, HTTP/1.1, QPACK, * WebTransport, WebSocket and CONNECT-UDP, for Node.js. * * The API mirrors node:https (server, request, Agent) and the browser's * WebTransport and WebSocket. See README.md for every option and event. */ /// import { IncomingMessage as NodeIncomingMessage, ServerResponse as NodeServerResponse, ClientRequest as NodeClientRequest } from 'node:http'; import { Duplex, Readable, Writable } from 'node:stream'; import { EventEmitter } from 'node:events'; import { Socket as DgramSocket, RemoteInfo } from 'node:dgram'; import { Socket as NetSocket } from 'node:net'; // ============================================================ // Shared // ============================================================ type PemInput = string | Buffer | Array; /** RFC 9218 priority: urgency 0 (first) – 7, default 3. */ interface Priority { urgency?: number; incremental?: boolean; } /** QUIC's windows and stream limits, by RFC 9000's names in camelCase. */ interface TransportParams { initialMaxData?: number; initialMaxStreamDataBidiLocal?: number; initialMaxStreamDataBidiRemote?: number; initialMaxStreamDataUni?: number; initialMaxStreamsBidi?: number; initialMaxStreamsUni?: number; maxIdleTimeout?: number; disableActiveMigration?: boolean; } type StartupCap = 'stall' | 'always' | 'off'; type QuicVersion = 'v1' | 'v2' | number; /** QUIC connection options, as the server, a request, WebTransport and connectUdp take them. */ interface QuicConnectionOptions { initialCongestionWindow?: number; startupCap?: StartupCap; pmtuDiscovery?: boolean; maxUdpPayloadSize?: number; initialRtt?: number; reorderWindow?: number; } /** A fingerprint: its hash, the raw string it was taken from, and its parts. */ interface Fingerprint { hash: string; raw: string; [part: string]: any; } // ============================================================ // Server // ============================================================ type RequestHandler = (req: QuicoIncomingMessage, res: QuicoServerResponse) => void; interface EchKey { config: Uint8Array | Buffer; privateKey: Uint8Array | Buffer; /** false: kept to decrypt, not sent as retry_configs (rotation). */ retry?: boolean; } interface ConnectionOptionsInfo { remoteAddress: string; remotePort: number; servername: string | null; resumed: boolean; previous: { rtt: number; bandwidth: number; age: number } | null; } interface ConnectionOptionsAnswer extends QuicConnectionOptions { transportParams?: TransportParams; carefulResume?: boolean; } interface ConnectUdpServerOptions { template?: string; allowPrivate?: boolean; maxTunnelsPerConnection?: number; idleTimeout?: number; lookup?: (host: string, options: { all: true }, cb: (err: Error | null, addresses: Array<{ address: string; family: number }>) => void) => void; proxyName?: string; } interface ServerOptions extends QuicConnectionOptions { key?: PemInput; cert?: PemInput; ca?: PemInput; passphrase?: string; SNICallback?: (servername: string, cb: (err: Error | null, ctx?: any) => void, info?: { transport: 'tcp' | 'quic' }) => void; http3?: boolean; http2?: boolean; http1?: boolean; ALPNProtocols?: string[]; maxConnections?: number; ticketKeys?: Buffer; ech?: { keys: EchKey[] }; earlyData?: boolean; earlyDataMaxAge?: number; earlyDataReplay?: (id: string, ttl: number, cb: (err: Error | null, isNew?: boolean) => void) => void; earlyDataFilter?: (req: QuicoIncomingMessage, cb: (err: Error | null, allow?: boolean) => void) => void; sessionTimeout?: number; requestCert?: boolean; rejectUnauthorized?: boolean; minVersion?: 'TLSv1.2' | 'TLSv1.3'; maxVersion?: 'TLSv1.2' | 'TLSv1.3'; certificateCompression?: Array<'brotli' | 'zstd' | 'zlib'> | boolean; honorCipherOrder?: boolean; ciphers?: string; ecdhCurve?: string; sigalgs?: string; handshakeTimeout?: number; validateAddress?: boolean; handshakeLoadThreshold?: number; maxHandshakesPerAddress?: number; resetTokenSecret?: Buffer; tokenSecret?: Buffer; socket?: DgramSocket; socket6?: DgramSocket; greaseQuicBit?: boolean; port?: number; altSvc?: string | false; connectUdp?: ConnectUdpServerOptions; shouldUpgradeCallback?: (req: QuicoIncomingMessage) => boolean; headersTimeout?: number; requestTimeout?: number; keepAliveTimeout?: number; streamResetBurst?: number; streamResetRate?: number; maxRequestsPerSocket?: number; maxHeaderSize?: number; maxHeaderCount?: number; noDelay?: boolean; keepAlive?: boolean; keepAliveInitialDelay?: number; transportParams?: TransportParams; carefulResume?: boolean; quicVersions?: QuicVersion[]; connectionOptions?: (info: ConnectionOptionsInfo, callback: (err: Error | null, options?: ConnectionOptionsAnswer) => void) => void; maxWebTransportSessions?: number; // HTTP/2 settings maxConcurrentStreams?: number; headerTableSize?: number; initialWindowSize?: number; maxFrameSize?: number; maxHeaderListSize?: number; connectionWindowSize?: number; enableConnectProtocol?: boolean; maxSessionMemory?: number; } /** The handle of an HTTP/2 or HTTP/3 connection ('connection', 'stream_open', 'stream_reset'). */ interface ConnectionHandle { readonly remoteAddress: string; readonly remotePort: number; readonly destroyed?: boolean; /** HTTP/3: 'h3'-side handle; HTTP/2: the session. */ readonly httpVersion?: string; fingerprint(): Fingerprint | null; sendRstStream(streamId: number, code: 'refused' | 'cancelled' | number): void; goaway(code?: 'overloaded' | number): void; destroy(code?: 'overloaded' | number): void; /** QUIC only: this connection's options (see README "One connection's"). */ setOptions?(options: QuicConnectionOptions & { carefulResume?: boolean }): this; getOptions?(): QuicConnectionOptions & { carefulResume?: boolean }; } interface ServerStats { connections: { quic: number; tcp: number }; handshakes: { quic: { completed: number; failed: number }; tcp: { completed: number; failed: number }; resumed: number }; protocols: { h3: number; h2: number; 'http/1.1': number; other: number }; earlyData: { accepted: number; rejected: number; tooEarly: number }; closes: { quic: { sent: Record; received: Record; silent: number }; h3: { sent: Record; received: Record }; h2: { sent: Record; received: Record }; http1: { sent: Record }; }; streamResets: { h3: { sent: Record; received: Record }; h2: { sent: Record; received: Record }; }; amplificationBlocked: number; internalErrors: number; since: number; uptime: number; } interface ConnectUdpTunnelStats { target: { host: string; port: number }; address: string | null; port: number | null; httpVersion: string; state: 'pending' | 'open' | 'closed'; transport: 'datagram' | 'capsule'; openedAt: number | null; closedAt: number | null; durationMs: number | null; packetsFromClient: number; bytesFromClient: number; packetsToClient: number; bytesToClient: number; droppedFromClient: number; droppedToClient: number; } /** A CONNECT-UDP request, server side ('connectUdp'). */ interface ConnectUdpTunnel extends EventEmitter { readonly req: QuicoIncomingMessage; readonly target: { host: string; port: number }; readonly state: 'pending' | 'open' | 'closed'; readonly closeReason: 'client' | 'local' | 'idle' | 'aborted' | 'socket' | 'rejected' | null; readonly error: Error | null; readonly stats: ConnectUdpTunnelStats; accept(options?: { target?: { host: string; port: number }; socket?: DgramSocket; relay?: boolean; headers?: Record; allowPrivate?: boolean; }): void; reject(status: number, headers?: Record): void; send(buf: Buffer | Uint8Array): boolean; close(): void; on(event: 'open', listener: () => void): this; on(event: 'message', listener: (msg: Buffer) => void): this; on(event: 'close', listener: (stats: ConnectUdpTunnelStats) => void): this; on(event: string, listener: (...args: any[]) => void): this; } interface QuicoServer extends EventEmitter { listen(port?: number, callback?: () => void): this; listen(port: number, host: string, callback?: () => void): this; /** As node:http's: requests in flight finish (HTTP/2, HTTP/3: GOAWAY), idle connections close now; then 'close'. */ close(callback?: (err?: Error) => void): this; /** Connections with no request in flight, all versions. */ closeIdleConnections(): void; /** Every connection, in flight or not (not HTTP/1.1 sockets handed to 'upgrade'). */ closeAllConnections(): void; getConnections(callback: (err: Error | null, count: number) => void): this; readonly listening: boolean; headersTimeout: number; requestTimeout: number; /** HTTP/1.1. */ keepAliveTimeout: number; /** HTTP/1.1. */ maxHeadersCount: number; maxRequestsPerSocket: number; ref(): this; unref(): this; [Symbol.asyncDispose](): Promise; /** node:tls's: the default certificate, from the next handshake on — all three versions. Throws on a bad one (the old stays). */ setSecureContext(options: { key?: PemInput | PemInput[]; cert?: PemInput | PemInput[]; ca?: PemInput; passphrase?: string } | ReturnType): void; /** node:tls's: a certificate for a name (SNI; '*.example.com' covers one label). */ addContext(hostname: string, context: { key?: PemInput | PemInput[]; cert?: PemInput | PemInput[]; ca?: PemInput; passphrase?: string } | ReturnType): void; address(): { address: string; family: string; port: number } | null; handleConnection(socket: NetSocket): void; setTimeout(msecs?: number, callback?: (socket: NetSocket) => void): this; timeout: number; /** External socket mode: feed a UDP datagram. */ handlePacket(msg: Buffer, rinfo: RemoteInfo): void; /** External socket mode: whether a datagram belongs to a connection of this server. */ hasConnection(rinfo: RemoteInfo, msg?: Buffer): boolean; addProtocol(name: string, handler: (socket: Duplex, protocol: string) => void, options?: { sniff?: (firstBytes: Buffer) => boolean | null }): this; getTicketKeys(): Buffer; setTicketKeys(keys: Buffer): void; getEchKeys(): { keys: EchKey[] } | null; setEchKeys(ech: { keys: EchKey[] }): void; getValidateAddress(): boolean; setValidateAddress(on: boolean): void; getResetTokenSecret(): Buffer; setResetTokenSecret(secret: Buffer): void; getTokenSecret(): Buffer; setTokenSecret(secret: Buffer): void; setOptions(options: Partial): this; getOptions(): ServerOptions; getPendingHandshakes(): number; getStats(): ServerStats; getConnectUdpStats(): { active: number; accepted: number; rejected: number; closed: number; [counter: string]: number }; on(event: 'request', listener: RequestHandler): this; on(event: 'upgrade', listener: (req: QuicoIncomingMessage, socket: Duplex, head: Buffer) => void): this; on(event: 'connectUdp', listener: (req: QuicoIncomingMessage, tunnel: ConnectUdpTunnel) => void): this; on(event: 'timeout', listener: (socket: NetSocket) => void): this; on(event: 'connection', listener: (conn: NetSocket | ConnectionHandle) => void): this; on(event: 'secureConnection', listener: (conn: NetSocket | ConnectionHandle) => void): this; on(event: 'stream_open', listener: (conn: ConnectionHandle, streamId: number) => void): this; on(event: 'stream_reset', listener: (conn: ConnectionHandle, streamId: number, code: number) => void): this; on(event: 'handshakeLoad', listener: (info: { pending: number; high: boolean }) => void): this; on(event: 'OCSPRequest', listener: (certificate: Buffer, issuer: Buffer | null, callback: (err: Error | null, response: Buffer | null) => void) => void): this; on(event: 'tlsClientError', listener: (err: Error, socket: NetSocket | ConnectionHandle) => void): this; on(event: 'clientError', listener: (err: Error, socket: NetSocket) => void): this; on(event: 'sessionError', listener: (err: Error, session: ConnectionHandle) => void): this; on(event: 'listening', listener: () => void): this; on(event: 'close', listener: () => void): this; on(event: 'error', listener: (err: Error) => void): this; on(event: string, listener: (...args: any[]) => void): this; } // ============================================================ // Request, response // ============================================================ /** A request on the server, or a response on the client (node:http's). */ interface QuicoIncomingMessage extends NodeIncomingMessage { /** '3.0', '2.0' or '1.1'. */ httpVersion: string; /** Server: true if it came in 0-RTT. Client: 'accepted', 'rejected' or null. */ earlyData: boolean | 'accepted' | 'rejected' | null; /** HTTP/2 request's fingerprint. */ h2Fingerprint?(): Fingerprint; /** HTTP/3 request's fingerprint. */ h3Fingerprint?(): Fingerprint; // ---- WebTransport (server, after res.writeHead(200) on a session) ---- /** The protocols the client offered (WT-Available-Protocols), in its order. */ webTransportProtocols?: string[]; /** The client's CLOSE_WEBTRANSPORT_SESSION, when it sent one. */ closeInfo?: WebTransportCloseInfo; on(event: 'stream', listener: (stream: WebTransportNodeStream & Duplex) => void): this; on(event: 'unidirectionalStream', listener: (stream: WebTransportNodeStream & Readable) => void): this; on(event: 'datagram', listener: (data: Buffer) => void): this; on(event: 'draining', listener: () => void): this; on(event: 'aborted', listener: () => void): this; on(event: string | symbol, listener: (...args: any[]) => void): this; } interface QuicoServerResponse extends NodeServerResponse { writeEarlyHints(hints: Record, callback?: () => void): void; /** HTTP/2 and HTTP/3: the response's priority (RFC 9218). */ setPriority(priority: Priority): void; // ---- WebTransport (after res.writeHead(200) on a session) ---- /** true when queued, false when it cannot be sent at all. */ sendDatagram?(data: Buffer | Uint8Array | string, options?: WebTransportSendOptions): boolean; maxDatagramSize?(): number; readonly datagrams?: WebTransportDatagramDuplexStream | null; getStats?(): Promise; exportKeyingMaterial?(label: BufferSource, context: BufferSource, outputLength: number): Promise; /** null, without waitUntilAvailable, past the client's MAX_STREAMS. */ createBidirectionalStream?(options?: WebTransportSendStreamOptions | null, callback?: (err: Error | null, stream?: WebTransportNodeStream & Duplex) => void): (WebTransportNodeStream & Duplex) | null; createUnidirectionalStream?(options?: WebTransportSendStreamOptions | null, callback?: (err: Error | null, stream?: WebTransportNodeStream & Writable) => void): (WebTransportNodeStream & Writable) | null; canOpenStream?(options?: { bidirectional?: boolean }): boolean; createSendGroup?(): WebTransportSendGroup; /** Before writeHead(200): the protocol chosen among req.webTransportProtocols. */ setWebTransportProtocol?(name: string): void; closeSession?(code?: number, reason?: string): void; drainSession?(): void; } interface RequestOptions extends QuicConnectionOptions { protocol?: string; hostname?: string; host?: string; port?: number; path?: string; method?: string; headers?: Record; priority?: Priority; ech?: Buffer | Uint8Array | string; echGrease?: boolean; ecdhCurve?: string; sigalgs?: string; rejectUnauthorized?: boolean; ca?: PemInput; key?: PemInput; cert?: PemInput; passphrase?: string; servername?: string; earlyData?: boolean; session?: Buffer; requestOCSP?: boolean; quicVersions?: QuicVersion[]; agent?: Agent | false; http2?: boolean; http1?: boolean; h3Timeout?: number; timeout?: number; } interface QuicoClientRequest extends NodeClientRequest { /** The transport actually used: 'h3', 'h2' or 'https'. */ protocol: string; setPriority(priority: Priority): void; on(event: 'response', listener: (res: QuicoIncomingMessage) => void): this; on(event: 'session', listener: (session: Buffer) => void): this; on(event: 'OCSPResponse', listener: (response: Buffer) => void): this; on(event: string | symbol, listener: (...args: any[]) => void): this; } // ============================================================ // Agent // ============================================================ interface AgentOptions { http2?: boolean; http1?: boolean; h3Timeout?: number; keepAlive?: boolean; keepAliveMsecs?: number; maxSockets?: number; maxFreeSockets?: number; timeout?: number; maxCachedSessions?: number; ecdhCurve?: string; } declare class Agent extends EventEmitter { constructor(options?: AgentOptions); getName(options?: RequestOptions): string; destroy(): void; } // ============================================================ // WebTransport — the browser's API (W3C), and a Node-style one // ============================================================ type WebTransportReliabilityMode = 'pending' | 'reliable-only' | 'supports-unreliable'; type WebTransportCongestionControl = 'default' | 'throughput' | 'low-latency'; type WebTransportErrorSource = 'stream' | 'session'; interface WebTransportHash { algorithm: string; value: BufferSource; } interface WebTransportCloseInfo { closeCode?: number; reason?: string; } interface WebTransportSendOptions { sendGroup?: WebTransportSendGroup | null; sendOrder?: number; } interface WebTransportSendStreamOptions extends WebTransportSendOptions { waitUntilAvailable?: boolean; } interface WebTransportDatagramStats { droppedIncoming: number; expiredIncoming: number; expiredOutgoing: number; lostOutgoing: number | null; } interface WebTransportConnectionStats { timestamp: number; /** The application's bytes: new stream data and datagram payloads (no framing, no retransmissions). */ bytesSent: number | null; bytesSentOverhead: number | null; bytesAcknowledged: number | null; packetsSent: number | null; bytesLost: number | null; packetsLost: number | null; bytesReceived: number | null; packetsReceived: number | null; smoothedRtt: number | null; rttVariation: number | null; minRtt: number | null; /** Bits per second; null until measured. */ estimatedSendRate: number | null; atSendCapacity: boolean | null; /** QUICO's: the congestion window, and the bytes in flight. */ congestionWindow: number | null; bytesInFlight: number | null; datagrams: WebTransportDatagramStats; } interface WebTransportSendStreamStats { bytesWritten: number; bytesSent: number; bytesAcknowledged: number; } interface WebTransportReceiveStreamStats { bytesReceived: number; bytesRead: number; } interface WebTransportOptions extends QuicConnectionOptions { // ---- The browser's ---- allowPooling?: boolean; requireUnreliable?: boolean; /** The browser's HeadersInit: a Headers, [name, value] pairs, or an object. */ headers?: HeadersInit | Record; serverCertificateHashes?: WebTransportHash[]; congestionControl?: WebTransportCongestionControl; anticipatedConcurrentIncomingUnidirectionalStreams?: number | null; anticipatedConcurrentIncomingBidirectionalStreams?: number | null; protocols?: string[]; datagramsReadableType?: 'bytes'; // ---- Node's (as https.request / QUICO's WebSocket) ---- /** Sent as Origin on the CONNECT (a browser sends its page's). */ origin?: string; /** Verify the server's certificate (default true). */ rejectUnauthorized?: boolean; ca?: PemInput; key?: PemInput; cert?: PemInput; passphrase?: string; /** A TLS session from an earlier 'session' event, to resume with. */ session?: Buffer; transportParams?: TransportParams; quicVersions?: QuicVersion[]; ech?: Buffer | Uint8Array | string; echGrease?: boolean; ecdhCurve?: string; sigalgs?: string; } /** The browser's WebTransportError: a DOMException named 'WebTransportError'. */ declare class WebTransportError extends DOMException { constructor(message?: string, options?: { source?: WebTransportErrorSource; streamErrorCode?: number | null }); constructor(init: { message?: string; source?: WebTransportErrorSource; streamErrorCode?: number | null }); readonly source: WebTransportErrorSource; readonly streamErrorCode: number | null; /** QUICO's Node code, where it gives one (e.g. 'ERR_WEBTRANSPORT_SESSION_LOST'), in place of DOMException's number. */ readonly code: any; /** The QUIC close that lost the session, when the peer closed the connection. */ readonly quicError?: { code: number; application: boolean; reason: string }; } declare class WebTransportSendGroup { private constructor(); getStats(): Promise; } declare class WebTransportWriter extends WritableStreamDefaultWriter { private constructor(); /** Sent only if all of it fits the flow-control window; otherwise rejects with an AbortError. */ atomicWrite(chunk: BufferSource): Promise; /** What was written so far reaches the peer even if the stream is aborted later (RESET_STREAM_AT). */ commit(): void; } declare class WebTransportSendStream extends WritableStream { private constructor(); sendGroup: WebTransportSendGroup | null; sendOrder: number; getWriter(): WebTransportWriter; getStats(): Promise; } /** A byte stream: getReader({ mode: 'byob' }) works. */ declare class WebTransportReceiveStream extends ReadableStream { private constructor(); getStats(): Promise; } /** What a stream also is in QUICO: a Node stream with these. */ interface WebTransportNodeStream { readonly id: number; readonly sessionId: number; readonly sendState: 'open' | 'finished' | 'committed' | 'reset' | 'stopped' | null; readonly recvState: 'open' | 'finished' | 'reset' | 'stopped' | null; readonly pending: boolean; readonly reliableResetSupported: boolean; readonly streamErrorCode: number | null; sendOrder: number; sendGroup: WebTransportSendGroup | null; resetStream(code?: number, options?: { reliableSize?: number }): boolean; stopSending(code?: number): void; getStats(): Promise>; on(event: 'peerReset' | 'peerStopSending', listener: (code: number | null) => void): this; on(event: 'committed' | 'ready', listener: () => void): this; on(event: string | symbol, listener: (...args: any[]) => void): this; } /** * The browser's WebTransportBidirectionalStream. In QUICO it is a Node * Duplex with the browser's .readable and .writable; instanceof recognises it. */ declare class WebTransportBidirectionalStream { private constructor(); readonly readable: WebTransportReceiveStream; readonly writable: WebTransportSendStream; } type WebTransportBidirectionalNodeStream = WebTransportBidirectionalStream & WebTransportNodeStream & Duplex; /** createUnidirectionalStream(): the browser's send stream, and Node's Writable on it. */ type WebTransportUnidirectionalNodeStream = WebTransportSendStream & { write(chunk: any, cb?: (err?: Error | null) => void): boolean; write(chunk: any, encoding: BufferEncoding, cb?: (err?: Error | null) => void): boolean; end(chunk?: any, cb?: () => void): void; destroy(err?: Error): void; resetStream(code?: number, options?: { reliableSize?: number }): boolean; readonly id: number; readonly sessionId: number; readonly sendState: 'open' | 'finished' | 'committed' | 'reset' | 'stopped'; readonly pending: boolean; readonly writableLength: number; readonly writableNeedDrain: boolean; on(event: string | symbol, listener: (...args: any[]) => void): any; once(event: string | symbol, listener: (...args: any[]) => void): any; }; declare class WebTransportDatagramsWritable extends WritableStream { private constructor(); sendGroup: WebTransportSendGroup | null; sendOrder: number; } declare class WebTransportDatagramDuplexStream { private constructor(); createWritable(options?: WebTransportSendOptions): WebTransportDatagramsWritable; readonly readable: ReadableStream; readonly maxDatagramSize: number; /** ms; null (or 0 set) is none. */ incomingMaxAge: number | null; outgoingMaxAge: number | null; incomingMaxBufferedDatagrams: number; outgoingMaxBufferedDatagrams: number; /** @deprecated The spec's earlier names: use createWritable() and …MaxBufferedDatagrams. */ readonly writable: WebTransportDatagramsWritable; /** @deprecated Use incomingMaxBufferedDatagrams. */ incomingHighWaterMark: number; /** @deprecated Use outgoingMaxBufferedDatagrams. */ outgoingHighWaterMark: number; } declare class WebTransport extends EventEmitter { constructor(url: string | URL, options?: WebTransportOptions); // ---- The browser's ---- readonly ready: Promise; readonly closed: Promise; readonly draining: Promise; readonly reliability: WebTransportReliabilityMode; readonly congestionControl: WebTransportCongestionControl; readonly protocol: string; /** The server's answer to the CONNECT (immutable); null until ready. */ readonly responseHeaders: Headers | null; anticipatedConcurrentIncomingUnidirectionalStreams: number | null; anticipatedConcurrentIncomingBidirectionalStreams: number | null; readonly datagrams: WebTransportDatagramDuplexStream; readonly incomingBidirectionalStreams: ReadableStream; readonly incomingUnidirectionalStreams: ReadableStream; /** false: HTTP/3 only. */ static readonly supportsReliableOnly: boolean; close(closeInfo?: WebTransportCloseInfo): void; getStats(): Promise; exportKeyingMaterial(label: BufferSource, context: BufferSource, outputLength: number): Promise; createBidirectionalStream(options?: WebTransportSendStreamOptions): Promise; createUnidirectionalStream(options?: WebTransportSendStreamOptions): Promise; createSendGroup(): WebTransportSendGroup; // ---- QUICO's, for Node code ---- /** 'connecting', 'connected', 'closed' or 'failed'. */ readonly state: 'connecting' | 'connected' | 'closed' | 'failed'; readonly url: string; /** The server's CLOSE_WEBTRANSPORT_SESSION, or ours. */ readonly closeInfo?: WebTransportCloseInfo; canOpenStream(options?: { bidirectional?: boolean }): boolean; /** true when queued, false when it cannot be sent at all. */ sendDatagram(data: Buffer | Uint8Array | string, options?: WebTransportSendOptions): boolean; maxDatagramSize(): number; on(event: 'ready' | 'draining' | 'close', listener: () => void): this; on(event: 'stream', listener: (stream: WebTransportBidirectionalNodeStream) => void): this; on(event: 'unidirectionalStream', listener: (stream: WebTransportNodeStream & Readable) => void): this; on(event: 'datagram', listener: (data: Buffer) => void): this; on(event: 'session', listener: (session: Buffer) => void): this; on(event: 'error', listener: (err: WebTransportError) => void): this; on(event: string | symbol, listener: (...args: any[]) => void): this; } // ============================================================ // WebSocket // ============================================================ interface WebSocketClientOptions { origin?: string; headers?: Record; ca?: PemInput; rejectUnauthorized?: boolean; maxPayload?: number; agent?: Agent; session?: Buffer; } /** * The ws package's listeners, beside the browser's addEventListener: they get * Node-style arguments ('message': (data, isBinary); 'close': (code, reason)). */ interface WebSocketNodeEvents { on(event: 'message', listener: (this: T, data: Buffer, isBinary: boolean) => void): T; on(event: 'close', listener: (this: T, code: number, reason: Buffer) => void): T; on(event: 'ping' | 'pong', listener: (this: T, data: Buffer) => void): T; on(event: 'open', listener: (this: T) => void): T; on(event: 'error', listener: (this: T, err: Error) => void): T; on(event: 'session', listener: (this: T, session: Buffer) => void): T; on(event: string, listener: (this: T, ...args: any[]) => void): T; once(event: string, listener: (this: T, ...args: any[]) => void): T; addListener(event: string, listener: (this: T, ...args: any[]) => void): T; off(event: string, listener: (...args: any[]) => void): T; removeListener(event: string, listener: (...args: any[]) => void): T; removeAllListeners(event?: string): T; listenerCount(event: string): number; eventNames(): string[]; } /** What a WebSocket has, client or server side: the browser's interface, ping() and onpong. */ interface WebSocketBase extends EventTarget { readonly CONNECTING: 0; readonly OPEN: 1; readonly CLOSING: 2; readonly CLOSED: 3; readonly url: string; readonly readyState: 0 | 1 | 2 | 3; readonly bufferedAmount: number; readonly protocol: string; readonly extensions: string; /** 'blob' (the browser's default) or 'arraybuffer'; 'nodebuffer' too. */ binaryType: 'blob' | 'arraybuffer' | 'nodebuffer'; onopen: ((event: Event) => void) | null; onmessage: ((event: MessageEvent) => void) | null; onerror: ((event: Event) => void) | null; onclose: ((event: CloseEvent) => void) | null; /** Not in the browser: the answer to ping(). */ onpong: ((event: Event) => void) | null; /** options (the ws package's): binary forces a frame's type. */ send(data: string | ArrayBufferLike | ArrayBufferView | Blob, options?: { binary?: boolean }): void; /** Not in the browser: a keepalive for a long-lived connection. */ ping(data?: string | Buffer | Uint8Array): void; close(code?: number, reason?: string): void; } /** The browser's WebSocket (WHATWG), with a third argument for Node's options. */ declare class WebSocket extends EventTarget implements WebSocketBase { /** protocols may also be an options bag, { protocols } (as WPT has it). */ constructor(url: string | URL, protocols?: string | string[] | { protocols?: string | string[] }, options?: WebSocketClientOptions); static readonly CONNECTING: 0; static readonly OPEN: 1; static readonly CLOSING: 2; static readonly CLOSED: 3; readonly CONNECTING: 0; readonly OPEN: 1; readonly CLOSING: 2; readonly CLOSED: 3; readonly url: string; readonly readyState: 0 | 1 | 2 | 3; readonly bufferedAmount: number; readonly protocol: string; readonly extensions: string; binaryType: 'blob' | 'arraybuffer' | 'nodebuffer'; onopen: ((event: Event) => void) | null; onmessage: ((event: MessageEvent) => void) | null; onerror: ((event: Event) => void) | null; onclose: ((event: CloseEvent) => void) | null; onpong: ((event: Event) => void) | null; send(data: string | ArrayBufferLike | ArrayBufferView | Blob, options?: { binary?: boolean }): void; ping(data?: string | Buffer | Uint8Array): void; close(code?: number, reason?: string): void; } interface WebSocket extends WebSocketNodeEvents {} interface WebSocketStreamOptions extends WebSocketClientOptions { protocols?: string[]; signal?: AbortSignal; } interface WebSocketOpenInfo { /** One chunk per message: a string for text, a Uint8Array for binary. */ readable: ReadableStream; /** An ArrayBuffer or a view goes as binary, anything else as text. */ writable: WritableStream; protocol: string; extensions: string; } interface WebSocketCloseInfo { closeCode?: number; reason?: string; } /** * EXPERIMENTAL — the browser's streams WebSocket (WICG explainer; Chrome has * it, not yet a standard). Backpressure both ways: an unread message pauses * the socket; write() resolves once the frame is sent. */ declare class WebSocketStream { constructor(url: string | URL, options?: WebSocketStreamOptions); readonly url: string; readonly opened: Promise; readonly closed: Promise<{ closeCode: number; reason: string }>; close(closeInfo?: WebSocketCloseInfo): void; } /** The error of a WebSocketStream: a DOMException named 'WebSocketError'. */ declare class WebSocketError extends DOMException { constructor(message?: string, init?: WebSocketCloseInfo); readonly closeCode: number | null; readonly reason: string; } /** A connection a WebSocketServer accepted (or upgradeWebSocket made). */ interface ServerWebSocket extends WebSocketBase, WebSocketNodeEvents {} interface UpgradeWebSocketOptions { origin?: string | string[] | ((origin: string, req: QuicoIncomingMessage) => boolean); verifyClient?: (req: QuicoIncomingMessage) => boolean | { code: number; message?: string; headers?: Record }; protocol?: string | ((offered: string[], req: QuicoIncomingMessage) => string | false | null | undefined); protocols?: string[]; headers?: Record; maxPayload?: number; } interface WebSocketServerOptions extends UpgradeWebSocketOptions { server?: QuicoServer; noServer?: boolean; path?: string; pingInterval?: number; } declare class WebSocketServer extends EventTarget { constructor(options: WebSocketServerOptions); readonly clients: Set; onconnection: ((event: Event & { socket?: ServerWebSocket; request?: QuicoIncomingMessage }) => void) | null; handleUpgrade(req: QuicoIncomingMessage, socket: Duplex, head: Buffer, cb: (ws: ServerWebSocket, req: QuicoIncomingMessage) => void): void; startAutoPing(intervalMs: number): void; stopAutoPing(): void; close(code?: number, reason?: string): void; on(event: 'connection', listener: (ws: ServerWebSocket, req: QuicoIncomingMessage) => void): this; on(event: string, listener: (...args: any[]) => void): this; once(event: string, listener: (...args: any[]) => void): this; off(event: string, listener: (...args: any[]) => void): this; } /** The socket, or null after answering the client itself (403, 401, …). */ declare function upgradeWebSocket(req: QuicoIncomingMessage, socket: Duplex, head: Buffer, options?: UpgradeWebSocketOptions): ServerWebSocket | null; // ============================================================ // CONNECT-UDP client // ============================================================ interface ConnectUdpOptions extends QuicConnectionOptions { protocol?: 'auto' | 'h3' | 'h2' | 'http/1.1'; h3Timeout?: number; fallback?: boolean; shareConnection?: boolean; headers?: Record; ech?: Buffer | Uint8Array | string; echGrease?: boolean; ecdhCurve?: string; sigalgs?: string; template?: string; rejectUnauthorized?: boolean; ca?: PemInput; key?: PemInput; cert?: PemInput; passphrase?: string; servername?: string; quicVersions?: QuicVersion[]; agent?: Agent | false; session?: Buffer; } interface UdpProxySocketStats { protocol: string | null; transport: 'datagram' | 'capsule' | null; target: { host: string; port: number }; openedAt: number | null; closedAt: number | null; durationMs: number | null; packetsSent: number; bytesSent: number; packetsReceived: number; bytesReceived: number; dropped: number; } /** A UDP socket through a proxy, behaving as a connected dgram.Socket. */ interface UdpProxySocket extends EventEmitter { send(msg: string | Buffer | Uint8Array | ReadonlyArray, callback?: (err: Error | null, bytes?: number) => void): void; send(msg: Buffer | Uint8Array, offset: number, length: number, callback?: (err: Error | null, bytes?: number) => void): void; close(callback?: () => void): void; remoteAddress(): { address: string; family: string; port: number }; address(): { address: string; family: string; port: number }; readonly maxPayloadSize: number; readonly protocol: 'h3' | 'h2' | 'http/1.1' | null; readonly statusCode: number | null; readonly responseHeaders: Record | null; readonly closeReason: 'local' | 'proxy' | 'error' | null; readonly stats: UdpProxySocketStats; on(event: 'connect' | 'close', listener: () => void): this; on(event: 'message', listener: (msg: Buffer, rinfo: RemoteInfo) => void): this; on(event: 'error', listener: (err: Error & { code?: string; statusCode?: number; proxyStatus?: string }) => void): this; on(event: 'session', listener: (session: Buffer) => void): this; on(event: string | symbol, listener: (...args: any[]) => void): this; } declare function connectUdp( proxy: string | URL | { hostname: string; port?: number; template?: string }, target: { host: string; port: number }, options?: ConnectUdpOptions, callback?: () => void ): UdpProxySocket; declare function connectUdp( proxy: string | URL | { hostname: string; port?: number; template?: string }, target: { host: string; port: number }, callback?: () => void ): UdpProxySocket; // ============================================================ // Fingerprinting // ============================================================ declare function ja4h(req: NodeIncomingMessage): Fingerprint & { a: string; b: string; c: string; d: string }; declare function h2Fingerprint(observed: any): Fingerprint; declare function h3Fingerprint(observed: any): Fingerprint; declare function observeH2(...args: any[]): any; declare function profileRequest(req: NodeIncomingMessage): { claimed: any; protocol: string; fingerprints: { ja4: string | null; ja4s: string | null; h2: string | null; h2raw: string | null; h3: string | null; h3raw: string | null }; signals: any[]; suspicion: number; }; // ============================================================ // TLS // ============================================================ declare function createSecureContext(options: { key?: PemInput; cert?: PemInput; ca?: PemInput; passphrase?: string; [option: string]: any }): any; declare function generateEchKey(options: { publicName: string; [option: string]: any }): { config: Uint8Array; privateKey: Uint8Array; configListBase64: string; [field: string]: any; }; // ============================================================ // Transport and protocol layers (see README "Protocol layers", "Low-level QUIC API") // ============================================================ /** A QUIC connection (low-level API). */ interface QUICConnection extends EventEmitter { readonly state: 'idle' | 'handshaking' | 'connected' | 'closing' | 'draining' | 'closed'; readonly remoteAddress: string; readonly remotePort: number; readonly earlyDataStatus: null | 'offered' | 'accepted' | 'rejected'; readonly resetStreamAtSupported: boolean; readonly alpnProtocol: string | null; readonly servername: string | null; sendStream(id: number, data: Uint8Array | Buffer, fin?: boolean): void; whenStreamCommitted(id: number, cb: () => void): void; resetStream(id: number, code: number, reliableSize?: number): boolean; stopSending(id: number, code: number): void; sendDatagram(data: Uint8Array | Buffer, prio?: { urgency?: number; group?: object; order?: number } | null): boolean; setStreamPriority(id: number, priority: Priority & { group?: object; order?: number }, fromPeer?: boolean): boolean; getStreamPriority(id: number): (Priority & { group?: object; order?: number }) | null; canOpenStream(bidi: boolean): boolean; isReceiving(id: number): boolean; isEarlyStream(id: number): boolean; maxDatagramSize(): number; whenConfirmed(cb: () => void): void; close(errorCode?: number, reason?: string, application?: boolean): void; getCertificate(): any; getTicketInfo(): any; isSessionReused(): boolean; exportKeyingMaterial(length: number, label: string, context?: Uint8Array | null): Buffer | null; getStats(): Record; on(event: 'stream', listener: (id: number, data: Uint8Array, fin: boolean) => void): this; on(event: 'stream_reset', listener: (id: number, code: number, finalSize: number) => void): this; on(event: 'stop_sending', listener: (id: number, code: number) => void): this; on(event: 'datagram', listener: (data: Uint8Array) => void): this; on(event: 'path', listener: (address: string, port: number) => void): this; /** The moment the connection is over for the application ('close' follows after the draining period). */ on(event: 'closing', listener: (peerClose: { error: number; application: boolean; reason: string } | null) => void): this; on(event: string | symbol, listener: (...args: any[]) => void): this; } interface QuicServer extends EventEmitter { listen(port?: number, host?: string, callback?: () => void): this; close(callback?: () => void): void; handlePacket(msg: Buffer, rinfo: RemoteInfo): void; hasConnection(rinfo: RemoteInfo, msg?: Buffer): boolean; getPendingHandshakes(): number; setOptions(options: Record): this; getOptions(): Record; on(event: 'connection', listener: (quic: QUICConnection, peer: { address: string; port: number }) => void): this; on(event: string | symbol, listener: (...args: any[]) => void): this; [method: string]: any; } declare function createQuicServer(options: ServerOptions & { alpn?: string[]; [option: string]: any }): QuicServer; declare function createQuicClientSocket(options: { remoteIp: string; remotePort: number; hostname?: string; alpn?: string | string[]; ecdhCurve?: string; idleTimeout?: number; keepAlive?: boolean | number; onConnect?: (quic: QUICConnection, socket: DgramSocket) => void; onSocket?: (quic: QUICConnection, socket: DgramSocket) => void; onError?: (err: Error) => void; onClose?: () => void; [option: string]: any; }): DgramSocket; declare function createTcpListener(options: Record): { register(alpn: string, handler: (socket: Duplex, protocol: string) => void, options?: { sniff?: (firstBytes: Buffer) => boolean | null }): void; listen(port: number, host?: string, callback?: () => void): void; [method: string]: any; }; declare function createH1Server(options: ServerOptions, handler?: RequestHandler): any; declare function createH2Server(options: ServerOptions, handler?: RequestHandler): any; declare function createH3Server(options: ServerOptions, handler?: RequestHandler): any; declare function h1Request(options: RequestOptions, callback?: (res: QuicoIncomingMessage) => void): QuicoClientRequest; declare function h2Request(options: RequestOptions, callback?: (res: QuicoIncomingMessage) => void): QuicoClientRequest; declare function h3Request(options: RequestOptions, callback?: (res: QuicoIncomingMessage) => void): QuicoClientRequest; // ============================================================ // Main module // ============================================================ declare function createServer(handler?: RequestHandler): QuicoServer; declare function createServer(options: ServerOptions, handler?: RequestHandler): QuicoServer; declare function request(url: string | URL, callback?: (res: QuicoIncomingMessage) => void): QuicoClientRequest; declare function request(url: string | URL, options: RequestOptions, callback?: (res: QuicoIncomingMessage) => void): QuicoClientRequest; declare function request(options: RequestOptions, callback?: (res: QuicoIncomingMessage) => void): QuicoClientRequest; declare function get(url: string | URL, callback?: (res: QuicoIncomingMessage) => void): QuicoClientRequest; declare function get(url: string | URL, options: RequestOptions, callback?: (res: QuicoIncomingMessage) => void): QuicoClientRequest; declare function get(options: RequestOptions, callback?: (res: QuicoIncomingMessage) => void): QuicoClientRequest; declare const globalAgent: Agent; /** node:http's classes, as QUICO's request and response are made. */ declare const IncomingMessage: typeof NodeIncomingMessage; declare const ServerResponse: typeof NodeServerResponse; declare const ClientRequest: typeof NodeClientRequest; declare const quico: { createServer: typeof createServer; request: typeof request; get: typeof get; Agent: typeof Agent; globalAgent: Agent; IncomingMessage: typeof NodeIncomingMessage; ServerResponse: typeof NodeServerResponse; ClientRequest: typeof NodeClientRequest; connectUdp: typeof connectUdp; createSecureContext: typeof createSecureContext; generateEchKey: typeof generateEchKey; WebTransport: typeof WebTransport; WebTransportError: typeof WebTransportError; WebTransportBidirectionalStream: typeof WebTransportBidirectionalStream; WebTransportReceiveStream: typeof WebTransportReceiveStream; WebTransportSendStream: typeof WebTransportSendStream; WebTransportWriter: typeof WebTransportWriter; WebTransportSendGroup: typeof WebTransportSendGroup; WebTransportDatagramsWritable: typeof WebTransportDatagramsWritable; WebTransportDatagramDuplexStream: typeof WebTransportDatagramDuplexStream; WebSocket: typeof WebSocket; WebSocketStream: typeof WebSocketStream; WebSocketError: typeof WebSocketError; WebSocketServer: typeof WebSocketServer; upgradeWebSocket: typeof upgradeWebSocket; ja4h: typeof ja4h; h2Fingerprint: typeof h2Fingerprint; h3Fingerprint: typeof h3Fingerprint; observeH2: typeof observeH2; profileRequest: typeof profileRequest; createQuicServer: typeof createQuicServer; createQuicClientSocket: typeof createQuicClientSocket; createTcpListener: typeof createTcpListener; createH1Server: typeof createH1Server; createH2Server: typeof createH2Server; createH3Server: typeof createH3Server; h1Request: typeof h1Request; h2Request: typeof h2Request; h3Request: typeof h3Request; }; export default quico; export { createServer, request, get, Agent, globalAgent, IncomingMessage, ServerResponse, ClientRequest, connectUdp, createSecureContext, generateEchKey, WebTransport, WebTransportError, WebTransportBidirectionalStream, WebTransportReceiveStream, WebTransportSendStream, WebTransportWriter, WebTransportSendGroup, WebTransportDatagramsWritable, WebTransportDatagramDuplexStream, WebSocket, WebSocketStream, WebSocketError, WebSocketServer, upgradeWebSocket, ja4h, h2Fingerprint, h3Fingerprint, observeH2, profileRequest, createQuicServer, createQuicClientSocket, createTcpListener, createH1Server, createH2Server, createH3Server, h1Request, h2Request, h3Request }; export type { ServerOptions, RequestOptions, RequestHandler, AgentOptions, Priority, TransportParams, StartupCap, QuicConnectionOptions, ConnectionOptionsInfo, ConnectionHandle, ServerStats, EchKey, Fingerprint, QuicoServer, QuicoIncomingMessage, QuicoServerResponse, QuicoClientRequest, ConnectUdpServerOptions, ConnectUdpTunnel, ConnectUdpTunnelStats, ConnectUdpOptions, UdpProxySocket, UdpProxySocketStats, WebTransportOptions, WebTransportHash, WebTransportCloseInfo, WebTransportSendOptions, WebTransportSendStreamOptions, WebTransportConnectionStats, WebTransportDatagramStats, WebTransportSendStreamStats, WebTransportReceiveStreamStats, WebTransportReliabilityMode, WebTransportCongestionControl, WebTransportErrorSource, WebTransportNodeStream, WebTransportBidirectionalNodeStream, WebTransportUnidirectionalNodeStream, WebSocketClientOptions, WebSocketStreamOptions, WebSocketOpenInfo, WebSocketCloseInfo, WebSocketServerOptions, UpgradeWebSocketOptions, ServerWebSocket, QUICConnection, QuicServer };