RFC 9114
HTTP/3
HTTP/3 (RFC 9114) ๐
RFC number: 9114 Title: HTTP/3 Author: M. Bishop, Ed. (Akamai) ๐ฆ Category: Standards Track Date: June 2022 ISSN: 2070-1721 Obsoletes/Updates: The header doesn't list anything this RFC obsoletes or updates. ๐
Abstract โจ
okay so QUIC is the transport protocol that's giving HTTP exactly what it wants ๐ญ stream multiplexing, per-stream flow control, and super quick connection setup. This doc is the mapping of HTTP semantics over QUIC. It also calls out which HTTP/2 features QUIC already handles (iykyk) and explains how HTTP/2 extensions can be brought over to HTTP/3. ๐ซถ
Status of This Memo ๐
This is an Internet Standards Track document. ๐ธ
This document is a product of the Internet Engineering Task Force (IETF). It represents the consensus of the IETF community. It got public review and was approved for publication by the Internet Engineering Steering Group (IESG). More info on Internet Standards is in Section 2 of RFC 7841. ๐
Info about the current status of this document, any errata, and how to give feedback is at https://www.rfc-editor.org/info/rfc9114. ๐ง
Copyright Notice ยฉ๏ธ
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 of publication. Please actually read these, bestie, because they spell out your rights and restrictions for this document. Code Components pulled from 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. Prior Versions of HTTP
1.2. Delegation to QUIC
2. HTTP/3 Protocol Overview
2.1. Document Organization
2.2. Conventions and Terminology
3. Connection Setup and Management
3.1. Discovering an HTTP/3 Endpoint
3.1.1. HTTP Alternative Services
3.1.2. Other Schemes
3.2. Connection Establishment
3.3. Connection Reuse
4. Expressing HTTP Semantics in HTTP/3
4.1. HTTP Message Framing
4.1.1. Request Cancellation and Rejection
4.1.2. Malformed Requests and Responses
4.2. HTTP Fields
4.2.1. Field Compression
4.2.2. Header Size Constraints
4.3. HTTP Control Data
4.3.1. Request Pseudo-Header Fields
4.3.2. Response Pseudo-Header Fields
4.4. The CONNECT Method
4.5. HTTP Upgrade
4.6. Server Push
5. Connection Closure
5.1. Idle Connections
5.2. Connection Shutdown
5.3. Immediate Application Closure
5.4. Transport Closure
6. Stream Mapping and Usage
6.1. Bidirectional Streams
6.2. Unidirectional Streams
6.2.1. Control Streams
6.2.2. Push Streams
6.2.3. Reserved Stream Types
7. HTTP Framing Layer
7.1. Frame Layout
7.2. Frame Definitions
7.2.1. DATA
7.2.2. HEADERS
7.2.3. CANCEL_PUSH
7.2.4. SETTINGS
7.2.5. PUSH_PROMISE
7.2.6. GOAWAY
7.2.7. MAX_PUSH_ID
7.2.8. Reserved Frame Types
8. Error Handling
8.1. HTTP/3 Error Codes
9. Extensions to HTTP/3
10. Security Considerations
10.1. Server Authority
10.2. Cross-Protocol Attacks
10.3. Intermediary-Encapsulation Attacks
10.4. Cacheability of Pushed Responses
10.5. Denial-of-Service Considerations
10.5.1. Limits on Field Section Size
10.5.2. CONNECT Issues
10.6. Use of Compression
10.7. Padding and Traffic Analysis
10.8. Frame Parsing
10.9. Early Data
10.10. Migration
10.11. Privacy Considerations
11. IANA Considerations
11.1. Registration of HTTP/3 Identification String
11.2. New Registries
11.2.1. Frame Types
11.2.2. Settings Parameters
11.2.3. Error Codes
11.2.4. Stream Types
12. References
12.1. Normative References
12.2. Informative References
Appendix A. Considerations for Transitioning from HTTP/2
A.1. Streams
A.2. HTTP Frame Types
A.2.1. Prioritization Differences
A.2.2. Field Compression Differences
A.2.3. Flow-Control Differences
A.2.4. Guidance for New Frame Type Definitions
A.2.5. Comparison of HTTP/2 and HTTP/3 Frame Types
A.3. HTTP/2 SETTINGS Parameters
A.4. HTTP/2 Error Codes
A.4.1. Mapping between HTTP/2 and HTTP/3 Errors
Acknowledgments
Index
Author's Address
1. Introduction ๐
okay so HTTP semantics ([HTTP]) are used for a huge range of services all over the Internet ๐. Mostly they've been paired with HTTP/1.1 and HTTP/2. HTTP/1.1 has run over all kinds of transport and session layers, while HTTP/2 has mostly lived on TLS over TCP. HTTP/3 keeps the same semantics but runs them over a brand new transport protocol: QUIC ๐.
1.1. Prior Versions of HTTP ๐ฐ๏ธ
HTTP/1.1 ([HTTP/1.1]) uses whitespace-delimited text fields to send messages. These exchanges are readable by humans, which is cute, but using whitespace for formatting makes parsing messy and means way too much tolerance for variant behavior ๐.
HTTP/1.1 has no multiplexing layer, so people open multiple TCP connections to handle requests in parallel. Ngl, that's not great for congestion control or network efficiency, because TCP doesn't share congestion control across multiple connections ๐.
HTTP/2 ([HTTP/2]) brought in a binary framing and multiplexing layer to cut down latency without touching the transport layer โจ. The catch is that the parallel nature of HTTP/2 multiplexing isn't visible to TCP's loss recovery. So when one packet is lost or reordered, every active transaction stalls, even the ones the lost packet never touched ๐ญ.
1.2. Delegation to QUIC ๐ฆ
The QUIC transport protocol already has stream multiplexing and per-stream flow control, kind of like what the HTTP/2 framing layer gives you ๐. QUIC gives reliability at the stream level and congestion control across the whole connection, so it can make HTTP perform better than a TCP mapping can. QUIC also bakes in TLS 1.3 ([TLS]) at the transport layer, which gives you confidentiality and integrity like TLS over TCP, plus faster connection setup, like TCP Fast Open ([TFO]) ๐ฅ.
This document defines HTTP/3: a mapping of HTTP semantics over the QUIC transport protocol that borrows heavily from HTTP/2's design ๐. HTTP/3 leans on QUIC for confidentiality and integrity protection of data, peer authentication, and reliable, in-order, per-stream delivery. QUIC handles stream lifetime and flow-control stuff, while each stream still uses a binary framing that looks a lot like HTTP/2 framing ๐ง. Some HTTP/2 features are subsumed by QUIC, and others get built on top of QUIC.
QUIC is described in [QUIC-TRANSPORT]. If you want the full HTTP/2 tea, check out [HTTP/2] ๐.
2. HTTP/3 Protocol Overview ๐บ๏ธ
HTTP/3 carries HTTP semantics using the QUIC transport protocol plus an internal framing layer that's similar to HTTP/2 ๐.
Once a client knows an HTTP/3 server lives at a certain endpoint, it opens a QUIC connection. QUIC handles protocol negotiation, stream-based multiplexing, and flow control. You can read about finding an HTTP/3 endpoint in Section 3.1 ๐.
Inside each stream, the basic unit of HTTP/3 communication is a frame (Section 7.2). Each frame type has its own job. For example, HEADERS and DATA frames are the foundation of HTTP requests and responses (Section 4.1) ๐ . Frames that apply to the whole connection go on a dedicated control stream ๐๏ธ.
Multiplexing requests uses the QUIC stream abstraction, which is described in Section 2 of [QUIC-TRANSPORT]. Each request-response pair eats up a single QUIC stream ๐ฝ๏ธ. Streams are independent of each other, so a stream that's blocked or hit by packet loss doesn't stop the others from moving along โญ.
Server push is an interaction mode that HTTP/2 ([HTTP/2]) introduced. It lets a server send a request-response exchange to a client before the client even asks for it, because the server expects that request is coming ๐ฎ. It's a trade-off between network usage and a possible latency win ๐ค. HTTP/3 uses several frames to manage server push: PUSH_PROMISE, MAX_PUSH_ID, and CANCEL_PUSH ๐ง .
Just like HTTP/2, request and response fields get compressed for transmission ๐๏ธ. HPACK ([HPACK]) depends on compressed field sections arriving in order, and QUIC doesn't guarantee that. So HTTP/3 swaps HPACK out for QPACK ([QPACK]) ๐. QPACK uses separate unidirectional streams to change and track the field table state, while encoded field sections just refer to that state without changing it ๐งฉ.
2.1. Document Organization ๐
The following sections give a detailed overview of the lifecycle of an HTTP/3 connection:
- "Connection Setup and Management" (Section 3) covers how an HTTP/3 endpoint gets discovered and how an HTTP/3 connection gets established ๐.
- "Expressing HTTP Semantics in HTTP/3" (Section 4) describes how HTTP semantics get expressed using frames ๐.
- "Connection Closure" (Section 5) describes how HTTP/3 connections get terminated, either gracefully or abruptly ๐.
The details of the wire protocol and how it interacts with the transport are in the sections that follow:
- "Stream Mapping and Usage" (Section 6) describes how QUIC streams get used ๐.
- "HTTP Framing Layer" (Section 7) describes the frames used on most streams ๐งฑ.
- "Error Handling" (Section 8) describes how error conditions get handled and expressed, either on a particular stream or for the connection as a whole ๐จ.
Extra resources live in the final sections:
- "Extensions to HTTP/3" (Section 9) describes how new capabilities can get added in future documents ๐.
- A more detailed comparison between HTTP/2 and HTTP/3 is in Appendix A ๐.
2.2. Conventions and Terminology โจ
okay so let's talk lingo ๐ When "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" show up in this doc, they're BCP 14 [RFC2119] [RFC8174] words, but only when they're written in all capitals like that ๐ Lowercase "should" is just a regular word, no pressure ๐ซถ
this doc also borrows the variable-length integer encoding from [QUIC-TRANSPORT] ๐งฎ
here are the terms we're using, bestie:
- abort: an abrupt termination of a connection or stream, maybe because something went wrong ๐
- client: the endpoint that starts an HTTP/3 connection ๐ Clients send HTTP requests and receive HTTP responses.
- connection: a transport-layer connection between two endpoints that uses QUIC as its transport protocol ๐ฃ๏ธ
- connection error: an error that hits the whole HTTP/3 connection, not just one piece of it ๐ญ
- endpoint: either the client or the server of the connection ๐ซ
- frame: the smallest unit of communication on a stream in HTTP/3 ๐งฉ It's made of a header plus a variable-length sequence of bytes, structured according to the frame type.
okay this part is important: frames exist in both this document AND [QUIC-TRANSPORT]! So when we reference a QUIC frame, we preface the frame name with "QUIC", like "QUIC CONNECTION_CLOSE frames". Anything without that preface means the frames defined in Section 7.2 ๐
- HTTP/3 connection: a QUIC connection where the negotiated application protocol is HTTP/3 ๐ฆ
- peer: an endpoint. When we're talking about one particular endpoint, "peer" means the endpoint on the other side, the one that's remote to the main subject of the convo ๐ซถ
- receiver: an endpoint that is receiving frames ๐ฅ
- sender: an endpoint that is transmitting frames ๐ค
- server: the endpoint that accepts an HTTP/3 connection โจ Servers receive HTTP requests and send HTTP responses.
- stream: a bidirectional or unidirectional bytestream provided by the QUIC transport ๐ All streams within an HTTP/3 connection can be considered "HTTP/3 streams", but multiple stream types are defined within HTTP/3.
- stream error: an application-level error on an individual stream ๐
the term "content" is defined in Section 6.4 of [HTTP] ๐ and then, finally, the terms "resource", "message", "user agent", "origin server", "gateway", "intermediary", "proxy", and "tunnel" are defined in Section 3 of [HTTP] ๐
packet diagrams in this doc use the format from Section 1.3 of [QUIC-TRANSPORT] to show the order and size of fields ๐
3. Connection Setup and Management โจ
3.1. Discovering an HTTP/3 Endpoint
okay so HTTP runs on the idea of an authoritative response ๐ That's a response that's been determined to be the most appropriate one for the request, given the state of the target resource when the response was created by (or at the direction of) the origin server named in the target URI. Finding an authoritative server for an HTTP URI is covered in Section 4.3 of [HTTP] ๐
the "https" scheme ties authority to having a certificate the client considers trustworthy for the host in the URI's authority component ๐ When the client gets a server certificate during the TLS handshake, the client MUST verify that the certificate is an acceptable match for the URI's origin server, using the process from Section 4.3.4 of [HTTP]. If the certificate can't be verified for the URI's origin server, the client MUST NOT consider the server authoritative for that origin. no trust, no authority ๐ซ
a client MAY try to access a resource with an "https" URI by resolving the host to an IP address, opening a QUIC connection to that address on the indicated port (including checking the server certificate like we just talked about), and then sending an HTTP/3 request targeting the URI over that secured connection ๐ฆ Unless some other mechanism is used to select HTTP/3, the token h3 is used in the Application-Layer Protocol Negotiation (ALPN; see [RFC7301]) extension during the TLS handshake ๐ค
connectivity problems, like blocking UDP, can make it fail to set up a QUIC connection ๐ญ In that case, clients SHOULD attempt to use TCP-based versions of HTTP instead ๐ซถ
servers MAY serve HTTP/3 on any UDP port ๐ An alternative service advertisement always includes an explicit port, and URIs contain either an explicit port or a default port tied to the scheme ๐
3.1.1. HTTP Alternative Services
an HTTP origin can advertise an equivalent HTTP/3 endpoint using the Alt-Svc HTTP response header field, or the HTTP/2 ALTSVC frame ([ALTSVC]), with the h3 ALPN token ๐ฃ
for example, an origin could say in an HTTP response that HTTP/3 is available on UDP port 50781 at the same hostname by including this header field:
Alt-Svc: h3=":50781"
when a client gets an Alt-Svc record that indicates HTTP/3 support, it MAY try to establish a QUIC connection to the indicated host and port ๐ช If that connection works, the client can send HTTP requests using the mapping described in this document โจ
3.1.2. Other Schemes
HTTP doesn't care which transport protocol is used ๐ง But the "http" scheme ties authority to being able to receive TCP connections on the indicated port of whatever host is in the authority component. Because HTTP/3 doesn't use TCP, HTTP/3 can't be used for direct access to the authoritative server for a resource with an "http" URI ๐
โโ๏ธ Protocol extensions like [ALTSVC], however, let the authoritative server point to other services that are also authoritative and that might be reachable over HTTP/3 ๐
before making requests for an origin whose scheme is not "https", the client MUST ensure the server is willing to serve that scheme ๐ก๏ธ For origins with the "http" scheme, an experimental method for this is described in [RFC8164]. Other mechanisms might be defined for various schemes in the future ๐ฎ
3.2. Connection Establishment
HTTP/3 relies on QUIC version 1 as its underlying transport ๐ Other QUIC transport versions MAY be defined for use with HTTP/3 by future specifications ๐ญ
QUIC version 1 uses TLS version 1.3 or greater as its handshake protocol ๐ HTTP/3 clients MUST support a mechanism to indicate the target host to the server during the TLS handshake. If the server is identified by a domain name ([DNS-TERMS]), clients MUST send the Server Name Indication (SNI; [RFC6066]) TLS extension, unless an alternative mechanism to indicate the target host is used ๐ท๏ธ
QUIC connections are set up as described in [QUIC-TRANSPORT] ๐งต During connection establishment, HTTP/3 support is indicated by selecting the ALPN token h3 in the TLS handshake. Support for other application-layer protocols MAY be offered in that same handshake ๐
connection-level options for core QUIC are set in the initial crypto handshake, but settings specific to HTTP/3 are carried in the SETTINGS frame โ๏ธ After the QUIC connection is established, each endpoint MUST send a SETTINGS frame as the initial frame of its respective HTTP control stream ๐๏ธ
3.3. Connection Reuse
HTTP/3 connections are persistent across multiple requests โป๏ธ For best performance, clients aren't expected to close connections until they've figured out no more communication with the server is needed (like when a user navigates away from a web page), or until the server closes the connection ๐ธ
once a connection to a server endpoint exists, it MAY be reused for requests with multiple different URI authority components ๐ But to use an existing connection for a new origin, clients MUST validate the certificate the server presents for the new origin server, using the process from Section 4.3.4 of [HTTP]. This means clients need to keep the server certificate and any extra info needed to verify it. Clients that don't keep that stuff won't be able to reuse the connection for other origins ๐ซฃ
if the certificate isn't acceptable for the new origin for any reason, the connection MUST NOT be reused, and a new connection SHOULD be set up for the new origin ๐ If the reason the certificate can't be verified might also apply to other origins already tied to the connection, the client SHOULD revalidate the server certificate for those origins too. For example, if validation fails because the certificate has expired or been revoked, that could invalidate every other origin the certificate was used to establish authority for ๐
clients SHOULD NOT open more than one HTTP/3 connection to a given IP address and UDP port, where the IP address and port might come from a URI, a selected alternative service ([ALTSVC]), a configured proxy, or name resolution of any of those ๐ซ A client MAY open multiple HTTP/3 connections to the same IP address and UDP port using different transport or TLS configurations, but it SHOULD avoid making multiple connections with the same configuration ๐ง
servers are encouraged to keep HTTP/3 connections open for as long as possible ๐ They're allowed to close idle connections if they need to. When either endpoint decides to close the HTTP/3 connection, the endpoint that's terminating SHOULD first send a GOAWAY frame (Section 5.2) so both endpoints can reliably tell whether previously sent frames were processed, and gracefully finish or end any remaining tasks ๐ท
if a server doesn't want clients to reuse HTTP/3 connections for a particular origin, it can say it's not authoritative for a request by sending a 421 (Misdirected Request) status code in response to that request ๐ โโ๏ธ See Section 7.4 of [HTTP] ๐
4. Expressing HTTP Semantics in HTTP/3 โจ
4.1. HTTP Message Framing โจ
okay bestie, let's talk about how messages actually get packed into streams ๐ A client sends an HTTP request on a request stream, which is a client-initiated bidirectional QUIC stream (see Section 6.1). A client MUST send only a single request on a given stream. A server sends zero or more interim HTTP responses on the same stream as the request, followed by a single final HTTP response, as detailed below. See Section 15 of [HTTP] for a description of interim and final HTTP responses.
Pushed responses go out on a server-initiated unidirectional QUIC stream (see Section 6.2.2) ๐ฆ A server sends zero or more interim HTTP responses, followed by a single final HTTP response, the same way it does for a standard response. Push gets a deeper dive in Section 4.6.
On a given stream, receiving multiple requests, or receiving an additional HTTP response after a final HTTP response, MUST be treated as malformed. That's a hard no ๐ โโ๏ธ
An HTTP message (request or response) is made of:
- the header section, including message control data, sent as a single HEADERS frame,
- optionally, the content, if present, sent as a series of DATA frames, and
- optionally, the trailer section, if present, sent as a single HEADERS frame.
Header and trailer sections are described in Sections 6.3 and 6.5 of [HTTP], and the content is described in Section 6.4 of [HTTP] ๐
Receiving an invalid sequence of frames MUST be treated as a connection error of type H3_FRAME_UNEXPECTED. ๐ In particular, a DATA frame before any HEADERS frame, or a HEADERS or DATA frame after the trailing HEADERS frame, is considered invalid. Other frame types, especially unknown frame types, might be permitted subject to their own rules, so see Section 9 ๐ธ
A server MAY send one or more PUSH_PROMISE frames before, after, or interleaved with the frames of a response message. These PUSH_PROMISE frames are not part of the response (see Section 4.6 for more details). PUSH_PROMISE frames are not permitted on push streams, and a pushed response that includes PUSH_PROMISE frames MUST be treated as a connection error of type H3_FRAME_UNEXPECTED. Not allowed, periodt ๐
Frames of unknown types (Section 9), including reserved frames (Section 7.2.8), MAY be sent on a request or push stream before, after, or interleaved with the other frames described in this section. Unknown frames are kinda like guests who showed up uninvited but are still allowed to hang ๐ง
The HEADERS and PUSH_PROMISE frames might reference updates to the QPACK dynamic table. While these updates are not directly part of the message exchange, they must be received and processed before the message can be consumed. See Section 4.2 for more details ๐ง
Transfer codings (see Section 7 of [HTTP/1.1]) are not defined for HTTP/3, so the Transfer-Encoding header field MUST NOT be used. Absolutely not ๐
A response MAY consist of multiple messages when and only when one or more interim responses (1xx; see Section 15.2 of [HTTP]) precede a final response to the same request. Interim responses do not contain content or trailer sections. Only the interim ones get to be extra, the final one is the main character ๐
An HTTP request/response exchange fully consumes a client-initiated bidirectional QUIC stream. After sending a request, a client MUST close the stream for sending. Unless using the CONNECT method (see Section 4.4), clients MUST NOT make stream closure dependent on receiving a response to their request. After sending a final response, the server MUST close the stream for sending. At this point, the QUIC stream is fully closed ๐
When a stream is closed, that signals the end of the final HTTP message. Because some messages are large or unbounded, endpoints SHOULD begin processing partial HTTP messages once enough of the message has been received to make progress. 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. Incomplete is incomplete, bestie ๐ญ
A server can send a complete response before the client has sent the entire request, if the response does not depend on any portion of the request that has not been sent and received. When the server does not need to receive the remainder of the request, it MAY abort reading the request stream, send a complete response, and cleanly close the sending part of the stream. The error code H3_NO_ERROR SHOULD be used when requesting that the client stop sending on the request stream. Clients MUST NOT discard complete responses as a result of having their request terminated abruptly, though clients can always discard responses at their discretion for other reasons. If the server sends a partial or complete response but does not abort reading the request, clients SHOULD continue sending the content of the request and close the stream normally. Server said "I've got it" and the client keeps the receipt ๐งพโจ
4.1.1. Request Cancellation and Rejection
Once a request stream has been opened, the request MAY be cancelled by either endpoint. Clients cancel requests if the response is no longer of interest, and servers cancel requests if they are unable to or choose not to respond. When possible, it is RECOMMENDED that servers send an HTTP response with an appropriate status code rather than cancelling a request they have already begun processing. Ghosting is not the move ๐ป
Implementations SHOULD cancel requests by abruptly terminating any directions of a stream that are still open. To do so, an implementation resets the sending parts of streams and aborts reading on the receiving parts of streams (see Section 2.4 of [QUIC-TRANSPORT]) ๐
When the server cancels a request without performing any application processing, the request is considered "rejected". The server SHOULD abort its response stream with the error code H3_REQUEST_REJECTED. In this context, "processed" means that some data from the stream was passed to some higher layer of software that might have taken some action as a result. The client can treat requests rejected by the server as though they had never been sent at all, which means they can be retried later. Rejected means "never happened," so no harm, just try again later ๐
Servers MUST NOT use the H3_REQUEST_REJECTED error code for requests that were partially or fully processed. When a server abandons a response after partial processing, it SHOULD abort its response stream with the error code H3_REQUEST_CANCELLED. Don't lie about whether the work got done ๐
โโ๏ธ
Client SHOULD use the error code H3_REQUEST_CANCELLED to cancel requests. Upon receipt of this error code, a server MAY abruptly terminate the response using the error code H3_REQUEST_REJECTED if no processing was performed. Clients MUST NOT use the H3_REQUEST_REJECTED error code, except when a server has requested closure of the request stream with this error code. Only the server gets to call it rejected, bestie ๐โโ๏ธ
If a stream is cancelled after the client has received a complete response, the client MAY ignore the cancellation and use the response. However, if a stream is cancelled after receiving a partial response, the response SHOULD NOT be used. Only idempotent actions such as GET, PUT, or DELETE can be safely retried. A client SHOULD NOT automatically retry a request with a non-idempotent method unless it has some means to know that the request semantics are idempotent independent of the method, or some means to detect that the original request was never applied. See Section 9.2.2 of [HTTP] for more details ๐
4.1.2. Malformed Requests and Responses
A malformed request or response is one that is an otherwise valid sequence of frames but is invalid due to:
- the presence of prohibited fields or pseudo-header fields,
- the absence of mandatory pseudo-header fields,
- invalid values for pseudo-header fields,
- pseudo-header fields after fields,
- an invalid sequence of HTTP messages,
- the inclusion of uppercase field names, or
- the inclusion of invalid characters in field names or values.
A request or response that is defined as having content when it contains a Content-Length header field (Section 8.6 of [HTTP]) is malformed if the value of the Content-Length header field does not equal the sum of the DATA frame lengths received. A response that is defined as never having content, even when a Content-Length is present, can have a non-zero Content-Length header field even though no content is included in DATA frames. The math has to mathematically mathematic ๐งฎ
Intermediaries that process HTTP requests or responses (i.e., any intermediary not acting as a tunnel) MUST NOT forward a malformed request or response. Malformed requests or responses that are detected MUST be treated as a stream error of type H3_MESSAGE_ERROR. Middle-person, no forwarding the bad stuff ๐ซ
For malformed requests, a server MAY send an HTTP response indicating the error prior to closing or resetting the stream. Clients MUST NOT accept a malformed response. Note that these requirements are intended to protect against several types of common attacks against HTTP. They are deliberately strict, because being permissive can expose implementations to these vulnerabilities. Strict on purpose, and honestly iconic for it ๐ก๏ธ๐ฅ
4.2. HTTP Fields
HTTP messages carry metadata as a series of key-value pairs called "HTTP fields" (see Sections 6.3 and 6.5 of [HTTP]). For a listing of registered HTTP fields, check the "Hypertext Transfer Protocol (HTTP) Field Name Registry" maintained at <https://www.iana.org/assignments/http-fields/> ๐ Like HTTP/2, HTTP/3 has additional considerations related to the use of characters in field names, the Connection header field, and pseudo-header fields. Same energy as HTTP/2, but with a few extra rules ๐ธ
Field names are strings containing a subset of ASCII characters. Properties of HTTP field names and values are discussed in more detail in Section 5.1 of [HTTP]. Characters in field names MUST be converted to lowercase prior to their encoding. A request or response containing uppercase characters in field names MUST be treated as malformed. Lowercase or get rejected, no exceptions ๐ค
HTTP/3 does not use the Connection header field to indicate connection-specific fields. In this protocol, connection-specific metadata is conveyed by other means. An endpoint MUST NOT generate an HTTP/3 field section containing connection-specific fields, and any message containing connection-specific fields MUST be treated as malformed. No Connection header drama here ๐ โโ๏ธ
The only exception is the TE header field, which MAY be present in an HTTP/3 request header. When it is, it MUST NOT contain any value other than "trailers". Only "trailers" gets to be on the guest list ๐๏ธ
An intermediary transforming an HTTP/1.x message to HTTP/3 MUST remove connection-specific header fields as discussed in Section 7.6.1 of [HTTP], or their messages will be treated by other HTTP/3 endpoints as malformed. Clean up before you leave the party, or the other endpoints will be so done with you ๐งนโจ
4.2.1. Field Compression
[QPACK] describes a variation of HPACK that gives an encoder some control over how much head-of-line blocking can be caused by compression. This allows an encoder to balance compression efficiency with latency. HTTP/3 uses QPACK to compress header and trailer sections, including the control data present in the header section. Compression with a side of balance, obsessed ๐๏ธ๐
To allow for better compression efficiency, the Cookie header field ([COOKIES]) MAY be split into separate field lines, each with one or more cookie-pairs, before compression. If a decompressed field section contains multiple cookie field lines, these MUST be concatenated into a single byte string using the two-byte delimiter of "; " (ASCII 0x3b, 0x20) before being passed into a context other than HTTP/2 or HTTP/3, such as an HTTP/1.1 connection or a generic HTTP server application. Cookies get split up for the trip and glued back together at the door, iykyk ๐ช๐
4.2.2. Header Size Constraints ๐
okay so bestie, an HTTP/3 implementation is allowed to set a limit on how big a message header it'll accept on one HTTP message ๐ง (MAY). if a server gets a header section that's too big for it, it can send an HTTP 431 (Request Header Fields Too Large) status code ([RFC6585]) ๐ โโ๏ธ. clients can also just toss responses they can't process ๐๏ธ. and the size of a field list gets counted from the uncompressed fields: the length of the name and the value in bytes, plus 32 bytes of overhead for every single field ๐งฎโจ
if an implementation wants to tell its peer what its limit is, it can send that number in the SETTINGS_MAX_FIELD_SECTION_SIZE parameter ๐. once an implementation has received that parameter, it SHOULD NOT send a header that's bigger than the number it was given ๐
, because the peer will probably refuse to process it ๐. BUT ๐ค an HTTP message can go through one or more intermediaries before it reaches the origin server (see Section 3.7 of [HTTP]) ๐ช. since every implementation along the way applies the limit separately, a message that's under the limit is not guaranteed to get accepted ๐ซ . so it's not a guarantee, bestie, it's a vibe ๐ธ
4.3. HTTP Control Data ๐
okay so HTTP/3 is giving the same energy as HTTP/2 here ๐ฏโโ๏ธ and uses a bunch of pseudo-header fields, where the field name starts with the : character (ASCII 0x3a) โจ. these little guys carry the message control data (see Section 6.2 of [HTTP]) ๐๏ธ
pseudo-header fields are not HTTP fields ๐ โโ๏ธ and endpoints MUST NOT generate any pseudo-header fields other than the ones defined in this document ๐ซ. the one exception is that an extension could negotiate a change to this restriction (see Section 9) ๐งฉ
pseudo-header fields only work in the context they're defined for ๐. request pseudo-header fields MUST NOT show up in responses, and response pseudo-header fields MUST NOT show up in requests ๐. pseudo-header fields also MUST NOT appear in trailer sections ๐ซ๐. and if a request or response has undefined or invalid pseudo-header fields, endpoints MUST treat it as malformed ๐๏ธ๐
all pseudo-header fields MUST show up in the header section before the regular header fields ๐. any request or response with a pseudo-header field that shows up after a regular header field MUST be treated as malformed ๐. the order matters, periodt ๐ซถ
4.3.1. Request Pseudo-Header Fields ๐ฏ
these are the pseudo-header fields defined for requests, no cap ๐ง
:method: contains the HTTP method (Section 9 of [HTTP]) ๐โโ๏ธ:scheme: contains the scheme portion of the target URI (Section 3.1 of [URI]) ๐
the :scheme pseudo-header is not restricted to URIs with the scheme "http" and "https" ๐. a proxy or gateway can translate requests for non-HTTP schemes, which lets you use HTTP to talk to non-HTTP services ๐คฏ. see Section 3.1.2 for guidance on using a scheme other than "https" ๐
:authority: contains the authority portion of the target URI (Section 3.2 of [URI]) ๐ก. the authority MUST NOT include the deprecated userinfo subcomponent for URIs of scheme "http" or "https" ๐ โโ๏ธ
so the HTTP/1.1 request line can be reproduced accurately ๐, this pseudo-header field MUST be omitted when translating from an HTTP/1.1 request that has a request target in a method-specific form (see Section 7.1 of [HTTP]) โ๏ธ. clients that generate HTTP/3 requests directly SHOULD use the :authority pseudo-header field instead of the Host header field ๐
. and an intermediary that converts an HTTP/3 request to HTTP/1.1 MUST create a Host field if one isn't present in the request, by copying the value of the :authority pseudo-header field ๐ช
:path: contains the path and query parts of the target URI (the "path-absolute" production, and optionally a?character (ASCII 0x3f) followed by the "query" production; see Sections 3.3 and 3.4 of [URI]) ๐ฃ๏ธ
this pseudo-header field MUST NOT be empty for "http" or "https" URIs ๐
, and "http" or "https" URIs that don't have a path component MUST include a value of / (ASCII 0x2f) ๐. an OPTIONS request without a path component uses the value * (ASCII 0x2a) for the :path pseudo-header field (see Section 7.1 of [HTTP]) โจ
all HTTP/3 requests MUST include exactly one value for the :method, :scheme, and :path pseudo-header fields, unless the request is a CONNECT request (see Section 4.4) ๐
if the :scheme pseudo-header field identifies a scheme with a mandatory authority component (that includes "http" and "https"), the request MUST contain either an :authority pseudo-header field or a Host header field ๐ท๏ธ. if these fields are present, they MUST NOT be empty ๐ซ. and if both fields are present, they MUST contain the same value ๐ฏ. but if the scheme doesn't have a mandatory authority component and none is given in the request target, the request MUST NOT contain the :authority pseudo-header or Host header fields ๐
an HTTP request that leaves out mandatory pseudo-header fields, or has invalid values for them, is malformed ๐๏ธ
HTTP/3 doesn't have a way to carry the version identifier that's in the HTTP/1.1 request line ๐คทโโ๏ธ. HTTP/3 requests implicitly have a protocol version of "3.0" ๐ฆ
4.3.2. Response Pseudo-Header Fields ๐ธ
for responses, there's a single :status pseudo-header field that carries the HTTP status code (see Section 15 of [HTTP]) ๐ข. this pseudo-header field MUST be included in all responses ๐ฃ, otherwise the response is malformed (see Section 4.1.2) ๐
HTTP/3 also doesn't define a way to carry the version or reason phrase from an HTTP/1.1 status line ๐. HTTP/3 responses implicitly have a protocol version of "3.0" ๐
4.4. The CONNECT Method ๐
the CONNECT method asks the recipient to set up a tunnel to the destination origin server identified by the request-target (see Section 9.3.6 of [HTTP]) ๐งฆ. it's mainly used with HTTP proxies to establish a TLS session with an origin server so you can interact with "https" resources ๐
in HTTP/1.x, CONNECT turns a whole HTTP connection into a tunnel to a remote host ๐. but in HTTP/2 and HTTP/3, CONNECT sets up a tunnel over a single stream ๐ฏ, which is so much cleaner, bestie ๐ซถ
a CONNECT request MUST be built like this ๐งฑ:
- the
:methodpseudo-header field is set to "CONNECT" ๐๏ธ - the
:schemeand:pathpseudo-header fields are omitted โ๏ธ - the
:authoritypseudo-header field contains the host and port to connect to (this is the same as the authority-form of the request-target of CONNECT requests; see Section 7.1 of [HTTP]) ๐
the request stream stays open at the end of the request so it can carry the data being transferred ๐. a CONNECT request that doesn't follow these restrictions is malformed ๐๏ธ
a proxy that supports CONNECT sets up a TCP connection ([RFC0793]) to the server identified in the :authority pseudo-header field ๐. once that connection is successfully established, the proxy sends a HEADERS frame with a 2xx series status code to the client, as defined in Section 15.3 of [HTTP] ๐
every DATA frame on the stream lines up with data sent or received on the TCP connection ๐งต. the payload of any DATA frame the client sends gets transmitted by the proxy to the TCP server, and data the proxy gets back from the TCP server gets packaged up into DATA frames ๐ฆ. heads up, though: the size and number of TCP segments isn't guaranteed to map predictably onto the size and number of HTTP DATA or QUIC STREAM frames ๐ค
once the CONNECT method has completed, only DATA frames are allowed on the stream ๐
โโ๏ธ. extension frames MAY be used if the definition of the extension specifically permits it ๐งฉ. and receiving any other known frame type MUST be treated as a connection error of type H3_FRAME_UNEXPECTED ๐ฅ
either peer can close the TCP connection ๐ช. when the client ends the request stream (meaning the receive stream at the proxy enters the "Data Recvd" state), the proxy will set the FIN bit on its connection to the TCP server ๐. when the proxy gets a packet with the FIN bit set, it closes the send stream it sends to the client ๐. TCP connections that stay half closed in one direction aren't invalid ๐โโ๏ธ, but servers often handle them poorly ๐ฌ, so clients SHOULD NOT close a stream for sending while they still expect to receive data from the target of the CONNECT ๐ซท
a TCP connection error gets signaled by abruptly terminating the stream ๐ฅ. a proxy treats any error in the TCP connection (which includes receiving a TCP segment with the RST bit set) as a stream error of type H3_CONNECT_ERROR ๐จ
on the flip side, if a proxy detects an error with the stream or the QUIC connection, it MUST close the TCP connection ๐. if the proxy detects that the client reset the stream or aborted reading from it, it MUST close the TCP connection too ๐. and if the client resets the stream or aborts reading, a proxy SHOULD do the same thing on the other direction so both directions of the stream get cancelled ๐ช. in all of these cases, if the underlying TCP implementation permits it, the proxy SHOULD send a TCP segment with the RST bit set ๐
since CONNECT creates a tunnel to an arbitrary server, proxies that support CONNECT SHOULD restrict its use to a set of known ports or a list of safe request targets ๐จ (see Section 9.3.6 of [HTTP] for more details). this is a big deal, bestie, so don't skip it ๐
4.5. HTTP Upgrade ๐ฆ
HTTP/3 does not support the HTTP Upgrade mechanism (Section 7.8 of [HTTP]) or the 101 (Switching Protocols) informational status code (Section 15.2.2 of [HTTP]) ๐ โโ๏ธโจ so no switching protocols here, that's just not the HTTP/3 lifestyle ๐
4.6. Server Push โจ
okay so server push is when the server goes "I think you're gonna ask for this next" and sends the request-response pair to the client early ๐ฎ. It's a trade: you burn more network usage for a shot at lower latency ๐ . HTTP/3 server push is kinda giving Section 8.2 of [HTTP/2] vibes, but the mechanisms are totally different ๐ฆ.
Every push gets its own push ID from the server ๐. That ID is how everyone refers to the push for the entire lifetime of the HTTP/3 connection, so it's the main character of the whole thing ๐.
The push ID space starts at zero and goes up to a max set by the MAX_PUSH_ID frame ๐ง. The server literally can't push until the client has sent a MAX_PUSH_ID frame, so the client is the bouncer at the door ๐
. Clients use MAX_PUSH_ID frames to control how many pushes the server is allowed to promise. A server SHOULD use push IDs sequentially, beginning from zero ๐ข. A client MUST treat receipt of a push stream as a connection error of type H3_ID_ERROR when no MAX_PUSH_ID frame has been sent, or when the stream references a push ID that is greater than the maximum push ID ๐.
The push ID shows up in one or more PUSH_PROMISE frames that carry the control data and header fields of the request message ๐. Those frames get sent on the request stream that generated the push, which is how the server push gets tied back to a client request ๐ซถ. When the same push ID is promised on multiple request streams, the decompressed request field sections MUST contain the same fields in the same order, and both the name and the value in each field MUST be identical ๐. No cap, that's a strict vibe.
Then the push ID gets attached to the push stream that actually fulfills those promises โจ. The push stream names the push ID of the promise it fulfills, then carries a response to the promised request as described in Section 4.1 ๐.
Last but not least, the push ID can show up in CANCEL_PUSH frames; see Section 7.2.3 ๐ซ. Clients use this frame to say "nah, I don't want that promised resource." Servers use it to say "cool, I won't be fulfilling a previous promise after all" ๐ญ.
Not every request can be pushed, sorry bestie ๐ฅบ. A server MAY push requests that have the following properties:
- cacheable; see Section 9.2.3 of [HTTP]
- safe; see Section 9.2.1 of [HTTP]
- does not include request content or a trailer section
The server MUST include a value in the :authority pseudo-header field for which the server is authoritative ๐ธ. If the client hasn't validated the connection for the origin indicated by the pushed request yet, it MUST perform the same verification process it would do before sending a request for that origin on the connection; see Section 3.3 ๐. If this verification fails, the client MUST NOT consider the server authoritative for that origin ๐ท.
Clients SHOULD send a CANCEL_PUSH frame upon receipt of a PUSH_PROMISE frame carrying a request that is not cacheable, is not known to be safe, indicates the presence of request content, or is one the client doesn't consider the server authoritative for ๐
โโ๏ธ. Any corresponding responses MUST NOT be used or cached. Period ๐
.
Each pushed response is associated with one or more client requests ๐งฉ. The push is associated with the request stream where the PUSH_PROMISE frame was received. The same server push can be linked to additional client requests using a PUSH_PROMISE frame with the same push ID on multiple request streams. These associations don't change how the protocol operates, but user agents MAY consider them when deciding how to use pushed resources ๐ง .
Ordering matters here, ngl ๐ฆ. The server SHOULD send PUSH_PROMISE frames before sending HEADERS or DATA frames that reference the promised responses. This lowers the chance that a client requests a resource the server is already about to push ๐โโ๏ธ.
Due to reordering, push stream data can show up before its PUSH_PROMISE frame ๐. When a client gets a new push stream with a push ID it hasn't seen yet, both the associated client request and the pushed request header fields are unknown, which is a lot ๐ต. The client can buffer the stream data while it waits for the matching PUSH_PROMISE. It can also use stream flow control (Section 4.1 of [QUIC-TRANSPORT]) to limit how much data a server may commit to the pushed stream ๐ง. Clients SHOULD abort reading and discard data already read from push streams if no corresponding PUSH_PROMISE frame is processed in a reasonable amount of time โฐ.
Push stream data can also arrive after a client has cancelled a push ๐ป. In that case, the client can abort reading the stream with an error code of H3_REQUEST_CANCELLED ๐. That tells the server not to transfer any more data, and the data that does arrive will get tossed when it shows up ๐๏ธ.
Pushed responses that are cacheable (see Section 3 of [HTTP-CACHING]) can be stored by the client, if it implements an HTTP cache ๐๏ธ. Pushed responses are considered successfully validated on the origin server (e.g., if the "no-cache" cache response directive is present; see Section 5.2.2.4 of [HTTP-CACHING]) at the time the pushed response is received โ
.
Pushed responses that are not cacheable MUST NOT be stored by any HTTP cache ๐ โโ๏ธ. They MAY be made available to the application separately ๐ฒ.
5. Connection Closure ๐
Once it's established, an HTTP/3 connection can be used for many requests and responses over time until it gets closed ๐. Connection closure can happen in a few different ways, so keep your eyes open ๐.
5.1. Idle Connections ๐ค
Each QUIC endpoint declares an idle timeout during the handshake ๐ค. If the QUIC connection stays idle (no packets received) for longer than that duration, the peer will assume the connection has been closed ๐ด. HTTP/3 implementations will need to open a new HTTP/3 connection for new requests if the existing connection has been idle for longer than the idle timeout negotiated during the QUIC handshake. They SHOULD do so when they're approaching the idle timeout too; see Section 10.1 of [QUIC-TRANSPORT] ๐โโ๏ธ.
HTTP clients are expected to ask the transport to keep connections open while responses are still outstanding for requests or server pushes, as described in Section 10.1.2 of [QUIC-TRANSPORT] ๐ซง. If the client isn't expecting a response from the server, letting an idle connection time out is preferred over spending effort keeping a connection alive that might not be needed ๐ฅฑ. A gateway MAY maintain connections in anticipation of need, rather than take the latency hit of establishing a connection to servers ๐. Servers SHOULD NOT actively keep connections open ๐ โโ๏ธ.
5.2. Connection Shutdown โจ
okay so even when a connection is totally busy, either endpoint can decide it's time to stop using it and start a graceful close ๐ซถ To kick off a graceful shutdown of an HTTP/3 connection, the endpoint sends a GOAWAY frame. That frame carries an identifier telling the receiver the range of requests or pushes that were, or might have been, processed on this connection. The server puts in a client-initiated bidirectional stream ID, and the client puts in a push ID. Any requests or pushes with that identifier or higher get rejected (Section 4.1.1) by whoever sent the GOAWAY. This identifier MAY be zero if nothing got processed ๐ธ
The info in the GOAWAY frame is what lets the client and server agree on which requests or pushes were accepted before the HTTP/3 connection shuts down, so nobody's left guessing ๐ฅ When an endpoint sends a GOAWAY, it SHOULD explicitly cancel (see Sections 4.1.1 and 7.2.3) any requests or pushes with identifiers greater than or equal to the one it sent. That cleans up the transport state for those streams. It SHOULD keep doing that as more requests or pushes show up ๐
Endpoints MUST NOT start new requests or promise new pushes on the connection once they've received a GOAWAY from their peer. Clients MAY open a new connection to send more requests ๐ฆ
some requests or pushes might already be in transit, so here's the tea:
- Once a client gets a
GOAWAYframe, if it already sent requests with a stream ID greater than or equal to the identifier in that frame, those requests won't be processed. Clients can safely retry unprocessed requests on a different HTTP connection ๐ง A client that can't retry requests loses every request that's in flight when the server closes the connection ๐
Requests on stream IDs lower than the stream ID in a GOAWAY frame from the server might have been processed. Their status can't be known until one of these happens: a response arrives, the stream gets reset on its own, another GOAWAY shows up with a lower stream ID than that request's, or the connection ends ๐
Servers MAY reject individual requests on streams below the indicated ID if those requests weren't processed.
- If a server gets a
GOAWAYframe after it has promised pushes with a push ID greater than or equal to the identifier in that frame, those pushes won't be accepted ๐ญ
Servers SHOULD send a GOAWAY frame whenever they know ahead of time that a connection is closing, even if the heads-up is tiny. That way the remote peer can tell whether a request was partially processed ๐ For example, if an HTTP client sends a POST at the same moment a server closes a QUIC connection, the client has no way to know whether the server started working on that POST, unless the server sent a GOAWAY frame saying which streams it might have acted on. Main character energy for the server is sending that heads-up ๐
An endpoint MAY send multiple GOAWAY frames with different identifiers, but the identifier in each new frame MUST NOT be greater than the identifier in any earlier frame, since clients might already have retried unprocessed requests on another HTTP connection. If an endpoint receives a GOAWAY with a bigger identifier than one it already got, that MUST be treated as a connection error of type H3_ID_ERROR ๐ซ
An endpoint trying to shut down gracefully can send a GOAWAY frame with the maximum possible value (2^62-4 for servers, 2^62-1 for clients). That makes sure the peer stops creating new requests or pushes ๐ After giving in-flight requests or pushes time to arrive, the endpoint can send another GOAWAY frame saying which requests or pushes it might still accept before the connection ends. This is how a connection gets cleanly shut down without losing requests โจ
Clients have a bit more freedom with the Push ID field in the GOAWAY they send ๐ A value of 2^62-1 says the server can keep fulfilling pushes that were already promised. A smaller value means the client will reject pushes with push IDs greater than or equal to that value. Just like the server, the client MAY send later GOAWAY frames, as long as the push ID in them is no greater than any value it sent before ๐
Even when a GOAWAY says a request or push won't be processed or accepted, the transport resources underneath still exist ๐ The endpoint that started those requests can cancel them to clean up transport state.
Once every accepted request and push has been processed, the endpoint can let the connection go idle, or it MAY start an immediate closure of the connection ๐ฆ An endpoint that finishes a graceful shutdown SHOULD use the H3_NO_ERROR error code when it closes the connection ๐ธ
If a client has used up all its available bidirectional stream IDs with requests, the server doesn't need to send a GOAWAY frame, since the client can't make any more requests anyway ๐
5.3. Immediate Application Closure ๐
An HTTP/3 implementation can slam the brakes and immediately close the QUIC connection at any time ๐ค That sends a QUIC CONNECTION_CLOSE frame to the peer, which tells it the application layer has ended the connection. The application error code in that frame tells the peer why the connection is closing. See Section 8 for the error codes you can use when closing a connection in HTTP/3 ๐ง
Before closing, a GOAWAY frame MAY be sent so the client can retry some requests ๐ซถ Putting the GOAWAY frame in the same packet as the QUIC CONNECTION_CLOSE frame improves the chances that clients actually receive it ๐ฆ
If there are open streams that haven't been explicitly closed, they get implicitly closed when the connection closes. See Section 10.2 of [QUIC-TRANSPORT] for that ๐
5.4. Transport Closure ๐
For various reasons, the QUIC transport might tell the application layer that the connection has terminated ๐ฎ That could be an explicit closure by the peer, a transport-level error, or a change in network topology that interrupts connectivity ๐ก
If a connection terminates without a GOAWAY frame, clients MUST assume that any request that was sent, whether in whole or in part, might have been processed. no skipping this part, bestie ๐จ
6. Stream Mapping and Usage ๐บ๏ธ
A QUIC stream gives you reliable, in-order delivery of bytes, but it promises nothing about the order of delivery relative to bytes on other streams ๐งต In version 1 of QUIC, the stream data carrying HTTP frames rides in QUIC STREAM frames, but that framing is invisible to the HTTP framing layer. The transport layer buffers and orders the received stream data, then hands the application a reliable byte stream ๐ QUIC does allow out-of-order delivery within a stream, but HTTP/3 doesn't use that feature ๐
โโ๏ธ
QUIC streams can be unidirectional, carrying data only from initiator to receiver, or bidirectional, carrying data both ways ๐ฆ Either the client or the server can initiate them. For more detail on QUIC streams, see Section 2 of [QUIC-TRANSPORT] ๐
When HTTP fields and data go over QUIC, the QUIC layer handles most of the stream management, so HTTP doesn't have to do any separate multiplexing. Data sent over a QUIC stream always maps to one particular HTTP transaction or to the entire HTTP/3 connection context ๐
6.1. Bidirectional Streams ๐ธ
All client-initiated bidirectional streams are used for HTTP requests and responses ๐ Being bidirectional means the response can be easily matched up with its request. These streams are called request streams ๐ซถ
So the client's first request goes on QUIC stream 0, and later requests go on streams 4, 8, and so on ๐ง To let those streams open, an HTTP/3 server SHOULD configure non-zero minimum values for the number of permitted streams and the initial stream flow-control window. To avoid unnecessarily limiting parallelism, at least 100 request streams SHOULD be permitted at a time ๐ฅ
HTTP/3 doesn't use server-initiated bidirectional streams, though an extension could define a use for them ๐ Clients MUST treat receiving a server-initiated bidirectional stream as a connection error of type H3_STREAM_CREATION_ERROR, unless such an extension has been negotiated ๐ซ
6.2. Unidirectional Streams ๐ฆ
okay so unidirectional streams can go either direction, and they're used for a bunch of different jobs โจ The job gets announced by a stream type, sent as a variable-length integer right at the start of the stream. Whatever comes after that integer is shaped by the stream type, so the type basically tells you how to read the rest ๐
Unidirectional Stream Header { Stream Type (i), }
Figure 1: Unidirectional Stream Header
this doc defines two stream types: control streams (Section 6.2.1) and push streams (Section 6.2.2) ๐ธ [QPACK] adds two more, and extensions to HTTP/3 can define others (see Section 9 for the deets). Some stream types are reserved too (Section 6.2.3) ๐ง
ok real talk, the early part of an HTTP/3 connection is super sensitive to what happens on unidirectional streams ๐ญ If an endpoint is stingy with how many of these streams or how much flow-control window they get, the other side is way more likely to hit that limit early and get stuck. So implementations should keep in mind that remote peers may want to play with reserved stream behavior (Section 6.2.3) on some of the unidirectional streams they're allowed to open ๐
Every endpoint needs to make at least one unidirectional stream for the HTTP control stream ๐ QPACK needs two more, and other extensions might need even more. So the transport parameters that clients and servers send MUST let the peer create at least three unidirectional streams ๐ฅ Those transport parameters SHOULD also give each unidirectional stream at least 1,024 bytes of flow-control credit ๐
Heads up, an endpoint doesn't have to hand out extra credit for more unidirectional streams if its peer burns through all the starting credit before opening the critical ones ๐ Endpoints SHOULD make the HTTP control stream first, along with the unidirectional streams that mandatory extensions need (like the QPACK encoder and decoder streams), and only then make more streams as their peer allows ๐ซถ
If the stream header says the type isn't supported by the recipient, the rest of the stream can't be consumed, because the recipient doesn't know what it means ๐ Recipients of unknown stream types MUST either abort reading the stream or throw away the incoming data without any further processing. If they do abort reading, the recipient SHOULD use the H3_STREAM_CREATION_ERROR error code or a reserved error code (Section 8.1). And the recipient MUST NOT treat unknown stream types as a connection error of any kind ๐ no drama, just ignore it, bestie โจ
Some stream types can actually change connection state, so a recipient SHOULD NOT throw away data from incoming unidirectional streams before it has read the stream type ๐
Implementations MAY send stream types before they know whether the peer supports them ๐ง BUT stream types that could change the state or semantics of existing protocol parts, including QPACK or other extensions, MUST NOT be sent until the peer is known to support them ๐ญ do not skip this one
A sender can close or reset a unidirectional stream unless the spec says otherwise ๐ A receiver MUST tolerate unidirectional streams being closed or reset before it gets the stream header ๐ซถ
6.2.1. Control Streams ๐
A control stream is marked with a stream type of 0x00 ๐ The data on it is HTTP/3 frames, as defined in Section 7.2 โจ
Each side MUST start a single control stream at the very beginning of the connection, and its SETTINGS frame has to be the first frame on that stream ๐ฅ If the first frame on the control stream is any other frame type, that MUST be treated as a connection error of type H3_MISSING_SETTINGS ๐ Only one control stream per peer is allowed. If a second stream shows up claiming to be a control stream, that MUST be treated as a connection error of type H3_STREAM_CREATION_ERROR ๐
โโ๏ธ The sender MUST NOT close the control stream, and the receiver MUST NOT ask the sender to close it ๐ญ If either control stream gets closed at any point, that MUST be treated as a connection error of type H3_CLOSED_CRITICAL_STREAM ๐ฅ (Connection errors are covered in Section 8.)
The control stream is how everything else gets managed, so endpoints SHOULD give it enough flow-control credit that the peer's control stream never gets blocked ๐ซง
Why a pair of unidirectional streams instead of one bidirectional stream? Because it lets either peer send data the second it's ready ๐ฆ Depending on whether 0-RTT is available on the QUIC connection, either the client or the server might be able to send stream data first โจ
6.2.2. Push Streams ๐ธ
Server push is an optional HTTP/2 feature that lets a server start a response before anyone has made a request ๐ Check out Section 4.6 for the full tea ๐
A push stream is marked with a stream type of 0x01, followed by the push ID of the promise it fulfills, encoded as a variable-length integer ๐ The rest of the data on the stream is HTTP/3 frames (Section 7.2), and they fulfill a promised server push with zero or more interim HTTP responses followed by a single final HTTP response (Section 4.1) ๐ง Server push and push IDs are explained in Section 4.6 โจ
Only servers can push ๐
โโ๏ธ If a server gets a client-initiated push stream, that MUST be treated as a connection error of type H3_STREAM_CREATION_ERROR ๐
Push Stream Header { Stream Type (i) = 0x01, Push ID (i), }
Figure 2: Push Stream Header
A client SHOULD NOT abort reading on a push stream before it has read the push stream header ๐ Doing that could leave the client and server disagreeing about which push IDs have already been used ๐ตโ๐ซ
Each push ID MUST only be used once in a push stream header ๐ If a client notices that a push stream header has a push ID that was already used in another push stream header, the client MUST treat this as a connection error of type H3_ID_ERROR ๐จ
6.2.3. Reserved Stream Types ๐ง
Stream types in the format 0x1f * N + 0x21, where N is a non-negative integer, are reserved so that the rule "unknown types get ignored" actually gets tested ๐งช These streams don't have any semantics, and they can be sent whenever application-layer padding is wanted โจ They MAY also be sent on connections where no data is moving at the moment. Endpoints MUST NOT treat these streams as having any meaning when they receive them ๐ซถ
The payload and length of these streams can be picked however the sending implementation likes ๐ฒ When an implementation sends a reserved stream type, it MAY either end the stream cleanly or reset it. If it resets the stream, either the H3_NO_ERROR error code or a reserved error code (Section 8.1) SHOULD be used ๐
7. HTTP Framing Layer ๐
HTTP frames ride on QUIC streams, as described in Section 6 ๐ฆ HTTP/3 has three stream types: control stream, request stream, and push stream. This section covers the HTTP/3 frame formats and which stream types each one is allowed on. Table 1 has the overview โจ And if you want to compare HTTP/2 and HTTP/3 frames side by side, that's in Appendix A.2 ๐
<pre> +==============+================+================+========+=========+
| Frame | Control Stream | Request | Push | Section |
|---|---|---|---|---|
| Stream | Stream |
+==============+================+================+========+=========+
| DATA | No | Yes | Yes | Section |
|---|---|---|---|---|
| 7.2.1 |
+--------------+----------------+----------------+--------+---------+
| HEADERS | No | Yes | Yes | Section |
|---|---|---|---|---|
| 7.2.2 |
+--------------+----------------+----------------+--------+---------+
| CANCEL_PUSH | Yes | No | No | Section |
|---|---|---|---|---|
| 7.2.3 |
+--------------+----------------+----------------+--------+---------+
| SETTINGS | Yes (1) | No | No | Section |
|---|---|---|---|---|
| 7.2.4 |
+--------------+----------------+----------------+--------+---------+
| PUSH_PROMISE | No | Yes | No | Section |
|---|---|---|---|---|
| 7.2.5 |
+--------------+----------------+----------------+--------+---------+
| GOAWAY | Yes | No | No | Section |
|---|---|---|---|---|
| 7.2.6 |
+--------------+----------------+----------------+--------+---------+
| MAX_PUSH_ID | Yes | No | No | Section |
|---|---|---|---|---|
| 7.2.7 |
+--------------+----------------+----------------+--------+---------+
| Reserved | Yes | Yes | Yes | Section |
|---|---|---|---|---|
| 7.2.8 |
+--------------+----------------+----------------+--------+---------+ </pre>
Table 1: HTTP/3 Frames and Stream Type Overview
The SETTINGS frame can only show up as the first frame of a control stream ๐ Table 1 flags that with a (1). Specific guidance lives in each frame's section ๐ง
Heads up that unlike QUIC frames, HTTP/3 frames can span multiple packets ๐ฆ
7.1. Frame Layout ๐ฆ
Every frame has this format:
HTTP/3 Frame Format { Type (i), Length (i), Frame Payload (..), }
Figure 3: HTTP/3 Frame Format
A frame has these fields:
- Type: a variable-length integer that identifies the frame type ๐ท๏ธ
- Length: a variable-length integer that says how many bytes the Frame Payload is ๐
- Frame Payload: the payload, whose meaning is set by the Type field ๐
Each frame's payload MUST contain exactly the fields listed in its description ๐ A frame payload with extra bytes after those fields, or one that ends before the listed fields are done, MUST be treated as a connection error of type H3_FRAME_ERROR ๐ฅ Redundant length encodings in particular MUST be checked to make sure they agree with each other (see Section 10.8) ๐
When a stream ends cleanly and the last frame on it was cut off, that MUST be treated as a connection error of type H3_FRAME_ERROR ๐ฑ Streams that end abruptly can get reset at any point in a frame ๐ซ
7.2. Frame Definitions ๐
7.2.1. DATA ๐
DATA frames (type=0x00) carry arbitrary, variable-length sequences of bytes that go with HTTP request or response content ๐ซง
DATA frames MUST be associated with an HTTP request or response ๐ฏ If a DATA frame arrives on a control stream, the recipient MUST respond with a connection error of type H3_FRAME_UNEXPECTED ๐จ
<pre> DATA Frame { Type (i) = 0x00, Length (i), Data (..), } </pre>
Figure 4: DATA Frame
7.2.2. HEADERS ๐จ
okay bestie, HEADERS frames (type=0x01 ๐ฆ) carry an HTTP field section that's been squished down with QPACK ๐
. Go read [QPACK] if you want the deets ๐ง.
HEADERS Frame {
Type (i) = 0x01,
Length (i),
Encoded Field Section (..),
}
Figure 5: HEADERS Frame
HEADERS frames can only be sent on request streams or push streams ๐. If one shows up on a control stream, the recipient MUST respond with a connection error of type H3_FRAME_UNEXPECTED ๐ญ. Not a vibe.
7.2.3. CANCEL_PUSH ๐ โโ๏ธ
the CANCEL_PUSH frame (type=0x03) is how you say "nah, I don't want that server push anymore" ๐
before the push stream even shows up. It names the push by its push ID (see Section 4.6), which is a variable-length integer ๐ข.
When a client sends a CANCEL_PUSH frame, it's basically saying it doesn't wish to receive the promised resource ๐ซถ. The server SHOULD abort sending the resource, but how it does that depends on what the push stream is doing ๐ธ. If the server hasn't created a push stream yet, it doesn't make one. If the push stream is open, the server SHOULD abruptly terminate that stream. If the push stream has already ended, the server MAY still abruptly terminate the stream or MAY take no action. Choices, choices ๐.
Now the server side ๐. A server sends a CANCEL_PUSH frame to say it won't be fulfilling a promise it already sent. The client can't count on that promise coming true, unless it already got and processed the promised response ๐. Whether or not a push stream has been opened, a server SHOULD send a CANCEL_PUSH frame when it figures out the promise won't be fulfilled โจ. If a stream is already open, the server can abort sending on it with an error code of H3_REQUEST_CANCELLED.
Sending a CANCEL_PUSH frame doesn't directly change existing push streams ๐ฆ. A client SHOULD NOT send a CANCEL_PUSH frame once it has already received a corresponding push stream ๐. And here's the plot twist: a push stream could still show up after the client sent a CANCEL_PUSH, because the server might not have processed it yet ๐. In that case the client SHOULD abort reading the stream with an error code of H3_REQUEST_CANCELLED. Respect the bounce.
A CANCEL_PUSH frame goes on the control stream ๐. Receiving one on any other stream MUST be treated as a connection error of type H3_FRAME_UNEXPECTED ๐
โโ๏ธ.
CANCEL_PUSH Frame {
Type (i) = 0x03,
Length (i),
Push ID (i),
}
Figure 6: CANCEL_PUSH Frame
The frame carries a push ID as a variable-length integer. That Push ID field tells you which server push is getting cancelled (see Section 4.6) ๐ง. If a CANCEL_PUSH frame references a push ID that's greater than what's currently allowed on the connection, this MUST be treated as a connection error of type H3_ID_ERROR ๐ญ.
Reordering is sneaky ๐. If the client gets a CANCEL_PUSH frame, that frame might name a push ID that no PUSH_PROMISE frame has mentioned yet, because things arrived out of order. But if a server gets a CANCEL_PUSH frame for a push ID that no PUSH_PROMISE frame has mentioned yet, this MUST be treated as a connection error of type H3_ID_ERROR ๐
.
7.2.4. SETTINGS โ๏ธ
the SETTINGS frame (type=0x04) is the config drop ๐. It carries parameters that shape how endpoints talk to each other, like preferences and limits on what the peer can do ๐. A single SETTINGS parameter is called a "setting", and its identifier and value are called a "setting identifier" and a "setting value" ๐ซถ.
SETTINGS frames always apply to the whole HTTP/3 connection, never one single stream ๐. Each peer MUST send a SETTINGS frame as the first frame of each control stream (see Section 6.2.1), and it MUST NOT send another one after that ๐
โโ๏ธ. If an endpoint gets a second SETTINGS frame on the control stream, it MUST respond with a connection error of type H3_FRAME_UNEXPECTED ๐ญ.
SETTINGS frames MUST NOT be sent on any stream other than the control stream ๐ซ. If an endpoint receives one on a different stream, it MUST respond with a connection error of type H3_FRAME_UNEXPECTED ๐.
Settings aren't negotiated, fr ๐ง . They just describe what the sending peer is like, so the receiver can use that info. BUT, it's kinda like a negotiation: each peer advertises the values it supports ๐ค. The definition of each setting says how the two sets get combined to pick what's actually used. SETTINGS doesn't give you a way to tell when that choice kicks in ๐.
Each peer can advertise different values for the same parameter ๐ฆ. For example, a client might be totally fine eating a huge response field section, while servers are more cautious about how big requests can get ๐.
The same setting identifier MUST NOT show up more than once in a SETTINGS frame ๐ซ. A receiver MAY treat duplicate setting identifiers as a connection error of type H3_SETTINGS_ERROR ๐.
The payload is zero or more parameters, and each one is a setting identifier plus a value, both encoded as QUIC variable-length integers ๐ข.
Setting {
Identifier (i),
Value (i),
}
SETTINGS Frame {
Type (i) = 0x04,
Length (i),
Setting (..) ...,
}
Figure 7: SETTINGS Frame
An implementation MUST ignore any parameter whose identifier it doesn't understand ๐ซถ. Main character energy: be chill about the stuff you don't know.
7.2.4.1. Defined SETTINGS Parameters ๐
Here are the settings HTTP/3 defines ๐:
SETTINGS_MAX_FIELD_SECTION_SIZE (0x06): The default value is unlimited. See Section 4.2.2 for usage ๐.
Now for the reserved ones ๐ง. Setting identifiers in the format 0x1f * N + 0x21 (for non-negative integer values of N) are reserved to test the rule that unknown identifiers get ignored ๐. These have no defined meaning. Endpoints SHOULD include at least one such setting in their SETTINGS frame ๐
. Endpoints MUST NOT consider such settings to have any meaning when they receive them ๐. Since the setting has no defined meaning, its value can be whatever the implementation picks ๐ฒ.
Setting identifiers defined in [HTTP/2] that have no HTTP/3 setting counterpart are reserved too (Section 11.2.2) ๐ธ. These reserved settings MUST NOT be sent, and receiving one MUST be treated as a connection error of type H3_SETTINGS_ERROR ๐ญ. Don't be that person.
Extensions can define more settings as well; see Section 9 for more details ๐.
7.2.4.2. Initialization ๐ฑ
An HTTP implementation MUST NOT send frames or requests that would be invalid based on its current understanding of the peer's settings ๐ โโ๏ธ. Common sense, but make it a rule ๐ .
Every setting starts at an initial value โจ. Each endpoint SHOULD use those initial values to send messages before the peer's SETTINGS frame arrives, since packets carrying settings can get lost or delayed ๐ฆ. When the SETTINGS frame finally lands, any settings change to their new values ๐.
This means you don't have to wait for the SETTINGS frame before sending messages ๐ซถ. Endpoints MUST NOT require any data from the peer before sending the SETTINGS frame, and settings MUST be sent as soon as the transport is ready to send data ๐.
For servers, the initial value of each client setting is the default value ๐.
For clients using a 1-RTT QUIC connection, the initial value of each server setting is the default value ๐ธ. 1-RTT keys will always be available before the packet with SETTINGS gets processed by QUIC, even if the server sends SETTINGS right away ๐. Clients SHOULD NOT wait forever for SETTINGS to arrive before sending requests ๐ , but they SHOULD process received datagrams so they're more likely to process SETTINGS before the first request goes out ๐ง.
With a 0-RTT QUIC connection, the initial value of each server setting is whatever value was used in the previous session ๐. Clients SHOULD store the settings the server gave in the HTTP/3 connection where resumption info was provided, but they MAY opt not to store settings in certain cases (for example, if the session ticket arrives before the SETTINGS frame) ๐๏ธ. A client MUST comply with stored settings (or default values if nothing was stored) when attempting 0-RTT ๐. Once a server has provided new settings, clients MUST comply with those values ๐.
A server can remember the settings it advertised, or store an integrity-protected copy of the values in the ticket and recover that info when it accepts 0-RTT data ๐. A server uses the HTTP/3 settings values to decide whether to accept 0-RTT data ๐ค. If the server can't determine that the settings a client remembers are compatible with its current settings, it MUST NOT accept 0-RTT data ๐ โโ๏ธ. Remembered settings are compatible if a client following those settings wouldn't violate the server's current settings โจ.
A server MAY accept 0-RTT and then provide different settings in its SETTINGS frame ๐ฆ. If the server accepts 0-RTT data, its SETTINGS frame MUST NOT reduce any limits or change any values the client might violate with its 0-RTT data ๐ซ. The server MUST include all settings that differ from their default values ๐. If a server accepts 0-RTT and then sends settings that aren't compatible with the previously specified ones, this MUST be treated as a connection error of type H3_SETTINGS_ERROR ๐ญ. And if a server accepts 0-RTT but then sends a SETTINGS frame that leaves out a setting value the client understands (apart from reserved setting identifiers) that was previously specified to have a non-default value, this MUST be treated as a connection error of type H3_SETTINGS_ERROR ๐. Consistency is everything, bestie ๐ซถ.
7.2.5. PUSH_PROMISE ๐
okay so the PUSH_PROMISE frame (type=0x05) is how a server sends a promised request header section to the client on a request stream. it's basically the server going "hey bestie, I've got this for you before you even ask" ๐ง
PUSH_PROMISE Frame {
Type (i) = 0x05,
Length (i),
Push ID (i),
Encoded Field Section (..),
}
Figure 8: PUSH_PROMISE Frame
the payload has:
- Push ID: a variable-length integer that identifies the server push operation ๐ฆ. push IDs show up in push stream headers (Section 4.6) and in CANCEL_PUSH frames.
- Encoded Field Section: QPACK-encoded request header fields for the promised response ๐ . see [QPACK] for more details.
ok the rules, and these are serious, so listen up ๐. a server MUST NOT use a push ID that's bigger than what the client gave it in a MAX_PUSH_ID frame (Section 7.2.7). if a client gets a PUSH_PROMISE with a push ID bigger than the one it advertised, it MUST treat that as a connection error of H3_ID_ERROR. no going over the limit, period ๐.
a server MAY reuse the same push ID in more than one PUSH_PROMISE frame ๐ธ. but if it does, the decompressed request header sets MUST have the same fields in the same order, and both the name and the value of every field MUST be exact matches. clients SHOULD compare the request header sections for resources that got promised more than once ๐ฅ. if a client sees a push ID it already promised and the fields don't match, it MUST respond with a connection error of type H3_GENERAL_PROTOCOL_ERROR. but if the decompressed field sections match exactly, the client SHOULD tie the pushed content to every stream that got a PUSH_PROMISE frame โจ.
the reuse thing is mainly there to cut down on duplicates when requests are happening at the same time ๐. servers SHOULD avoid reusing a push ID over a long stretch. clients are likely to use server push responses and not hang onto them for later, so if a client gets a PUSH_PROMISE with a push ID it already used and threw away, it's forced to ignore the promise ๐.
if a PUSH_PROMISE frame shows up on the control stream, the client MUST respond with a connection error of type H3_FRAME_UNEXPECTED ๐ซ.
clients MUST NOT send a PUSH_PROMISE frame. servers MUST treat receiving a PUSH_PROMISE frame as a connection error of type H3_FRAME_UNEXPECTED. the direction is one-way, bestie ๐.
see Section 4.6 for the full picture of how server push works overall ๐.
7.2.6. GOAWAY ๐
the GOAWAY frame (type=0x07) is how either endpoint starts a graceful shutdown of an HTTP/3 connection ๐. GOAWAY lets an endpoint stop taking new requests or pushes, while still finishing the stuff it already got. this is great for admin stuff, like server maintenance ๐ ๏ธ. and here's the key bit: GOAWAY by itself does NOT close a connection ๐ค.
GOAWAY Frame {
Type (i) = 0x07,
Length (i),
Stream ID/Push ID (i),
}
Figure 9: GOAWAY Frame
GOAWAY frames are always sent on the control stream ๐ง. in the server-to-client direction, the frame carries a QUIC stream ID for a client-initiated bidirectional stream, encoded as a variable-length integer. if a client gets a GOAWAY frame with a stream ID of any other type, it MUST treat that as a connection error of type H3_ID_ERROR ๐ โโ๏ธ.
in the client-to-server direction, the GOAWAY frame carries a push ID encoded as a variable-length integer ๐.
the GOAWAY frame applies to the whole connection, not one specific stream ๐. a client MUST treat a GOAWAY frame showing up on any stream other than the control stream as a connection error of type H3_FRAME_UNEXPECTED. no sneaking it in elsewhere ๐ญ.
see Section 5.2 for more on how the GOAWAY frame gets used ๐ฆ.
7.2.7. MAX_PUSH_ID โจ
the MAX_PUSH_ID frame (type=0x0d) is how clients control how many server pushes the server is allowed to start ๐. it sets the biggest push ID the server can use in PUSH_PROMISE and CANCEL_PUSH frames. which also means it caps how many push streams the server can open, on top of the limit that the QUIC transport already keeps ๐.
the MAX_PUSH_ID frame is always sent on the control stream. if a MAX_PUSH_ID frame shows up on any other stream, it MUST be treated as a connection error of type H3_FRAME_UNEXPECTED ๐ โโ๏ธ.
a server MUST NOT send a MAX_PUSH_ID frame. a client MUST treat receiving a MAX_PUSH_ID frame as a connection error of type H3_FRAME_UNEXPECTED. the server isn't allowed to set its own limit, bestie ๐ .
the maximum push ID is unset when an HTTP/3 connection gets created ๐ฑ. that means a server can't push anything until it gets a MAX_PUSH_ID frame. a client that wants to manage how many promised pushes there are can raise the maximum push ID by sending MAX_PUSH_ID frames as the server fulfills or cancels its pushes ๐ง.
MAX_PUSH_ID Frame {
Type (i) = 0x0d,
Length (i),
Push ID (i),
}
Figure 10: MAX_PUSH_ID Frame
the MAX_PUSH_ID frame carries one variable-length integer, which is the maximum push ID the server can use. see Section 4.6 for more ๐ธ. a MAX_PUSH_ID frame CANNOT reduce the maximum push ID, so if a MAX_PUSH_ID frame has a smaller value than one already received, it MUST be treated as a connection error of type H3_ID_ERROR. the limit only goes up, never down ๐.
Oops, I'll fix that line since "CANNOT" is not a key word and shouldn't appear in capitals:
the MAX_PUSH_ID frame carries one variable-length integer, which is the maximum push ID the server can use. see Section 4.6 for more ๐ธ. a MAX_PUSH_ID frame cannot reduce the maximum push ID. if a MAX_PUSH_ID frame has a smaller value than one already received, it MUST be treated as a connection error of type H3_ID_ERROR. the limit only goes up, never down ๐.
7.2.8. Reserved Frame Types ๐ง
frame types in the format 0x1f * N + 0x21 (where N is any non-negative integer) are reserved so the "ignore unknown types" rule (Section 9) gets exercised ๐ฆ. these frames have no meaning, and they MAY be sent on any stream where frames are allowed. that makes them useful for application-layer padding ๐ซถ. endpoints MUST NOT treat these frames as having any meaning when they receive them ๐.
the payload and length of these frames can be picked in any way the implementation likes โจ.
frame types that were used in HTTP/2, but have no HTTP/3 counterpart, are also reserved (Section 11.2.1) ๐. these frame types MUST NOT be sent. if they are received, that MUST be treated as a connection error of type H3_FRAME_UNEXPECTED ๐ญ.
8. Error Handling ๐
when a stream can't be completed successfully, QUIC lets the application abruptly terminate (reset) that stream and share a reason. see Section 2.4 of [QUIC-TRANSPORT] ๐. this is called a "stream error". an HTTP/3 implementation can decide to close a QUIC stream and say what kind of error it was. the wire encodings of error codes are defined in Section 8.1 ๐. stream errors are different from HTTP status codes that describe error conditions. stream errors mean the sender didn't transfer or consume the whole request or response, while status codes describe the result of a request that was received fine ๐.
if the whole connection needs to be shut down, QUIC has a similar way to share a reason. see Section 5.3 of [QUIC-TRANSPORT] ๐. this is called a "connection error". just like with stream errors, an HTTP/3 implementation can terminate a QUIC connection and give the reason using an error code from Section 8.1 ๐ง.
the reasons for closing streams and connections are called "errors", but that doesn't always mean something is wrong ๐. the connection or either implementation might be perfectly fine. for example, a stream can get reset if the requested resource isn't needed anymore ๐ธ.
an endpoint MAY choose to treat a stream error as a connection error in certain situations, closing the entire connection because of something on one stream ๐ฌ. implementations need to think about the impact on outstanding requests before they make that call โ๏ธ.
because new error codes can be defined without negotiation (see Section 9), using an error code in an unexpected context, or getting an unknown error code, MUST be treated as equivalent to H3_NO_ERROR ๐ซถ. closing a stream can still have other effects no matter the error code, though. for example, see Section 4.1 ๐.
8.1. HTTP/3 Error Codes ๐
these error codes are defined for use when streams are abruptly terminated, when reading of streams is aborted, or when HTTP/3 connections are closed right away ๐.
H3_NO_ERROR (0x0100): no error ๐ซง. used when the connection or stream needs closing, but there's nothing to report.H3_GENERAL_PROTOCOL_ERROR (0x0101): the peer broke protocol requirements in a way that doesn't match a more specific error code, or the endpoint chooses not to use the more specific one ๐ โโ๏ธ.H3_INTERNAL_ERROR (0x0102): something went wrong inside the HTTP stack ๐ ๏ธ.H3_STREAM_CREATION_ERROR (0x0103): the endpoint noticed its peer created a stream it won't accept ๐ซ.H3_CLOSED_CRITICAL_STREAM (0x0104): a stream the HTTP/3 connection needs was closed or reset ๐ฑ.H3_FRAME_UNEXPECTED (0x0105): a frame arrived that isn't allowed in the current state or on the current stream ๐.H3_FRAME_ERROR (0x0106): a frame arrived that fails the layout requirements or has an invalid size ๐.H3_EXCESSIVE_LOAD (0x0107): the endpoint noticed its peer acting in a way that might be creating excessive load ๐ฅต.H3_ID_ERROR (0x0108): a stream ID or push ID got used wrong, like going past a limit, reducing a limit, or being reused ๐.H3_SETTINGS_ERROR (0x0109): the endpoint found an error in the payload of a SETTINGS frame โ๏ธ.H3_MISSING_SETTINGS (0x010a): no SETTINGS frame showed up at the start of the control stream ๐ง.H3_REQUEST_REJECTED (0x010b): a server turned down a request without doing any application processing ๐ .H3_REQUEST_CANCELLED (0x010c): the request or its response (including a pushed response) got cancelled ๐๏ธ.H3_REQUEST_INCOMPLETE (0x010d): the client's stream ended without a fully formed request ๐ฅบ.H3_MESSAGE_ERROR (0x010e): an HTTP message was malformed and can't be processed ๐งฉ.H3_CONNECT_ERROR (0x010f): the TCP connection set up for a CONNECT request got reset or closed abnormally ๐.H3_VERSION_FALLBACK (0x0110): the requested operation can't be served over HTTP/3 ๐. the peer should retry over HTTP/1.1.
error codes in the format 0x1f * N + 0x21 (where N is any non-negative integer) are reserved so that unknown error codes get treated as equivalent to H3_NO_ERROR (Section 9) ๐ฒ. implementations SHOULD pick an error code from this space with some probability whenever they'd otherwise have sent H3_NO_ERROR ๐ป. it's giving unpredictability, and I'm obsessed ๐ซ .
9. Extensions to HTTP/3 โจ
HTTP/3 lets you extend the protocol, bestie ๐ฆ Inside the limits in this section, extensions can add extra services or change pretty much any part of the protocol. The catch is that extensions only live inside one HTTP/3 connection ๐ซถ
This covers the protocol bits defined in this document. It does NOT touch the existing ways of extending HTTP, like defining new methods, status codes, or fields ๐
Extensions can use new frame types (Section 7.2), new settings (Section 7.2.4.1), new error codes (Section 8), or new unidirectional stream types (Section 6.2). There are registries set up to manage these extension points: frame types (Section 11.2.1), settings (Section 11.2.2), error codes (Section 11.2.3), and stream types (Section 11.2.4) ๐โจ
Implementations MUST ignore unknown or unsupported values in all extensible protocol elements ๐ธ Implementations MUST discard data or abort reading on unidirectional streams that have unknown or unsupported types ๐ So any of these extension points can be safely used by extensions without any prior arrangement or negotiation, iconic ๐ However, where a known frame type is required to be in a specific location, like the SETTINGS frame being the first frame on the control stream (see Section 6.2.1), an unknown frame type does NOT satisfy that requirement and SHOULD be treated as an error โ ๏ธ
Extensions that could change the semantics of existing protocol components MUST be negotiated before being used ๐จ Like, an extension that changes the layout of the HEADERS frame can't be used until the peer has given a positive signal that it's okay ๐
Coordinating when that revised layout kicks in could get messy, ngl ๐ญ So allocating new identifiers for new definitions of existing protocol elements is likely to be way more effective ๐
This document does not mandate a specific way to negotiate an extension ๐ It does note that a setting (Section 7.2.4.1) could be used for that purpose ๐ If both peers set a value that shows they're willing to use the extension, then the extension can be used ๐ฆ If a setting is used for extension negotiation, the default value MUST be defined so the extension is disabled if the setting is omitted ๐ฅ
10. Security Considerations ๐
Okay, this part is seriously important, so pay attention, bestie ๐ซถ The security considerations of HTTP/3 should be comparable to those of HTTP/2 with TLS ๐ However, many of the considerations from Section 10 of [HTTP/2] apply to [QUIC-TRANSPORT] and are discussed in that document ๐
10.1. Server Authority ๐
HTTP/3 relies on the HTTP definition of authority ๐ The security considerations of establishing authority are discussed in Section 17.1 of [HTTP] โจ
10.2. Cross-Protocol Attacks ๐ต๏ธโโ๏ธ
Using ALPN in the TLS and QUIC handshakes establishes the target application protocol before any application-layer bytes get processed ๐ฅ This means endpoints have strong assurances that their peers are using the same protocol ๐ซถ
This does NOT guarantee protection from every cross-protocol attack, though ๐ฌ Section 21.5 of [QUIC-TRANSPORT] describes some ways the plaintext of QUIC packets can be used to perform request forgery against endpoints that don't use authenticated transports ๐
10.3. Intermediary-Encapsulation Attacks ๐ง
The HTTP/3 field encoding lets you express names that aren't valid field names in the syntax HTTP uses (Section 5.1 of [HTTP]) ๐ Requests or responses containing invalid field names MUST be treated as malformed ๐ซ So an intermediary can't translate an HTTP/3 request or response with an invalid field name into an HTTP/1.1 message, periodt ๐
Similarly, HTTP/3 can carry field values that aren't valid ๐ญ Most values that can be encoded won't alter field parsing, but carriage return (ASCII 0x0d), line feed (ASCII 0x0a), and the null character (ASCII 0x00) might be exploited by an attacker if they get translated verbatim ๐ Any request or response that contains a character not permitted in a field value MUST be treated as malformed ๐ซ Valid characters are defined by the "field-content" ABNF rule in Section 5.5 of [HTTP] ๐
10.4. Cacheability of Pushed Responses ๐
Pushed responses don't have an explicit request from the client ๐ฅบ The request is provided by the server in the PUSH_PROMISE frame instead ๐
Caching pushed responses is possible based on the guidance the origin server gives in the Cache-Control header field ๐ However, this can cause issues if a single server hosts more than one tenant ๐ฌ For example, a server might offer multiple users each a small portion of its URI space ๐ธ
Where multiple tenants share space on the same server, that server MUST ensure that tenants are NOT able to push representations of resources they don't have authority over ๐จ Failing to enforce this would let a tenant provide a representation that gets served out of cache, overriding the actual representation the authoritative tenant provides ๐ฑ Clients are required to reject pushed responses for which an origin server is not authoritative; see Section 4.6 ๐ โโ๏ธ
10.5. Denial-of-Service Considerations ๐ช๏ธ
An HTTP/3 connection can demand a bigger commitment of resources to operate than an HTTP/1.1 or HTTP/2 connection ๐ฅต Field compression and flow control depend on committing resources to store a larger amount of state ๐ฆ Settings for these features make sure that memory commitments for them are strictly bounded ๐
The number of PUSH_PROMISE frames is constrained in a similar fashion ๐๏ธ A client that accepts server push SHOULD limit the number of push IDs it issues at a time ๐
Processing capacity can't be guarded as effectively as state capacity ๐ค
The ability to send undefined protocol elements that the peer is required to ignore can be abused to make a peer spend extra processing time ๐ฅด This might be done by setting multiple undefined SETTINGS parameters, unknown frame types, or unknown stream types ๐ Note, however, that some uses are totally legit, like optional-to-understand extensions and padding to increase resistance to traffic analysis ๐ซถ
Compression of field sections also offers some chances to waste processing resources; see Section 7 of [QPACK] for more details on potential abuses ๐๏ธ
All of these features (server push, unknown protocol elements, field compression) have legitimate uses ๐ They only become a burden when they're used unnecessarily or to excess ๐ฎโ๐จ
An endpoint that doesn't monitor this behavior exposes itself to a risk of denial-of-service attack ๐จ Implementations SHOULD track the use of these features and set limits on their use ๐ An endpoint MAY treat suspicious activity as a connection error of type H3_EXCESSIVE_LOAD, but false positives will disrupt valid connections and requests ๐ญ
10.5.1. Limits on Field Section Size ๐
A large field section (Section 4.1) can make an implementation commit a large amount of state ๐๏ธ Header fields that are critical for routing can appear toward the end of a header section, which stops the header section from being streamed to its ultimate destination ๐ Because of this ordering and other reasons, like ensuring cache correctness, an endpoint likely needs to buffer the entire header section ๐ง Since there's no hard limit on the size of a field section, some endpoints could be forced to commit a large amount of available memory for header fields ๐ซ
An endpoint can use the SETTINGS_MAX_FIELD_SECTION_SIZE (Section 4.2.2) setting to advise peers of limits that might apply to the size of field sections ๐ฃ This setting is only advisory, so endpoints MAY choose to send field sections that exceed this limit and risk having the request or response treated as malformed ๐ฒ This setting is specific to an HTTP/3 connection, so any request or response could hit a hop with a lower, unknown limit ๐คทโโ๏ธ An intermediary can try to avoid this problem by passing on values presented by different peers, but they aren't obligated to do so ๐ซถ
A server that receives a bigger field section than it's willing to handle can send an HTTP 431 (Request Header Fields Too Large) status code ([RFC6585]) ๐ โโ๏ธ A client can discard responses it can't process ๐๏ธ
10.5.2. CONNECT Issues ๐
The CONNECT method can create disproportionate load on a proxy, since stream creation is relatively cheap compared to creating and maintaining a TCP connection ๐ธ Therefore, a proxy that supports CONNECT might be more conservative about the number of simultaneous requests it accepts ๐ฆ
A proxy might also keep some resources for a TCP connection beyond the closing of the stream that carries the CONNECT request, since the outgoing TCP connection stays in the TIME_WAIT state โณ To account for this, a proxy might delay increasing the QUIC stream limits for some time after a TCP connection terminates ๐งโโ๏ธ
10.6. Use of Compression ๐๏ธ
Compression can let an attacker recover secret data when it's compressed in the same context as data the attacker controls ๐ต๏ธ HTTP/3 enables compression of fields (Section 4.2); the following concerns also apply to the use of HTTP compressed content-codings, see Section 8.4.1 of [HTTP] ๐
There are demonstrable attacks on compression that exploit the characteristics of the web (e.g., [BREACH]) ๐ฑ The attacker induces multiple requests containing varying plaintext and observes the length of the resulting ciphertext in each one, which reveals a shorter length when a guess about the secret is correct ๐ซฃ
Implementations communicating on a secure channel MUST NOT compress content that includes both confidential and attacker-controlled data, unless separate compression contexts are used for each source of data ๐ซ Compression MUST NOT be used if the source of data can't be reliably determined ๐
Further considerations about compressing field sections are described in [QPACK] ๐โจ
10.7. Padding and Traffic Analysis ๐
okay so padding is basically extra filler bytes tucked into a message to hide how big the real stuff is ๐ซฃ. HTTP uses it to help block specific attacks, like when compressed content mixes attacker-controlled plaintext with secret data (hi, [BREACH] ๐).
Where HTTP/2 uses PADDING frames and Padding fields in other frames to make a connection harder to traffic analyze, HTTP/3 can either rely on transport-layer padding or use the reserved frame and stream types from Sections 7.2.8 and 6.2.3 ๐ฆ. These methods behave differently in a few ways: how fine-grained the padding is, where it sits relative to the info it's protecting, whether it still gets applied when packets are lost, and how an implementation actually controls it ๐ .
Reserved stream types can be used to look like you're sending traffic even when the connection is idle ๐ธ. HTTP traffic tends to come in bursts, so that fake traffic can hide when a burst happens or how long it lasts, even to the point of looking like one constant stream of data โจ. BUT (and this is the tea ๐ต), that traffic is still flow controlled by the receiver. If the receiver doesn't promptly drain those streams and hand out more flow-control credit, the sender gets stuck and can't send real traffic anymore ๐ญ.
To mitigate attacks that rely on compression, disabling or limiting compression might be preferable to padding as a countermeasure ๐ง.
Padding can give you less protection than it seems like at first glance, and redundant padding could even backfire ๐. At best, padding just makes it harder for an attacker to infer length information, because they have to observe more frames to figure it out. Badly implemented padding schemes can be easily defeated ๐. Randomized padding with a predictable distribution gives very little protection ๐ฅฑ. Padding payloads to a fixed size is also a trap, since payload sizes that cross the fixed-size boundary reveal information, which could happen if an attacker can control the plaintext ๐จ.
10.8. Frame Parsing ๐งฉ
Several protocol elements contain nested length elements, usually as frames with an explicit length that holds variable-length integers. This could pose a security risk to an incautious implementer ๐ฌ. An implementation MUST ensure that the length of a frame exactly matches the length of the fields it contains ๐ซถ.
10.9. Early Data โฉ
Using 0-RTT with HTTP/3 opens the door to replay attacks ๐. The anti-replay mitigations in [HTTP-REPLAY] MUST be applied when using HTTP/3 with 0-RTT ๐. When applying [HTTP-REPLAY] to HTTP/3, references to the TLS layer refer to the handshake performed within QUIC, while all references to application data refer to the contents of streams ๐.
10.10. Migration ๐ฆ
Certain HTTP implementations use the client address for logging or access-control purposes ๐. A QUIC client's address can change during a connection (and future versions might support simultaneous use of multiple addresses), so those implementations will need to either actively retrieve the client's current address or addresses when they're relevant, or explicitly accept that the original address might change ๐งญ.
10.11. Privacy Considerations ๐ต๏ธโโ๏ธ
Several characteristics of HTTP/3 give an observer a chance to correlate the actions of a single client or server over time ๐๏ธ. These include the value of settings, the timing of reactions to stimulus, and the handling of any features that are controlled by settings ๐.
As far as these create observable differences in behavior, they could be used as a basis for fingerprinting a specific client ๐ซฃ.
HTTP/3 prefers using a single QUIC connection, which allows correlation of a user's activity on a site ๐. Reusing connections for different origins allows correlation of activity across those origins too ๐.
Several features of QUIC solicit immediate responses and can be used by an endpoint to measure latency to its peer. This might have privacy implications in certain scenarios โฑ๏ธ.
11. IANA Considerations ๐
This document registers a new ALPN protocol ID (Section 11.1) and creates new registries that manage the assignment of code points in HTTP/3 ๐.
11.1. Registration of HTTP/3 Identification String ๐ท๏ธ
This document creates a new registration for identifying HTTP/3 in the "TLS Application-Layer Protocol Negotiation (ALPN) Protocol IDs" registry established in [RFC7301] โจ.
The "h3" string identifies HTTP/3:
Protocol: HTTP/3
Identification Sequence: 0x68 0x33 ("h3")
Specification: This document
11.2. New Registries ๐๏ธ
New registries created in this document operate under the QUIC registration policy documented in Section 22.1 of [QUIC-TRANSPORT] ๐. These registries all include the common set of fields listed in Section 22.1.1 of [QUIC-TRANSPORT]. They're all collected under the "Hypertext Transfer Protocol version 3 (HTTP/3)" heading ๐.
The initial allocations in these registries all get permanent status and list a change controller of the IETF and a contact of the HTTP working group (ietf-http-wg@w3.org) ๐ธ.
11.2.1. Frame Types ๐งพ
This document sets up a registry for HTTP/3 frame type codes. The "HTTP/3 Frame Types" registry governs a 62-bit space ๐ข. It follows the QUIC registry policy (see Section 11.2). Permanent registrations here are assigned using the Specification Required policy ([RFC8126]), except for values between 0x00 and 0x3f (in hexadecimal; inclusive), which are assigned using Standards Action or IESG Approval as defined in Sections 4.9 and 4.10 of [RFC8126] ๐.
This registry is separate from the "HTTP/2 Frame Type" registry defined in [HTTP/2], but it's preferable that the assignments parallel each other where the code spaces overlap ๐ช. If an entry only shows up in one registry, every effort SHOULD be made to avoid assigning the matching value to an unrelated operation ๐. Expert reviewers MAY reject unrelated registrations that would conflict with the same value in the corresponding registry ๐ง.
Along with the common fields from Section 11.2, permanent registrations in this registry MUST include the following field:
Frame Type: A name or label for the frame type.
Specifications of frame types MUST include a description of the frame layout and its semantics, including any parts of the frame that are conditionally present ๐.
The entries in Table 2 are registered by this document ๐:
+==============+=======+===============+
| Frame Type | Value | Specification |
+==============+=======+===============+
| DATA | 0x00 | Section 7.2.1 |
+--------------+-------+---------------+
| HEADERS | 0x01 | Section 7.2.2 |
+--------------+-------+---------------+
| Reserved | 0x02 | This document |
+--------------+-------+---------------+
| CANCEL_PUSH | 0x03 | Section 7.2.3 |
+--------------+-------+---------------+
| SETTINGS | 0x04 | Section 7.2.4 |
+--------------+-------+---------------+
| PUSH_PROMISE | 0x05 | Section 7.2.5 |
+--------------+-------+---------------+
| Reserved | 0x06 | This document |
+--------------+-------+---------------+
| GOAWAY | 0x07 | Section 7.2.6 |
+--------------+-------+---------------+
| Reserved | 0x08 | This document |
+--------------+-------+---------------+
| Reserved | 0x09 | This document |
+--------------+-------+---------------+
| MAX_PUSH_ID | 0x0d | Section 7.2.7 |
+--------------+-------+---------------+
Table 2: Initial HTTP/3 Frame Types
Each code of the format 0x1f * N + 0x21 for non-negative integer values of N (that is, 0x21, 0x40, ..., through 0x3ffffffffffffffe) MUST NOT be assigned by IANA and MUST NOT appear in the listing of assigned values ๐ซ.
11.2.2. Settings Parameters โ๏ธ
This document sets up a registry for HTTP/3 settings. The "HTTP/3 Settings" registry governs a 62-bit space ๐ข. It follows the QUIC registry policy (see Section 11.2). Permanent registrations here are assigned using the Specification Required policy ([RFC8126]), except for values between 0x00 and 0x3f (in hexadecimal; inclusive), which are assigned using Standards Action or IESG Approval as defined in Sections 4.9 and 4.10 of [RFC8126] ๐.
This registry is separate from the "HTTP/2 Settings" registry defined in [HTTP/2], but it's preferable that the assignments parallel each other ๐ช. If an entry only shows up in one registry, every effort SHOULD be made to avoid assigning the matching value to an unrelated operation ๐. Expert reviewers MAY reject unrelated registrations that would conflict with the same value in the corresponding registry ๐ง.
Along with the common fields from Section 11.2, permanent registrations in this registry MUST include the following fields:
Setting Name: A symbolic name for the setting. Specifying a setting name is optional.Default: The value of the setting unless otherwise indicated. A default SHOULD be the most restrictive possible value.
The entries in Table 3 are registered by this document ๐:
+========================+=======+=================+===========+
| Setting Name | Value | Specification | Default |
+========================+=======+=================+===========+
| Reserved | 0x00 | This document | N/A |
+------------------------+-------+-----------------+-----------+
| Reserved | 0x02 | This document | N/A |
+------------------------+-------+-----------------+-----------+
| Reserved | 0x03 | This document | N/A |
+------------------------+-------+-----------------+-----------+
| Reserved | 0x04 | This document | N/A |
+------------------------+-------+-----------------+-----------+
| Reserved | 0x05 | This document | N/A |
+------------------------+-------+-----------------+-----------+
| MAX_FIELD_SECTION_SIZE | 0x06 | Section 7.2.4.1 | Unlimited |
+------------------------+-------+-----------------+-----------+
Table 3: Initial HTTP/3 Settings
For formatting reasons, setting names can be abbreviated by removing the SETTINGS_ prefix ๐.
Each code of the format 0x1f * N + 0x21 for non-negative integer values of N (that is, 0x21, 0x40, ..., through 0x3ffffffffffffffe) MUST NOT be assigned by IANA and MUST NOT appear in the listing of assigned values ๐ซ.
11.2.3. Error Codes ๐จโจ
Okay bestie, so this section sets up a registry for HTTP/3 error codes ๐ The "HTTP/3 Error Codes" registry covers a 62-bit space ๐ฆ It follows the QUIC registry policy (see Section 11.2 ๐). Permanent registrations get assigned using the Specification Required policy ([RFC8126]), except for values between 0x00 and 0x3f (in hex, inclusive). Those go through Standards Action or IESG Approval, as defined in Sections 4.9 and 4.10 of [RFC8126] ๐ธ
Registrations for error codes are required to include a description of the error code. An expert reviewer is advised to check new registrations for possible duplication with existing error codes. Using existing registrations is encouraged but not mandated ๐ซถ Using values that are registered in the "HTTP/2 Error Code" registry is discouraged, and expert reviewers MAY reject such registrations ๐
In addition to common fields as described in Section 11.2, this registry has two extra fields. Permanent registrations in this registry MUST include the following field:
Name: A name for the error code.Description: A brief description of the error code semantics.
The entries in Table 4 are registered by this document. These error codes were picked from the range that runs on a Specification Required policy, so they don't collide with HTTP/2 error codes โจ
+===========================+========+==============+===============+
| Name | Value | Description | Specification |
+===========================+========+==============+===============+
| H3_NO_ERROR | 0x0100 | No error | Section 8.1 |
+---------------------------+--------+--------------+---------------+
| H3_GENERAL_PROTOCOL_ERROR | 0x0101 | General | Section 8.1 |
| | | protocol | |
| | | error | |
+---------------------------+--------+--------------+---------------+
| H3_INTERNAL_ERROR | 0x0102 | Internal | Section 8.1 |
| | | error | |
+---------------------------+--------+--------------+---------------+
| H3_STREAM_CREATION_ERROR | 0x0103 | Stream | Section 8.1 |
| | | creation | |
| | | error | |
+---------------------------+--------+--------------+---------------+
| H3_CLOSED_CRITICAL_STREAM | 0x0104 | Critical | Section 8.1 |
| | | stream was | |
| | | closed | |
+---------------------------+--------+--------------+---------------+
| H3_FRAME_UNEXPECTED | 0x0105 | Frame not | Section 8.1 |
| | | permitted | |
| | | in the | |
| | | current | |
| | | state | |
+---------------------------+--------+--------------+---------------+
| H3_FRAME_ERROR | 0x0106 | Frame | Section 8.1 |
| | | violated | |
| | | layout or | |
| | | size rules | |
+---------------------------+--------+--------------+---------------+
| H3_EXCESSIVE_LOAD | 0x0107 | Peer | Section 8.1 |
| | | generating | |
| | | excessive | |
| | | load | |
+---------------------------+--------+--------------+---------------+
| H3_ID_ERROR | 0x0108 | An | Section 8.1 |
| | | identifier | |
| | | was used | |
| | | incorrectly | |
+---------------------------+--------+--------------+---------------+
| H3_SETTINGS_ERROR | 0x0109 | SETTINGS | Section 8.1 |
| | | frame | |
| | | contained | |
| | | invalid | |
| | | values | |
+---------------------------+--------+--------------+---------------+
| H3_MISSING_SETTINGS | 0x010a | No SETTINGS | Section 8.1 |
| | | frame | |
| | | received | |
+---------------------------+--------+--------------+---------------+
| H3_REQUEST_REJECTED | 0x010b | Request not | Section 8.1 |
| | | processed | |
+---------------------------+--------+--------------+---------------+
| H3_REQUEST_CANCELLED | 0x010c | Data no | Section 8.1 |
| | | longer | |
| | | needed | |
+---------------------------+--------+--------------+---------------+
| H3_REQUEST_INCOMPLETE | 0x010d | Stream | Section 8.1 |
| | | terminated | |
| | | early | |
+---------------------------+--------+--------------+---------------+
| H3_MESSAGE_ERROR | 0x010e | Malformed | Section 8.1 |
| | | message | |
+---------------------------+--------+--------------+---------------+
| H3_CONNECT_ERROR | 0x010f | TCP reset | Section 8.1 |
| | | or error on | |
| | | CONNECT | |
| | | request | |
+---------------------------+--------+--------------+---------------+
| H3_VERSION_FALLBACK | 0x0110 | Retry over | Section 8.1 |
| | | HTTP/1.1 | |
+---------------------------+--------+--------------+---------------+
Table 4: Initial HTTP/3 Error Codes
Each code of the format 0x1f * N + 0x21 for non-negative integer values of N (that is, 0x21, 0x40, ..., through 0x3ffffffffffffffe) MUST NOT be assigned by IANA and MUST NOT appear in the listing of assigned values ๐ซ๐
11.2.4. Stream Types ๐๐ฆ
This document sets up a registry for HTTP/3 unidirectional stream types ๐ The "HTTP/3 Stream Types" registry governs a 62-bit space. It follows the QUIC registry policy (see Section 11.2). Permanent registrations get assigned using the Specification Required policy ([RFC8126]), except for values between 0x00 and 0x3f (in hex, inclusive). Those are assigned using Standards Action or IESG Approval, as defined in Sections 4.9 and 4.10 of [RFC8126] ๐ง
In addition to common fields as described in Section 11.2, permanent registrations in this registry MUST include the following fields:
Stream Type: A name or label for the stream type.Sender: Which endpoint on an HTTP/3 connection may initiate a stream of this type. Values are "Client", "Server", or "Both".
Specifications for permanent registrations MUST include a description of the stream type, including the layout and semantics of the stream contents ๐โจ
The entries in Table 5 are registered by this document.
+================+=======+===============+========+
| Stream Type | Value | Specification | Sender |
+================+=======+===============+========+
| Control Stream | 0x00 | Section 6.2.1 | Both |
+----------------+-------+---------------+--------+
| Push Stream | 0x01 | Section 4.6 | Server |
+----------------+-------+---------------+--------+
Table 5: Initial Stream Types
Each code of the format 0x1f * N + 0x21 for non-negative integer values of N (that is, 0x21, 0x40, ..., through 0x3ffffffffffffffe) MUST NOT be assigned by IANA and MUST NOT appear in the listing of assigned values ๐
โโ๏ธ๐
12. References ๐๐
12.1. Normative References ๐โจ
Here are the normative references, the ones the spec leans on the hardest ๐ซถ
[ALTSVC] Nottingham, M., McManus, P., and J. Reschke, "HTTP
Alternative Services", RFC 7838, DOI 10.17487/RFC7838,
April 2016, <https://www.rfc-editor.org/info/rfc7838>.
[COOKIES] Barth, A., "HTTP State Management Mechanism", RFC 6265,
DOI 10.17487/RFC6265, April 2011,
<https://www.rfc-editor.org/info/rfc6265>.
[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-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>.
[HTTP-REPLAY]
Thomson, M., Nottingham, M., and W. Tarreau, "Using Early
Data in HTTP", RFC 8470, DOI 10.17487/RFC8470, September
2018, <https://www.rfc-editor.org/info/rfc8470>.
[QPACK] Krasic, C., Bishop, M., and A. Frindell, Ed., "QPACK:
Field Compression for HTTP/3", RFC 9204,
DOI 10.17487/RFC9204, June 2022,
<https://www.rfc-editor.org/info/rfc9204>.
[QUIC-TRANSPORT]
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>.
[RFC0793] Postel, J., "Transmission Control Protocol", STD 7,
RFC 793, DOI 10.17487/RFC0793, September 1981,
<https://www.rfc-editor.org/info/rfc793>.
[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>.
[RFC6066] Eastlake 3rd, D., "Transport Layer Security (TLS)
Extensions: Extension Definitions", RFC 6066,
DOI 10.17487/RFC6066, January 2011,
<https://www.rfc-editor.org/info/rfc6066>.
[RFC7301] Friedl, S., Popov, A., Langley, A., and E. Stephan,
"Transport Layer Security (TLS) Application-Layer Protocol
Negotiation Extension", RFC 7301, DOI 10.17487/RFC7301,
July 2014, <https://www.rfc-editor.org/info/rfc7301>.
[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>.
[URI] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
Resource Identifier (URI): Generic Syntax", STD 66,
RFC 3986, DOI 10.17487/RFC3986, January 2005,
<https://www.rfc-editor.org/info/rfc3986>.
12.2. Informative References ๐๐ธ
And these are the informative ones, the extra reading that gives you the full context ๐
[BREACH] Gluck, Y., Harris, N., and A. Prado, "BREACH: Reviving the
CRIME Attack", July 2013,
<http://breachattack.com/resources/
BREACH%20-%20SSL,%20gone%20in%2030%20seconds.pdf>.
[DNS-TERMS]
Hoffman, P., Sullivan, A., and K. Fujiwara, "DNS
Terminology", BCP 219, RFC 8499, DOI 10.17487/RFC8499,
January 2019, <https://www.rfc-editor.org/info/rfc8499>.
[HPACK] Peon, R. and H. Ruellan, "HPACK: Header Compression for
HTTP/2", RFC 7541, DOI 10.17487/RFC7541, May 2015,
<https://www.rfc-editor.org/info/rfc7541>.
[HTTP/1.1] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke,
Ed., "HTTP/1.1", STD 99, RFC 9112, DOI 10.17487/RFC9112,
June 2022, <https://www.rfc-editor.org/info/rfc9112>.
[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>.
[RFC6585] Nottingham, M. and R. Fielding, "Additional HTTP Status
Codes", RFC 6585, DOI 10.17487/RFC6585, April 2012,
<https://www.rfc-editor.org/info/rfc6585>.
[RFC8164] Nottingham, M. and M. Thomson, "Opportunistic Security for
HTTP/2", RFC 8164, DOI 10.17487/RFC8164, May 2017,
<https://www.rfc-editor.org/info/rfc8164>.
[TFO] Cheng, Y., Chu, J., Radhakrishnan, S., and A. Jain, "TCP
Fast Open", RFC 7413, DOI 10.17487/RFC7413, December 2014,
<https://www.rfc-editor.org/info/rfc7413>.
[TLS] Rescorla, E., "The Transport Layer Security (TLS) Protocol
Version 1.3", RFC 8446, DOI 10.17487/RFC8446, August 2018,
<https://www.rfc-editor.org/info/rfc8446>.
Appendix A. Considerations for Transitioning from HTTP/2 ๐ฆ
okay bestie, HTTP/3 is SO strongly influenced by HTTP/2 ๐ they share a ton of similarities, so this appendix is the tea on how HTTP/3 was designed, the big differences from HTTP/2, and how to map HTTP/2 extensions over to HTTP/3 ๐งฉ
HTTP/3 starts from the vibe that being similar to HTTP/2 is preferable ๐ but it's not a hard requirement. It departs from HTTP/2 wherever QUIC is different from TCP, either to take advantage of QUIC features (like streams) or to fix important shortcomings (like the lack of total ordering) โจ Even though HTTP/3 is similar to HTTP/2 in key ways, like how requests and responses relate to streams, the details are seriously different ๐
Some important departures are noted in this section. ๐
A.1. Streams ๐
HTTP/3 lets you use a much bigger number of streams (2^62-1) than HTTP/2 ๐คฏ The same ideas about running out of stream identifier space still apply, but the space is so huge that other QUIC limits, like the cap on the connection flow-control window, are probably hit first ๐
Here's the difference: in HTTP/2, stream concurrency is handled by HTTP/2 itself, but in HTTP/3 it's managed by QUIC ๐ QUIC considers a stream closed once all the data has been received and the data it sent has been acknowledged by the peer. HTTP/2 considers a stream closed once the frame carrying the END_STREAM bit has been committed to the transport. So the stream for an equivalent exchange could stay "active" for a longer time โณ HTTP/3 servers might choose to allow a larger number of concurrent client-initiated bidirectional streams to get equivalent concurrency to HTTP/2, depending on the expected usage patterns ๐ง
In HTTP/2, only request and response bodies (the frame payload of DATA frames) are subject to flow control. In HTTP/3, all frames are sent on QUIC streams, so all frames on all streams are flow controlled ๐ฅ
Because there are other unidirectional stream types, HTTP/3 doesn't rely only on the number of concurrent unidirectional streams to control how many pushes are in flight ๐ Instead, HTTP/3 clients use the MAX_PUSH_ID frame to control the number of pushes they receive from an HTTP/3 server ๐
A.2. HTTP Frame Types ๐ง
Lots of HTTP/2 framing concepts can be dropped on QUIC, because the transport handles them for you ๐ซถ Since frames already live on a stream, they can leave out the stream number. Since frames don't block multiplexing (QUIC's multiplexing happens below this layer), support for variable-maximum-length packets can be removed. Since stream termination is handled by QUIC, an END_STREAM flag isn't required, which means the Flags field can be removed from the generic frame layout ๐
Frame payloads are largely drawn from [HTTP/2]. However, QUIC includes many features (e.g., flow control) that are also present in HTTP/2, and in those cases the HTTP mapping doesn't re-implement them ๐ง So several HTTP/2 frame types aren't required in HTTP/3. When an HTTP/2-defined frame is no longer used, its frame ID has been reserved to maximize portability between HTTP/2 and HTTP/3 implementations ๐ฆ Even frame types that show up in both mappings don't have identical semantics, so be careful, bestie โ ๏ธ
A lot of the differences come from the fact that HTTP/2 gives an absolute ordering between frames across all streams, while QUIC only gives this guarantee on each stream. So if a frame type assumes frames from different streams will still be received in the order they were sent, HTTP/3 will break it ๐
Some examples of feature adaptations are described below, along with general guidance for extension frame implementors who are converting an HTTP/2 extension to HTTP/3 ๐ธ
A.2.1. Prioritization Differences ๐ฏ
HTTP/2 specifies priority assignments in PRIORITY frames and (optionally) in HEADERS frames. HTTP/3 doesn't provide a way to signal priority ๐
โโ๏ธ
Even though there's no explicit signaling for priority, that doesn't mean prioritization isn't important for good performance ๐ โจ
A.2.2. Field Compression Differences ๐ฆ
HPACK was designed assuming in-order delivery. A sequence of encoded field sections must arrive (and be decoded) at an endpoint in the same order they were encoded, which keeps the dynamic state at both endpoints in sync ๐
Because QUIC doesn't provide this total ordering, HTTP/3 uses a modified version of HPACK called QPACK ๐ QPACK uses a single unidirectional stream to make all modifications to the dynamic table, which guarantees a total order of updates. All frames that contain encoded fields just reference the table state at a given time without modifying it ๐ง
[QPACK] provides additional details ๐
A.2.3. Flow-Control Differences ๐ฆ
HTTP/2 specifies a stream flow-control mechanism. Although all HTTP/2 frames are delivered on streams, only the DATA frame payload is subject to flow control. QUIC provides flow control for stream data, and all HTTP/3 frame types defined in this document are sent on streams. So all frame headers and payloads are subject to flow control ๐
A.2.4. Guidance for New Frame Type Definitions ๐
Frame type definitions in HTTP/3 often use the QUIC variable-length integer encoding ๐งฎ Stream IDs use this encoding, which allows a much larger range of possible values than the encoding used in HTTP/2. Some HTTP/3 frames use an identifier other than a stream ID (e.g., push IDs). If an extension frame type's encoding includes a stream ID, redefining that encoding might be necessary ๐ซถ
Since the Flags field isn't present in generic HTTP/3 frames, frames that depend on flags need to allocate space for flags as part of their frame payload ๐ท๏ธ
Other than these issues, frame type HTTP/2 extensions are typically portable to QUIC just by replacing stream 0 in HTTP/2 with a control stream in HTTP/3 ๐ HTTP/3 extensions won't assume ordering, but they wouldn't be harmed by ordering, and they're expected to be portable to HTTP/2 ๐
A.2.5. Comparison of HTTP/2 and HTTP/3 Frame Types
okay so this is the big frame showdown between HTTP/2 and HTTP/3 ๐ฆ bestie, grab a snack, it's giving comparison chart energy ๐
DATA (0x00): Padding is not defined in HTTP/3 frames. ๐ธ See Section 7.2.1.
HEADERS (0x01): The PRIORITY region of HEADERS is not defined in HTTP/3 frames. Padding is not defined in HTTP/3 frames either ๐ See Section 7.2.2.
PRIORITY (0x02): As described in Appendix A.2.1, HTTP/3 does not provide a means of signaling priority. ๐
RST_STREAM (0x03): RST_STREAM frames do not exist in HTTP/3, since QUIC provides stream lifecycle management โจ The same code point is used for the CANCEL_PUSH frame (Section 7.2.3).
SETTINGS (0x04): SETTINGS frames are sent only at the beginning of the connection. See Section 7.2.4 and Appendix A.3. ๐
PUSH_PROMISE (0x05): The PUSH_PROMISE frame does not reference a stream; instead, the push stream references the PUSH_PROMISE frame using a push ID. See Section 7.2.5. ๐ง
PING (0x06): PING frames do not exist in HTTP/3, as QUIC provides equivalent functionality. ๐
GOAWAY (0x07): GOAWAY does not contain an error code. In the client-to-server direction, it carries a push ID instead of a server-initiated stream ID. See Section 7.2.6. ๐ซถ
WINDOW_UPDATE (0x08): WINDOW_UPDATE frames do not exist in HTTP/3, since QUIC provides flow control. ๐ฆ
CONTINUATION (0x09): CONTINUATION frames do not exist in HTTP/3; instead, larger HEADERS/PUSH_PROMISE frames than HTTP/2 are permitted. ๐
Frame types defined by extensions to HTTP/2 need to be separately registered for HTTP/3 if still applicable. The IDs of frames defined in [HTTP/2] have been reserved for simplicity. Note that the frame type space in HTTP/3 is substantially larger (62 bits versus 8 bits), so many HTTP/3 frame types have no equivalent HTTP/2 code points. See Section 11.2.1. ๐ญโจ
A.3. HTTP/2 SETTINGS Parameters โ๏ธ๐
An important difference from HTTP/2 is that settings are sent once, as the first frame of the control stream, and thereafter cannot change. ๐ This eliminates many corner cases around synchronization of changes, which is honestly so iconic.
Some transport-level options that HTTP/2 specifies via the SETTINGS frame are superseded by QUIC transport parameters in HTTP/3. The HTTP-level setting that is retained in HTTP/3 has the same value as in HTTP/2. The superseded settings are reserved, and their receipt is an error. See Section 7.2.4.1 for discussion of both the retained and reserved values. ๐ฅ
Below is a listing of how each HTTP/2 SETTINGS parameter is mapped:
SETTINGS_HEADER_TABLE_SIZE (0x01): See [QPACK]. ๐
SETTINGS_ENABLE_PUSH (0x02): This is removed in favor of the MAX_PUSH_ID frame, which provides a more granular control over server push. Specifying a setting with the identifier 0x02 (corresponding to the SETTINGS_ENABLE_PUSH parameter) in the HTTP/3 SETTINGS frame is an error. ๐ โโ๏ธ
SETTINGS_MAX_CONCURRENT_STREAMS (0x03): QUIC controls the largest open stream ID as part of its flow-control logic. Specifying a setting with the identifier 0x03 (corresponding to the SETTINGS_MAX_CONCURRENT_STREAMS parameter) in the HTTP/3 SETTINGS frame is an error. ๐
SETTINGS_INITIAL_WINDOW_SIZE (0x04): QUIC requires both stream and connection flow-control window sizes to be specified in the initial transport handshake. Specifying a setting with the identifier 0x04 (corresponding to the SETTINGS_INITIAL_WINDOW_SIZE parameter) in the HTTP/3 SETTINGS frame is an error. ๐ธ
SETTINGS_MAX_FRAME_SIZE (0x05): This setting has no equivalent in HTTP/3. Specifying a setting with the identifier 0x05 (corresponding to the SETTINGS_MAX_FRAME_SIZE parameter) in the HTTP/3 SETTINGS frame is an error. ๐
SETTINGS_MAX_HEADER_LIST_SIZE (0x06): This setting identifier has been renamed SETTINGS_MAX_FIELD_SECTION_SIZE. ๐ง
In HTTP/3, setting values are variable-length integers (6, 14, 30, or 62 bits long) rather than fixed-length 32-bit fields as in HTTP/2. This will often produce a shorter encoding, but can produce a longer encoding for settings that use the full 32-bit space. Settings ported from HTTP/2 might choose to redefine their value to limit it to 30 bits for more efficient encoding or to make use of the 62-bit space if more than 30 bits are required. ๐ฆ
Settings need to be defined separately for HTTP/2 and HTTP/3. The IDs of settings defined in [HTTP/2] have been reserved for simplicity. Note that the settings identifier space in HTTP/3 is substantially larger (62 bits versus 16 bits), so many HTTP/3 settings have no equivalent HTTP/2 code point. See Section 11.2.2. ๐
As QUIC streams might arrive out of order, endpoints are advised not to wait for the peers' settings to arrive before responding to other streams. See Section 7.2.4.2. ๐ซถ
A.4. HTTP/2 Error Codes ๐จ
QUIC has the same concepts of "stream" and "connection" errors that HTTP/2 provides. However, the differences between HTTP/2 and HTTP/3 mean that error codes are not directly portable between versions. ๐
The HTTP/2 error codes defined in Section 7 of [HTTP/2] logically map to the HTTP/3 error codes as follows:
NO_ERROR (0x00): H3_NO_ERROR in Section 8.1. โจ
PROTOCOL_ERROR (0x01): This is mapped to H3_GENERAL_PROTOCOL_ERROR except in cases where more specific error codes have been defined. Such cases include H3_FRAME_UNEXPECTED, H3_MESSAGE_ERROR, and H3_CLOSED_CRITICAL_STREAM defined in Section 8.1. ๐
INTERNAL_ERROR (0x02): H3_INTERNAL_ERROR in Section 8.1. ๐ฅ
FLOW_CONTROL_ERROR (0x03): Not applicable, since QUIC handles flow control. ๐ฆ
SETTINGS_TIMEOUT (0x04): Not applicable, since no acknowledgment of SETTINGS is defined. ๐
STREAM_CLOSED (0x05): Not applicable, since QUIC handles stream management. ๐
FRAME_SIZE_ERROR (0x06): H3_FRAME_ERROR error code defined in Section 8.1. ๐
REFUSED_STREAM (0x07): H3_REQUEST_REJECTED (in Section 8.1) is used to indicate that a request was not processed. Otherwise, not applicable because QUIC handles stream management. ๐ง
CANCEL (0x08): H3_REQUEST_CANCELLED in Section 8.1. ๐
COMPRESSION_ERROR (0x09): Multiple error codes are defined in [QPACK]. ๐ธ
CONNECT_ERROR (0x0a): H3_CONNECT_ERROR in Section 8.1. ๐
ENHANCE_YOUR_CALM (0x0b): H3_EXCESSIVE_LOAD in Section 8.1. ๐
INADEQUATE_SECURITY (0x0c): Not applicable, since QUIC is assumed to provide sufficient security on all connections. ๐ฅ
HTTP_1_1_REQUIRED (0x0d): H3_VERSION_FALLBACK in Section 8.1. ๐ซถ
Error codes need to be defined for HTTP/2 and HTTP/3 separately. See Section 11.2.3. ๐
A.4.1. Mapping between HTTP/2 and HTTP/3 Errors ๐ฆ
An intermediary that converts between HTTP/2 and HTTP/3 may encounter error conditions from either upstream. It is useful to communicate the occurrence of errors to the downstream, but error codes largely reflect connection-local problems that generally do not make sense to propagate. ๐
An intermediary that encounters an error from an upstream origin can indicate this by sending an HTTP status code such as 502 (Bad Gateway), which is suitable for a broad class of errors. โจ
There are some rare cases where it is beneficial to propagate the error by mapping it to the closest matching error type to the receiver. For example, an intermediary that receives an HTTP/2 stream error of type REFUSED_STREAM from the origin has a clear signal that the request was not processed and that the request is safe to retry. Propagating this error condition to the client as an HTTP/3 stream error of type H3_REQUEST_REJECTED allows the client to take the action it deems most appropriate. In the reverse direction, the intermediary might deem it beneficial to pass on client request cancellations that are indicated by terminating a stream with H3_REQUEST_CANCELLED; see Section 4.1.1. ๐
Conversion between errors is described in the logical mapping. The error codes are defined in non-overlapping spaces in order to protect against accidental conversion that could result in the use of inappropriate or unknown error codes for the target version. An intermediary is permitted to promote stream errors to connection errors but they should be aware of the cost to the HTTP/3 connection for what might be a temporary or intermittent error. ๐ธ
Acknowledgments ๐ซถ
Robbie Shade and Mike Warres were the authors of draft-shade-quic-http2-mapping, a precursor of this document. ๐
The IETF QUIC Working Group received an enormous amount of support from many people. Among others, the following people provided substantial contributions to this document: ๐
- Bence Beky
- Daan De Meyer
- Martin Duke
- Roy Fielding
- Alan Frindell
- Alessandro Ghedini
- Nick Harper
- Ryan Hamilton
- Christian Huitema
- Subodh Iyengar
- Robin Marx
- Patrick McManus
- Luca Niccolini
- ๅฅฅ ไธ็ฉ (Kazuho Oku)
- Lucas Pardue
- Roberto Peon
- Julian Reschke
- Eric Rescorla
- Martin Seemann
- Ben Schwartz
- Ian Swett
- Willy Taureau
- Martin Thomson
- Dmitri Tikhonov
- Tatsuhiro Tsujikawa
A portion of Mike Bishop's contribution was supported by Microsoft during his employment there. ๐
Index ๐
C D G H M P R S
C
CANCEL_PUSH Section 2, Paragraph 5; Section 4.6, Paragraph 6; Section 4.6, Paragraph 10; Table 1; _Section 7.2.3_; Section 7.2.5, Paragraph 4.2.1; Section 7.2.7, Paragraph 1; Table 2; Appendix A.2.5, Paragraph 1.8.1
connection error Section 2.2; Section 4.1, Paragraph 7; Section 4.1, Paragraph 8; Section 4.4, Paragraph 8; Section 4.4, Paragraph 10; Section 4.6, Paragraph 3; Section 5.2, Paragraph 7; Section 6.1, Paragraph 3; Section 6.2, Paragraph 7; Section 6.2.1, Paragraph 2; Section 6.2.1, Paragraph 2; Section 6.2.1, Paragraph 2; Section 6.2.2, Paragraph 3; Section 6.2.2, Paragraph 6; Section 7.1, Paragraph 5; Section 7.1, Paragraph 6; Section 7.2.1, Paragraph 2; Section 7.2.2, Paragraph 3; Section 7.2.3, Paragraph 5; Section 7.2.3, Paragraph 7; Section 7.2.3, Paragraph 8; Section 7.2.4, Paragraph 2; Section 7.2.4, Paragraph 3; Section 7.2.4, Paragraph 6; Section 7.2.4.1, Paragraph 5; Section 7.2.4.2, Paragraph 8; Section 7.2.4.2, Paragraph 8; Section 7.2.5, Paragraph 5; Section 7.2.5, Paragraph 6; Section 7.2.5, Paragraph 8; Section 7.2.5, Paragraph 9; Section 7.2.6, Paragraph 3; Section 7.2.6, Paragraph 5; Section 7.2.7, Paragraph 2; Section 7.2.7, Paragraph 3; Section 7.2.7, Paragraph 6; Section 7.2.8, Paragraph 3; _Section 8_; Section 10.5, Paragraph 7; Appendix A.4.1, Paragraph 4
control stream Section 2, Paragraph 3; Section 3.2, Paragraph 4; Section 6.2, Paragraph 3; Section 6.2, Paragraph 5; Section 6.2, Paragraph 6; _Section 6.2.1_; Section 7, Paragraph 1; Section 7.2.1, Paragraph 2; Section 7.2.2, Paragraph 3; Section 7.2.3, Paragraph 5; Section 7.2.3, Paragraph 5; Section 7.2.4, Paragraph 2; Section 7.2.4, Paragraph 2; Section 7.2.4, Paragraph 3; Section 7.2.5, Paragraph 8; Section 7.2.6, Paragraph 3; Section 7.2.6, Paragraph 5; Section 7.2.7, Paragraph 2; Section 8.1, Paragraph 2.22.1; Section 9, Paragraph 4; Appendix A.2.4, Paragraph 3; Appendix A.3, Paragraph 1
D
DATA Section 2, Paragraph 3; Section 4.1, Paragraph 5, Item 2;
Section 4.1, Paragraph 7; Section 4.1, Paragraph 7;
Section 4.1.2, Paragraph 3; Section 4.1.2, Paragraph 3;
Section 4.4, Paragraph 7; Section 4.4, Paragraph 7;
Section 4.4, Paragraph 7; Section 4.4, Paragraph 7;
Section 4.4, Paragraph 8; Section 4.6, Paragraph 12;
Table 1; *_Section 7.2.1_*; Table 2; Appendix A.1, Paragraph
3; Appendix A.2.3, Paragraph 1; Appendix A.2.5
G
GOAWAY Section 3.3, Paragraph 5; Section 5.2, Paragraph 1;
Section 5.2, Paragraph 1; Section 5.2, Paragraph 1;
Section 5.2, Paragraph 2; Section 5.2, Paragraph 2;
Section 5.2, Paragraph 3; Section 5.2, Paragraph 5.1.1;
Section 5.2, Paragraph 5.1.1; Section 5.2, Paragraph 5.1.2;
Section 5.2, Paragraph 5.1.2; Section 5.2, Paragraph 5, Item
2; Section 5.2, Paragraph 5, Item 2; Section 5.2, Paragraph
6; Section 5.2, Paragraph 6; Section 5.2, Paragraph 7;
Section 5.2, Paragraph 7; Section 5.2, Paragraph 8;
Section 5.2, Paragraph 8; Section 5.2, Paragraph 9;
Section 5.2, Paragraph 9; Section 5.2, Paragraph 10;
Section 5.2, Paragraph 12; Section 5.3, Paragraph 2;
Section 5.3, Paragraph 2; Section 5.4, Paragraph 2; Table 1;
*_Section 7.2.6_*; Table 2; Appendix A.2.5; Appendix A.2.5,
Paragraph 1.16.1
H
H3_CLOSED_CRITICAL_STREAM Section 6.2.1, Paragraph 2;
Section 8.1; Table 4; Appendix A.4, Paragraph 3.4.1
H3_CONNECT_ERROR Section 4.4, Paragraph 10; Section 8.1;
Table 4; Appendix A.4, Paragraph 3.22.1
H3_EXCESSIVE_LOAD Section 8.1; Section 10.5, Paragraph 7;
Table 4; Appendix A.4, Paragraph 3.24.1
H3_FRAME_ERROR Section 7.1, Paragraph 5; Section 7.1,
Paragraph 6; Section 8.1; Table 4; Appendix A.4, Paragraph
3.14.1
H3_FRAME_UNEXPECTED Section 4.1, Paragraph 7; Section 4.1,
Paragraph 8; Section 4.4, Paragraph 8; Section 7.2.1,
Paragraph 2; Section 7.2.2, Paragraph 3; Section 7.2.3,
Paragraph 5; Section 7.2.4, Paragraph 2; Section 7.2.4,
Paragraph 3; Section 7.2.5, Paragraph 8; Section 7.2.5,
Paragraph 9; Section 7.2.6, Paragraph 5; Section 7.2.7,
Paragraph 2; Section 7.2.7, Paragraph 3; Section 7.2.8,
Paragraph 3; Section 8.1; Table 4; Appendix A.4, Paragraph
3.4.1
H3_GENERAL_PROTOCOL_ERROR Section 7.2.5, Paragraph 6;
Section 8.1; Table 4; Appendix A.4, Paragraph 3.4.1
H3_ID_ERROR Section 4.6, Paragraph 3; Section 5.2, Paragraph
7; Section 6.2.2, Paragraph 6; Section 7.2.3, Paragraph 7;
Section 7.2.3, Paragraph 8; Section 7.2.5, Paragraph 5;
Section 7.2.6, Paragraph 3; Section 7.2.7, Paragraph 6;
Section 8.1; Table 4
H3_INTERNAL_ERROR Section 8.1; Table 4; Appendix A.4,
Paragraph 3.6.1
H3_MESSAGE_ERROR Section 4.1.2, Paragraph 4; Section 8.1;
Table 4; Appendix A.4, Paragraph 3.4.1
H3_MISSING_SETTINGS Section 6.2.1, Paragraph 2; Section 8.1;
Table 4
H3_NO_ERROR Section 4.1, Paragraph 15; Section 5.2, Paragraph
11; Section 6.2.3, Paragraph 2; Section 8, Paragraph 5;
Section 8.1; Section 8.1, Paragraph 3; Section 8.1,
Paragraph 3; Table 4; Appendix A.4, Paragraph 3.2.1
H3_REQUEST_CANCELLED Section 4.1.1, Paragraph 4;
Section 4.1.1, Paragraph 5; Section 4.6, Paragraph 14;
Section 7.2.3, Paragraph 3; Section 7.2.3, Paragraph 4;
Section 8.1; Table 4; Appendix A.4, Paragraph 3.18.1;
Appendix A.4.1, Paragraph 3
H3_REQUEST_INCOMPLETE Section 4.1, Paragraph 14; Section 8.1;
Table 4
H3_REQUEST_REJECTED Section 4.1.1, Paragraph 3; Section 4.1.1,
Paragraph 4; Section 4.1.1, Paragraph 5; Section 4.1.1,
Paragraph 5; Section 8.1; Table 4; Appendix A.4, Paragraph
3.16.1; Appendix A.4.1, Paragraph 3
H3_SETTINGS_ERROR Section 7.2.4, Paragraph 6; Section 7.2.4.1,
Paragraph 5; Section 7.2.4.2, Paragraph 8; Section 7.2.4.2,
Paragraph 8; Section 8.1; Table 4
H3_STREAM_CREATION_ERROR Section 6.1, Paragraph 3;
Section 6.2, Paragraph 7; Section 6.2.1, Paragraph 2;
Section 6.2.2, Paragraph 3; Section 8.1; Table 4
H3_VERSION_FALLBACK Section 8.1; Table 4; Appendix A.4,
Paragraph 3.28.1
HEADERS Section 2, Paragraph 3; Section 4.1, Paragraph 5, Item
1; Section 4.1, Paragraph 5, Item 3; Section 4.1, Paragraph
7; Section 4.1, Paragraph 7; Section 4.1, Paragraph 7;
Section 4.1, Paragraph 10; Section 4.4, Paragraph 6;
Section 4.6, Paragraph 12; Table 1; *_Section 7.2.2_*;
Section 9, Paragraph 5; Table 2; Appendix A.2.1, Paragraph
1; Appendix A.2.5; Appendix A.2.5, Paragraph 1.4.1;
Appendix A.2.5, Paragraph 1.20.1
M
malformed Section 4.1, Paragraph 3; *_Section 4.1.2_*;
Section 4.2, Paragraph 2; Section 4.2, Paragraph 3;
Section 4.2, Paragraph 5; Section 4.3, Paragraph 3;
Section 4.3, Paragraph 4; Section 4.3.1, Paragraph 5;
Section 4.3.2, Paragraph 1; Section 4.4, Paragraph 5;
Section 8.1, Paragraph 2.30.1; Section 10.3, Paragraph 1;
Section 10.3, Paragraph 2; Section 10.5.1, Paragraph 2
MAX_PUSH_ID Section 2, Paragraph 5; Section 4.6, Paragraph 3;
Section 4.6, Paragraph 3; Section 4.6, Paragraph 3;
Section 4.6, Paragraph 3; Table 1; Section 7.2.5, Paragraph
5; *_Section 7.2.7_*; Table 2; Appendix A.1, Paragraph 4;
Appendix A.3, Paragraph 4.4.1
P
push ID *_Section 4.6_*; Section 5.2, Paragraph 1;
Section 5.2, Paragraph 5, Item 2; Section 5.2, Paragraph 9;
Section 6.2.2, Paragraph 2; Section 6.2.2, Paragraph 6;
Section 6.2.2, Paragraph 6; Section 7.2.3, Paragraph 1;
Section 7.2.3, Paragraph 7; Section 7.2.3, Paragraph 7;
Section 7.2.3, Paragraph 8; Section 7.2.3, Paragraph 8;
Section 7.2.5, Paragraph 4.2.1; Section 7.2.5, Paragraph 5;
Section 7.2.5, Paragraph 5; Section 7.2.5, Paragraph 6;
Section 7.2.5, Paragraph 6; Section 7.2.5, Paragraph 7;
Section 7.2.5, Paragraph 7; Section 7.2.5, Paragraph 7;
Section 7.2.6, Paragraph 4; Section 7.2.7, Paragraph 1;
Section 7.2.7, Paragraph 4; Section 7.2.7, Paragraph 4;
Section 7.2.7, Paragraph 6; Section 7.2.7, Paragraph 6;
Section 8.1, Paragraph 2.18.1; Appendix A.2.5, Paragraph
1.12.1; Appendix A.2.5, Paragraph 1.16.1
push stream Section 4.1, Paragraph 8; Section 4.1, Paragraph
9; Section 4.6, Paragraph 3; Section 4.6, Paragraph 5;
Section 4.6, Paragraph 5; Section 4.6, Paragraph 13;
Section 4.6, Paragraph 13; Section 4.6, Paragraph 13;
Section 6.2, Paragraph 3; *_Section 6.2.2_*; Section 7,
Paragraph 1; Section 7.2.2, Paragraph 3; Section 7.2.3,
Paragraph 1; Section 7.2.3, Paragraph 2; Section 7.2.3,
Paragraph 2; Section 7.2.3, Paragraph 2; Section 7.2.3,
Paragraph 2; Section 7.2.3, Paragraph 3; Section 7.2.3,
Paragraph 4; Section 7.2.3, Paragraph 4; Section 7.2.3,
Paragraph 4; Section 7.2.5, Paragraph 4.2.1; Section 7.2.7,
Paragraph 1; Appendix A.2.5, Paragraph 1.12.1
PUSH_PROMISE Section 2, Paragraph 5; Section 4.1, Paragraph 8;
Section 4.1, Paragraph 8; Section 4.1, Paragraph 8;
Section 4.1, Paragraph 8; Section 4.1, Paragraph 10;
Section 4.6, Paragraph 4; Section 4.6, Paragraph 10;
Section 4.6, Paragraph 11; Section 4.6, Paragraph 11;
Section 4.6, Paragraph 12; Section 4.6, Paragraph 12;
Section 4.6, Paragraph 13; Section 4.6, Paragraph 13;
Section 4.6, Paragraph 13; Table 1; Section 7.2.3, Paragraph
8; Section 7.2.3, Paragraph 8; *_Section 7.2.5_*;
Section 7.2.7, Paragraph 1; Section 10.4, Paragraph 1;
Section 10.5, Paragraph 2; Table 2; Appendix A.2.5;
Appendix A.2.5, Paragraph 1.12.1; Appendix A.2.5, Paragraph
1.12.1; Appendix A.2.5, Paragraph 1.20.1
R
request stream Section 4.1, Paragraph 1; Section 4.1,
Paragraph 15; Section 4.1, Paragraph 15; Section 4.1.1,
Paragraph 1; Section 4.1.1, Paragraph 5; Section 4.4,
Paragraph 5; Section 4.4, Paragraph 9; Section 4.6,
Paragraph 4; Section 4.6, Paragraph 4; Section 4.6,
Paragraph 11; Section 4.6, Paragraph 11; *_Section 6.1_*;
Section 7, Paragraph 1; Section 7.2.2, Paragraph 3;
Section 7.2.5, Paragraph 1
S
SETTINGS Section 3.2, Paragraph 4; Section 3.2, Paragraph 4;
Section 6.2.1, Paragraph 2; Table 1; Section 7, Paragraph 3;
*_Section 7.2.4_*; Section 8.1, Paragraph 2.20.1;
Section 8.1, Paragraph 2.22.1; Section 9, Paragraph 4;
Section 10.5, Paragraph 4; Table 2; Table 4; Table 4;
Appendix A.2.5; Appendix A.2.5, Paragraph 1.10.1;
Appendix A.3, Paragraph 2; Appendix A.3, Paragraph 3;
Appendix A.3, Paragraph 4.4.1; Appendix A.3, Paragraph
4.6.1; Appendix A.3, Paragraph 4.8.1; Appendix A.3,
Paragraph 4.10.1; Appendix A.4, Paragraph 3.10.1
SETTINGS_MAX_FIELD_SECTION_SIZE Section 4.2.2, Paragraph 2;
Section 7.2.4.1; Section 10.5.1, Paragraph 2; Appendix A.3,
Paragraph 4.12.1
stream error Section 2.2; Section 4.1.2, Paragraph 4;
Section 4.4, Paragraph 10; *_Section 8_*; Appendix A.4.1,
Paragraph 3; Appendix A.4.1, Paragraph 3; Appendix A.4.1,
Paragraph 4
Author's Address ๐
Mike Bishop (editor)
Akamai
Email: mbishop@evequefou.be
๐ฆ where this page comes from
- ietf: rfc9114 2022-06
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms