RFC 9218

Extensible Prioritization Scheme for HTTP

πŸ“… published June 2022, from ietf

RFC 9218: Extensible Prioritization Scheme for HTTP πŸ’–

okay bestie, this one is all about how an HTTP client tells a server which responses it wants first πŸŽ€ It was published in June 2022 as a Standards Track document, so it's a real Internet standard πŸ”₯ The authors are K. Oku (ε₯₯ δΈ€η©‚) at Fastly and L. Pardue at Cloudflare 🫢 It's published as ISSN 2070-1721, and the title block doesn't list anything it obsoletes or updates, so it's a fresh start 🌸

Abstract ✨

okay so the big idea is a scheme that lets an HTTP client say how it wants the upstream server to prioritize responses to its requests πŸ’… It also lets a server hint to a downstream intermediary how its responses should be prioritized when they get forwarded πŸ¦‹ This doc defines the Priority header field for sharing the initial priority in a way that works no matter which HTTP version you're on πŸ’œ It also defines HTTP/2 and HTTP/3 frames for reprioritizing responses 🧚 All of these share one common format structure, which is designed so the whole thing can be extended later on 🌈

Status of This Memo

this is an Internet Standards Track document πŸŽ€

it's a product of the Internet Engineering Task Force (IETF) 🌸 it represents the consensus of the IETF community 🫢 it went through public review and was approved for publication by the Internet Engineering Steering Group (IESG) ✨ if you want more on Internet Standards, check out Section 2 of RFC 7841 πŸ“š

for the current status of this document, any errata, and how to send feedback on it, head to https://www.rfc-editor.org/info/rfc9218 πŸ”—

Copyright (c) 2022 IETF Trust and the persons identified as the document authors. All rights reserved. πŸ’–

this document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) as they stand on the date this document was published πŸ“œ please actually read these, because they spell out your rights and restrictions for this document πŸ™ˆ code components pulled out of this document must include Revised BSD License text, as described in Section 4.e of the Trust Legal Provisions, and they come without warranty, as described in the Revised BSD License βš–οΈ

Table of Contents 🧚

1.  Introduction
  1.1.  Notational Conventions
2.  Motivation for Replacing RFC 7540 Stream Priorities
  2.1.  Disabling RFC 7540 Stream Priorities
    2.1.1.  Advice when Using Extensible Priorities as the
            Alternative
3.  Applicability of the Extensible Priority Scheme
4.  Priority Parameters
  4.1.  Urgency
  4.2.  Incremental
  4.3.  Defining New Priority Parameters
    4.3.1.  Registration
5.  The Priority HTTP Header Field
6.  Reprioritization
7.  The PRIORITY_UPDATE Frame
  7.1.  HTTP/2 PRIORITY_UPDATE Frame
  7.2.  HTTP/3 PRIORITY_UPDATE Frame
8.  Merging Client- and Server-Driven Priority Parameters
9.  Client Scheduling
10. Server Scheduling
  10.1.  Intermediaries with Multiple Backend Connections
11. Scheduling and the CONNECT Method
12. Retransmission Scheduling
13. Fairness
  13.1.  Coalescing Intermediaries
  13.2.  HTTP/1.x Back Ends
  13.3.  Intentional Introduction of Unfairness
14. Why Use an End-to-End Header Field?
15. Security Considerations
16. IANA Considerations
17. References
  17.1.  Normative References
  17.2.  Informative References
Acknowledgements
Authors' Addresses

1. Introduction ✨

it's super common for representations of an HTTP [HTTP] resource to be related to one or more other resources πŸ¦‹ clients often find out about these relationships while they're processing a representation they already got, and that can kick off more retrieval requests πŸ” the nature of those relationships decides whether a client is blocked from carrying on with the stuff it already has locally 🧐 for example, the visual rendering of an HTML document could be blocked by fetching a Cascading Style Sheets (CSS) file that the document points to 🎨 inline images, on the other hand, don't block rendering and just get drawn bit by bit as their chunks arrive πŸ–ΌοΈ

HTTP/2 [HTTP/2] and HTTP/3 [HTTP/3] both support multiplexing requests and responses over a single connection 🌈 a big deal for any protocol that does multiplexing is being able to prioritize what gets sent first πŸ’… so, for instance, to show a meaningful HTML document as early as possible, it matters that an HTTP server prioritizes the HTTP responses, or the chunks of those responses, that it sends to a client πŸš€

HTTP/2 and HTTP/3 servers can schedule the transmission of concurrent response data by any means they choose πŸŽ€ servers can ignore client priority signals and still successfully serve HTTP responses πŸ™ƒ but servers that don't pay attention to how clients make requests and consume responses can cause suboptimal client application performance 😭 priority signals let clients share their view of request priority πŸ’– servers have their own needs that are separate from client needs, so they often mix priority signals with other available information to inform how they schedule response data 🧠

RFC 7540 [RFC7540] stream priority let a client send a series of priority signals that tell the server about a "priority tree" ✨ the structure of this tree shows the client's preferred relative ordering and the weighted split of bandwidth among HTTP responses 🌳 servers could use these signals as input when making prioritization decisions 🧚

the design and implementation of RFC 7540 stream priority were seen to have shortcomings, which Section 2 explains πŸ™ˆ HTTP/2 [HTTP/2] has since deprecated the use of these stream priority signals πŸ’€ the prioritization scheme and priority signals defined here can act as a substitute for RFC 7540 stream priority 🫢

this document describes an extensible scheme for prioritizing HTTP responses that uses absolute values πŸ’œ Section 4 defines priority parameters, which are a standardized and extensible format for priority information πŸŽ€ Section 5 defines the Priority HTTP header field, an end-to-end priority signal that doesn't depend on the protocol version 🌸 clients can send this header field to signal how they'd like responses prioritized πŸ’… servers sitting behind an intermediary can use it too, to signal priority to that intermediary πŸ¦‹ after sending a request, a client can change its view of response priority (see Section 6) by sending HTTP-version-specific frames, as defined in Sections 7.1 and 7.2 πŸ”

