Since QUIC-LY uses a single PN space and no crypto, you can keep this simple:
- Transport parameter
Add a peer-advertised max_datagram_frame_size to your QUIC-LY transport parameters.
If the peer omits it, treat DATAGRAM as unsupported and drop any incoming DATAGRAM frames (optionally log/qlog a warning).
- Frame types and encoding/decoding
Implement two frame types:
- DATAGRAM: payload runs to end of packet.
- DATAGRAM_WITH_LENGTH: includes a varint length then that many payload bytes (so you can carry several in one packet).
For both, payload is just bytes; no stream ID, no offset.
- Sender behavior
Only send if peer advertised support.
Enqueue datagrams in a best-effort queue.
When packetizing:
- Respect congestion control and PMTU.
- Prefer the “with length” variant when coalescing multiple datagrams; otherwise the no-length variant is smallest.
On send, mark the packet ack-eliciting and account bytes in flight. Do not schedule retransmission of the datagram payload (it’s fine to retransmit a later, fresh datagram if your app chooses).
- Receiver behavior
If a DATAGRAM frame is larger than your local receive limit, drop the frame (do not close the connection). Deliver what you accept directly to the application (unordered).
Generate ACKs at the packet level as usual (there’s no per-datagram acknowledgment).
- Recovery & timers
Packets that carry DATAGRAM frames are ack-eliciting → they drive ACK generation, RTT sampling, PTO, and loss detection just like packets with STREAM frames.
If a packet with a DATAGRAM is declared lost by your loss-detection rules, you simply forget that datagram (no retransmit). Congestion control reacts to the loss as usual.
PTO firing should probe with an ack-eliciting packet; if you have no STREAM data, a PING (optionally with PADDING) is fine—sending another DATAGRAM is also fine if your application has one ready.
- API surface
Once moving away from SimpleQuicConnection to QuiclyConnection, allow for starting streams and datagram channels separately. The streams will implement the Trio trio.abc.Stream API (just like SimpleQuicConnection is currently doing) and the datagram channels will implement trio.abs.Channle[bytes].
max_datagram_size_local, max_datagram_size_peer: expose limits to callers or maybe just the latter one?
- Spec text you can lift (QUIC-LY flavor)
“Endpoints that support unreliable messages advertise max_datagram_frame_size > 0. Endpoints MUST NOT send DATAGRAM frames to a peer that did not advertise support.”
“DATAGRAM frames are ack-eliciting and congestion-controlled. They are not retransmitted by the transport and are not subject to stream flow control.”
“Endpoints MUST discard DATAGRAM frames that exceed their configured maximum size; this is not a connection error.”
Consider also to amend spec with respect to idle_timeout = inf and no STREAM support, only DATAGRAM support: should timers be not used and only send ACK alongside other payload, at least in one direction? I.e., a curser-on-target application is mostly client -> server via UDP.
Since QUIC-LY uses a single PN space and no crypto, you can keep this simple:
Add a peer-advertised
max_datagram_frame_sizeto your QUIC-LY transport parameters.If the peer omits it, treat DATAGRAM as unsupported and drop any incoming DATAGRAM frames (optionally log/qlog a warning).
Implement two frame types:
For both, payload is just bytes; no stream ID, no offset.
Only send if peer advertised support.
Enqueue datagrams in a best-effort queue.
When packetizing:
On send, mark the packet ack-eliciting and account bytes in flight. Do not schedule retransmission of the datagram payload (it’s fine to retransmit a later, fresh datagram if your app chooses).
If a DATAGRAM frame is larger than your local receive limit, drop the frame (do not close the connection). Deliver what you accept directly to the application (unordered).
Generate ACKs at the packet level as usual (there’s no per-datagram acknowledgment).
Packets that carry DATAGRAM frames are ack-eliciting → they drive ACK generation, RTT sampling, PTO, and loss detection just like packets with STREAM frames.
If a packet with a DATAGRAM is declared lost by your loss-detection rules, you simply forget that datagram (no retransmit). Congestion control reacts to the loss as usual.
PTO firing should probe with an ack-eliciting packet; if you have no STREAM data, a PING (optionally with PADDING) is fine—sending another DATAGRAM is also fine if your application has one ready.
Once moving away from
SimpleQuicConnectiontoQuiclyConnection, allow for starting streams and datagram channels separately. The streams will implement the Triotrio.abc.StreamAPI (just like SimpleQuicConnection is currently doing) and the datagram channels will implementtrio.abs.Channle[bytes].max_datagram_size_local,max_datagram_size_peer: expose limits to callers or maybe just the latter one?“Endpoints that support unreliable messages advertise
max_datagram_frame_size > 0. Endpoints MUST NOT send DATAGRAM frames to a peer that did not advertise support.”“DATAGRAM frames are ack-eliciting and congestion-controlled. They are not retransmitted by the transport and are not subject to stream flow control.”
“Endpoints MUST discard DATAGRAM frames that exceed their configured maximum size; this is not a connection error.”
Consider also to amend spec with respect to idle_timeout = inf and no STREAM support, only DATAGRAM support: should timers be not used and only send ACK alongside other payload, at least in one direction? I.e., a curser-on-target application is mostly client -> server via UDP.