探索HTTP/2: 流的状态
探索HTTP/2系列的第四篇文章,解读了HTTP/2流的状态,以及状态之间的转化。(2016.10.09最后更新)
1. 概述
HTTP/2的流(Stream)是有状态的。当客户端或服务器端在使用某个流去发送或接收特定帧(Frame)或包含特定标签(Flag)的帧时,会引起流的状态的转化。HTTP 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 |
`----------------------->| |<----------------------'
+--------+
send: endpoint sends this frame
recv: endpoint receives this frame
H: HEADERS frame (with implied CONTINUATIONs)
PP: PUSH_PROMISE frame (with implied CONTINUATIONs)
ES: END_STREAM flag
R: RST_STREAM frame
总的说,HTTP/2为流的整个生命周期定义了7种状态:idle,reserved (local),reserved (remote),open,half closed (local),half closed (remote)和closed。当一端发送或接收头部块(由一个HEADERS/PUSH_PROMISE帧和紧随它的零到多个CONTINUATION帧组成的集合)或RST_STREAM帧,或包含有END_STREAM标签的帧(HEADERS和DATA)之后,将改变流的状态。
流的状态基于各端自己的视角。由于帧的传输会有网络延迟,在同一时刻,不同端认为的流的状态可能是不同的。比如,当发送端使用一个处于idle状态的流发送一个不包含END_STREAM标签的HEADERS帧之后会立即认为该流处于open状态,但此时接收端尚未得到该HEADERS帧,所以在那一时刻,接收端依然认为该流的状态是idle。
2. idle
所有的流在创建之初都处于idle状态。处于idle状态的流,只允许被用于发送HEADERS帧,但可以被用于接收HEADERS和PRIORITY帧。在一端使用该状态的流发送或接收HEADERS帧之后,该端会认为此流的状态转变为open。接收PRIORITY帧不会改变流的状态。
一个idle状态的流可被另一个流通过发送/接收PUSH_PROMISE帧保留着,使其在将来被用于服务器端推送。被保留的流的状态则从idle变为reserved (local/remote)。
3. open
处于open状态的流可被用于发送任何类型的帧。使用该状态的流去发送/接收包含有END_STREAM标签的帧(HEADERS和DATA)之后,会使该流的状态变成half closed (local/remote)。使用open状态的流发送或接收RST_STREAM帧之后,则会使它的状态转变为closed。
4. half closed (local/remote)
状态half closed (local)与half closed (remote)中的local与remote的区别,完全是基于各端自己的视角。对于同一个流的两端,如果一端认为这个流的状态是half closed (local),那么另一端只能认为这个流的状态是half closed (remote)。
处于half closed (local)状态的流只能被用于发送WINDOW_UPDATE,PRIORITY和RST_STREAM帧,但可以被用于接收任何类型的帧。相对应地,处于half closed (remote)状态的流只能被用于接收WINDOW_UPDATE,PRIORITY和RST_STREAM帧,但可以被用于发送任何类型的帧。
5. reserved (local/remote)
与half closed (local/remote)状态相似,reserved (local/remote)状态中的local与remote也是基于流两端各自的视角。更具体的是,服务器端发送PUSH_PROMISE将一个idle状态的流保留着以用于未来的推送,并视这个被保留的流的状态为reserved (local),而客户端则视这个流的状态为reserved (remote)。
服务器端使用reserved (local)状态的流向客户端发送HEADERS帧。该HEADERS帧就是服务器端推送(Server Push)中被推送的响应的头部。当发送了HEADERS帧之后,服务器端将视该流的状态为half closed (remote)。
相应地,客户端通过reserved (remote)状态的流接收到服务器端推送的响应的头部,然后会视该流的状态为half closed (local)。
扩展一下,服务器端推送中被保留的流的状态在变为half closed(local/remote)之后才可能被用于接收/发送被推送的响应的体部,也就是DATA帧。
6. closed
当一端使用一个流发送或接收到RST_STREAM帧,或通过状态为half closed (local/remote)的流接收/发送包含有END_STREAM标签的帧之后,都会视这个流的状态为closed。
closed状态预示着流的终结,处于该状态的流将只能发送或接收PRIORITY帧。但有一个特例。即,如果通过使用half closed (local/remote)状态的流去接收或发送包含有END_STREAM标签的帧(HEADERS或DATA),以使该流的状态变为closed,那么在此之后的较短时间内,仍然可以接收WINDOW_UPDATE或RST_STREAM帧。