headers 如何在 HTTP/2 中保持客户端和服务器端的同步?

How does headers keep sync in both client and server side in HTTP/2?

由于HTTP/2使用hpack压缩headers,每个连接有一个静态table和一个动态table。但我有一个问题,客户端和服务器端如何同步 headers?

有两种 table:一种用于客户端发起的消息,一种用于服务器发起的消息。

根据 spec:

When used for bidirectional communication, such as in HTTP, the encoding and decoding dynamic tables maintained by an endpoint are completely independent, i.e., the request and response dynamic tables are separate.

客户端和服务器都必须管理两个 table 的副本。因此,当客户端发送消息时,它会更新它的客户端副本 table,而当客户端收到消息时,它会更新它的服务器副本 table。在服务器端也类似。

由于 TCP 保证了消息的传递顺序,因此可以使客户端 table 在客户端和服务器端保持同步(与服务器 table 端类似)。这在无法保证按顺序消息传递的连接上会更加复杂,因此 QUIC 在 HPACK 上使用了一个名为 QPACK 的变体,它具有额外的功能来处理这个问题。

要启动 HTTP2 连接,应该已经建立了 TCP 连接。然后,客户端准备并发送第一个 HTTP2 数据包,其中包含以魔术字符串为前缀的 SETTINGS 帧,并为此特定的 TCP 连接创建两个单独的动态 tables,一个用于 client-orignated 消息,另一个用于 server-originated 消息。当服务器端收到一个以魔术字符串为前缀的 HTTP2 数据包时,它同样会创建两个独立的动态 tables.

当客户端准备第一个 HTTP2 请求时,它首先构建 HEADERS 框架并在其中附加所有需要的 header。第一个 HEADERS 帧的每个 header 是静态 table 的 1 字节长度索引(例如 0x02 -> 方法:GET)或 header 的文字表示由几个字节组成。在后一种情况下,字节序列的第一个字节是向服务器提供有关如何处理和存储这个新的 header 的信息。同时,客户端已经在其动态 table.

中存储了 header

当服务器端接收到第一个 HTTP2 HEADERS 帧时,它使用其静态 table 解码 header 块来解码 one-byte header秒。对于每个文字 header,它根据从该 header 字节序列的第一个字节检索到的信息在动态 table 中创建一个新条目。

第二次从客户端准备包含headers(HEDERS、CONTINUATION或PUSH PROMISE)的帧,每个帧都使用header,它已经存储在动态table 表示为 1 个字节*(动态 table 索引)。对于每个第一次使用且在静态table中不存在的header,再次使用文字表示并在客户端动态table中添加一个新条目。当服务器端接收到第二帧时,它会查看其静态或动态 table 来解码 one-byte header。对于每个文字 header(第一次出现),它还在其动态 table.

中创建一个新条目

这样 client-originated 消息的动态 tables 在双方保持同步。以类似的方式保持 server-originated 条消息的动态 table 同步。

相同的过程一直持续到 TCP 连接终止。渐渐地,动态 table 被填充,需要更少的文字表示。

很明显,in-order 帧的传送很关键。这是由底层 TCP 协议保护的。此外,headers 应该始终在两侧以串行方式处理。

HTTP2 使用 HPACK 格式进行 header 压缩。

*为了简单起见,在上面的示例中假设 'Literal Header Fields with Incremental Indexing and New Names'