RFC 9113
HTTP/2 —— 在 TCP 上用帧和流表达 9110 的语义。字段压缩算法在 RFC 7541,https 上必须经 ALPN 谈成 h2。
h2,解密后按 9 字节帧头拆 Type / Stream ID,并分清伪首部规则。HPACK 算法本身在 RFC 7541,不在 9113。帧、流、不再是文本行
HTTP/2 provides an optimized transport for HTTP semantics. HTTP/2 supports all of the core features of HTTP but aims to be more efficient than HTTP/1.1. HTTP/2 is a connection-oriented application-layer protocol that runs over a TCP connection ([TCP]). The client is the TCP connection initiator. The basic protocol unit in HTTP/2 is a frame (Section 4.1). Each frame type serves a different purpose. For example, HEADERS and DATA frames form the basis of HTTP requests and responses (Section 8.1); other frame types like SETTINGS, WINDOW_UPDATE, and PUSH_PROMISE are used in support of other HTTP/2 features. Multiplexing of requests is achieved by having each HTTP request/ response exchange associated with its own stream (Section 5). Streams are largely independent of each other, so a blocked or stalled request or response does not prevent progress on other streams. Effective use of multiplexing depends on flow control and prioritization. Flow control (Section 5.2) ensures that it is possible to efficiently use multiplexed streams by restricting data that is transmitted to what the receiver is able to handle. Prioritization (Section 5.3) ensures that limited resources are used most effectively. This revision of HTTP/2 deprecates the priority signaling scheme from [RFC7540].
注解
9113 是 HTTP 语义(9110)在 TCP 上的一种编码:基本单位是帧,一次请求/响应对应一条流。多路复用靠在同一条 TCP 连接上交错不同流的帧;一条流卡住,按设计不应挡住别的流。这只消除应用层排队;TCP 丢段仍会让同连接所有流等待(TCP 层队头阻塞)。
优先级信令在本修订里从 RFC 7540 的方案降级为 deprecated。流控仍在(WINDOW_UPDATE)。
h2 是 ALPN 标识,不是 Upgrade: h2c
* The string "h2" identifies the protocol where HTTP/2 uses
Transport Layer Security (TLS); see Section 9.2. This identifier
is used in the TLS Application-Layer Protocol Negotiation (ALPN)
extension [TLS-ALPN] field and in any place where HTTP/2 over TLS
is identified.
The "h2" string is serialized into an ALPN protocol identifier as
the two-octet sequence: 0x68, 0x32.
* The "h2c" string was previously used as a token for use in the
HTTP Upgrade mechanism's Upgrade header field (Section 7.8 of
[HTTP]). This usage was never widely deployed and is deprecated
by this document. The same applies to the HTTP2-Settings header
field, which was used with the upgrade to "h2c".
[……下接 https URI 必须用 ALPN]
A client that makes a request to an "https" URI uses TLS [TLS13] with the ALPN extension [TLS-ALPN]. HTTP/2 over TLS uses the "h2" protocol identifier. The "h2c" protocol identifier MUST NOT be sent by a client or selected by a server; the "h2c" protocol identifier describes a protocol that does not use TLS. Once TLS negotiation is complete, both the client and the server MUST send a connection preface (Section 3.4).
注解
h2 标识“HTTP/2 over TLS”。ALPN 里就是两个八位组 0x68 0x32(ASCII “h2”)。https URI 的 HTTP/2 必须经 TLS ALPN 协商;客户端不得发、服务器不得选 h2c。
h2c(明文 TCP + HTTP Upgrade)在 7540 里定义过,从未广泛部署,本文件废弃,连同 HTTP2-Settings 首部一起作废。
协商完成后双方还要发连接前言(§3.4),协议才算真正开跑。
24 字节魔法串 + SETTINGS
The client connection preface starts with a sequence of 24 octets,
which in hex notation is:
0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a
That is, the connection preface starts with the string "PRI *
HTTP/2.0\r\n\r\nSM\r\n\r\n". This sequence MUST be followed by a
SETTINGS frame (Section 6.5), which MAY be empty. The client sends
the client connection preface as the first application data octets of
a connection.
[……节选给 1.1 服务器看的 Note,下接服务器前言]
The server connection preface consists of a potentially empty SETTINGS frame (Section 6.5) that MUST be the first frame the server sends in the HTTP/2 connection.
注解
客户端前言是 PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n,后接一帧 SETTINGS(可空)。选这串是为了让误接到这条连接的 HTTP/1.x 设备尽快放弃解析。服务器前言就是它发出的第一帧:一帧 SETTINGS(可空)。双方前言里的 SETTINGS 都必须被 ACK。
固定 9 字节头,然后是载荷
All frames begin with a fixed 9-octet header followed by a variable-
length frame payload.
HTTP Frame {
Length (24),
Type (8),
Flags (8),
Reserved (1),
Stream Identifier (31),
Frame Payload (..),
}
Figure 1: Frame Layout
[……下接各字段定义]
Length: The length of the frame payload expressed as an unsigned
24-bit integer in units of octets. Values greater than 2^14
(16,384) MUST NOT be sent unless the receiver has set a larger
value for SETTINGS_MAX_FRAME_SIZE.
The 9 octets of the frame header are not included in this value.
Type: The 8-bit type of the frame. The frame type determines the
format and semantics of the frame. Frames defined in this
document are listed in Section 6. Implementations MUST ignore and
discard frames of unknown types.
Flags: An 8-bit field reserved for boolean flags specific to the
frame type.
Flags are assigned semantics specific to the indicated frame type.
Unused flags are those that have no defined semantics for a
particular frame type. Unused flags MUST be ignored on receipt
and MUST be left unset (0x00) when sending.
Reserved: A reserved 1-bit field. The semantics of this bit are
undefined, and the bit MUST remain unset (0x00) when sending and
MUST be ignored when receiving.
Stream Identifier: A stream identifier (see Section 5.1.1) expressed
as an unsigned 31-bit integer. The value 0x00 is reserved for
frames that are associated with the connection as a whole as
opposed to an individual stream.
The structure and content of the frame payload are dependent entirely
on the frame type.
[……下接 §6 各类型起句]
DATA frames (type=0x00) convey arbitrary, variable-length sequences of octets associated with a stream. One or more DATA frames are used, for instance, to carry HTTP request or response message contents.
注解
线上顺序:Length(24) · Type(8) · Flags(8) · R+Stream ID(1+31) · Payload。Length 不含 这 9 字节头。默认最大载荷 214(16384),可用 SETTINGS_MAX_FRAME_SIZE 提到最多 224−1。
Stream ID = 0 表示“整条连接”,不是某条流。SETTINGS、PING、GOAWAY 走 0;DATA / HEADERS 走非 0。
本规范定义的类型(教学对照,不是原文表):DATA 0x00、HEADERS 0x01、PRIORITY 0x02(已弃用)、RST_STREAM 0x03、SETTINGS 0x04、PUSH_PROMISE 0x05、PING 0x06、GOAWAY 0x07、WINDOW_UPDATE 0x08、CONTINUATION 0x09。未知类型必须忽略并丢弃。
字段压缩不在 9113 里规定算法
Field section compression is the process of compressing a set of field lines (Section 5.2 of [HTTP]) to form a field block. Field section decompression is the process of decoding a field block into a set of field lines. Details of HTTP/2 field section compression and decompression are defined in [COMPRESSION], which, for historical reasons, refers to these processes as header compression and decompression. Each field block carries all of the compressed field lines of a single field section. Header sections also include control data associated with the message in the form of pseudo-header fields (Section 8.3) that use the same format as a field line.
[……下接 HPACK 编解码器状态]
Field compression is stateful. Each endpoint has an HPACK encoder context and an HPACK decoder context that are used for encoding and decoding all field blocks on a connection. Section 4 of [COMPRESSION] defines the dynamic table, which is the primary state for each context. The dynamic table has a maximum size that is set by an HPACK decoder. An endpoint communicates the size chosen by its HPACK decoder context using the SETTINGS_HEADER_TABLE_SIZE setting; see Section 6.5.2. When a connection is established, the dynamic table size for the HPACK decoder and encoder at both endpoints starts at 4,096 bytes, the initial value of the SETTINGS_HEADER_TABLE_SIZE setting.
[……下接规范性引用]
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>.
注解
9113 只规定:字段块怎么拆进 HEADERS / PUSH_PROMISE / CONTINUATION,动态表大小怎么用 SETTINGS_HEADER_TABLE_SIZE 通告(初始 4096)。HPACK 的整数编码、霍夫曼、静态表/动态表指令在 RFC 7541。说“HTTP/2 规范自己定义了 HPACK”是错的。
字段块必须连续发送,中间不能插别的帧或别的流。解不出就整条连接 COMPRESSION_ERROR。
流状态机
The lifecycle of a stream is shown in Figure 2.
+--------+
send PP | | recv PP
,--------+ idle +--------.
/ | | \
v +--------+ v
+----------+ | +----------+
| | | send H / | |
,------+ reserved | | recv H | reserved +------.
| | (local) | | | (remote) | |
| +---+------+ v +------+---+ |
| | +--------+ | |
| | recv ES | | send ES | |
| send H | ,-------+ open +-------. | recv H |
| | / | | \ | |
| v v +---+----+ v v |
| +----------+ | +----------+ |
| | half- | | | half- | |
| | closed | | send R / | closed | |
| | (remote) | | recv R | (local) | |
| +----+-----+ | +-----+----+ |
| | | | |
| | send ES / | recv ES / | |
| | send R / v send R / | |
| | recv R +--------+ recv R | |
| send R / `----------->| |<-----------' send R / |
| recv R | closed | recv R |
`----------------------->| |<-----------------------'
+--------+
Figure 2: Stream States
send: endpoint sends this frame
recv: endpoint receives this frame
H: HEADERS frame (with implied CONTINUATION frames)
ES: END_STREAM flag
R: RST_STREAM frame
PP: PUSH_PROMISE frame (with implied CONTINUATION frames); state
transitions are for the promised stream
注解
idle 因 HEADERS(H)变成 open,因 PUSH_PROMISE(PP)变成 reserved。open 上 END_STREAM(ES)走到 half-closed,RST_STREAM(R)直接 closed。两端对同一条流的看法可以暂时不一致(帧还在路上)。
CONTINUATION 不单独改状态,算它所跟随的 HEADERS / PUSH_PROMISE 的一部分。带 END_STREAM 的 HEADERS 可以一次触发两次转移。
客户端发奇数流号,服务器发偶数;0 留给连接控制。与 QUIC 的 Stream ID 编码(RFC 9000 §2.1)不要记混。
不是协商,是通告;必须 ACK
The SETTINGS frame (type=0x04) conveys configuration parameters that
affect how endpoints communicate, such as preferences and constraints
on peer behavior. The SETTINGS frame is also used to acknowledge the
receipt of those settings. Individually, a configuration parameter
from a SETTINGS frame is referred to as a "setting".
Settings are not negotiated; they describe characteristics of the
sending peer, which are used by the receiving peer. Different values
for the same setting can be advertised by each peer. For example, a
client might set a high initial flow-control window, whereas a server
might set a lower value to conserve resources.
A SETTINGS frame MUST be sent by both endpoints at the start of a
connection and MAY be sent at any other time by either endpoint over
the lifetime of the connection. Implementations MUST support all of
the settings defined by this specification.
Each parameter in a SETTINGS frame replaces any existing value for
that parameter. Settings are processed in the order in which they
appear, and a receiver of a SETTINGS frame does not need to maintain
any state other than the current value of each setting. Therefore,
the value of a SETTINGS parameter is the last value that is seen by a
receiver.
SETTINGS frames are acknowledged by the receiving peer. To enable
this, the SETTINGS frame defines the ACK flag:
ACK (0x01): When set, the ACK flag indicates that this frame
acknowledges receipt and application of the peer's SETTINGS frame.
When this bit is set, the frame payload of the SETTINGS frame MUST
be empty. Receipt of a SETTINGS frame with the ACK flag set and a
length field value other than 0 MUST be treated as a connection
error (Section 5.4.1) of type FRAME_SIZE_ERROR. For more
information, see Section 6.5.3 ("Settings Synchronization").
SETTINGS frames always apply to a connection, never a single stream.
The stream identifier for a SETTINGS frame MUST be zero (0x00). If
an endpoint receives a SETTINGS frame whose Stream Identifier field
is anything other than 0x00, the endpoint MUST respond with a
connection error (Section 5.4.1) of type PROTOCOL_ERROR.
The SETTINGS frame affects connection state. A badly formed or
incomplete SETTINGS frame MUST be treated as a connection error
(Section 5.4.1) of type PROTOCOL_ERROR.
[……下接长度须为 6 的倍数,以及帧格式]
SETTINGS Frame {
Length (24),
Type (8) = 0x04,
Unused Flags (7),
ACK Flag (1),
Reserved (1),
Stream Identifier (31) = 0,
Setting (48) ...,
}
Setting {
Identifier (16),
Value (32),
}
Figure 7: SETTINGS Frame Format
[……下接已定义参数]
The following settings are defined:
SETTINGS_HEADER_TABLE_SIZE (0x01): This setting allows the sender to
inform the remote endpoint of the maximum size of the compression
table used to decode field blocks, in units of octets. The
encoder can select any size equal to or less than this value by
using signaling specific to the compression format inside a field
block (see [COMPRESSION]). The initial value is 4,096 octets.
SETTINGS_ENABLE_PUSH (0x02): This setting can be used to enable or
disable server push. A server MUST NOT send a PUSH_PROMISE frame
if it receives this parameter set to a value of 0; see
Section 8.4. A client that has both set this parameter to 0 and
had it acknowledged MUST treat the receipt of a PUSH_PROMISE frame
as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
The initial value of SETTINGS_ENABLE_PUSH is 1. For a client,
this value indicates that it is willing to receive PUSH_PROMISE
frames. For a server, this initial value has no effect, and is
equivalent to the value 0. Any value other than 0 or 1 MUST be
treated as a connection error (Section 5.4.1) of type
PROTOCOL_ERROR.
A server MUST NOT explicitly set this value to 1. A server MAY
choose to omit this setting when it sends a SETTINGS frame, but if
a server does include a value, it MUST be 0. A client MUST treat
receipt of a SETTINGS frame with SETTINGS_ENABLE_PUSH set to 1 as
a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
SETTINGS_MAX_CONCURRENT_STREAMS (0x03): This setting indicates the
maximum number of concurrent streams that the sender will allow.
This limit is directional: it applies to the number of streams
that the sender permits the receiver to create. Initially, there
is no limit to this value. It is recommended that this value be
no smaller than 100, so as to not unnecessarily limit parallelism.
A value of 0 for SETTINGS_MAX_CONCURRENT_STREAMS SHOULD NOT be
treated as special by endpoints. A zero value does prevent the
creation of new streams; however, this can also happen for any
limit that is exhausted with active streams. Servers SHOULD only
set a zero value for short durations; if a server does not wish to
accept requests, closing the connection is more appropriate.
SETTINGS_INITIAL_WINDOW_SIZE (0x04): This setting indicates the
sender's initial window size (in units of octets) for stream-level
flow control. The initial value is 2^16-1 (65,535) octets.
This setting affects the window size of all streams (see
Section 6.9.2).
Values above the maximum flow-control window size of 2^31-1 MUST
be treated as a connection error (Section 5.4.1) of type
FLOW_CONTROL_ERROR.
SETTINGS_MAX_FRAME_SIZE (0x05): This setting indicates the size of
the largest frame payload that the sender is willing to receive,
in units of octets.
The initial value is 2^14 (16,384) octets. The value advertised
by an endpoint MUST be between this initial value and the maximum
allowed frame size (2^24-1 or 16,777,215 octets), inclusive.
Values outside this range MUST be treated as a connection error
(Section 5.4.1) of type PROTOCOL_ERROR.
SETTINGS_MAX_HEADER_LIST_SIZE (0x06): This advisory setting informs
a peer of the maximum field section size that the sender is
prepared to accept, in units of octets. The value is based on the
uncompressed size of field lines, including the length of the name
and value in units of octets plus an overhead of 32 octets for
each field line.
For any given request, a lower limit than what is advertised MAY
be enforced. The initial value of this setting is unlimited.
An endpoint that receives a SETTINGS frame with any unknown or
unsupported identifier MUST ignore that setting.
注解
SETTINGS 不是双方谈妥一个值:每端通告自己的参数,对端按收到的最新值来。连接开始双方各发一帧;Stream ID 必须是 0。
ACK 标志位置位时载荷必须空,表示“对端那帧我已应用”。非 ACK 的 SETTINGS 长度必须是 6 的倍数(每个设置 16+32 bit)。
常考初值:HEADER_TABLE_SIZE = 4096;ENABLE_PUSH = 1(但服务器不得显式设为 1);MAX_CONCURRENT_STREAMS 起初无上限;INITIAL_WINDOW_SIZE = 65535;MAX_FRAME_SIZE = 16384;MAX_HEADER_LIST_SIZE 起初不限。
以冒号开头的控制数据,不是普通首部
HTTP/2 uses special pseudo-header fields beginning with a ':' character (ASCII 0x3a) to convey message control data (see Section 6.2 of [HTTP]). Pseudo-header fields are not HTTP header fields. Endpoints MUST NOT generate pseudo-header fields other than those defined in this document. Note that an extension could negotiate the use of additional pseudo-header fields; see Section 5.5. Pseudo-header fields are only valid in the context in which they are defined. Pseudo-header fields defined for requests MUST NOT appear in responses; pseudo-header fields defined for responses MUST NOT appear in requests. Pseudo-header fields MUST NOT appear in a trailer section. Endpoints MUST treat a request or response that contains undefined or invalid pseudo-header fields as malformed (Section 8.1.1). All pseudo-header fields MUST appear in a field block before all regular field lines. Any request or response that contains a pseudo- header field that appears in a field block after a regular field line MUST be treated as malformed (Section 8.1.1). The same pseudo-header field name MUST NOT appear more than once in a
[……下接请求伪首部]
The following pseudo-header fields are defined for HTTP/2 requests:
* The ":method" pseudo-header field includes the HTTP method
(Section 9 of [HTTP]).
[……节选 :scheme,下接 :authority]
* The ":authority" pseudo-header field conveys the authority portion
(Section 3.2 of [RFC3986]) of the target URI (Section 7.1 of
[HTTP]). The recipient of an HTTP/2 request MUST NOT use the Host
header field to determine the target URI if ":authority" is
present.
[……节选 :authority 细则,下接强制字段与 :status]
All HTTP/2 requests MUST include exactly one valid value for the ":method", ":scheme", and ":path" pseudo-header fields, unless they are CONNECT requests (Section 8.5). An HTTP request that omits mandatory pseudo-header fields is malformed (Section 8.1.1).
For HTTP/2 responses, a single ":status" pseudo-header field is defined that carries the HTTP status code field (see Section 15 of [HTTP]). This pseudo-header field MUST be included in all responses, including interim responses; otherwise, the response is malformed
注解
伪首部以 :(0x3a)开头,不是 HTTP 首部字段。必须出现在所有常规字段之前,同一名字不得重复,不得进 trailer。请求用的不得出现在响应里,反之亦然。
普通请求强制 :method、:scheme、:path 各恰好一个(CONNECT 例外,省略 :scheme/:path)。响应强制 :status,包括 1xx。
有 :authority 时,接收方 MUST NOT 再用 Host 决定目标 URI。客户端直接生成的 HTTP/2 请求 MUST 用 :authority 传权威信息;Host 若也出现,不得指向不同实体。这是 9110 §7.2 在 HTTP/2 上的落地。
抓包对照
ClientHello 里的 ALPN offer 明文可见。TLS 1.2 服务器选择在 ServerHello 可见;TLS 1.3 选择在 EncryptedExtensions(已加密,需 SSLKEYLOGFILE / 会话密钥)。没密钥时不能把“客户端提议了 h2”当成“已经谈成 h2”。
- 过滤器记两层:握手看
tls.handshake里的 ALPN;应用看解密后的http2。不要指望在 TCP 载荷里直接搜GET /。tls.handshake.extensions_alpn_str == "h2"命中 ClientHello 只证明客户端提议了 h2,不等于服务器已选定。 - 连接前言的
PRI * HTTP/2.0只出现在客户端应用数据开头。服务器第一帧就是 SETTINGS。 - HEADERS 后面若无 END_HEADERS,必须紧跟 CONTINUATION,中间不能插 DATA。
- HPACK 动态表是连接级状态。中途丢帧再重连,旧压缩上下文不能接着用。
考点与易错点
- https 上的 HTTP/2 用 ALPN 标识
h2(0x68 0x32)。h2c已废弃;不得靠 Upgrade 在 TLS 里谈 HTTP/2。 - HPACK 属于 RFC 7541,9113 只规定字段块的装帧与动态表尺寸通告。
- 帧头固定 9 字节;Length 不含头。SETTINGS 的 Stream ID 必须为 0。
- SETTINGS 是通告不是协商;带 ACK 的那帧载荷必须为空。
- 伪首部不是普通首部:必须在常规字段前、不可重复、不可进 trailer。请求强制 :method/:scheme/:path(CONNECT 除外),响应强制 :status。
- 有 :authority 就不要用 Host 决定目标 URI。客户端直接发的 h2 请求必须用 :authority 传权威信息。