header field and frame priority signals are inputs to a server's response prioritization process 🚨 they're only a suggestion and don't guarantee any particular processing or transmission order for one response relative to another 🚨 Sections 10 and 12 give considerations and guidance about how servers might act on signals πŸ“–

1.1. Notational Conventions πŸ“

the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here πŸŽ€

this document uses the following terminology from Section 3 of [STRUCTURED-FIELDS] to specify syntax and parsing: "Boolean", "Dictionary", and "Integer" 🧩

example HTTP requests and responses use the HTTP/2-style formatting from [HTTP/2] πŸ–ŠοΈ

this document uses the variable-length integer encoding from [QUIC] πŸ”’

the term "control stream" describes both the HTTP/2 stream with identifier 0x0 and the HTTP/3 control stream, see Section 6.2.1 of [HTTP/3] 🌊

the term "HTTP/2 priority signal" describes the priority information sent from clients to servers in HTTP/2 frames, see Section 5.3.2 of [HTTP/2] πŸ’Œ

2. Motivation for Replacing RFC 7540 Stream Priorities πŸ’­

RFC 7540 stream priority (see Section 5.3 of [RFC7540]) is a complicated system where clients signal stream dependencies and weights to describe an unbalanced tree 🌲 it had limited deployment and interoperability problems, and it's been deprecated in a revision of HTTP/2 [HTTP/2] 😬 HTTP/2 keeps these protocol elements around to maintain wire compatibility (see Section 5.3.2 of [HTTP/2]) πŸ”Œ which means they might still get used even when there's alternative signaling, like the scheme this document describes πŸ§ƒ

a lot of RFC 7540 server implementations don't act on HTTP/2 priority signals πŸ₯²

prioritization can use info that servers have about resources or the order in which requests are generated πŸ“¦ for example, a server that knows the structure of an HTML document might want to prioritize delivering the images that are critical to user experience above other images πŸ–ΌοΈ with RFC 7540, it's hard for servers to interpret client signals for prioritization, because the same conditions could produce very different signaling from different clients 🀯 this document describes signaling that's simpler and more constrained, so it needs less interpretation and allows less variation ✨

RFC 7540 doesn't define a method a server can use to provide a priority signal for intermediaries πŸ™…β€β™€οΈ

RFC 7540 stream priority is expressed relative to other requests sharing the same connection at the same time πŸ”— it's hard to fit that kind of design into applications that generate requests without knowing how other requests might share a connection, or into protocols that don't have strong ordering guarantees across streams, like HTTP/3 [HTTP/3] πŸŒ€

experiments from independent research [MARX] have shown that simpler schemes can reach at least equivalent performance characteristics compared to the more complex RFC 7540 setups seen in practice, at least for the Web use case πŸ†

2.1. Disabling RFC 7540 Stream Priorities

okay so all those problems we just talked about? they're the whole reason this alternative exists πŸ’… it's basically the upgrade for RFC 7540 stream priority (see Section 5.3 of [HTTP/2]) and it's giving main character energy πŸ‘‘

This document defines the SETTINGS_NO_RFC7540_PRIORITIES HTTP/2 setting so endpoints can omit or ignore HTTP/2 priority signals (see Section 5.3.2 of [HTTP/2]), as described below. The value of SETTINGS_NO_RFC7540_PRIORITIES MUST be 0 or 1. Any value other than 0 or 1 MUST be treated as a connection error (see Section 5.4.1 of [HTTP/2]) of type PROTOCOL_ERROR. The initial value is 0. πŸŽ€

If endpoints use SETTINGS_NO_RFC7540_PRIORITIES, they MUST send it in the first SETTINGS frame. Senders MUST NOT change the SETTINGS_NO_RFC7540_PRIORITIES value after the first SETTINGS frame. Receivers that detect a change MAY treat it as a connection error of type PROTOCOL_ERROR. 😭

Clients can send SETTINGS_NO_RFC7540_PRIORITIES with a value of 1 to say they're not using HTTP/2 priority signals πŸ¦‹ The SETTINGS frame comes before any HTTP/2 priority signal a client sends, so servers can figure out whether they even need to set up any resources for signal handling before signals show up. A server that receives SETTINGS_NO_RFC7540_PRIORITIES with a value of 1 MUST ignore HTTP/2 priority signals. πŸ™ˆ

Servers can send SETTINGS_NO_RFC7540_PRIORITIES with a value of 1 to say they'll ignore HTTP/2 priority signals sent by clients. 🌸

Endpoints that send SETTINGS_NO_RFC7540_PRIORITIES are encouraged to use alternative priority signals (for example, see Section 5 or Section 7.1), but there is no requirement to use a specific signal type. ✨

2.1.1. Advice when Using Extensible Priorities as the Alternative

bestie, here's the tea 🍡 before a client gets a SETTINGS frame from a server, it has no idea whether that server is ignoring HTTP/2 priority signals. So until the client gets that SETTINGS frame, the client SHOULD send both the HTTP/2 priority signals and the signals from this prioritization scheme (see Sections 5 and 7.1). 🧚

Once the client receives the first SETTINGS frame that contains the SETTINGS_NO_RFC7540_PRIORITIES parameter with a value of 1, it SHOULD stop sending the HTTP/2 priority signals. This avoids sending redundant signals that are known to be ignored. πŸ’€

Similarly, if the client receives SETTINGS_NO_RFC7540_PRIORITIES with a value of 0 or if the settings parameter was absent, it SHOULD stop sending PRIORITY_UPDATE frames (Section 7.1), since those frames are likely to be ignored. However, the client MAY continue sending the Priority header field (Section 5), as it is an end-to-end signal that might be useful to nodes behind the server that the client is directly connected to. πŸ’œ


