js/net/quic/stream

js/net/quic/stream.ts

fino:net/quic/stream — bidirectional and unidirectional QUIC streams.

A QuicStream exposes QUIC stream data through Web Streams and lower-level Fino byte reader and writer objects. Streams are created by QuicConnection.openStream() or accepted with QuicConnection.acceptStream().

import type { QuicConnection } from 'fino:net/quic/connection';
import type { QuicStream } from 'fino:net/quic/stream';

export async function send(connection: QuicConnection, data: Uint8Array) {
  const stream: QuicStream = await connection.openStream();
  await stream.writer.write(data);
  await stream.close();
}

Classes

class QuicStream extends EventTarget {

One QUIC stream multiplexed inside a QuicConnection.

Obtained from connection.openBidirectionalStream() / openUnidirectionalStream() or accepted with connection.acceptStream() / the 'stream' event; never constructed directly. A bidirectional stream has both a readable and a writable half; a unidirectional stream has only one, depending on which side opened it. Read and write through the byte-oriented reader/writer, or through the readable/writable Web Streams adapters. It extends EventTarget and emits 'reset', 'stopsending', and 'blocked'.

reset() abruptly terminates the sending side with an application error code; stopSending() asks the peer to stop sending on the receiving side. resetAt() resets after reliably delivering a prefix, when the ngtcp2 build supports it.

const stream = await conn.openBidirectionalStream();
await stream.writer.write(new TextEncoder().encode('ping'));
await stream.writer.close();
for await (const chunk of stream.readable) console.log(chunk.length);

Readonly Properties

readonly id: number

Numeric QUIC stream identifier, encoding initiator and directionality.

readonly direction: 'bidirectional' | 'unidirectional'

Whether the stream is bidirectional or send/receive-only.

readonly reader: BytesReader

Byte-oriented reader for the receiving half (closed on send-only streams).

readonly writer: QuicBytesWriter

Byte-oriented writer for the sending half (throws on receive-only streams).

Constructors

constructor( id: number, direction: 'bidirectional' | 'unidirectional', connection: QuicConnection, incoming = false )

Getters

get readable(): ReadableStream<Uint8Array>

Web ReadableStream view of the receiving half.

Lazily created and cached. Cancelling the stream issues stopSending(0). Prefer reader for lower-level byte access.

get writable(): WritableStream<Uint8Array>

Web WritableStream view of the sending half.

Lazily created and cached. Closing it sends FIN; aborting it resets the stream (with the abort reason as the error code when it is a number). Prefer writer for lower-level byte access.

get stats(): QuicStreamStats

Frozen snapshot of this stream's byte and offset counters.

Methods

reset(errorCode: number): void

Abruptly terminate the sending half with an application error code.

Sends RESET_STREAM to the peer; any unacknowledged outgoing data is discarded. Throws if the owning connection is already closed.

stream.reset(0x101); // abort with an application-defined code
resetAt(errorCode: number, finalSize: number | bigint): void

Reset the stream after reliably delivering bytes up to finalSize.

This requires ngtcp2 support for the QUIC reliable reset extension. Builds without that native symbol throw a clear unsupported error.

stopSending(errorCode: number): void

Ask the peer to stop sending on the receiving half.

Sends STOP_SENDING with the given application error code and closes the local receive side. No-op on a locally opened unidirectional stream (which has no receiving half). Throws if the owning connection is already closed.

stream.stopSending(0); // we no longer want incoming data

Types

type QuicStreamStats = { readonly createdAt: number; readonly openedAt: number | null; readonly receivedAt: number | null; readonly ackedAt: number | null; readonly destroyedAt: number | null; readonly bytesReceived: number; readonly bytesSent: number; readonly bytesAcked: number; readonly finalSize: number | null; readonly maxOffset: number; readonly maxOffsetAcked: number; readonly maxOffsetReceived: number; readonly maxOffsetSent: number; readonly bytesAccumulated: number; readonly maxBytesAccumulated: number; }

Per-stream byte and offset counters, read from QuicStream.stats.

Each read returns a frozen snapshot. finalSize is null until the stream's final size is known (a FIN was received or the stream was reset). The maxOffset* fields track how far each end of the stream has progressed, and bytesAccumulated/maxBytesAccumulated report current and peak buffered receive bytes so a slow reader's backlog is visible.