@@ -1138,7 +1138,7 @@ const { QuicError } = require('node:quic');
11381138```
11391139
11401140When a ` QuicError ` is supplied to APIs that emit a wire frame
1141- (currently [ ` writer.fail() ` ] [ ] ), the QUIC stack uses
1141+ ([ ` writer.fail() ` ] [ ] , [ ` stream.destroy ()` ] [ ] ), the QUIC stack uses
11421142[ ` error.errorCode ` ] [ ] as the wire code for the resulting frame.
11431143When any other value is supplied (for example a plain ` Error ` ), the
11441144implementation falls back to the negotiated application protocol's
@@ -1231,17 +1231,54 @@ an `ERR_QUIC_APPLICATION_ERROR` or `ERR_QUIC_TRANSPORT_ERROR` when the
12311231stream is closed due to a QUIC error (e.g., stream reset by the peer,
12321232CONNECTION\_ CLOSE with a non-zero error code).
12331233
1234- ### ` stream.destroy([error]) `
1234+ ### ` stream.destroy([error[, options] ]) `
12351235
12361236<!-- YAML
12371237added: v23.8.0
1238+ changes:
1239+ - version: REPLACEME
1240+ pr-url: https://github.com/nodejs/node/pull/62876
1241+ description: Added the `options` parameter accepting `code` and `reason`.
12381242-->
12391243
12401244* ` error ` {any}
1245+ * ` options ` {Object}
1246+ * ` code ` {bigint|number} The application error code to include in the
1247+ ` RESET_STREAM ` and ` STOP_SENDING ` frames sent to the peer. Numbers are
1248+ coerced to ` BigInt ` . When omitted, the wire code is derived from ` error `
1249+ (see below).
1250+ * ` reason ` {string} An optional human-readable reason string. Accepted for
1251+ symmetry with [ ` session.close() ` ] [ ] and [ ` session.destroy() ` ] [ ] , but
1252+ ** not transmitted on the wire** — neither ` RESET_STREAM ` nor
1253+ ` STOP_SENDING ` carry a reason field. Provided for application logging
1254+ and for use by the [ ` stream.onerror ` ] [ ] callback.
12411255
12421256Immediately and abruptly destroys the stream. If ` error ` is provided and
12431257[ ` stream.onerror ` ] [ ] is set, the ` onerror ` callback is invoked before
1244- destruction. The ` stream.closed ` promise will reject with the error.
1258+ destruction. The ` stream.closed ` promise rejects with the error.
1259+
1260+ When the stream is destroyed with an ` error ` (or with an explicit
1261+ ` options.code ` ), the QUIC stack signals the abort to the peer:
1262+
1263+ * If the writable side is still open, a ` RESET_STREAM ` frame is sent.
1264+ * If the readable side is still open (a bidirectional stream, or a
1265+ remote-initiated unidirectional stream), a ` STOP_SENDING ` frame is sent.
1266+
1267+ Both frames carry the same wire code, resolved with the following
1268+ precedence:
1269+
1270+ 1 . ` options.code ` , when explicitly provided.
1271+ 2 . [ ` error.errorCode ` ] [ ] , when ` error ` is a [ ` QuicError ` ] [ ] .
1272+ 3 . The negotiated application protocol's "internal error" code
1273+ (` H3_INTERNAL_ERROR ` (` 0x102 ` ) for HTTP/3, or the QUIC transport-layer
1274+ ` INTERNAL_ERROR ` (` 0x1 ` ) for raw QUIC).
1275+
1276+ A clean destroy — no ` error ` and no ` options.code ` — does not emit
1277+ ` RESET_STREAM ` or ` STOP_SENDING ` ; the stream's existing close machinery
1278+ handles teardown.
1279+
1280+ See [ Aborting a stream] [ ] for an overview of the available stream-abort
1281+ APIs.
12451282
12461283### ` stream.destroyed `
12471284
@@ -1253,6 +1290,28 @@ added: v23.8.0
12531290
12541291True if ` stream.destroy() ` has been called.
12551292
1293+ ### Aborting a stream
1294+
1295+ A QuicStream can be aborted in three ways, each producing different
1296+ wire-frame side effects:
1297+
1298+ * [ ` writer.fail(reason) ` ] [ ] — Aborts only the writable side. Sends
1299+ ` RESET_STREAM ` to the peer. The readable side is unaffected; any data
1300+ already buffered for read remains available.
1301+ * [ ` stream.destroy() ` ] [ ] with an ` error ` argument — Tears the stream
1302+ down completely. Sends ` RESET_STREAM ` on any still-open writable side
1303+ ** and** ` STOP_SENDING ` on any still-open readable side. The wire code
1304+ is derived from ` error ` (see [ ` stream.destroy() ` ] [ ] for the precedence
1305+ rules).
1306+ * [ ` stream.destroy() ` ] [ ] with an explicit ` options.code ` — Same as the
1307+ previous form but with a caller-supplied wire code, which takes
1308+ precedence over any code carried by ` error ` .
1309+
1310+ When ` error ` is a [ ` QuicError ` ] [ ] , its [ ` error.errorCode ` ] [ ] is used as
1311+ the wire code for both ` writer.fail() ` and ` stream.destroy() ` . Otherwise
1312+ the implementation falls back to the negotiated application protocol's
1313+ "internal error" code (see [ ` QuicError ` ] [ ] ).
1314+
12561315### ` stream.early `
12571316
12581317* Type: {boolean}
@@ -1332,7 +1391,19 @@ added: v23.8.0
13321391
13331392* Type: {quic.OnStreamErrorCallback}
13341393
1335- The callback to invoke when the stream is reset. Read/write.
1394+ The callback to invoke when the peer aborts a direction of the stream by
1395+ sending a ` RESET_STREAM ` frame (the peer abandons their writable side, so
1396+ no further data will arrive on our readable side) or a ` STOP_SENDING `
1397+ frame (the peer asks us to stop writing on our writable side).
1398+
1399+ The callback receives a Node.js error whose ` errorCode ` (` bigint ` )
1400+ property carries the application error code from the wire frame.
1401+
1402+ The stream is ** not** automatically destroyed when this callback fires —
1403+ the application chooses how to react. Common patterns are: ignore (and
1404+ continue using the still-active direction on a bidirectional stream),
1405+ abort the other direction with [ ` writer.fail() ` ] [ ] , or tear down the
1406+ whole stream with [ ` stream.destroy() ` ] [ ] . Read/write.
13361407
13371408### ` stream.headers `
13381409
@@ -1563,12 +1634,14 @@ The Writer has the following methods:
15631634* ` writev(chunks[, options]) ` — Async vectored write.
15641635* ` endSync() ` — Synchronous close. Returns total bytes or ` -1 ` .
15651636* ` end([options]) ` — Async close.
1566- * ` fail(reason) ` — Errors the stream (sends RESET \_ STREAM to peer).
1637+ * ` fail(reason) ` — Errors the stream (sends ` RESET_STREAM ` to peer).
15671638 When ` reason ` is a [ ` QuicError ` ] [ ] , its [ ` error.errorCode ` ] [ ] is used
1568- as the wire code on the resulting RESET \_ STREAM frame; otherwise
1639+ as the wire code on the resulting ` RESET_STREAM ` frame; otherwise
15691640 the wire code falls back to the negotiated application protocol's
15701641 "internal error" code (` H3_INTERNAL_ERROR ` (` 0x102 ` ) for HTTP/3, or
15711642 the QUIC transport-layer ` INTERNAL_ERROR ` (` 0x1 ` ) for raw QUIC).
1643+ See [ ` stream.destroy() ` ] [ ] for a full-stream abort that also resets
1644+ the readable side via ` STOP_SENDING ` .
15721645* ` desiredSize ` — Available capacity in bytes, or ` null ` if closed/errored.
15731646
15741647### ` stream.setBody(body) `
@@ -3251,6 +3324,7 @@ Published when a stream is flow-control blocked and cannot send data
32513324until the peer increases the flow control window. Useful for diagnosing
32523325throughput issues caused by flow control.
32533326
3327+ [Aborting a stream]: #aborting-a-stream
32543328[Callback error handling]: #callback-error-handling
32553329[JSON-SEQ]: https://www.rfc-editor.org/rfc/rfc7464
32563330[NSS Key Log Format]: https://udn.realityripple.com/docs/Mozilla/Projects/NSS/Key_Log_Format
@@ -3263,6 +3337,8 @@ throughput issues caused by flow control.
32633337[`fs.promises.open(path, ' r' )`]: fs.md#fspromisesopenpath-flags-mode
32643338[`quic.connect()`]: #quicconnectaddress-options
32653339[`quic.listen()`]: #quiclistencallback-options
3340+ [`session.close()`]: #sessioncloseoptions
3341+ [`session.destroy()`]: #sessiondestroyerror-options
32663342[`session.maxPendingDatagrams`]: #sessionmaxpendingdatagrams
32673343[`session.onerror`]: #sessiononerror
32683344[`session.onkeylog`]: #sessiononkeylog
@@ -3272,6 +3348,7 @@ throughput issues caused by flow control.
32723348[`sessionOptions.keylog`]: #sessionoptionskeylog
32733349[`sessionOptions.qlog`]: #sessionoptionsqlog
32743350[`sessionOptions.sni`]: #sessionoptionssni-server-only
3351+ [`stream.destroy()`]: #streamdestroyerror-options
32753352[`stream.onerror`]: #streamonerror
32763353[`stream.onwanttrailers`]: #streamonwanttrailers
32773354[`stream.pendingTrailers`]: #streampendingtrailers
@@ -3280,5 +3357,6 @@ throughput issues caused by flow control.
32803357[`stream.setPriority()`]: #streamsetpriorityoptions
32813358[`stream.writer`]: #streamwriter
32823359[`writer.fail()`]: #streamwriter
3360+ [`writer.fail(reason)`]: #streamwriter
32833361[qlog]: https://datatracker.ietf.org/doc/draft-ietf-quic-qlog-main-schema/
32843362[qvis]: https://qvis.quictools.info/
0 commit comments