@@ -358,6 +358,29 @@ delivered out of order. The [`session.ondatagramstatus`][] callback reports
358358whether each sent datagram was ` 'acknowledged' ` , ` 'lost' ` , or ` 'abandoned' `
359359(never sent on the wire).
360360
361+ #### HTTP/3 datagrams
362+
363+ On HTTP/3 sessions, datagrams are associated with an individual request stream
364+ rather than with the session as a whole. Send and receive them per-stream with
365+ [ ` stream.sendDatagram() ` ] [ ] and [ ` stream.ondatagram ` ] [ ] , passing and receiving
366+ your application payload directly; the protocol framing that binds a datagram to
367+ its stream is handled internally and never visible to your code. Both peers must
368+ set [ ` application.enableDatagrams ` ] [ ] to ` true ` .
369+
370+ The session-level datagram API does not apply to HTTP/3 sessions:
371+ [ ` session.sendDatagram() ` ] [ ] throws ` ERR_INVALID_STATE ` and
372+ [ ` session.ondatagram ` ] [ ] never fires. (Only non-HTTP/3 ALPNs use the raw,
373+ session-level datagrams described above.)
374+
375+ Because datagrams are unordered with respect to stream data, a datagram is only
376+ delivered while its associated stream still exists on the receiver; one that
377+ arrives for an unknown or already-closed stream is dropped.
378+
379+ Under the hood, each HTTP/3 datagram ([ RFC 9297] [ ] ) is carried in a QUIC
380+ ` DATAGRAM ` frame prefixed with a _ Quarter Stream ID_ (the request stream's id
381+ divided by four) that binds it to the stream. This prefix is added and stripped
382+ automatically; most applications never need to think about it.
383+
361384### 0-RTT early data and session resumption
362385
363386QUIC supports 0-RTT early data, allowing a client that has previously connected
@@ -1114,7 +1137,12 @@ added: v23.8.0
11141137
11151138* Type: {quic.OnDatagramCallback}
11161139
1117- The callback to invoke when a new datagram is received from a remote peer. Read/write.
1140+ The callback to invoke when a new raw datagram is received from a remote peer.
1141+ Read/write.
1142+
1143+ This is only used for non-HTTP/3 sessions. On HTTP/3 sessions datagrams are
1144+ bound to a request stream and delivered via [ ` stream.ondatagram ` ] [ ] instead;
1145+ this callback never fires. See [ Datagrams] [ ] .
11181146
11191147### ` session.ondatagramstatus `
11201148
@@ -1384,9 +1412,13 @@ added: v23.8.0
13841412 ** Default:** ` 'utf8' ` .
13851413* Returns: {Promise} for a {bigint} datagram ID.
13861414
1387- Sends an unreliable datagram to the remote peer, returning a promise for
1415+ Sends an unreliable raw datagram to the remote peer, returning a promise for
13881416the datagram ID.
13891417
1418+ This method is for non-HTTP/3 sessions only. On HTTP/3 sessions datagrams are
1419+ per-stream, so this method throws ` ERR_INVALID_STATE ` ; use
1420+ [ ` stream.sendDatagram() ` ] [ ] instead. See [ Datagrams] [ ] .
1421+
13901422If ` datagram ` is a string, it will be encoded using the specified ` encoding ` .
13911423
13921424If ` datagram ` is an ` ArrayBufferView ` , the bytes are copied into an
@@ -1403,12 +1435,6 @@ inherently unreliable).
14031435If the datagram payload is zero-length (empty string after encoding, detached
14041436buffer, or zero-length view), ` 0n ` is returned and no datagram is sent.
14051437
1406- For HTTP/3 sessions, the peer must advertise ` SETTINGS_H3_DATAGRAM=1 `
1407- (via ` application: { enableDatagrams: true } ` ) for datagrams to be sent.
1408- If the peer's setting is ` 0 ` , ` sendDatagram() ` returns ` 0n ` (per RFC 9297
1409- §3, an endpoint MUST NOT send HTTP Datagrams unless the peer indicated
1410- support).
1411-
14121438Datagrams cannot be fragmented — each must fit within a single QUIC packet.
14131439The maximum datagram size is determined by the peer's
14141440` maxDatagramFrameSize ` transport parameter (which the peer advertises during
@@ -2012,6 +2038,20 @@ continue using the still-active direction on a bidirectional stream),
20122038abort the other direction with [ ` writer.fail() ` ] [ ] , or tear down the
20132039whole stream with [ ` stream.destroy() ` ] [ ] . Read/write.
20142040
2041+ ### ` stream.ondatagram `
2042+
2043+ <!-- YAML
2044+ added: REPLACEME
2045+ -->
2046+
2047+ * Type: {quic.OnStreamDatagramCallback}
2048+
2049+ The callback invoked when a datagram associated with this stream is received.
2050+ It receives the datagram payload as a ` Uint8Array ` and a ` boolean ` indicating
2051+ whether it arrived as 0-RTT early data. Read/write.
2052+
2053+ Only applies to HTTP/3 sessions. See [ Datagrams] [ ] .
2054+
20152055### ` stream.headers `
20162056
20172057<!-- YAML
@@ -2146,6 +2186,29 @@ the [`stream.onwanttrailers`][] callback, or set ahead of time via
21462186[ ` stream.pendingTrailers ` ] [ ] . Throws ` ERR_INVALID_STATE ` if the session
21472187does not support headers.
21482188
2189+ ### ` stream.sendDatagram(datagram[, encoding]) `
2190+
2191+ <!-- YAML
2192+ added: REPLACEME
2193+ -->
2194+
2195+ * ` datagram ` {string|ArrayBufferView}
2196+ * ` encoding ` {string} The encoding to use if ` datagram ` is a string.
2197+ ** Default:** ` 'utf8' ` .
2198+ * Returns: {bigint} The datagram id, or ` 0n ` if the datagram was not sent.
2199+
2200+ Sends an unreliable datagram associated with this stream to the peer, where it
2201+ is delivered to the corresponding stream's [ ` stream.ondatagram ` ] [ ] callback. The
2202+ payload is sent as-is; the framing that binds it to the stream is handled
2203+ internally.
2204+
2205+ Delivery is best-effort: datagrams may be lost, reordered, or dropped. ` 0n ` is
2206+ returned (and the payload silently discarded) if the datagram cannot be sent.
2207+ The delivery status of a sent datagram is reported via the session's
2208+ [ ` session.ondatagramstatus ` ] [ ] callback.
2209+
2210+ Only applies to HTTP/3 sessions. See [ Datagrams] [ ] .
2211+
21492212### ` stream.priority `
21502213
21512214<!-- YAML
@@ -3745,6 +3808,16 @@ added: v23.8.0
37453808* ` this ` {quic.QuicStream}
37463809* ` error ` {any}
37473810
3811+ ### Callback: ` OnStreamDatagramCallback `
3812+
3813+ <!-- YAML
3814+ added: REPLACEME
3815+ -->
3816+
3817+ * ` this ` {quic.QuicStream}
3818+ * ` datagram ` {Uint8Array} The datagram payload.
3819+ * ` early ` {boolean} ` true ` if the datagram arrived as 0-RTT early data.
3820+
37483821### Callback: ` OnHeadersCallback `
37493822
37503823<!-- YAML
@@ -4424,6 +4497,7 @@ throughput issues caused by flow control.
44244497
44254498[Aborting a stream]: #aborting-a-stream
44264499[Callback error handling]: #callback-error-handling
4500+ [Datagrams]: #datagrams
44274501[JSON-SEQ]: https://www.rfc-editor.org/rfc/rfc7464
44284502[NSS Key Log Format]: https://udn.realityripple.com/docs/Mozilla/Projects/NSS/Key_Log_Format
44294503[Permission Model]: permissions.md#permission-model
@@ -4504,10 +4578,12 @@ throughput issues caused by flow control.
45044578[`sessionOptions.token`]: #sessionoptionstoken-client-only
45054579[`stream.destroy()`]: #streamdestroyerror-options
45064580[`stream.headers`]: #streamheaders
4581+ [`stream.ondatagram`]: #streamondatagram
45074582[`stream.onerror`]: #streamonerror
45084583[`stream.onwanttrailers`]: #streamonwanttrailers
45094584[`stream.pendingTrailers`]: #streampendingtrailers
45104585[`stream.priority`]: #streampriority
4586+ [`stream.sendDatagram()`]: #streamsenddatagramdatagram-encoding
45114587[`stream.sendHeaders()`]: #streamsendheadersheaders-options
45124588[`stream.sendInformationalHeaders()`]: #streamsendinformationalheadersheaders
45134589[`stream.sendTrailers()`]: #streamsendtrailersheaders
0 commit comments