3. Applicability of the Extensible Priority Scheme

so where does this priority scheme actually work? πŸ€” It's primarily focused on prioritizing HTTP response messages (see Section 3.4 of [HTTP]). It defines new priority parameters (Section 4) and a way to convey them (Sections 5 and 7), which is meant to communicate the priority of responses to a server that's in charge of prioritizing them. Section 10 has considerations for servers about acting on those signals alongside other inputs and factors. πŸŽ€

The CONNECT method (see Section 9.3.6 of [HTTP]) can be used to establish tunnels. Signaling applies similarly to tunnels, and additional considerations for server prioritization are given in Section 11. πŸ¦‹

Section 9 describes how clients can optionally apply elements of this scheme locally to the request messages they generate. πŸ’…

Some forms of HTTP extensions might change HTTP/2 or HTTP/3 stream behavior or define new data carriage mechanisms. Such extensions can themselves define how this priority scheme is to be applied. 🌈


4. Priority Parameters

the priority info is a sequence of key-value pairs, with room for future extensions πŸ“ Each key-value pair represents one priority parameter.

The Priority HTTP header field (Section 5) is the end-to-end way to send this set of priority parameters when a request or a response is issued. After sending a request, a client can change its view of response priority (Section 6) by sending HTTP-version-specific PRIORITY_UPDATE frames as defined in Sections 7.1 and 7.2. Frames transmit priority parameters on a single hop only. 🫢

Intermediaries can consume and produce priority signals in a PRIORITY_UPDATE frame or a Priority header field. An intermediary that passes only the Priority request header field to the next hop preserves the original end-to-end signal from the client; see Section 14. An intermediary could pass the Priority header field and also send a PRIORITY_UPDATE frame. This would preserve the original client end-to-end signal while telling the next hop to use a different priority, per the guidance in Section 7. An intermediary that replaces or adds a Priority request header field overrides the original client end-to-end signal, which can affect prioritization for all subsequent recipients of the request. πŸ”₯

For both the Priority header field and the PRIORITY_UPDATE frame, the set of priority parameters is encoded as a Dictionary (see Section 3.2 of [STRUCTURED-FIELDS]). 🧁

This document defines the urgency (u) and incremental (i) priority parameters. When receiving an HTTP request that does not carry these priority parameters, a server SHOULD act as if their default values were specified. πŸ‘‘

An intermediary can combine signals from the requests and responses it forwards. Note that omission of priority parameters in responses is handled differently from omission in requests; see Section 8. 🌸

Receivers parse the Dictionary as described in Section 4.2 of [STRUCTURED-FIELDS]. Where the Dictionary is successfully parsed, this document adds the extra requirement that unknown priority parameters, priority parameters with out-of-range values, or values of unexpected types MUST be ignored. πŸ™ˆ

4.1. Urgency

the urgency (u) parameter value is an Integer (see Section 3.3.1 of [STRUCTURED-FIELDS]), between 0 and 7 inclusive, in descending order of priority. The default is 3. ⭐

Endpoints use this parameter to communicate their view of the precedence of HTTP responses. The chosen urgency value can be based on the expectation that servers might use this information to transmit HTTP responses in the order of their urgency. The smaller the value, the higher the precedence. πŸ¦‹

The following example shows a request for a CSS file with the urgency set to 0:

:method = GET
:scheme = https
:authority = example.net
:path = /style.css
priority = u=0

A client that fetches a document that likely consists of multiple HTTP resources (e.g., HTML) SHOULD assign the default urgency level to the main resource. This convention lets servers refine the urgency using knowledge specific to the website (see Section 8). πŸ’–

The lowest urgency level (7) is reserved for background tasks such as delivery of software updates. This urgency level SHOULD NOT be used for fetching responses that have any impact on user interaction. 🧚

4.2. Incremental

the incremental (i) parameter value is a Boolean (see Section 3.3.6 of [STRUCTURED-FIELDS]). It indicates whether an HTTP response can be processed incrementally, i.e., whether it can provide some meaningful output as chunks of the response arrive. 🌈

The default value of the incremental parameter is false (0). πŸŽ€

If a client makes concurrent requests with the incremental parameter set to false, there's no benefit in serving responses with the same urgency concurrently, because the client is not going to process those responses incrementally. Serving non-incremental responses with the same urgency one by one, in the order those requests were generated, is considered the best strategy. πŸ™„βœ¨

If a client makes concurrent requests with the incremental parameter set to true, serving them concurrently might be beneficial. Doing this distributes the connection bandwidth, which means responses take longer to complete. Incremental delivery is most useful where multiple partial responses might give clients some value ahead of a complete response being available. 🌊

The following example shows a request for a JPEG file with the urgency parameter set to 5 and the incremental parameter set to true:

:method = GET
:scheme = https
:authority = example.net
:path = /image.jpg
priority = u=5, i

4.3. Defining New Priority Parameters

okay, if you're making a brand new priority parameter, care must be taken so it doesn't mess with prioritization done by existing endpoints or intermediaries that don't understand it πŸ₯Ί Since unknown priority parameters are ignored, new ones should not change the meaning of, or modify, the urgency (see Section 4.1) or incremental (see Section 4.2) parameters in a way that isn't backwards compatible or fallback safe. 🫢

For example, if you need more granularity than eight urgency levels, you could subdivide the range using an additional priority parameter. Implementations that don't recognize the parameter can safely keep using the less granular eight levels. 🍰

Alternatively, the urgency can be augmented. For example, a graphical user agent could send a visible priority parameter to indicate whether the requested resource is within the viewport. πŸ‘€

Generic priority parameters are preferred over vendor-specific, application-specific, or deployment-specific values. If a generic value can't be agreed upon in the community, the parameter's name should be correspondingly specific (e.g., with a prefix that identifies the vendor, application, or deployment). πŸ’œ

