quic: emit RFC 9114 stream-closure error codes (H3_REQUEST_CANCELLED, H3_REQUEST_INCOMPLETE)
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 122k
- Forks
- 37.3k
- Avg merge
- 4d 2h
- Merged PRs (30d)
- 283
Description
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") andSTOP_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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the QUIC HTTP/3 stream teardown paths described in the issue and review RFC 9114 sections 4.1 and 4.1.1. First resolve whether a combined stream.cancel([reason]) API is wanted, then ensure cancellation uses H3_REQUEST_CANCELLED and incomplete client request streams use H3_REQUEST_INCOMPLETE; add or update coverage for both behaviors.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, nodejs
- Domain
- networking
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100