Following #65442, which made rejected request streams reset with H3_REQUEST_REJECTED, two more RFC 9114 stream-closure codes are still not emitted. Filing to agree on direction before implementing — one needs a small API decision.
1. Cancellation — H3_REQUEST_CANCELLED (0x10c)
When an endpoint gives up on a request it no longer wants (client aborting a request, or a server abandoning a response after starting it), the stream should be reset as cancelled. There are two gaps today:
- A cancel is two operations, with no single one. Fully cancelling a bidirectional stream means aborting both halves —
RESET_STREAM ("I will not send more") and STOP_SENDING ("do not send me more").
- The code is wrong. Both default to
0n (H3_NO_ERROR), so a cancel currently signals "no error" to the peer.
// Today: two calls, and both default to H3_NO_ERROR (0) — the peer is told "no error"
stream.resetStream();
stream.stopSending();
RFC 9114 §4.1.1: "Client SHOULD use the error code H3_REQUEST_CANCELLED to cancel requests", and a server abandoning a response after partial processing "SHOULD abort its response stream with the error code H3_REQUEST_CANCELLED." node neither defaults to 0x10c nor exposes it.
Proposal: a stream.cancel([reason]) method that resets both halves with H3_REQUEST_CANCELLED. Role-agnostic (client-cancel and server-abandon use the same code); the existing resetStream()/stopSending() stay as low-level primitives.
// Proposed: one call, resets both halves with H3_REQUEST_CANCELLED (0x10c)
stream.cancel();
This keeps the numeric code out of consumer code (e.g. a future h3 client in undici, #5471), matching how node:http2 offers stream.close(code) and how fetch/AbortController sits a layer above.
Alternative considered: expose a named H3_REQUEST_CANCELLED constant for the existing resets. Still two calls, and it pushes the code choice onto every caller:
// Alternative: still two calls, caller supplies the code via an exposed constant
stream.resetStream(<QUIC_CONSTANTS>.H3_REQUEST_CANCELLED);
stream.stopSending(<QUIC_CONSTANTS>.H3_REQUEST_CANCELLED);
2. Incomplete request — H3_REQUEST_INCOMPLETE (0x10d)
RFC 9114 §4.1: "If a client-initiated stream terminates without enough of the HTTP message to provide a complete response, the server SHOULD abort its response stream with the error code H3_REQUEST_INCOMPLETE."
This needs no API — the HTTP/3 layer can emit it when a request stream closes before a complete message. Today the teardown uses a generic code (H3_NO_ERROR or H3_INTERNAL_ERROR), so the client can't distinguish "the server errored" from "my request was incomplete." Emitting 0x10d puts the signal where it belongs.
Following #65442, which made rejected request streams reset with
H3_REQUEST_REJECTED, two more RFC 9114 stream-closure codes are still not emitted. Filing to agree on direction before implementing — one needs a small API decision.1. Cancellation —
H3_REQUEST_CANCELLED(0x10c)When an endpoint gives up on a request it no longer wants (client aborting a request, or a server abandoning a response after starting it), the stream should be reset as cancelled. There are two gaps today:
RESET_STREAM("I will not send more") andSTOP_SENDING("do not send me more").0n(H3_NO_ERROR), so a cancel currently signals "no error" to the peer.RFC 9114 §4.1.1: "Client SHOULD use the error code H3_REQUEST_CANCELLED to cancel requests", and a server abandoning a response after partial processing "SHOULD abort its response stream with the error code H3_REQUEST_CANCELLED." node neither defaults to
0x10cnor exposes it.Proposal: a
stream.cancel([reason])method that resets both halves withH3_REQUEST_CANCELLED. Role-agnostic (client-cancel and server-abandon use the same code); the existingresetStream()/stopSending()stay as low-level primitives.This keeps the numeric code out of consumer code (e.g. a future h3 client in undici, #5471), matching how
node:http2offersstream.close(code)and howfetch/AbortControllersits a layer above.Alternative considered: expose a named
H3_REQUEST_CANCELLEDconstant for the existing resets. Still two calls, and it pushes the code choice onto every caller:2. Incomplete request —
H3_REQUEST_INCOMPLETE(0x10d)RFC 9114 §4.1: "If a client-initiated stream terminates without enough of the HTTP message to provide a complete response, the server SHOULD abort its response stream with the error code H3_REQUEST_INCOMPLETE."
This needs no API — the HTTP/3 layer can emit it when a request stream closes before a complete message. Today the teardown uses a generic code (
H3_NO_ERRORorH3_INTERNAL_ERROR), so the client can't distinguish "the server errored" from "my request was incomplete." Emitting0x10dputs the signal where it belongs.