moonbitstack/moonquic/conn does not have a README file

    Refused

    pub(all) suberror Refused {
    Malformed(String)
    } derive(Eq)

    A malformed transport-parameter block: a truncated parameter, or a length running past the block. decode raises it rather than answering with an empty list, so a caller cannot mistake a broken block for a peer that declared nothing.
    impl Show for Refused

    Refused::equal

    fn Refused::equal(Refused, Refused) -> Bool

    Refused::not_equal

    fn Refused::not_equal(x : Refused, y : Refused) -> Bool

    Refused::output

    fn Refused::output(self : Refused, logger : &Logger) -> Unit

    Refused::to_string

    fn Refused::to_string(self : Refused) -> String

    Client

    pub struct Client {
    tls :
    Conn

    original_dcid : Bytes
    scid : Bytes
    dcid : Bytes
    server_scid : Bytes
    token : Bytes
    retry_scid : Bytes?
    retried : Bool
    versions : Array[UInt]
    offered : Array[UInt]
    version : UInt
    sent_version : UInt
    constants :
    Version

    salt_dcid : Bytes
    negotiated : Bool
    initial_rx_other :
    Keys
    ?
    initial_tx :
    Keys

    initial_rx :
    Keys

    hs_tx :
    Keys
    ?
    hs_rx :
    Keys
    ?
    app_tx :
    Keys
    ?
    app_rx :
    Keys
    ?
    suite :
    Suite

    core : Conn
    app_crypto_at : UInt64
    initial_gone : Bool
    handshake_gone : Bool
    seen_server_cid : Bool
    sender : Sender
    streams :
    Streams

    events : Array[Event]
    datagrams : Array[Bytes]
    key_phase : Bool
    rx_phase : Bool
    app_tx_secret : Bytes
    app_rx_secret : Bytes
    phase_start : Int64
    confirmed : Bool
    next_uni : UInt64
    zero_rtt_tx :
    Keys
    ?
    zero_sent : Bool
    zero_rejected : Bool
    reset_token : Bytes?
    offered_ids : Array[(UInt64, Bytes, Bytes)]
    dcid_seq : UInt64
    reset_tokens : Array[Bytes]
    phase : Phase
    close_at : Int64
    close_frame :
    Frame
    ?
    close_level : Level
    last_rx : Int64
    last_tx : Int64
    idle_timeout : Int64
    at : Int64
    cfg : Config
    host : String
    sessions :
    Sessions
    ?
    wall : Int64
    peer_params : Bytes
    // private fields
    }

    A client connection mid-handshake.

    Client::add_path

    fn Client::add_path(self : Client, path : UInt64, dcid : Bytes, lcid : Bytes, now : Int64, nonce~ : Bytes, token~ : Bytes) -> Bool

    Start a second path from this client (draft-ietf-quic-multipath §3.1): the caller names the peer's connection id for it, the id this end will answer to on it, and eight bytes for the PATH_CHALLENGE. The datagrams that follow are handed back by poll_paths, tagged with the path they belong on, because which socket each goes out of is the caller's to know.

    Client::alpn

    fn Client::alpn(self : Client) -> String?

    The ALPN protocol the handshake settled on.

    Client::buffered

    fn Client::buffered(self : Client, id : UInt64) -> UInt64

    How many bytes written to stream id the server has not acknowledged.

    Client::close

    fn Client::close(self : Client, error_code : UInt64, reason : Bytes, now : Int64, application? : Bool) -> Unit

    Close the connection (RFC 9000 §10.2), in the shape Session::close has. An application's close before 1-RTT keys exist cannot name its own error code, so it goes as a transport close carrying APPLICATION_ERROR instead (RFC 9000 §10.2.3).

    Client::early_accepted

    fn Client::early_accepted(self : Client) -> Bool

    Whether the server accepted the early data this client offered.

    Client::finished

    fn Client::finished(self : Client, id : UInt64) -> Bool

    Whether stream id has ended and every byte of it has been read.

    Client::install_zero_rtt

    fn Client::install_zero_rtt(self : Client, keys :
    Keys
    ) -> Unit

    Install the client's 0-RTT (early) keys (RFC 9001 §5.2) by hand. A client given a ticket and early=true has them from its handshake already; this is for keys that came from elsewhere.

    Client::is_connected

    fn Client::is_connected(self : Client) -> Bool

    Whether the handshake has completed.

    Client::negotiated_version

    fn Client::negotiated_version(self : Client) -> UInt

    The QUIC version this connection settled on. It can differ from the version the first flight went out in: a server that converted that flight (RFC 9368 §2.3) answers in its own preferred version, which this client reads off the long headers and adopts.

    Client::new

    fn Client::new(config :
    Client
    , server_name : String, params : Bytes, rand : (Int) -> Bytes, dcid : Bytes, scid : Bytes, wall : Int64, cfg? : Config, resumption? : Remembered) -> Client raise

    A client for server_name, with config holding its roots and ALPN protocols. dcid is the connection id the Initial keys derive from (RFC 9001 §5.2) and scid the one this client calls itself; both are the caller's, so a test can make them reproducible.

    Client::next_cid

    fn Client::next_cid(self : Client) -> Bool

    Start addressing the server by the next id it offered, and retire the one in use. Do this before sending from a new address on purpose, so the two paths cannot be linked (RFC 9000 §9.5). False when it has offered none.

    Client::open_stream

    fn Client::open_stream(self : Client, id : UInt64) -> Bool

    Open a bidirectional stream this end initiates, without putting anything on it. A caller that places a stream's frames on a path of its own choosing — send_on — has to say the stream exists first: RFC 9000 §19.8 lets a peer answer only on a bidirectional stream this end opened, and the send loop's record of having opened it is what that turns on. Answers false for a stream this end may not initiate, which is a unidirectional one or the peer's own.

    Client::open_uni

    fn Client::open_uni(self : Client) -> UInt64

    A new unidirectional stream from this client (RFC 9000 §2.1).

    Client::path_lane

    fn Client::path_lane(self : Client, path : UInt64) -> Lane[Unit]?

    The sending state of path path, or None before anything has been sent on it.

    Client::paths

    fn Client::paths(self : Client) -> Paths

    This connection's paths, path 0 being the one the handshake ran on.

    Client::phase

    fn Client::phase(self : Client) -> Phase

    Where this connection is in its lifecycle.

    Client::poll_out

    fn Client::poll_out(self : Client, now : Int64) -> Array[Bytes]

    The datagrams to put on the wire: the queued CRYPTO bytes split so no Initial packet runs past a datagram, each Initial padded to the 1200-byte floor (RFC 9000 §14.1), the early data the send loop has ready, then 1-RTT traffic once the handshake is done.

    Client::poll_paths

    fn Client::poll_paths(self : Client, now : Int64) -> Array[(UInt64, Bytes)]

    The datagrams the further paths have ready, each with the path it belongs on: what a lost packet carried, then a probe a path owes. A client's caller sends each on the socket that path runs over.

    Client::probe

    fn Client::probe(self : Client, now : Int64) -> Array[Bytes]

    The datagrams to put on the wire for one more attempt at a deadlocked handshake: a PING in a padded Initial, which a server with no amplification budget left can still answer.

    Client::read

    fn Client::read(self : Client, id : UInt64) -> Bytes

    The bytes readable on stream id in order, emptied by the call; their flow-control credit goes back to the server here.

    Client::recv

    fn Client::recv(self : Client, datagram : Bytes, now : Int64) -> Unit raise

    Take a datagram from the server: split it into packets, unprotect each at the level its header names, reassemble its CRYPTO stream, and drive the handshake with what becomes contiguous. A server's first datagram may coalesce packets; the keys one brings are installed before the next is read.

    Client::recv_on

    fn Client::recv_on(self : Client, path : UInt64, datagram : Bytes, now : Int64) -> Unit raise

    Take a datagram that arrived on path path. A client has no address of its own — its caller owns the sockets — so the path a datagram belongs to is something only the caller knows, and it says so here (draft-ietf-quic-multipath §2.3). The path picks the packet-number space and the nonce the packet was sealed with.

    Client::remembered

    fn Client::remembered(self : Client) -> Remembered?

    The ticket this connection received and the parameters it was issued under, for the next connection to resume from. None before the handshake is done, when the server issued no ticket, or when the client was configured without a session store to keep one in.

    Client::send_datagram

    fn Client::send_datagram(self : Client, data : Bytes) -> Bool

    Queue an unreliable datagram (RFC 9221), answering whether the server will take it.

    Client::send_on

    fn Client::send_on(self : Client, path : UInt64, frames : Array[
    Frame
    ], now : Int64) -> Bool

    Put frames in one 1-RTT packet on path. Path 0 goes out the connection's own way and is picked up by poll_out; any other path is numbered, paced and recovered by its own lane.

    Client::server_cid

    fn Client::server_cid(self : Client) -> Bytes

    The connection id the client addresses the server by — the server's, once it has named it.

    Client::suite_code

    fn Client::suite_code(self : Client) -> Int

    The TLS code of the cipher suite in force, 0 before one is chosen.

    Client::take_datagrams

    fn Client::take_datagrams(self : Client) -> Array[Bytes]

    The datagrams that arrived since the last call, emptied by it.

    Client::take_events

    fn Client::take_events(self : Client) -> Array[Event]

    What has happened on this connection since the last call, emptied by it.

    Client::tick

    fn Client::tick(self : Client, now : Int64) -> Unit

    Run the timers at now (RFC 9002 §6.2): the probe timeout on each handshake level, the send loop's own, the idle timeout, the closing period, and the probe a client owes a server it cannot hear — the last of which is what keeps a handshake from wedging when the server's amplification budget is spent (RFC 9002 §6.2.2.1).

    Client::update_keys

    fn Client::update_keys(self : Client) -> Bool

    Move to the next key phase (RFC 9001 §6). The answer is false when it is not allowed yet: before the handshake is confirmed, or before something written in the current phase has been acknowledged, which is what stops an endpoint outrunning its peer's two sets of keys (§6.1, §6.2).

    Client::versions

    fn Client::versions(self : Client) -> Array[UInt]

    The versions a Version Negotiation packet offered, empty when none came. A non-empty answer means this attempt is over: RFC 9000 §6.2 leaves the caller to start another with one of them.

    Client::write

    fn Client::write(self : Client, id : UInt64, data : Bytes, fin? : Bool) -> Unit

    Queue data on stream id, ending it after the data when fin. Before the handshake finishes, data queued here leaves in 0-RTT packets when a ticket brought early keys, and the send loop carries it over to 1-RTT if the server refuses them (RFC 9000 §4.6.2).

    ClientEndpoint

    pub struct ClientEndpoint {
    conns : Map[Bytes, Client]
    ids : Map[Bytes, Bytes]
    cfg : Config
    rand : (Int) -> Bytes
    }

    A set of client connections sharing an id space. It holds no socket: recv takes what a socket read, poll_out gives back what to write, and tick runs every connection's timers.

    ClientEndpoint::client

    fn ClientEndpoint::client(self : ClientEndpoint, id : Bytes) -> Client?

    A connection by the id connect answered.

    ClientEndpoint::connect

    fn ClientEndpoint::connect(self : ClientEndpoint, config :
    Client
    , server_name : String, params : Bytes, wall : Int64, dcid? : Bytes, scid? : Bytes, cfg? : Config) -> Bytes raise

    Start a connection to server_name, answering the id this endpoint knows it by. scid is the id this client calls itself; left out, it is drawn from rand. dcid is what the first Initial is addressed to; left out, it is drawn too, since nothing has named it yet.

    ClientEndpoint::count

    fn ClientEndpoint::count(self : ClientEndpoint) -> Int

    How many connections this endpoint holds.

    ClientEndpoint::ids

    fn ClientEndpoint::ids(self : ClientEndpoint) -> Array[Bytes]

    The ids of the connections this endpoint holds.

    ClientEndpoint::new

    fn ClientEndpoint::new(cfg : Config, rand~ : (Int) -> Bytes) -> ClientEndpoint

    An endpoint that holds no connections yet. rand is where a connection's own id comes from when the caller does not pick one.

    ClientEndpoint::poll_out

    fn ClientEndpoint::poll_out(self : ClientEndpoint, now : Int64) -> Array[Bytes]

    Every datagram every connection has ready, in connection order. A caller loops until it is empty, sending each to the address it connected to.

    ClientEndpoint::recv

    fn ClientEndpoint::recv(self : ClientEndpoint, datagram : Bytes, now : Int64) -> Unit raise

    Take a datagram from any of this endpoint's servers and hand it to the connection its Destination Connection ID names. One that names nothing here is dropped: another endpoint's traffic is not this one's to answer.

    ClientEndpoint::remove

    fn ClientEndpoint::remove(self : ClientEndpoint, id : Bytes) -> Unit

    Forget a connection, as after it has closed and drained.

    ClientEndpoint::tick

    fn ClientEndpoint::tick(self : ClientEndpoint, now : Int64) -> Unit

    Run every connection's timers.

    Config

    pub(all) struct Config {
    versions : Array[UInt]
    idle_timeout : Int64
    close_period : Int64
    max_datagram : Int64
    max_ack_delay : Int64
    granularity : Int64
    initial_max_data : UInt64
    initial_max_stream_data : UInt64
    max_frame : UInt64
    streams_bidi : UInt64
    streams_uni : UInt64
    max_datagram_frame_size : UInt64
    connection_ids : UInt64
    zero_rtt_data : UInt64
    zero_rtt_stream_data : UInt64
    retry : Bool
    max_path_id : UInt64
    }

    What a server gives each new connection: the QUIC versions it speaks, the idle timeout it advertises, how long a closing or draining connection lingers, and the datagram size, timer tuning and flow-control credit the send loop starts from. Every duration is in microseconds, the unit the recovery code measures in.

    Config::default

    fn Config::default() -> Config

    QUIC version 1, a 30-second idle timeout, a 3-second closing period, 1200-byte datagrams, a 25-millisecond ACK delay, a mebibyte of flow-control credit on the connection and on each stream, and a hundred streams of each kind at a time, as HTTP/2 servers commonly allow.

    Conn

    A QUIC connection's per-space numbering/ACK state and per-level CRYPTO streams.

    Conn::build_ack

    fn Conn::build_ack(self : Conn, level : Level, ack_delay : UInt64) ->
    Frame
    ?

    Build the ACK frame owed at level, or None if nothing has been received there.

    Conn::crypto_stream

    fn Conn::crypto_stream(self : Conn, level : Level) ->
    Assembler

    The CRYPTO reassembler for level.

    Conn::is_server

    fn Conn::is_server(self : Conn) -> Bool

    Whether this endpoint is the server.

    Conn::new

    fn Conn::new(is_server : Bool) -> Conn

    A fresh connection (client or server) with empty spaces and CRYPTO streams.

    Conn::next_packet_number

    fn Conn::next_packet_number(self : Conn, level : Level) -> Int64

    Allocate the next packet number to send at level.

    Conn::on_ack_received

    fn Conn::on_ack_received(self : Conn, level : Level, largest : Int64) -> Unit

    Record the peer's acknowledgement of up to largest at level.

    Conn::on_crypto_frame

    fn Conn::on_crypto_frame(self : Conn, level : Level, offset : UInt64, data : Bytes) -> Bytes raise
    Refused

    Feed a CRYPTO frame's data at offset and level into that level's handshake stream, returning the newly contiguous TLS handshake bytes now readable (empty while an earlier gap is still outstanding). CRYPTO streams carry no fin.

    Conn::on_packet_received

    fn Conn::on_packet_received(self : Conn, level : Level, pn : Int64, ack_eliciting : Bool) -> Unit

    Record that a packet numbered pn arrived at level; ack_eliciting marks one that must be acknowledged.

    Conn::paths

    fn Conn::paths(self : Conn) -> Int

    How many paths the connection numbers packets for, path 0 among them.

    Conn::space

    The packet-number space for level.

    Conn::space_on

    fn Conn::space_on(self : Conn, path : UInt64, level : Level) ->
    Space

    The packet-number space for level on path path. Only Application has one per path: the handshake runs on path 0, and the draft gives 0-RTT and 1-RTT a space for each path id (draft-ietf-quic-multipath §2.3). Path 0 is the connection's own space.

    Endpoint

    pub struct Endpoint[A] {
    conns : Map[Bytes, Session[A]]
    cfg : Config
    // private fields
    }

    A QUIC server: the connections it is holding, keyed by the connection id peers address each by, and the settings a new one starts from. A is the peer-address type — a socket address under the native event loop, whatever a test finds convenient under a fake sink.

    Endpoint::add_path

    fn[A] Endpoint::add_path(self : Endpoint[A], cid : Bytes, path : UInt64, peer : A, dcid : Bytes, now : Int64, nonce~ : Bytes) -> Bool

    Open a second path on connection cid (draft-ietf-quic-multipath §3.1). This endpoint makes the connection id the path answers to, so a packet for it routes here like any other id the connection holds, and the peer is told which id to use on that path.

    Endpoint::close

    fn[A] Endpoint::close(self : Endpoint[A], cid : Bytes, error_code : UInt64, reason : Bytes, now : Int64) -> Unit

    Close the connection cid addresses with error_code and reason, if the server holds one (RFC 9000 §10.2).

    Endpoint::conn

    fn[A] Endpoint::conn(self : Endpoint[A], cid : Bytes) -> Session[A]?

    The connection cid addresses, or None if the server is not holding one.

    Endpoint::conn_ids

    fn[A] Endpoint::conn_ids(self : Endpoint[A]) -> Array[Bytes]

    The connection ids the server is holding.

    Endpoint::count

    fn[A] Endpoint::count(self : Endpoint[A]) -> Int

    How many connections the server is holding.

    Endpoint::new

    fn[A] Endpoint::new(cfg : Config) -> Endpoint[A]

    A server holding no connections, handing cfg to each one it opens.

    Endpoint::poll_out

    fn[A] Endpoint::poll_out(self : Endpoint[A], now : Int64) -> (Bytes, A)? raise

    The next datagram to put on the wire and the peer to send it to, or None when no connection has one ready.

    Endpoint::recv

    fn[A : Eq] Endpoint::recv(self : Endpoint[A], datagram : Bytes, from : A, now : Int64, wall? : Int64) -> Unit raise

    Take a datagram received at now from from: route it to the connection its Destination Connection ID names, opening one when an Initial packet arrives for an id the server does not hold, and drive that connection with it (RFC 9000 §5.2). A datagram naming no connection is dropped — RFC 9000 §10.3's stateless reset is not implemented.

    now is the timers' clock, in microseconds. wall is the time in seconds since the Unix epoch that tickets and client certificates are judged at, a different clock: left out, this endpoint neither issues nor takes a ticket.

    Endpoint::server

    fn[A] Endpoint::server(cfg : Config, tls :
    Server
    , rand~ : (Int) -> Bytes, cid? : () -> Bytes, address? : (A) -> Bytes) -> Endpoint[A]

    A server that runs the TLS handshake itself: tls holds its certificate chain, key and ALPN protocols, and rand is the source of its handshake randomness.

    cid makes the connection ids this server issues, cid_size bytes each; by default they are drawn from rand. A server behind something that routes datagrams by connection id (several processes on one port, or a load balancer in the manner of QUIC-LB) puts its own number in them here. An id that is not cid_size bytes, or that is already in use, is drawn again.

    Endpoint::take_busy

    fn[A] Endpoint::take_busy(self : Endpoint[A]) -> Array[Session[A]]

    The connections with something to tell their application, each once, emptied by the call.

    Endpoint::tick

    fn[A] Endpoint::tick(self : Endpoint[A], now : Int64) -> Unit

    Run the timers at now. An idle connection is forgotten without a CONNECTION_CLOSE, which is what RFC 9000 §10.1 asks for; a closing or draining one is forgotten when its period ends (§10.2); an active one whose probe timeout has come re-queues its oldest outstanding packet, to go out on the next poll_out (RFC 9002 §6.2.4).

    Event

    pub(all) enum Event {
    Connected
    Readable(UInt64)
    Reset(UInt64, UInt64)
    Stopped(UInt64, UInt64)
    Closed(UInt64)
    Datagram
    } derive(Eq,
    Debug
    )

    Something a connection has to tell its application.

    Event::equal

    fn Event::equal(Event, Event) -> Bool

    Event::not_equal

    fn Event::not_equal(x : Event, y : Event) -> Bool

    Event::to_repr

    Kind

    pub(all) enum Kind {
    OriginalDcid
    MaxIdleTimeout
    StatelessResetToken
    MaxUdpPayload
    MaxData
    MaxStreamDataBidiLocal
    MaxStreamDataBidiRemote
    MaxStreamDataUni
    MaxStreamsBidi
    MaxStreamsUni
    AckDelayExponent
    MaxAckDelay
    DisableActiveMigration
    PreferredAddress
    ActiveConnectionIdLimit
    InitialScid
    RetryScid
    MaxDatagramFrameSize
    InitialMaxPathId
    VersionInformation
    Other(UInt64)
    } derive(Eq,
    Debug
    )

    A transport parameter's identifier (RFC 9000 §18.2, and RFC 9221's datagram extension). Other carries anything else: the block is read and written generically, so a parameter this build has no name for survives a round trip intact.

    Kind::code

    fn Kind::code(self : Kind) -> UInt64

    The identifier on the wire.

    Kind::equal

    fn Kind::equal(Kind, Kind) -> Bool

    Kind::not_equal

    fn Kind::not_equal(x : Kind, y : Kind) -> Bool

    Kind::of

    fn Kind::of(code : UInt64) -> Kind

    The parameter a wire identifier names.

    Kind::server_only

    fn Kind::server_only(self : Kind) -> Bool

    Whether only a server may send this parameter (RFC 9000 §18.2's closing paragraph). A client that sends one is a TRANSPORT_PARAMETER_ERROR.

    Kind::to_repr

    Lane

    One path's sending state. space is the path's packet-number space, which is the same object the connection's core numbers that path's received packets in, so a number is used once whichever end asks. recovery is this path's alone: a path that loses a packet says nothing about the congestion on another.

    Lane::can_send

    fn[A] Lane::can_send(self : Lane[A], bytes : Int64) -> Bool

    Whether bytes more may go on this path without exceeding its congestion window.

    Lane::due

    The frames waiting to go out again on this path: what a lost packet carried, then a PING if a probe is owed and nothing else waits.

    Lane::id

    fn[A] Lane::id(self : Lane[A]) -> UInt64

    The path id this state belongs to.

    Lane::in_flight

    fn[A] Lane::in_flight(self : Lane[A]) -> Int

    How many of this path's packets are outstanding.

    Lane::new

    fn[A] Lane::new(id : UInt64, peer : A, space :
    Space
    , recovery :
    State
    , max_datagram : Int64) -> Lane[A]

    A path's state with nothing on it yet.

    Lane::on_ack

    fn[A] Lane::on_ack(self : Lane[A], frame :
    Frame
    , now : Int64, ack_exponent : Int) -> Unit raise

    An acknowledgement for this path (draft-ietf-quic-multipath §4.1): what it settles leaves the flight, and what it declares lost is queued for this path. Answers with the bytes that are still outstanding, which is what the caller's window is read from.

    Lane::on_pto

    fn[A] Lane::on_pto(self : Lane[A], now : Int64) -> Bool

    The probe timeout fired at now: the two oldest packets still outstanding go out again as probes, and the timer backs off (RFC 9002 §6.2.4). Answers whether this path had anything to send, which is what tells a driver to call due.

    Lane::peer

    fn[A] Lane::peer(self : Lane[A]) -> A

    The address this path's datagrams go to.

    Lane::rebind

    fn[A] Lane::rebind(self : Lane[A], peer : A) -> Unit

    The address this path's datagrams go to, changed when the peer rebinds it (draft §5.1).

    Lane::recovery

    This path's round-trip estimate, loss detection and congestion window.

    Lane::send

    fn[A] Lane::send(self : Lane[A], now : Int64, frames : Array[
    Frame
    ], size : Int64) -> Int64?

    Number frames into a packet on this path at now, or None when its window is closed or its pacing bucket is empty. The caller seals the packet; this keeps the numbering and the record a lost packet is sent again from.

    Lane::space

    This path's packet-number space.

    Level

    pub(all) enum Level {
    Initial
    Handshake
    Application
    } derive(Eq,
    Debug
    )

    A QUIC encryption level / packet-number space (RFC 9001 §4.1.1). 0-RTT shares the Application space, so the three long-lived spaces are Initial, Handshake, and Application.

    Level::equal

    fn Level::equal(Level, Level) -> Bool

    Level::not_equal

    fn Level::not_equal(x : Level, y : Level) -> Bool

    Level::to_repr

    Limits

    pub(all) struct Limits {
    idle_timeout : UInt64
    udp_payload : UInt64
    data : UInt64
    stream_data_bidi_local : UInt64
    stream_data_bidi_remote : UInt64
    stream_data_uni : UInt64
    streams_bidi : UInt64
    streams_uni : UInt64
    ack_delay_exponent : UInt64
    max_ack_delay : UInt64
    active_connection_ids : UInt64
    migration : Bool
    datagram_frame : UInt64
    } derive(Eq,
    Debug
    )

    What a block says, with RFC 9000 §18.2's default for everything it leaves out.

    The integer parameters only; the connection IDs, the preferred address and the stateless reset token stay in the block, because they are identifiers and addresses rather than limits and the connection reads them where it needs them.

    Limits::equal

    fn Limits::equal(Limits, Limits) -> Bool

    Limits::new

    fn Limits::new(idle_timeout? : UInt64, udp_payload? : UInt64, data? : UInt64, stream_data_bidi_local? : UInt64, stream_data_bidi_remote? : UInt64, stream_data_uni? : UInt64, streams_bidi? : UInt64, streams_uni? : UInt64, ack_delay_exponent? : UInt64, max_ack_delay? : UInt64, active_connection_ids? : UInt64, migration? : Bool, datagram_frame? : UInt64) -> Limits

    Limits by name, each defaulting to RFC 9000 §18.2's.

    Limits::not_equal

    fn Limits::not_equal(x : Limits, y : Limits) -> Bool

    Limits::params

    fn Limits::params(self : Limits) -> Array[Param]

    The block these limits amount to, leaving out everything still at its default so a handshake carries only what it changes.

    Limits::read

    fn Limits::read(params : ArrayView[Param], base? : Limits) -> Limits

    The limits a block conveys, starting from base and applying what the block sets.

    A parameter whose value is not a well-formed varint is left at its default rather than refused: §18.1 makes that a connection error, and deciding to close is the connection's call, not this reader's.

    Limits::to_repr

    Param

    pub(all) struct Param {
    kind : Kind
    value : Bytes
    } derive(Eq,
    Debug
    )

    One transport parameter: what it is, and its value bytes. An integer parameter's value is a single varint — Param::int writes one and Param::as_int reads one.

    Param::as_int

    fn Param::as_int(self : Param) -> UInt64?

    The integer a varint-valued parameter carries, or None when its value is not exactly one well-formed varint.

    Param::equal

    fn Param::equal(Param, Param) -> Bool

    Param::flag

    fn Param::flag(kind : Kind) -> Param

    A parameter with no value, which is how the flag parameters are carried.

    Param::int

    fn Param::int(kind : Kind, value : UInt64) -> Param

    A parameter whose value is one varint (RFC 9000 §18.2).

    Param::not_equal

    fn Param::not_equal(x : Param, y : Param) -> Bool

    Param::to_repr

    Path

    pub struct Path {
    challenge : Bytes
    validated : Bool
    }

    One path's validation state: the challenge outstanding, if any, and whether the path has ever been proven.

    Path::challenge

    fn Path::challenge(self : Path) -> Bytes

    The challenge still outstanding, or empty when there is none.

    Path::check

    fn Path::check(self : Path, data : Bytes) -> Bool

    Whether a PATH_RESPONSE's data answers the outstanding challenge. The comparison runs over the whole challenge and accumulates rather than stopping at the first byte, so how long it takes says nothing about how much of the value was guessed. A response with no challenge outstanding, or of another length, never validates.

    Path::new

    fn Path::new() -> Path

    A path nothing has been sent on yet: no challenge, not validated.

    Path::reset

    fn Path::reset(self : Path) -> Unit

    Forget the path: no challenge outstanding and no proof, as after a connection moves to a path that has never answered.

    Path::start

    fn Path::start(self : Path, nonce : Bytes) -> Bytes

    Begin validating the path with the eight-byte nonce, answering the challenge to put in a PATH_CHALLENGE. A new challenge supersedes one still outstanding, and a path that was validated has to prove itself again.

    Path::validated

    fn Path::validated(self : Path) -> Bool

    Whether the path has been proven.

    PathState

    pub(all) struct PathState {
    id : UInt64
    dcid : Bytes
    lcid : Bytes
    validated : Bool
    challenge : Bytes
    available : Bool
    peer_seq : UInt64
    heard : UInt64
    sent : UInt64
    abandoned : Bool
    }

    One path: the id both ends use for it, the connection id traffic on it is addressed by, and what each end has said about it.

    Paths

    pub struct Paths {
    paths : Map[UInt64, PathState]
    peer_max : UInt64
    local_max : UInt64
    }

    The paths of one connection, path 0 being the one the handshake ran on.

    Paths::abandon

    fn Paths::abandon(self : Paths, id : UInt64) -> Bool

    Give up a path. Path 0 is the connection's and cannot go (draft-ietf-quic-multipath §3.1).

    Paths::allowed

    fn Paths::allowed(self : Paths, id : UInt64) -> Bool

    Whether id is a path id the peer may use without a new one being negotiated.

    Paths::bind

    fn Paths::bind(self : Paths, id : UInt64, dcid : Bytes) -> Unit

    Give the path id the connection id dcid and start it unvalidated: the first thing on it is a PATH_CHALLENGE (draft-ietf-quic-multipath §3.2).

    Paths::bind_local

    fn Paths::bind_local(self : Paths, id : UInt64, cid : Bytes) -> Bool

    Give path id the connection id this end answers to on it (draft-ietf-quic-multipath §3.2). A packet that arrives for that id belongs to this path and to its packet-number space.

    Paths::count

    fn Paths::count(self : Paths) -> Int

    How many paths the connection has, path 0 among them.

    Paths::free

    fn Paths::free(self : Paths) -> UInt64?

    The lowest path id nobody is using, if the peer's limit leaves room for one. None means a MAX_PATH_ID has to arrive before this connection can grow another path.

    Paths::get

    fn Paths::get(self : Paths, id : UInt64) -> PathState?

    The path with this id, if the connection has it.

    Paths::ids

    fn Paths::ids(self : Paths) -> Array[UInt64]

    The ids of the paths the connection has.

    Paths::local_cid

    fn Paths::local_cid(self : Paths, id : UInt64) -> Bytes

    The connection id this end answers to on path id, empty when it has none.

    Paths::local_max

    fn Paths::local_max(self : Paths) -> UInt64

    The largest path id this end will open or accept.

    Paths::new

    fn Paths::new(local_max? : UInt64) -> Paths

    A connection that has only the path its handshake ran on. local_max is what this end advertises as initial_max_path_id: zero means multipath is not offered at all.

    Paths::next_status

    fn Paths::next_status(self : Paths, id : UInt64) -> UInt64?

    The next status sequence number to put on a PATH_STATUS for id: the two states share one counter space for each path, and it only rises (draft-ietf-quic-multipath §4.3).

    Paths::on_max_path_id

    fn Paths::on_max_path_id(self : Paths, max : UInt64) -> Unit

    The peer's MAX_PATH_ID: it may only rise, and it is never below what its initial_max_path_id already said (draft-ietf-quic-multipath §4.5).

    Paths::on_peer_cid

    fn Paths::on_peer_cid(self : Paths, id : UInt64, seq : UInt64, cid : Bytes) -> Bool

    Take a connection id the peer offered for id (draft-ietf-quic-multipath §4.4), which is what this end addresses that path by from now on. An older sequence number than the one in use is a reordering, not news.

    The peer issues the id for a path before anything has been sent on it — that is what the id is for — so a path with no state yet is opened here rather than refused. Without that, a peer that offers its paths on its own initiative, which is what the draft's own example does, leaves the other end unable to open any of them.

    Paths::on_status

    fn Paths::on_status(self : Paths, id : UInt64, seq : UInt64, available : Bool) -> Bool

    Take the peer's PATH_STATUS for id: a sequence number at or below one already heard is a reordering, not news (draft-ietf-quic-multipath §4.4).

    Paths::on_validated

    fn Paths::on_validated(self : Paths, id : UInt64) -> Unit

    The peer answered a challenge on id.

    Paths::open

    fn Paths::open(self : Paths) -> Array[UInt64]

    The path ids that are carrying traffic: everything bound and not given up.

    Paths::openable

    fn Paths::openable(self : Paths, id : UInt64) -> Bool

    Whether id is a path this end may open: the peer said it would maintain that many paths, and this end is willing to as well (draft-ietf-quic-multipath §2.1).

    Paths::path_of

    fn Paths::path_of(self : Paths, cid : Bytes) -> UInt64

    The path a connection id belongs to, or 0 when no path has claimed it. Path 0's id is the connection's own, which the table already routes by.

    Paths::peer_cid

    fn Paths::peer_cid(self : Paths, id : UInt64) -> Bytes

    The connection id this end addresses path id by, empty when the path is not bound.

    Paths::peer_max

    fn Paths::peer_max(self : Paths) -> UInt64

    The largest path id the peer said it would accept.

    Paths::prove

    fn Paths::prove(self : Paths, id : UInt64, data : Bytes) -> Bool

    Whether data answers the challenge outstanding on path id. The comparison runs over every byte rather than stopping at the first difference, so how long it takes says nothing about how much of the value was right. A path with no challenge outstanding never proves.

    Paths::retire_peer_cid

    fn Paths::retire_peer_cid(self : Paths, id : UInt64, seq : UInt64) -> Bool

    The peer gave up sequence number seq on path id (draft-ietf-quic-multipath §4.5). The id in use on a path is the peer's to keep, so this only says it is no longer spare.

    Paths::start

    fn Paths::start(self : Paths, id : UInt64, nonce : Bytes) -> Bytes

    Begin proving path id with the eight-byte nonce (draft-ietf-quic-multipath §3.1): answers with the challenge to put in a PATH_CHALLENGE, which goes out on the path itself.

    Paths::validated

    fn Paths::validated(self : Paths, id : UInt64) -> Bool

    Whether the path has answered a challenge.

    Phase

    pub(all) enum Phase {
    Active
    Closing
    Draining
    } derive(Eq,
    Debug
    )

    Where a connection is in its lifecycle (RFC 9000 §10.2): exchanging packets, closing — this endpoint sent a CONNECTION_CLOSE and answers anything further with it — or draining, where the peer closed and nothing more goes out.

    Phase::equal

    fn Phase::equal(Phase, Phase) -> Bool

    Phase::not_equal

    fn Phase::not_equal(x : Phase, y : Phase) -> Bool

    Phase::to_repr

    Remembered

    pub(all) struct Remembered {
    session :
    Session

    params : Bytes
    }

    A ticket and the transport parameters the server declared when it issued it: what a client keeps from one connection to resume the next with early data.

    Sender

    pub struct Sender {
    sched :
    Scheduler

    recovery :
    State

    datagram_max : UInt64
    max_datagram : Int64
    next_pn : Int64
    ack_exponent : Int
    probes : Int
    // private fields
    }

    A connection's send state for one packet-number space.

    Sender::abandon

    fn Sender::abandon(self : Sender, id : UInt64, code : UInt64) -> Unit

    Stop sending on stream id: nothing queued or lost on it goes out from now on, and a RESET_STREAM tells the peer where it ended.

    Sender::advance_past

    fn Sender::advance_past(self : Sender, pn : Int64) -> Unit

    Skip the send counter past pn, so a packet this sender did not build — a driver's ACK-only packet in the same space — does not collide with a number it later assigns. A pn the sender is already past leaves it alone.

    Sender::buffered

    fn Sender::buffered(self : Sender, id : UInt64) -> UInt64

    How many bytes written to stream id the peer has not acknowledged yet.

    Sender::new

    fn Sender::new(initial_max_data : UInt64, initial_max_stream_data : UInt64, max_datagram_size : Int64, max_ack_delay : Int64, granularity : Int64, max_frame : UInt64) -> Sender

    A fresh sender: initial_max_data/initial_max_stream_data are the peer's advertised flow limits, max_datagram_size sizes the congestion window, max_ack_delay/granularity tune the timers (microseconds), and max_frame caps a STREAM frame's payload.

    Sender::on_ack

    fn Sender::on_ack(self : Sender, frame :
    Frame
    , now : Int64) -> Unit raise

    Process a received ACK frame at now: clear acknowledged packets from the recovery state and re-queue the contents of any packet the ACK reveals as lost, to be retransmitted by a later poll_send (RFC 9002 §6). Raises on a malformed ACK.

    Sender::on_max_data

    fn Sender::on_max_data(self : Sender, new_max : UInt64) -> Unit

    Raise the connection-wide send limit from a received MAX_DATA frame.

    Sender::on_max_stream_data

    fn Sender::on_max_stream_data(self : Sender, id : UInt64, new_max : UInt64) -> Unit

    Raise stream id's send limit from a received MAX_STREAM_DATA frame.

    Sender::on_peer_datagram

    fn Sender::on_peer_datagram(self : Sender, limit : UInt64) -> Unit

    Adopt the peer's max_datagram_frame_size (RFC 9221 §3). Zero means the peer will not receive datagrams, and queue_datagram refuses them.

    Sender::on_pto_timeout

    fn Sender::on_pto_timeout(self : Sender, now : Int64) -> Bool

    Handle the probe timeout firing at now (RFC 9002 §6.2.4). If the PTO deadline has passed with packets still outstanding, re-queue the contents of the two oldest packets that still hold any as probes (§6.2.4 allows two, so one lost does not cost another timeout) — a later poll_send puts them back on the wire — and back off the timer for the next arming. A packet that went out again at an earlier timeout stays outstanding until an ACK settles it, which is why the oldest alone will not do. Unlike loss detection, this neither declares packets lost nor reduces the congestion window. Returns whether a probe was armed.

    Sender::outstanding

    fn Sender::outstanding(self : Sender) -> Array[Int64]

    The packet numbers still outstanding (sent, not yet acknowledged or declared lost).

    Sender::paced_at

    fn Sender::paced_at(self : Sender, now : Int64) -> Int64?

    When the next datagram's worth of credit accrues, or None when it is there now. A driver that got None from poll_send arms a timer on this and comes back.

    Sender::pacing_rate

    fn Sender::pacing_rate(self : Sender) -> Double

    The rate this sender paces at, in bytes per microsecond; zero while it is unpaced.

    Sender::pending_datagrams

    fn Sender::pending_datagrams(self : Sender) -> Int

    How many datagrams are waiting for a packet.

    Sender::poll_send

    fn Sender::poll_send(self : Sender, now : Int64, zero? : Bool) -> (Int64, Array[
    Frame
    ])? raise

    The next packet to send at now, or None when the congestion window is closed or nothing is queued (RFC 9002 §7). Control frames go first, then one retransmission or one fresh chunk; the packet is recorded in the recovery state and its contents remembered for retransmission. A probe is sent whatever the window says, since it is how a sender whose whole window was lost finds out (RFC 9002 §7.5), and is a PING when there is nothing else to carry.

    Sender::queue_control

    fn Sender::queue_control(self : Sender, frame :
    Frame
    ) -> Unit

    Queue a control frame to go out reliably: it is sent again if the packet carrying it is lost.

    Sender::queue_datagram

    fn Sender::queue_datagram(self : Sender, data : Bytes) -> Bool

    Queue an unreliable datagram for the next 1-RTT packet, answering whether it was taken. It is refused when the peer advertised no datagram support, when it is larger than the peer accepts, or when it would not fit a datagram beside the header, tag and a small ACK. A datagram is never retransmitted.

    Sender::queue_fin

    fn Sender::queue_fin(self : Sender, id : UInt64) -> Unit

    Mark stream id finished.

    Sender::queue_stream

    fn Sender::queue_stream(self : Sender, id : UInt64, bytes : Bytes) -> Unit

    Queue bytes of application data to send on stream id, after anything queued before.

    Sender::requeue_all

    fn Sender::requeue_all(self : Sender) -> Unit

    Put everything still in flight back in the queue, for the case where a packet space's keys change under it: 0-RTT refused by the server has to go again as 1-RTT (RFC 9000 §4.6.2). The congestion state is left alone; those packets come out of flight when they are declared lost, so the window is not opened twice.

    Sender::window

    fn Sender::window(self : Sender) -> Int64

    The current congestion window (bytes).

    Server

    pub struct Server {
    dcid : Bytes
    version : UInt
    initial_keys :
    Keys

    reply_keys :
    Keys

    handshake :
    Handshake

    conn : Conn
    }

    A QUIC server connection mid-handshake: the Initial keys, the packet-number/CRYPTO bookkeeping, and the TLS handshake it is driving.

    Server::client_hello

    fn Server::client_hello(self : Server) -> Bytes

    The raw ClientHello the connection received (empty before one arrives): the source a server runs the ECDHE key_share from to derive its handshake secrets.

    Server::convert_to

    fn Server::convert_to(self : Server, version : UInt) -> Unit

    Convert the first flight into version (RFC 9368 §2.3): both directions' Initial keys are re-derived from the same connection id under that version's salt, and the long headers this endpoint writes from now on name that version. Version 1 and version 2 first flights differ only in these version-dependent constants, so nothing in the CRYPTO stream has to change. The caller keeps the old receive keys for Initial packets already on the wire.

    Server::handshake_state

    fn Server::handshake_state(self : Server) ->
    Server

    The current TLS handshake state.

    Server::initial_ack_pending

    fn Server::initial_ack_pending(self : Server) -> Bool

    Whether an ACK is owed in the Initial space.

    Server::negotiate

    Negotiate the ClientHello this connection received (RFC 8446 §4.1.1): the group and key share to run the ECDHE with, and the signature scheme to sign the CertificateVerify under, read off what the client offered. Raises the §6 alert for a ClientHello this build cannot serve, and returns Retry when the answer is a HelloRetryRequest.

    Server::new

    fn Server::new(dcid : Bytes, version? : UInt) -> Server

    A server connection for a client whose Destination Connection ID is dcid (the Initial secret and keys derive from it, RFC 9001 §5.2).

    Server::on_frames

    fn Server::on_frames(self : Server, level : Level, frames : Array[
    Frame
    ], pn : Int64) -> Array[
    Kind
    ] raise

    Drive an already-unprotected packet's frames at level, numbered pn: record the packet for acknowledgement and feed its CRYPTO frames into that level's reassembler, driving the handshake with whatever handshake bytes become contiguous. Returns the handshake message types processed. A driver that unprotected the packet itself — because it also wants the ACK and CONNECTION_CLOSE frames beside the CRYPTO — enters here rather than through receive_initial.

    Server::on_handshake_frames

    fn Server::on_handshake_frames(self : Server, frames : Array[
    Frame
    ], pn : Int64, client_hs_secret : Bytes) -> Bool raise

    Drive the client's Handshake-space frames, numbered pn, already unprotected: record the packet, reassemble its CRYPTO stream, verify any Finished in it against client_hs_secret over the transcript as it stands, then fold the messages in. Returns whether the handshake is now connected with a Finished that authenticated — the frame-level half of receive_handshake, for a driver holding the decrypted packet.

    Server::receive_handshake

    fn Server::receive_handshake(self : Server, packet : Bytes, client_hs_keys :
    Keys
    , client_hs_secret : Bytes) -> Bool raise

    Receive the client's Handshake-space flight — in this no-client-authentication path, the client Finished (RFC 8446 §4.4.4): unprotect the Handshake packet with the client handshake keys, reassemble its CRYPTO stream, verify the Finished verify_data against client_hs_secret over the transcript through the server Finished, then fold it in to advance the handshake to CONNECTED. Returns whether the handshake is now connected with a Finished that authenticated. Raises if the packet fails to authenticate.

    Server::receive_initial

    fn Server::receive_initial(self : Server, packet : Bytes) -> Array[
    Kind
    ] raise

    Receive a client Initial packet: unprotect it, record its number for acknowledgement, reassemble its CRYPTO frames, and drive the handshake with the contiguous handshake bytes. Returns the handshake message types processed. Raises if the packet fails to authenticate.

    Server::reply_as

    fn Server::reply_as(self : Server, version : UInt) -> Unit

    Answer under version from now on: the reply keys are re-derived from the same connection id under that version's salt (RFC 9369 §3.3), which is what a version negotiated compatibly does to a server's own Initial packets (RFC 9368 §2.3). The keys the client's packets arrive under are left alone, since it may still be sending the version it started with.

    Server::send_handshake_flight

    fn Server::send_handshake_flight(self : Server, server_hs_secret : Bytes, encrypted_extensions : Bytes, certificate : Bytes, verify : (Bytes) -> Bytes, out_dcid : Bytes, out_scid : Bytes, pn : Int64) -> Bytes raise

    Send the server's Handshake-space flight (RFC 8446 §4, RFC 9001 §5): encode EncryptedExtensions, Certificate, and CertificateVerify, fold them into the transcript in order, compute the server Finished as HMAC(finished_key, transcript hash throughCertificateVerify) over server_hs_secret, fold the Finished too, and advance the state past the whole flight (RECVD_CH → WAIT_FINISHED). The four messages form one contiguous CRYPTO stream, protected into a Handshake packet with the handshake-space keys @crypto.Keys::of(server_hs_secret[:]), addressed to out_dcid from out_scid at Handshake packet number pn. server_hs_secret is the server handshake traffic secret the key schedule derives once ECDHE completes.

    Server::send_server_hello

    fn Server::send_server_hello(self : Server, server_hello :
    Server
    , out_dcid : Bytes, out_scid : Bytes, pn : Int64) -> Bytes

    Build the server's ServerHello response: encode it, fold it into the transcript, and protect it into an Initial packet addressed to out_dcid (the client's source connection id) from out_scid, at Initial packet number pn (RFC 9001 §5.3). The state stays at RECVD_CH — the server flight is not complete until the Handshake-space messages (EncryptedExtensions, Certificate, CertificateVerify, Finished) are sent by send_handshake_flight, which advances it to WAIT_FINISHED.

    Server::transcript_hash

    fn Server::transcript_hash(self : Server) -> Bytes

    The transcript hash over the handshake messages processed so far.

    Session

    pub struct Session[A] {
    peer : A
    cid : Bytes
    peer_cid : Bytes
    version : UInt
    first_version : UInt
    constants :
    Version

    initial_rx_other :
    Keys
    ?
    core : Server
    sender : Sender
    cfg : Config
    out : Array[Bytes]
    received : Array[(Level,
    Frame
    )]
    rx : Int64
    tx : Int64
    packets : Int
    validated : Bool
    last_rx : Int64
    idle_timeout : Int64
    phase : Phase
    close_at : Int64
    close_frame :
    Frame
    ?
    close_level : Level
    hs_rx :
    Keys
    ?
    hs_rx_secret : Bytes
    hs_tx :
    Keys
    ?
    app_rx :
    Keys
    ?
    app_tx :
    Keys
    ?
    streams :
    Streams

    path : Path
    zero_rtt_rx :
    Keys
    ?
    // private fields
    }

    One server-side connection: the peer it answers, the connection ids the two ends address each other by, the handshake and send state machines it drives, and the counters the amplification limit, the idle timer and the closing period are read from. A is the peer-address type the enclosing Endpoint was built with.

    Session::add_path

    fn[A] Session::add_path(self : Session[A], path : UInt64, peer : A, dcid : Bytes, lcid : Bytes, now : Int64, nonce~ : Bytes, token~ : Bytes) -> Bool

    Start a second path: give it path, address its datagrams to peer by the peer's dcid, and answer to lcid on it (draft-ietf-quic-multipath §3.1). A PATH_CHALLENGE goes out on the path at once, and the peer is told which connection id to use for it, so the two ends can tell the path's packets from another's. Answers false when the peer offered no room for that path id, or the handshake is not done.

    Session::alpn

    fn[A] Session::alpn(self : Session[A]) -> String?

    The ALPN protocol the handshake settled on.

    Session::apply_peer_params

    fn[A] Session::apply_peer_params(self : Session[A], params : Array[Param]) -> Unit

    Apply the peer's transport parameters (RFC 9000 §18): the idle timer settles at the smaller of the two ends' values, and the peer's initial flow-control credit opens the connection-wide send window, which §18.2 says is a MAX_DATA arriving at once.

    Session::buffered

    fn[A] Session::buffered(self : Session[A], id : UInt64) -> UInt64

    How many bytes written to stream id the peer has not acknowledged, which is what an application paces itself by.

    Session::can_send

    fn[A] Session::can_send(self : Session[A], n : Int) -> Bool

    Whether n more bytes may go to this peer: unrestricted once its address is validated, otherwise at most three times what it has sent this server (RFC 9000 §8.1).

    Session::cid

    fn[A] Session::cid(self : Session[A]) -> Bytes

    The connection id the peer addresses this server by — the table's key.

    Session::close

    fn[A] Session::close(self : Session[A], error_code : UInt64, reason : Bytes, now : Int64, application? : Bool) -> Unit

    Close the connection (RFC 9000 §10.2): put a CONNECTION_CLOSE carrying error_code and reason on the wire at the highest level with keys, drop whatever else was queued, and enter the closing period — during which every packet the peer sends is answered with the same CONNECTION_CLOSE, and at the end of which tick forgets the connection. Closing a connection that is already closing or draining does nothing.

    Session::core

    fn[A] Session::core(self : Session[A]) -> Server

    The handshake state machine underneath, for a caller driving the TLS flights.

    Session::finished

    fn[A] Session::finished(self : Session[A], id : UInt64) -> Bool

    Whether stream id has ended and every byte of it has been read.

    Session::idle_timeout

    fn[A] Session::idle_timeout(self : Session[A]) -> Int64

    The idle timeout in force, in microseconds; zero means none.

    Session::install_app_keys

    fn[A] Session::install_app_keys(self : Session[A], client_secret : Bytes, server_secret : Bytes) -> Unit

    Install the Application-space (1-RTT) keys, derived from the two application traffic secrets. Until they are installed, a short-header packet is dropped and the send loop has nowhere to put stream data.

    Session::install_handshake_keys

    fn[A] Session::install_handshake_keys(self : Session[A], client_secret : Bytes, server_secret : Bytes) -> Unit

    Install the Handshake-space keys, derived from the client's and this server's handshake traffic secrets (RFC 9001 §5.1). Until they are installed, a Handshake packet cannot be unprotected and is dropped; after, the connection reads and writes at that level. The client's secret is kept so an arriving Finished can be verified against it.

    Session::install_zero_rtt

    fn[A] Session::install_zero_rtt(self : Session[A], keys :
    Keys
    ) -> Unit

    Install the server's 0-RTT (early) keys (RFC 9001 §5.2), from the early traffic secret the handshake resumed a ticket for. Until they are installed, a 0-RTT packet is dropped.

    Session::largest_acked

    fn[A] Session::largest_acked(self : Session[A], level : Level) -> Int64?

    The largest packet number the peer has acknowledged at level, or None before its first ACK there.

    Session::negotiated_version

    fn[A] Session::negotiated_version(self : Session[A]) -> UInt

    The QUIC version this connection settled on, as its long headers and its own version_information name it (RFC 9368 §4). A server that converted a first flight may answer in a version other than the one the client sent it in.

    Session::open_uni

    fn[A] Session::open_uni(self : Session[A]) -> UInt64

    A new unidirectional stream from this server (RFC 9000 §2.1).

    Session::packets_received

    fn[A] Session::packets_received(self : Session[A]) -> Int

    Datagrams received on this connection.

    Session::path_lane

    fn[A] Session::path_lane(self : Session[A], path : UInt64) -> Lane[A]?

    The sending state of path path, or None before anything has been sent on it.

    Session::path_validated

    fn[A] Session::path_validated(self : Session[A]) -> Bool

    Whether the path the peer is on has answered a PATH_CHALLENGE (RFC 9000 §8.2). A fresh connection starts unvalidated; the handshake's own return traffic validates the path it arrived on, and a move to a new address starts again.

    Session::paths

    fn[A] Session::paths(self : Session[A]) -> Paths

    This connection's paths (draft-ietf-quic-multipath §3): path 0 is the handshake's, and any others are the ones the two ends have agreed to keep.

    Session::peer

    fn[A] Session::peer(self : Session[A]) -> A

    The peer this connection answers.

    Session::pending

    fn[A] Session::pending(self : Session[A]) -> Int

    Datagrams built and waiting to go out — non-zero while the amplification limit or a closed congestion window is holding them back.

    Session::phase

    fn[A] Session::phase(self : Session[A]) -> Phase

    Where this connection is in its lifecycle.

    Session::poll_lane

    fn[A] Session::poll_lane(self : Session[A], now : Int64) -> (Bytes, A)?

    The next datagram on a path other than the connection's own, with the address it belongs on, or None when no path has anything waiting. What a lost packet carried goes first, then a probe the path owes.

    Session::queue_stream

    fn[A] Session::queue_stream(self : Session[A], id : UInt64, bytes : Bytes) -> Unit

    Queue bytes to send on stream id; the send loop frames and paces them out through poll_out once the Application keys are installed.

    Session::read

    fn[A] Session::read(self : Session[A], id : UInt64) -> Bytes

    The bytes readable on stream id in order, emptied by the call. Their flow-control credit goes back to the peer here.

    Session::release

    fn[A] Session::release(self : Session[A], id : UInt64) -> Unit

    The application is done with stream id: its slot goes back to the peer (RFC 9000 §4.6).

    Session::reset

    fn[A] Session::reset(self : Session[A], id : UInt64, code : UInt64) -> Unit

    Abandon sending on stream id with code.

    Session::rx_bytes

    fn[A] Session::rx_bytes(self : Session[A]) -> Int64

    Bytes received from the peer, the numerator of the amplification limit.

    Session::send

    fn[A] Session::send(self : Session[A], level : Level, frames : Array[
    Frame
    ]) -> Unit

    Queue frames to go out at level on the next poll_out, numbered from that level's packet-number space. An Initial packet carrying anything ack-eliciting takes its datagram to the 1200-byte floor (RFC 9000 §14.1); a level whose keys are not installed drops the frames, since there is nothing to protect them with.

    Session::send_datagram

    fn[A] Session::send_datagram(self : Session[A], data : Bytes) -> Bool

    Queue an unreliable application datagram (RFC 9221), answering whether it was taken. It is refused when the peer did not advertise datagram support or when it would not fit.

    Session::send_level

    fn[A] Session::send_level(self : Session[A]) -> Level

    The highest encryption level this connection can send at — Application once its 1-RTT keys are in, else Handshake, else Initial, which is always available.

    Session::send_on

    fn[A] Session::send_on(self : Session[A], path : UInt64, frames : Array[
    Frame
    ], now : Int64) -> Bool

    Put frames in one 1-RTT packet on path: numbered from that path's space, paced and gated by that path's congestion window, and sealed with the nonce its id names (draft-ietf-quic-multipath §2.4). Path 0 keeps the connection's own send loop, so its packets are numbered and recorded there. Answers whether a packet went out.

    Session::server_name

    fn[A] Session::server_name(self : Session[A]) -> String?

    The host name the client asked for by SNI.

    Session::stop

    fn[A] Session::stop(self : Session[A], id : UInt64, code : UInt64) -> Unit

    Ask the peer to stop sending on stream id with code.

    Session::suite_code

    fn[A] Session::suite_code(self : Session[A]) -> Int

    The TLS code of the cipher suite in force, 0 before one is chosen.

    Session::take_datagrams

    fn[A] Session::take_datagrams(self : Session[A]) -> Array[Bytes]

    The datagrams that arrived since the last call, emptied by it, in arrival order. A datagram is whole; it is not a byte stream and nothing is retransmitted.

    Session::take_events

    fn[A] Session::take_events(self : Session[A]) -> Array[Event]

    What has happened on this connection since the last call, emptied by it.

    Session::take_received

    fn[A] Session::take_received(self : Session[A]) -> Array[(Level,
    Frame
    )]

    Take the frames received since the last call, each with the level it arrived at — where an application layer reads its streams out of.

    Session::tx_bytes

    fn[A] Session::tx_bytes(self : Session[A]) -> Int64

    Bytes sent to the peer, the quantity the amplification limit caps.

    Session::validated

    fn[A] Session::validated(self : Session[A]) -> Bool

    Whether the peer's address has been validated (RFC 9000 §8.1), which lifts the amplification limit.

    Session::write

    fn[A] Session::write(self : Session[A], id : UInt64, data : Bytes, fin? : Bool) -> Unit

    Queue data on stream id, and end the stream after it when fin.

    Token

    pub struct Token {
    address : Bytes
    odcid : Bytes
    }

    What a token says: the address it was issued for, and the connection id the first Initial named (the Retry's tag and the Initial keys are bound to it).

    amplification

    let amplification : Int64

    The anti-amplification factor (RFC 9000 §8.1): until it has validated a peer's address, a server may send it at most this many times the bytes it has received from it.

    cid_size

    let cid_size : Int

    How long a connection id this server issues is. A fixed length is what lets a short header, which does not say how long its id is, be read at all (RFC 9000 §5.2).

    decode

    fn decode(b : BytesView) -> Array[Param] raise Refused

    Decode a block, or raise Refused when a parameter is truncated. The strict face of decode_opt, and what the connection reads a peer's block through.

    encode

    fn encode(params : ArrayView[Param]) -> Bytes

    Encode a block: each parameter as its identifier varint, a length varint, then its value (RFC 9000 §18).

    extension

    The extension a block rides in. RFC 9001 §8.2 makes it mandatory for both endpoints, and moontls carries it without reading it, because the contents are QUIC's.

    find

    fn find(params : ArrayView[Param], kind : Kind) -> Param?

    The parameter in a block, or None. A repeated parameter is a connection error, so the first one is what counts.

    idle_timeout

    fn idle_timeout(ours : Int64, theirs : Int64) -> Int64

    The idle timeout in force on a connection (RFC 9000 §10.1): the smaller of the two endpoints' advertised max_idle_timeouts, where zero on either side means that end asks for no limit at all.

    is_stateless_reset

    fn is_stateless_reset(packet : BytesView, token : BytesView) -> Bool

    Whether packet is a stateless reset carrying token: a short-header-form datagram of at least 21 bytes whose last sixteen bytes are the token.

    issue_token

    fn issue_token(key : Bytes, address : Bytes, odcid : Bytes, now : Int64) -> Bytes

    The token for a client at address whose first Initial named odcid, stamped with now. An endpoint that cannot name an address passes an empty one, and the token then binds the connection id and the time alone.

    limits

    let limits : Limits

    RFC 9000 §18.2's defaults: no idle timeout, the largest UDP payload the protocol permits, no flow-control credit and no streams until the peer says otherwise, an acknowledgement delay exponent of three, twenty-five milliseconds of acknowledgement delay, two connection IDs, migration allowed, and no datagram frames.

    max_path_id

    let max_path_id : UInt64

    The largest path id a peer may name: it is folded into the packet nonce, which is twelve bytes wide, so the id has to fit in the low 32 bits (draft-ietf-quic-multipath §3.1).

    min_datagram

    let min_datagram : Int

    The smallest a datagram carrying an ack-eliciting Initial packet may be (RFC 9000 §14.1).

    of_extensions

    fn of_extensions(extensions : ArrayView[
    Ext
    ]) -> Array[Param]

    The block a TLS extension list carries (RFC 9001 §8.2), empty when the extension is absent or its block is truncated: an endpoint with no block falls back to RFC 9000 §18.2's defaults, which is what an empty list reads as.

    read_token

    fn read_token(key : Bytes, token : Bytes, now : Int64, address : Bytes) -> Token?

    What a token says, or None when it is short, of another format, tagged with another key, or older than token_life. now is the same clock the token was stamped from.

    read_version_information

    fn read_version_information(value : BytesView) -> (UInt, Array[UInt])?

    The versions a version_information value carries: the chosen one and the list. None when the value is shorter than one version or not a whole number of them, which is what RFC 9368 §4 makes a connection error.

    recv_long

    The frames a protected long-header packet carries, and its packet number, expanded against largest, the largest number received in its space. None when the tag does not match, which is a packet to drop rather than an error to report.

    recv_short

    fn recv_short(packet : Bytes, dcid_len : Int, keys :
    Keys
    , largest? : Int64, path? : UInt64) -> (Array[
    Frame
    ], Int64)? raise
    Refused

    The same for a 1-RTT packet, whose connection-id length only the receiver knows.

    reset_token

    fn reset_token(key : Bytes, cid : Bytes) -> Bytes

    The sixteen-byte stateless reset token for cid, derived from the endpoint's key. The same pair always yields the same token, which is what lets an endpoint answer for a connection it has already forgotten.

    seal_long

    fn seal_long(kind :
    Kind
    , version : UInt, dcid : Bytes, scid : Bytes, number : Int64, size : Int, payload : Bytes, keys :
    Keys
    , token? : Bytes) -> Bytes

    The same from a payload already encoded, which is what padding an Initial to the minimum datagram needs (RFC 9000 §14.1).

    send_long

    fn send_long(kind :
    Kind
    , version : UInt, dcid : Bytes, scid : Bytes, number : Int64, size : Int, frames : Array[
    Frame
    ], keys :
    Keys
    , token? : Bytes) -> Bytes

    A protected long-header packet carrying frames at this level.

    The three lines it stands for — build the header, seal the payload, protect the header — are worth writing out once to see, and worth not writing out every time after that. @packet.Long::header and @crypto.Keys::seal are still there for a sender that wants the pieces.

    send_short

    fn send_short(dcid : Bytes, number : Int64, size : Int, frames : Array[
    Frame
    ], keys :
    Keys
    , spin? : Bool, key_phase? : Bool, path? : UInt64) -> Bytes

    A protected 1-RTT packet carrying frames.

    send_zero_rtt

    fn send_zero_rtt(dcid : Bytes, scid : Bytes, number : Int64, size : Int, frames : Array[
    Frame
    ], keys :
    Keys
    , version? : UInt) -> Bytes

    A protected 0-RTT packet carrying frames (RFC 9001 §5.2): a long header of the 0-RTT type, no Initial token, the client's early keys, and the Application packet-number space.

    signed

    fn signed(signer : &
    Signer
    , scheme? : Int) -> ((Bytes) -> Bytes)

    A CertificateVerify body signed over the transcript as RFC 8446 §4.4.3 frames it.

    This is the seam that keeps a signing key out of this library: a flight takes a function from the transcript hash to a CertificateVerify body, and whoever holds the key builds one with this.

    stateless_reset

    fn stateless_reset(token : Bytes, rand : (Int) -> Bytes, min? : Int) -> Bytes

    A stateless reset carrying token: a short-header-looking packet of at least min bytes whose last sixteen are the token (RFC 9000 §10.3). The bytes before the token are random, so two resets for the same connection do not look alike.

    version_information

    fn version_information(chosen : UInt, available : ArrayView[UInt]) -> Param

    A version_information parameter (RFC 9368 §3): the version chosen, then the versions the sender would accept, four bytes each. Both sides send one, which is what makes a version negotiation the peer cannot forge: the list arrives under the handshake's own authentication.