4.3.1. Registration

new priority parameters can be defined by registering them in the "HTTP Priority" registry. This registry governs the keys (short textual strings) used in the Dictionary (see Section 3.2 of [STRUCTURED-FIELDS]). Since each HTTP request can have associated priority signals, there's value in short key lengths, especially single-character strings. To encourage extensions while avoiding unintended conflicts among attractive key values, the "HTTP Priority" registry uses two registration policies, depending on key length. πŸ“‹

  • Registration requests for priority parameters with a key length of one use the Specification Required policy, per Section 4.6 of [RFC8126].
  • Registration requests for priority parameters with a key length greater than one use the Expert Review policy, per Section 4.5 of [RFC8126]. A specification document is appreciated but not required. 🌷

When reviewing registration requests, the designated expert(s) can consider the additional guidance provided in Section 4.3, but they cannot use it as a basis for rejection. πŸ™Œ

Registration requests should use the following template:

Name:  [a name for the priority parameter that matches the parameter
   key]
Description:  [a description of the priority parameter semantics and
   value]
Reference:  [to a specification defining this priority parameter]

See the registry at <https://www.iana.org/assignments/http-priority> for details on where to send registration requests. πŸ¦‹βœ¨

5. The Priority HTTP Header Field ✨

okay so the Priority HTTP header field is a Dictionary that carries priority parameters (see Section 4) πŸŽ€ It can show up in requests and responses. It's an end-to-end signal that tells the other side what the endpoint thinks about how HTTP responses should be prioritized πŸ’… Section 8 explains how intermediaries can combine the priority info coming from clients and servers. Clients cannot treat the appearance or omission of a Priority response header field as confirmation that any prioritization has happened, so don't read into it πŸ™ˆ Guidance on how endpoints can act on Priority header values is in Sections 9 and 10.

An HTTP request with a Priority header field might get cached and reused for later requests, see [CACHING] 🧚 When an origin server builds the Priority response header field based on properties of an HTTP request it got, the server is expected to control the cacheability or applicability of the cached response by using header fields that control caching behavior (e.g., Cache-Control, Vary). Protect your cache, bestie πŸ”₯


6. Reprioritization

after a client sends a request, it may be beneficial to change the priority of the response πŸ¦‹ As an example, a web browser might issue a prefetch request for a JavaScript file with the urgency parameter of the Priority request header field set to u=7 (background). Then, while that prefetch is still in progress, the user navigates to a page that references the new JavaScript file. The browser would send a reprioritization signal with the Priority Field Value set to u=0 ⭐ The PRIORITY_UPDATE frame (Section 7) can be used for exactly that kind of glow-up πŸ’œ


7. The PRIORITY_UPDATE Frame

this document specifies a brand new PRIORITY_UPDATE frame for HTTP/2 [HTTP/2] and HTTP/3 [HTTP/3] 🌈 It carries priority parameters and references the target of the prioritization using a version-specific identifier. In HTTP/2, that identifier is the stream ID. In HTTP/3, the identifier is either the stream ID or the push ID. Unlike the Priority header field, the PRIORITY_UPDATE frame is a hop-by-hop signal, so it only goes one hop 🫢

PRIORITY_UPDATE frames are sent by clients on the control stream, which means they can be sent independently of the stream that carries the response ✨ This is so iconic, because they can be used to reprioritize a response or a push stream, or to signal the initial priority of a response instead of the Priority header field πŸ‘‘

A PRIORITY_UPDATE frame communicates a complete set of all priority parameters in the Priority Field Value field. Omitting a priority parameter is a signal to use its default value πŸ“ Failure to parse the Priority Field Value MAY be treated as a connection error. In HTTP/2, the error is of type PROTOCOL_ERROR. In HTTP/3, the error is of type H3_GENERAL_PROTOCOL_ERROR πŸ’€

A client MAY send a PRIORITY_UPDATE frame before the stream that it references is open (except for HTTP/2 push streams; see Section 7.1). Also, HTTP/3 offers no guaranteed ordering across streams, which could cause the frame to be received earlier than intended 😭 Either case leads to a race condition where a server receives a PRIORITY_UPDATE frame that references a request stream that is yet to be opened. To solve this, for the purposes of scheduling, the most recently received PRIORITY_UPDATE frame can be considered the most up-to-date information, and it overrides any other signal πŸ’… Servers SHOULD buffer the most recently received PRIORITY_UPDATE frame and apply it once the referenced stream is opened. Holding PRIORITY_UPDATE frames for each stream takes server resources, which can be bounded by local implementation policy. Although there is no limit to the number of PRIORITY_UPDATE frames that can be sent, storing only the most recently received frame limits resource commitment. Efficiency is everything 🧠✨

7.1. HTTP/2 PRIORITY_UPDATE Frame

The HTTP/2 PRIORITY_UPDATE frame (type=0x10) is used by clients to signal the initial priority of a response, or to reprioritize a response or push stream πŸŽ€ It carries the stream ID of the response and the priority in ASCII text, using the same representation as the Priority header field value.

The Stream Identifier field (see Section 5.1.1 of [HTTP/2]) in the PRIORITY_UPDATE frame header MUST be zero (0x0). Receiving a PRIORITY_UPDATE frame with a field of any other value MUST be treated as a connection error of type PROTOCOL_ERROR πŸ™…β€β™€οΈ No exceptions, periodt.

HTTP/2 PRIORITY_UPDATE Frame {
  Length (24),
  Type (8) = 0x10,

  Unused Flags (8),

  Reserved (1),
  Stream Identifier (31),

  Reserved (1),
  Prioritized Stream ID (31),
  Priority Field Value (..),
}

Figure 1: HTTP/2 PRIORITY_UPDATE Frame Format

The Length, Type, Unused Flag(s), Reserved, and Stream Identifier fields are described in Section 4 of [HTTP/2]. The PRIORITY_UPDATE frame payload contains these extra fields:

