Skip to content

quic: is stream priority compatible to w3c webtransport sendgroups ? #63654

Description

@martenrichter

Currently, when calling createBidirectionalStream you can specify a priority (same also for unidirectional stream):

priority {string} The priority level of the stream. One of 'high', 'default', or 'low'. Default: 'default'.

The W3C specification for webtransport defines a sendGroup mechanism; see https://www.w3.org/TR/webtransport/#sendGroup.

For me, this is not the most important mechanism (but I can not tell for others), but I just wanted to bring it to your attention (@jasnell) and ask if it is worth thinking about an implementation that aligns with this spec, as the workgroup has put much thought into it.

Currently, I think it is a very different approach.

Activity

  1. added
    quicIssues and PRs related to the QUIC transport implementation.
    on Jun 10, 2026
  2. SparshGarg999 commented on Jul 12, 2026

    @SparshGarg999
    Contributor

    Hi! I've opened a PR to align this: #64454.

    I introduced the numeric sendOrder option to align Node's experimental QUIC stream priority semantics with W3C WebTransport priority semantics:

    • sendOrder accepts an integer in the range [0, 7], representing the raw QUIC stream urgency value (where lower value = higher priority).
    • Passed as an option during stream creation (via createBidirectionalStream or createUnidirectionalStream) or set via setPriority(), it overrides the default string-based priority level.
    • The priority getter is updated to return sendOrder alongside level and incremental properties, exposing the exact urgency level of the stream.
    • Validates the range and type of sendOrder throwing ERR_OUT_OF_RANGE or ERR_INVALID_ARG_TYPE respectively.

    I verified the implementation by updating the HTTP/3 stream priority test suite in test/parallel/test-quic-h3-priority.mjs. All tests and ESLint formatting rules pass 100% cleanly.

  3. martenrichter commented on Jul 12, 2026

    @martenrichter
    ContributorAuthor

    @SparshGarg999
    But this lacks the sendGroups. And think it should be something handled upstream in ngtcp2.
    And sendOrder in W3C is not limited to {0,7].

  4. SparshGarg999 commented on Jul 12, 2026

    @SparshGarg999
    Contributor

    Hi! I opened a PR (#64454) to try to map sendOrder to the raw QUIC urgency parameter, but after discussion with reviewers, I've closed it.

    Since WebTransport's sendOrder is a signed 64-bit integer (with opposite priority direction) and requires sendGroups (which ngtcp2 doesn't expose yet), full alignment is a much larger architectural change that needs to be handled upstream in ngtcp2 first.

  5. added
    webtransportIssues and PRs related to the WebTransport API.
    on Aug 8, 2026
  6. pimterry commented on Sep 4, 2026

    @pimterry
    Member

    Coming back to this after it was mentioned in the QUIC meeting yesterday: I think we can actually remove some priority API to simplify things first, and then I think webtransport vs HTTP/3 priorities are actually different (opposite) things anyway: priority signalling vs local scheduling. I think there's a reasonable path to clean it up and support this all nicely, suggestion at the bottom.


    Context first:

    For HTTP/3, a client can tell the server the priority of a request with a header (priority: u=0) or with PRIORITY_UPDATE frames. We support priority and incremental options for signalling the initial priority of a request, but these are currently broken and have never worked, because they try to send a PRIORITY_UPDATE frame before the stream is open and ignore the error. In reality, this should just set the header, but I don't think we need a special option for that anyway - people can set a header by themselves, which also avoids awkward questions like conflicting options { headers: { priority: 'u=9' }, priority: 'high' }. HTTP libs on top (universal server fetch) can implement any priority API they like (with a single API that covers H1 + H2 + H3 somehow) and we can don't have to care. We should drop this option I think.

    We have setPriority too, for changing headers on requests in flight. This should indeed use PRIORITY_UPDATE frames on the client side. Nice to have, but niche, browsers apparently support it but use it very rarely, just to bump priority on requests like images that scroll into view while pending. Current name sounds a bit like it sets local state instead of sending a frame, which is a bit odd imo.

    For webtransport though, priority is a totally different thing, I didn't realise but it's local priority only - unlike HTTP/3 priorities, it doesn't send anything to the remote peer. There is no WT priority signalling mechanism to implement. The APIs sound the same, but they're opposite directions: h3ReqStream.setPriority('high') tells the remote peer to respond ASAP (but changes nothing locally). Hypothetical wtStream.setPriority('high') would tell our local packet scheduler to send our data ASAP (and tells the remote peer nothing). They shouldn't really use the same API. In theory you could even want to do both in different directions: sending a request with high local scheduling but low response priority (e.g. error reporting: put this on the wire ASAP, but I don't care about the response).

    For QUIC and all datagrams, we control scheduling fully ourselves, for H3/WT, it's managed by nghttp3. Today, nghttp3 scheduling on servers mirrors the request priority by default (can override with nghttp3_conn_set_server_stream_priority), but for clients there's no API to control outbound scheduling at all. That means if you set a client stream to high priority with nghttp3, it signals to the remote peer, but local outbound scheduling priority stays the same. For WT, in the current draft it seems you can't control WT scheduling at all, it's hardcoded.

    Imo we should change this upstream: propose a single 'set scheduling priority' API that you can call on any nghttp3 stream, including requests and WT (subsuming nghttp3_conn_set_server_stream_priority). Plumbing is all there, just needs an API to expose it.

    For webtransport, since this isn't actually part of the protocol, Node can expose any form of API we like - we can match w3c if we want to, but it's just a JS API and we presumably won't perfectly match the rest of their API anyway so it doesn't make a huge difference.

    Would be nice to have the same local scheduling API for QUIC too though - the concept & motivation for QUIC & WT scheduling is identical, no reason not to support both.


    How about we aim towards:

    • Drop the priority & incremental signalling options. In H3 requests, just set the header manually. Everywhere else it's impossible to signal priority.
    • Keep setPriority (maybe rename to sendPriorityUpdate to clarify?) for priority changes, but only support it for HTTP/3 requests. Servers can't send PRIORITY_UPDATE, QUIC/WT have no signalling.
    • Add a localPriority option and setLocalPriority() method (names to bikeshed) for local scheduling of a stream - how outbound traffic is prioritised.
    • Use that to support local scheduling with one API that works the same everywhere: H3, WT, QUIC.
      • This implies nghttp3 changes, since scheduling is currently impossible for H3 clients & all WT.
      • For H3 received request streams, the default is the signalled remote priority, for everything else it's some fixed middle value.
    • For the actual format of localPriority to control the local schedule: I think this depends a lot on nghttp3. If that sticks to the H3 format (urgency 0-7 + incremental flag) for WT as well (seems likely) then the easy option is to do the same in Node, and expose that one format everywhere. Is that OK? It's not as granular as the W3C option (2^64 order ids + groups) but it should cover the essentials and it's simple. We could extend this backward compatibly in future with extra fields if we really need to (and if nghttp3 agrees).

    All in, that gives us a single API (localPriority) for local scheduling in the same way everywhere, and a specific h3-client-only API (sendPriorityUpdate) for the "send a priority frame" signalling, which is only actually possible in that one scenario.

    What do you think @nodejs/quic? Next step would be to discuss with nghttp3 to expose scheduling there.

  7. martenrichter commented on Sep 4, 2026

    @martenrichter
    ContributorAuthor

    Forgot to link this in the morning ngtcp2/nghttp3#559 I have asked upstream.

  8. jasnell commented on Sep 4, 2026

    @jasnell
    Member

    I think this is fine for the short term. I'm not entirely happy with the rather unsophisticated way we currently handle send priority in the SendPendingData loop for non-h3 applications, tho. I do think it's important that we have the ability to experiment with different send prioritization schemes for things that aren't h3 so as long as we are still working towards that, I'm good. (Restricting the public supported APIs in the interim is a good approach to ensuring we don't box ourselves into a corner)

  9. martenrichter commented on Sep 5, 2026

    @martenrichter
    ContributorAuthor

    Ok, I have asked upstream. It currently is not on the agenda. Best would be to have no SendGroups in the moment, but to make it in a way that we can extend it later, if sendgroups prove to be something worth to implement.

    Edit: discussion with upstream is not final. But it will be wt only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    quicIssues and PRs related to the QUIC transport implementation.webtransportIssues and PRs related to the WebTransport API.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions