网络知识
RFC 精读 · Application · Proposed Standard

RFC 9113

HTTP/2 —— 在 TCP 上用帧和流表达 9110 的语义。字段压缩算法在 RFC 7541,https 上必须经 ALPN 谈成 h2。

编号 / 标题
RFC 9113 · HTTP/2
发布
June 2022 · M. Thomson, Ed. · C. Benfield, Ed.
状态
Proposed Standard(不是 STD 编号)
关系
Obsoletes 75408740 · 无 Updates · 无 Updated by
原文
rfc-editor HTML · 纯文本 · datatracker
配套
语义 9110 · HPACK 7541 · TLS 8446 · 明文已废的 h2c 见正文 §3.1
读这一篇的目标:能在 TLS 握手里指出 ALPN h2,解密后按 9 字节帧头拆 Type / Stream ID,并分清伪首部规则。HPACK 算法本身在 RFC 7541,不在 9113。
§2 HTTP/2 Protocol Overview

帧、流、不再是文本行

   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)。

§3.1 / §3.2 Version Identification

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),协议才算真正开跑。

§3.4 HTTP/2 Connection Preface

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。

§4.1 Frame Format · §6 Frame Definitions

固定 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。未知类型必须忽略并丢弃。

§4.3 / §4.3.1 · [COMPRESSION] = RFC 7541

字段压缩不在 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。

§5.1 Figure 2

流状态机

   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)不要记混。

§6.5 SETTINGS

不是协商,是通告;必须 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 起初不限。

§8.3 HTTP Control Data

以冒号开头的控制数据,不是普通首部

   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”。

客户端 offer(明文): tls.handshake.type == 1 tls.handshake.extensions_alpn_str == "h2" TLS 1.2 服务器选择(明文 ServerHello): tls.handshake.type == 2 tls.handshake.extensions_alpn_str == "h2" TLS 1.3 服务器选择(EncryptedExtensions,需密钥): 解密后才见选中的 ALPN 解密之后: http2 http2.magic 客户端 24 字节前言 http2.type == 4 SETTINGS http2.streamid == 0 http2.type == 1 HEADERS http2.header.name == ":method" http2.header.name == ":authority" http2.header.name == ":status" 帧头 9 字节(未压缩): Length[3] Type[1] Flags[1] R+StreamID[4] tcpdump: tcp port 443 Wireshark: tls.handshake.extensions_alpn_str == "h2" (命中 ClientHello 只证明客户端提议) 配置 SSLKEYLOGFILE / 会话密钥后再用 http2

考点与易错点

  1. https 上的 HTTP/2 用 ALPN 标识 h2(0x68 0x32)。h2c 已废弃;不得靠 Upgrade 在 TLS 里谈 HTTP/2。
  2. HPACK 属于 RFC 7541,9113 只规定字段块的装帧与动态表尺寸通告。
  3. 帧头固定 9 字节;Length 不含头。SETTINGS 的 Stream ID 必须为 0。
  4. SETTINGS 是通告不是协商;带 ACK 的那帧载荷必须为空。
  5. 伪首部不是普通首部:必须在常规字段前、不可重复、不可进 trailer。请求强制 :method/:scheme/:path(CONNECT 除外),响应强制 :status。
  6. 有 :authority 就不要用 Host 决定目标 URI。客户端直接发的 h2 请求必须用 :authority 传权威信息。