Prioritized Stream ID: A 31-bit stream identifier for the stream that is the target of the priority update πŸ¦‹

Priority Field Value: The priority update value in ASCII text, encoded using Structured Fields. This is the same representation as the Priority header field value ✨

When the PRIORITY_UPDATE frame applies to a request stream, clients SHOULD provide a prioritized stream ID that refers to a stream in the "open", "half-closed (local)", or "idle" state (i.e., streams where data might still be received) 🌸 Servers can discard frames where the prioritized stream ID refers to a stream in the "half-closed (local)" or "closed" state (i.e., streams where no further data will be sent). The number of streams that have been prioritized but remain in the "idle" state, plus the number of active streams (those in the "open" state or in either of the "half-closed" states; see Section 5.1.2 of [HTTP/2]), MUST NOT exceed the value of the SETTINGS_MAX_CONCURRENT_STREAMS parameter. Servers that receive such a PRIORITY_UPDATE MUST respond with a connection error of type PROTOCOL_ERROR 🚫

When the PRIORITY_UPDATE frame applies to a push stream, clients SHOULD provide a prioritized stream ID that refers to a stream in the "reserved (remote)" or "half-closed (local)" state. Servers can discard frames where the prioritized stream ID refers to a stream in the "closed" state. Clients MUST NOT provide a prioritized stream ID that refers to a push stream in the "idle" state. Servers that receive a PRIORITY_UPDATE for a push stream in the "idle" state MUST respond with a connection error of type PROTOCOL_ERROR πŸ₯²

If a PRIORITY_UPDATE frame is received with a prioritized stream ID of 0x0, the recipient MUST respond with a connection error of type PROTOCOL_ERROR πŸ’€

Servers MUST NOT send PRIORITY_UPDATE frames. If a client receives a PRIORITY_UPDATE frame, it MUST respond with a connection error of type PROTOCOL_ERROR. Only clients get to talk in this frame, bestie πŸ™ˆ

7.2. HTTP/3 PRIORITY_UPDATE Frame

The HTTP/3 PRIORITY_UPDATE frame (type=0xF0700 or 0xF0701) is used by clients to signal the initial priority of a response, or to reprioritize a response or push stream 🌈 It carries the identifier of the element being prioritized, plus the updated priority in ASCII text that uses the same representation as the Priority header field value. A PRIORITY_UPDATE with a frame type of 0xF0700 is used for request streams, while a PRIORITY_UPDATE with a frame type of 0xF0701 is used for push streams 🧚

The PRIORITY_UPDATE frame MUST be sent on the client control stream (see Section 6.2.1 of [HTTP/3]). Receiving a PRIORITY_UPDATE frame on a stream other than the client control stream MUST be treated as a connection error of type H3_FRAME_UNEXPECTED 🚨

HTTP/3 PRIORITY_UPDATE Frame {
  Type (i) = 0xF0700..0xF0701,
  Length (i),
  Prioritized Element ID (i),
  Priority Field Value (..),
}

Figure 2: HTTP/3 PRIORITY_UPDATE Frame

The PRIORITY_UPDATE frame payload has the following fields:

Prioritized Element ID: The stream ID or push ID that is the target of the priority update πŸ’œ

Priority Field Value: The priority update value in ASCII text, encoded using Structured Fields. This is the same representation as the Priority header field value ✨

The request-stream variant of PRIORITY_UPDATE (type=0xF0700) MUST reference a request stream. If a server receives a PRIORITY_UPDATE (type=0xF0700) for a stream ID that is not a request stream, this MUST be treated as a connection error of type H3_ID_ERROR. The stream ID MUST be within the client-initiated bidirectional stream limit. If a server receives a PRIORITY_UPDATE (type=0xF0700) with a stream ID that is beyond the stream limits, this SHOULD be treated as a connection error of type H3_ID_ERROR. Generating an error is not mandatory, because HTTP/3 implementations might have practical barriers to determining the active stream concurrency limit that is applied by the QUIC layer πŸ€·β€β™€οΈ

The push-stream variant of PRIORITY_UPDATE (type=0xF0701) MUST reference a promised push stream. If a server receives a PRIORITY_UPDATE (type=0xF0701) with a push ID that is greater than the maximum push ID, or that has not yet been promised, this MUST be treated as a connection error of type H3_ID_ERROR πŸ™…β€β™€οΈ

Servers MUST NOT send PRIORITY_UPDATE frames of either type. If a client receives a PRIORITY_UPDATE frame, this MUST be treated as a connection error of type H3_FRAME_UNEXPECTED πŸ’…


8. Merging Client- and Server-Driven Priority Parameters

it is not always the case that the client has the best understanding of how HTTP responses deserve to be prioritized 🫠 The server might have extra information that can be combined with the client's indicated priority to improve the prioritization of the response. For example, an HTML document might depend heavily on one of its inline images, and the existence of that dependency is typically best known to the server 🌸 Or, a server that receives requests for a font [RFC8081] and images with the same urgency might give higher precedence to the font, so that a visual client can render textual information at an early moment. Main character energy for the font πŸ‘‘

An origin can use the Priority response header field to indicate its view on how an HTTP response should be prioritized. An intermediary that forwards an HTTP response can use the priority parameters found in the Priority response header field, in combination with the client Priority request header field, as input to its prioritization process. No guidance is provided for merging priorities; this is left as an implementation decision 🧩

The absence of a priority parameter in an HTTP response indicates the server's disinterest in changing the client-provided value. This is different from the request header field, where omission of a priority parameter implies the use of its default value (see Section 4) πŸ™ˆ

As a non-normative example, when the client sends an HTTP request with the urgency parameter set to 5 and the incremental parameter set to true:

:method = GET
:scheme = https
:authority = example.net
:path = /menu.png
priority = u=5, i

and the origin responds with:

:status = 200
content-type = image/png
priority = u=1

