nodejs / nodejs/node

quic: emit RFC 9114 stream-closure error codes (H3_REQUEST_CANCELLED, H3_REQUEST_INCOMPLETE)

Open
#65,509 2 comments 0 reactions 0 assignees View on GitHub

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") 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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.