http2: PUSH_PROMISE 保留流 ID 验证

http2: PUSH_PROMISE reserved stream id validation

The spec 说:

The identifier of a newly established stream MUST be numerically greater than all streams that the initiating endpoint has opened or reserved. This governs streams that are opened using a HEADERS frame and streams that are reserved using PUSH_PROMISE. An endpoint that receives an unexpected stream identifier MUST respond with a connection error (Section 5.4.1) of type PROTOCOL_ERROR.

对于发送 PUSH_PROMISE 的服务器,符合要求的服务器必须发送严格递增的流 ID,这对我来说很有意义。但我不明白客户应该如何检测这种情况。

例如,在一个连接上,如果服务器发送:

  1. PUSH_PROMISE 承诺流 2
  2. PUSH_PROMISE 承诺流 4

由于并发性,客户端可能会收到

  1. PUSH_PROMISE 承诺流 4
  2. PUSH_PROMISE 承诺流 2

规范让我认为客户端应该在这方面出错,但服务器没有做错。

我在这里错过了什么?

如果服务器写入 PUSH_PROMISE[stream=2] 然后 PUSH_PROMISE[stream=4],那么这些帧将以相同的顺序传送(这是由 TCP 保证的)。

客户端的任务是按顺序从套接字中读取。 对于 HTTP/2 实现,要求甚至更严格,因为它不仅必须以有序的方式从套接字读取,而且还必须以有序的方式解析帧。

这是 PUSH_PROMISE 帧携带 HPACK 块这一事实所必需的,为了保持服务器和客户端 HPACK 上下文同步,帧(或至少这些帧的 HPACK 块)必须顺序处理,所以stream=2 before stream=4.

之后,客户端可以自由地同时处理2帧。

对于实现来说,这实际上很容易实现,因为分配给执行 I/O 读取的线程通常会执行:

loop
  read bytes from socket
  if no bytes or socket closed -> break loop
  parse read bytes (with HPACK decoding) -> produce frame objects
  pass frame objects to upper software layer
end loop

由于读取和解析是顺序的并且没有其他线程从同一个套接字读取,因此满足顺序保证。