the intermediary might alter its understanding of the urgency from 5 to 1, because it prefers the server-provided value over the client's πŸ’– The incremental value continues to be true, i.e., the value specified by the client, since the server did not specify the incremental (i) parameter. The server said "nah, I've got this" for urgency only, and that's a whole mood πŸ¦‹βœ¨

9. Client Scheduling ✨

okay so bestie, clients get to be the boss of their own requests πŸ’… A client MAY use priority values to make local processing or scheduling choices about the requests it initiates. Your app, your rules 🫢

10. Server Scheduling 🌊

okay this one is so important ngl. It is generally beneficial for an HTTP server to send all responses as early as possible πŸƒβ€β™€οΈ But when one connection is serving multiple requests, they can end up fighting over stuff like connection bandwidth 😭 This section is all about how servers can decide which competing responses go first when that fight happens.

Server scheduling is a whole prioritization process with lots of inputs, and priority signals are just one of them 🧁 Implementation choices and the deployment environment matter too. Every connection is likely to have tons of dynamic combos going on, so it's not possible to describe one universal scheduling algorithm πŸ’€ This document only gives some basic, non-exhaustive recommendations for how servers might act on priority parameters. It doesn't dive into how servers might mix priority signals with other factors. Endpoints cannot depend on particular treatment based on priority signals, and expressing priority is only a suggestion πŸ™ˆ

It is RECOMMENDED that, when possible, servers respect the urgency parameter (Section 4.1), sending higher-urgency responses before lower-urgency responses πŸ”₯

The incremental parameter tells you how a client processes response bytes as they arrive πŸŽ€ It is RECOMMENDED that, when possible, servers respect the incremental parameter (Section 4.2) πŸ’–

Non-incremental responses of the same urgency SHOULD be served by prioritizing bandwidth allocation in ascending order of the stream ID, which matches the order clients made their requests in ✨ That way clients can use request ordering to influence response order, which is so satisfying πŸ¦‹

Incremental responses of the same urgency SHOULD be served by sharing bandwidth among them 🌈 Message content of incremental responses gets used as parts (or chunks) are received. A client might benefit more from getting a portion of all these resources than the whole of just one 🫧 How big a portion is needed to actually help performance varies. Some resource types put critical stuff early, and others can use information bit by bit. This scheme doesn't give any explicit mandate on how a server should use size, type, or anything else to decide how to prioritize πŸ€·β€β™€οΈ

There can be scenarios where a server has to schedule multiple incremental and non-incremental responses at the same urgency level. Strictly following the urgency and request-order guidance might lead to suboptimal results for the client, because early non-incremental responses might block incremental responses that were requested later 😩 Here are some examples of that drama:

  1. At the same urgency level, a non-incremental request for a large resource followed by an incremental request for a small resource.
  2. At the same urgency level, an incremental request of indeterminate length followed by a non-incremental large resource.

It is RECOMMENDED that servers avoid such starvation where possible πŸ₯Ί How they do that is an implementation decision. For example, a server might preemptively send responses of a particular incremental type based on other information such as content size πŸ“

Optimal scheduling of server push is hard, especially when pushed resources contend with active concurrent requests πŸ˜΅β€πŸ’« Servers can consider lots of things, like the type or size of the resource being pushed, the priority of the request that triggered the push, the count of active concurrent responses, the priority of other active concurrent responses, etc. There's no general guidance on the best way to apply these πŸ€” A server that's too simple could easily push at too high a priority and block client requests, or push at too low a priority and delay the response, which kills the whole point of server push πŸ’€

Priority signals are a factor for server push scheduling πŸ’œ The idea of parameter value defaults applies a little differently here, because there's no explicit client-signaled initial priority. A server can apply priority signals provided in an origin response, and the merging guidance in Section 8 covers that. Without origin signals, applying default parameter values could be suboptimal 😬 Whatever way a server decides to schedule a pushed response, it can signal the intended priority to the client by including the Priority field in a PUSH_PROMISE or HEADERS frame 🧚

10.1. Intermediaries with Multiple Backend Connections 🧩

An intermediary serving an HTTP connection might split requests over multiple backend connections πŸ”€ If it applies prioritization rules strictly, low-priority requests can't make progress while higher-priority requests are in flight 😭 That blocking can spread to backend connections, and the peer might read it as a connection stall. Endpoints often have protections against stalls, like abruptly closing connections after a certain time period πŸšͺ To lower the chance of this, intermediaries can skip strict prioritization and instead allocate small amounts of bandwidth to all the requests they're forwarding, so every request can make some progress over time πŸ’πŸ’•

Similarly, servers SHOULD allocate some amount of bandwidth to streams acting as tunnels πŸš‡

11. Scheduling and the CONNECT Method πŸ”Œ

When a stream carries a CONNECT request, the scheduling guidance in this document applies to the frames on that stream ✨ A client that issues multiple CONNECT requests can set the incremental parameter to true. Servers that implement the recommendations for handling the incremental parameter (Section 10) are likely to schedule these fairly, so one CONNECT stream can't block the others 🫢

12. Retransmission Scheduling πŸ”

Transport protocols like TCP and QUIC keep things reliable by detecting packet losses and retransmitting lost information πŸ›Ÿ On top of the considerations in Section 10, scheduling retransmission data could compete with new data. The rest of this section talks about the QUIC considerations.

Section 13.3 of [QUIC] says this: "Endpoints SHOULD prioritize retransmission of data over sending new data, unless priorities specified by the application indicate otherwise". When an HTTP/3 application uses the priority scheme in this document, and the QUIC transport implementation supports application-indicated stream priority, a transport that considers the relative priority of streams when scheduling both new data and retransmission data might better match what the application expects 🎯 But there are no requirements on how a transport chooses to schedule based on this info, because the decision depends on several factors and trade-offs πŸ€Ήβ€β™€οΈ It could prioritize new data for a higher-urgency stream over retransmission data for a lower-priority stream, or it could prioritize retransmission data over new data no matter the urgencies πŸ”„

Section 6.2.4 of [QUIC-RECOVERY] also covers considerations about application priorities when sending probe packets after the Probe Timeout timer expires ⏰ A QUIC implementation that supports application-indicated priorities might use the relative priority of streams when choosing probe data 🌸

13. Fairness βš–οΈ

Typically, HTTP implementations depend on the underlying transport to keep things fair between connections competing for bandwidth 🀝 When an intermediary receives HTTP requests on client connections, it forwards them to backend connections. Depending on how the intermediary coalesces or splits requests across backend connections, different clients might get pretty different performance 😀 That difference might grow if the intermediary also uses priority signals when forwarding requests. Sections 13.1 and 13.2 talk about ways to mitigate that growing unfairness πŸ›‘οΈ

On the flip side, Section 13.3 talks about how servers might intentionally give some connections unequal bandwidth, depending on the priority signals πŸ‘‘

13.1. Coalescing Intermediaries πŸ«‚

When an intermediary coalesces HTTP requests from multiple clients into one HTTP/2 or HTTP/3 connection going to the backend server, requests from one client might carry signals that say they're higher priority than the ones from everyone else πŸ‘€

Sometimes it's actually beneficial for the server behind an intermediary to obey Priority header field values. For example, a resource-constrained server might defer sending software update files that have the background urgency level (7) πŸ§ƒ But in the worst case, the asymmetry between the priorities declared by multiple clients might cause every response going to one user agent to be delayed until all the responses going to another user agent have been sent 😱

To mitigate this fairness problem, a server could use the knowledge that it's behind an intermediary as another input in its prioritization decisions 🧠 For instance, if a server knows the intermediary is coalescing requests, it could avoid serving responses in their entirety and instead distribute bandwidth (for example, in a round-robin manner) 🎠 This can work if the constrained resource is the network capacity between the intermediary and the user agent, since the intermediary buffers responses and forwards the chunks based on the prioritization scheme it implements πŸͺ„

A server can figure out if a request came from an intermediary through configuration, or by checking whether the request contains one of the following header fields:

  • Forwarded [FORWARDED], X-Forwarded-For
  • Via (see Section 7.6.3 of [HTTP])

13.2. HTTP/1.x Back Ends 🧱

It's common for Content Delivery Network (CDN) infrastructure to support different HTTP versions on the front end and back end 🌐 For instance, the client-facing edge might support HTTP/2 and HTTP/3, while communication to backend servers is done using HTTP/1.1. Unlike connection coalescing, the CDN will "demux" requests into discrete connections to the back end πŸ”ͺ Response multiplexing in a single connection isn't supported by HTTP/1.1 (or older), so there's no fairness problem there. However, backend servers MAY still use client headers for request scheduling πŸ“¬ Backend servers SHOULD only schedule based on client priority information where that information can be scoped to individual end clients. Authentication and other session information might provide this linkability πŸ”—

13.3. Intentional Introduction of Unfairness 😈

Sometimes it's beneficial to deprioritize the transmission of one connection over others, even though doing so knowingly introduces some unfairness between the connections, and therefore between the requests served on those connections 🫠

For example, a server might use a scavenging congestion controller on connections that only carry background priority responses, such as software update images πŸ›Έ Doing so improves the responsiveness of other connections, at the cost of delaying delivery of the updates 🐌

14. Why Use an End-to-End Header Field? ✨

Okay bestie, so HTTP/2 does its prioritization with a hop-by-hop frame πŸ’…, but the Priority header field is end-to-end πŸŽ€. Totally different energy!

The way a client handles a response is a thing about the client that made the request, not some middle box 🫢. That makes it an end-to-end property πŸ’–. How those end-to-end values in the Priority header field shape the order of responses sharing one connection is a hop-by-hop issue πŸ¦‹.

It's also a big deal for caching intermediaries 🧚. They can save the Priority value alongside the response and use that saved value when they serve the cached response. They can only do this because the field is end-to-end and not hop-by-hop πŸ‘‘ iconic.


15. Security Considerations πŸ”₯

Okay this one is serious, so buckle up 😭.

Section 7 covers the server-side buffering of PRIORITY_UPDATE frames, so go read that one carefully ✨.

Section 10 shows examples where servers that order responses a certain way might get starved and be unable to send their responses at all πŸ’€. Yikes.

The security considerations from [STRUCTURED-FIELDS] also apply to how you process the priority parameters defined in Section 4 πŸ”’.


16. IANA Considerations 🌸

Here's what IANA needs to add to its registries, so let's go πŸ’….

First, a new entry goes in the "Hypertext Transfer Protocol (HTTP) Field Name Registry" defined in [HTTP/2]:

Field Name:  Priority
Status:  permanent
Reference:  This document

Next, a new entry goes in the "HTTP/2 Settings" registry defined in [HTTP/2]:

Code:  0x9
Name:  SETTINGS_NO_RFC7540_PRIORITIES
Initial Value:  0
Reference:  This document

Then, a new entry goes in the "HTTP/2 Frame Type" registry defined in [HTTP/2]:

Code:  0x10
Frame Type:  PRIORITY_UPDATE
Reference:  This document

Also, a new entry goes in the "HTTP/3 Frame Types" registry established by [HTTP/3]:

Value:  0xF0700-0xF0701
Frame Type:  PRIORITY_UPDATE
Status:  permanent
Reference:  This document
Change Controller:  IETF
Contact:  ietf-http-wg@w3.org

IANA has also created a brand new "Hypertext Transfer Protocol (HTTP) Priority" registry at https://www.iana.org/assignments/http-priority πŸŽ€ and filled it with the entries in Table 1. Check Section 4.3.1 for its procedures.

      +======+==================================+=============+
      | Name |           Description            | Reference   |
      +======+==================================+=============+
      | u    | The urgency of an HTTP response. | Section 4.1 |
      +------+----------------------------------+-------------+
      | i    | Whether an HTTP response can be  | Section 4.2 |
      |      |     processed incrementally.     |             |
      +------+----------------------------------+-------------+

                 Table 1: Initial Priority Parameters

17. References πŸ“š

17.1. Normative References

These are the must-read references, the ones the spec basically can't live without 🧠.

[HTTP]     Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke,
           Ed., "HTTP Semantics", STD 97, RFC 9110,
           DOI 10.17487/RFC9110, June 2022,
           <https://www.rfc-editor.org/info/rfc9110>.

[HTTP/2]   Thomson, M., Ed. and C. Benfield, Ed., "HTTP/2", RFC 9113,
           DOI 10.17487/RFC9113, June 2022,
           <https://www.rfc-editor.org/info/rfc9113>.

[HTTP/3]   Bishop, M., Ed., "HTTP/3", RFC 9114, DOI 10.17487/RFC9114,
           June 2022, <https://www.rfc-editor.org/info/rfc9114>.

[QUIC]     Iyengar, J., Ed. and M. Thomson, Ed., "QUIC: A UDP-Based
           Multiplexed and Secure Transport", RFC 9000,
           DOI 10.17487/RFC9000, May 2021,
           <https://www.rfc-editor.org/info/rfc9000>.

[RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
           Requirement Levels", BCP 14, RFC 2119,
           DOI 10.17487/RFC2119, March 1997,
           <https://www.rfc-editor.org/info/rfc2119>.

[RFC8126]  Cotton, M., Leiba, B., and T. Narten, "Guidelines for
           Writing an IANA Considerations Section in RFCs", BCP 26,
           RFC 8126, DOI 10.17487/RFC8126, June 2017,
           <https://www.rfc-editor.org/info/rfc8126>.

[RFC8174]  Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
           2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
           May 2017, <https://www.rfc-editor.org/info/rfc8174>.

[STRUCTURED-FIELDS]
           Nottingham, M. and P-H. Kamp, "Structured Field Values for
           HTTP", RFC 8941, DOI 10.17487/RFC8941, February 2021,
           <https://www.rfc-editor.org/info/rfc8941>.

17.2. Informative References

These are the extra-credit reads πŸ€“πŸ“–.

[CACHING]  Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke,
           Ed., "HTTP Caching", STD 98, RFC 9111,
           DOI 10.17487/RFC9111, June 2022,
           <https://www.rfc-editor.org/info/rfc9111>.

[FORWARDED]
           Petersson, A. and M. Nilsson, "Forwarded HTTP Extension",
           RFC 7239, DOI 10.17487/RFC7239, June 2014,
           <https://www.rfc-editor.org/info/rfc7239>.

[MARX]     Marx, R., De Decker, T., Quax, P., and W. Lamotte, "Of the
           Utmost Importance: Resource Prioritization in HTTP/3 over
           QUIC", SCITEPRESS Proceedings of the 15th International
           Conference on Web Information Systems and Technologies
           (pages 130-143), DOI 10.5220/0008191701300143, September
           2019, <https://www.doi.org/10.5220/0008191701300143>.

[PRIORITY-SETTING]
           Lassey, B. and L. Pardue, "Declaring Support for HTTP/2
           Priorities", Work in Progress, Internet-Draft, draft-
           lassey-priority-setting-00, 25 July 2019,
           <https://datatracker.ietf.org/doc/html/draft-lassey-
           priority-setting-00>.

[QUIC-RECOVERY]
           Iyengar, J., Ed. and I. Swett, Ed., "QUIC Loss Detection
           and Congestion Control", RFC 9002, DOI 10.17487/RFC9002,
           May 2021, <https://www.rfc-editor.org/info/rfc9002>.

[RFC7540]  Belshe, M., Peon, R., and M. Thomson, Ed., "Hypertext
           Transfer Protocol Version 2 (HTTP/2)", RFC 7540,
           DOI 10.17487/RFC7540, May 2015,
           <https://www.rfc-editor.org/info/rfc7540>.

[RFC8081]  Lilley, C., "The "font" Top-Level Media Type", RFC 8081,
           DOI 10.17487/RFC8081, February 2017,
           <https://www.rfc-editor.org/info/rfc8081>.

Acknowledgements 🫢

Huge shoutout to Roy Fielding, who pitched the idea of using a header field for representing priorities in https://www.ietf.org/proceedings/83/slides/slides-83-httpbis-5.pdf πŸ™Œ. Patrick Meenan pushed for representing priorities as a tuple of urgency and concurrency in https://github.com/pmeenan/http3-prioritization-proposal πŸ’œ.

The ability to turn off HTTP/2 prioritization was inspired by [PRIORITY-SETTING], written by Brad Lassey and Lucas Pardue. It has some changes based on feedback that never made it into an update of that document 🌈.

The motivation for making an alternative to HTTP/2 priorities comes from discussions across the whole HTTP community 🧚. Special thanks to Roberto Peon, Martin Thomson, and Netflix for text that got put directly into this document πŸŽ€.

Beyond everyone above, this document owes a lot to the massive discussion in the HTTP priority design team: Alan Frindell, Andrew Galloni, Craig Taylor, Ian Swett, Matthew Cox, Mike Bishop, Roberto Peon, Robin Marx, Roy Fielding, and the authors of this document πŸ‘‘.

Yang Chi wrote the section on retransmission scheduling, so Yang, you're iconic πŸ’–.


Authors' Addresses πŸ’Œ

Kazuho Oku
Fastly
Email: kazuhooku@gmail.com

Additional contact information:

   ε₯₯ δΈ€η©‚
   Fastly


Lucas Pardue
Cloudflare
Email: lucaspardue.24.7@gmail.com
πŸ“¦ where this page comes from