Skip to main content

6. Frame Definitions (帧定义)

本规范定义了多种帧类型,每种由唯一的8位类型代码标识。每种帧类型在建立和管理连接整体或单个流方面都有不同的用途。

特定帧类型的传输可以改变连接的状态。如果端点未能维护连接状态的同步视图,则连接内的成功通信将不再可能。因此,端点对于任何给定帧的使用如何影响状态有共同的理解是很重要的。

6.1. DATA

DATA帧(type=0x00)传送与流关联的任意、可变长度的八位字节序列。例如,一个或多个DATA帧用于携带HTTP请求或响应消息内容。

DATA帧也可以 (MAY) 包含填充 (padding)。可以向DATA帧添加填充以掩盖消息的大小。填充是一项安全功能;参见第10.7节。

DATA Frame {
Length (24),
Type (8) = 0x00,

Unused Flags (4),
PADDED Flag (1),
Unused Flags (2),
END_STREAM Flag (1),

Reserved (1),
Stream Identifier (31),

[Pad Length (8)],
Data (..),
Padding (..2040),
}

图3: DATA帧格式

Length、Type、Unused Flag(s)、Reserved和Stream Identifier字段在第4节中描述。DATA帧包含以下附加字段:

Pad Length (填充长度): 一个8位字段,包含帧填充的长度,单位为八位字节。此字段是条件性的,仅在设置了PADDED标志时才存在。

Data (数据): 应用数据。数据量是在减去存在的其他字段的长度后的帧有效载荷的剩余部分。

Padding (填充): 不包含应用语义值的填充八位字节。发送时填充八位字节必须 (MUST) 设置为零。接收方没有义务验证填充,但可以 (MAY) 将非零填充视为类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1)。

DATA帧定义以下标志:

PADDED (0x08): 设置时,PADDED标志表示存在Pad Length字段及其描述的任何填充。

END_STREAM (0x01): 设置时,END_STREAM标志表示此帧是端点将为标识的流发送的最后一帧。设置此标志会导致流进入"half-closed"状态之一或"closed"状态 (Section 5.1)。

| 注意:在发送所有数据后得知流关闭的端点可以通过发送具有零长度Data字段 | 和设置END_STREAM标志的STREAM帧来关闭流。这仅在端点不发送尾部时才可能, | 因为在这种情况下END_STREAM标志出现在HEADERS帧上;参见第8.1节。

DATA帧必须 (MUST) 与流关联。如果接收到Stream Identifier字段为0x00的DATA帧,接收方必须 (MUST) 以类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1) 响应。

DATA帧受流量控制约束,只能在流处于"open"或"half-closed (remote)"状态时发送。整个DATA帧有效载荷包含在流量控制中,包括Pad Length和Padding字段(如果存在)。如果接收到的DATA帧的流不处于"open"或"half-closed (local)"状态,接收方必须 (MUST) 以类型为STREAM_CLOSED的流错误 (Section 5.4.2) 响应。

填充八位字节的总数由Pad Length字段的值确定。如果填充的长度等于或大于帧有效载荷的长度,接收方必须 (MUST) 将其视为类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1)。

| 注意:通过包含值为零的Pad Length字段,可以将帧的大小增加一个八位字节。

6.2. HEADERS

HEADERS帧(type=0x01)用于打开流 (Section 5.1),并且还携带字段块片段。尽管名称如此,HEADERS帧可以携带头部段或尾部段。HEADERS帧可以在处于"idle"、"reserved (local)"、"open"或"half-closed (remote)"状态的流上发送。

HEADERS Frame {
Length (24),
Type (8) = 0x01,

Unused Flags (2),
PRIORITY Flag (1),
Unused Flag (1),
PADDED Flag (1),
END_HEADERS Flag (1),
Unused Flag (1),
END_STREAM Flag (1),

Reserved (1),
Stream Identifier (31),

[Pad Length (8)],
[Exclusive (1)],
[Stream Dependency (31)],
[Weight (8)],
Field Block Fragment (..),
Padding (..2040),
}

图4: HEADERS帧格式

Length、Type、Unused Flag(s)、Reserved和Stream Identifier字段在第4节中描述。HEADERS帧有效载荷具有以下附加字段:

Pad Length (填充长度): 一个8位字段,包含帧填充的长度,单位为八位字节。此字段仅在设置了PADDED标志时才存在。

Exclusive (独占): 单个位标志。此字段仅在设置了PRIORITY标志时才存在。HEADERS帧中的优先级信号已被废弃;参见第5.3.2节。

Stream Dependency (流依赖): 31位流标识符。此字段仅在设置了PRIORITY标志时才存在。

Weight (权重): 无符号8位整数。此字段仅在设置了PRIORITY标志时才存在。

Field Block Fragment (字段块片段): 字段块片段 (Section 4.3)。

Padding (填充): 不包含应用语义值的填充八位字节。发送时填充八位字节必须 (MUST) 设置为零。接收方没有义务验证填充,但可以 (MAY) 将非零填充视为类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1)。

HEADERS帧定义以下标志:

PRIORITY (0x20): 设置时,PRIORITY标志表示存在Exclusive、Stream Dependency和Weight字段。

PADDED (0x08): 设置时,PADDED标志表示存在Pad Length字段及其描述的任何填充。

END_HEADERS (0x04): 设置时,END_HEADERS标志表示此帧包含整个字段块 (Section 4.3),并且后面没有任何CONTINUATION帧。

未设置END_HEADERS标志的HEADERS帧必须 (MUST) 后跟同一流的CONTINUATION帧。 接收方必须 (MUST) 将接收任何其他类型的帧或不同流上的帧视为类型为PROTOCOL_ERROR 的连接错误 (Section 5.4.1)。

END_STREAM (0x01): 设置时,END_STREAM标志表示字段块 (Section 4.3) 是端点将为标识的流发送的最后一个。

设置了END_STREAM标志的HEADERS帧表示流的结束。但是,设置了END_STREAM标志的 HEADERS帧可以在同一流上后跟CONTINUATION帧。逻辑上,CONTINUATION帧是HEADERS 帧的一部分。

HEADERS帧的帧有效载荷包含字段块片段 (Section 4.3)。不适合HEADERS帧的字段块在CONTINUATION帧 (Section 6.10) 中继续。

HEADERS帧必须 (MUST) 与流关联。如果接收到Stream Identifier字段为0x00的HEADERS帧,接收方必须 (MUST) 以类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1) 响应。

HEADERS帧按第4.3节中的描述更改连接状态。

填充八位字节的总数由Pad Length字段的值确定。如果填充的长度等于或大于帧有效载荷的长度,接收方必须 (MUST) 将其视为类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1)。

| 注意:通过包含值为零的Pad Length字段,可以将帧的大小增加一个八位字节。

6.3. PRIORITY

PRIORITY帧(type=0x02)已被废弃;参见第5.3.2节。PRIORITY帧可以在任何流状态下发送,包括idle或closed流。

PRIORITY Frame {
Length (24) = 0x05,
Type (8) = 0x02,

Unused Flags (8),

Reserved (1),
Stream Identifier (31),

Exclusive (1),
Stream Dependency (31),
Weight (8),
}

图5: PRIORITY帧格式

Length、Type、Unused Flag(s)、Reserved和Stream Identifier字段在第4节中描述。PRIORITY帧的帧有效载荷包含以下附加字段:

Exclusive (独占): 单个位标志。

Stream Dependency (流依赖): 31位流标识符。

Weight (权重): 无符号8位整数。

PRIORITY帧不定义任何标志。

PRIORITY帧始终标识一个流。如果接收到流标识符为0x00的PRIORITY帧,接收方必须 (MUST) 以类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1) 响应。

发送或接收PRIORITY帧不会影响任何流的状态 (Section 5.1)。PRIORITY帧可以在任何状态的流上发送,包括"idle"或"closed"。PRIORITY帧不能在组成单个字段块 (Section 4.3) 的连续帧之间发送。

长度不是5个八位字节的PRIORITY帧必须 (MUST) 被视为类型为FRAME_SIZE_ERROR的流错误 (Section 5.4.2)。

6.4. RST_STREAM

RST_STREAM帧(type=0x03)允许立即终止流。发送RST_STREAM以请求取消流或指示发生了错误情况。

RST_STREAM Frame {
Length (24) = 0x04,
Type (8) = 0x03,

Unused Flags (8),

Reserved (1),
Stream Identifier (31),

Error Code (32),
}

图6: RST_STREAM帧格式

Length、Type、Unused Flag(s)、Reserved和Stream Identifier字段在第4节中描述。此外,RST_STREAM帧包含一个无符号的32位整数,标识错误码 (Section 7)。错误码指示流被终止的原因。

RST_STREAM帧不定义任何标志。

RST_STREAM帧完全终止引用的流并使其进入"closed"状态。在流上接收到RST_STREAM后,接收方禁止 (MUST NOT) 为该流发送额外的帧,PRIORITY除外。但是,在发送RST_STREAM后,发送端点必须 (MUST) 准备好接收和处理在流上发送的额外帧,这些帧可能是对等方在RST_STREAM到达之前发送的。

RST_STREAM帧必须 (MUST) 与流关联。如果接收到流标识符为0x00的RST_STREAM帧,接收方必须 (MUST) 将其视为类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1)。

禁止 (MUST NOT) 为处于"idle"状态的流发送RST_STREAM帧。如果接收到标识idle流的RST_STREAM帧,接收方必须 (MUST) 将其视为类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1)。

长度不是4个八位字节的RST_STREAM帧必须 (MUST) 被视为类型为FRAME_SIZE_ERROR的连接错误 (Section 5.4.1)。

6.5. SETTINGS

SETTINGS帧(type=0x04)传送影响端点通信方式的配置参数,例如对对等行为的偏好和约束。SETTINGS帧还用于确认这些设置的接收。单独地,SETTINGS帧中的配置参数称为"设置 (setting)"。

设置不是协商的;它们描述发送对等方的特征,由接收对等方使用。每个对等方可以为同一设置通告不同的值。例如,客户端可能设置较高的初始流量控制窗口,而服务器可能设置较低的值以节省资源。

连接开始时,两个端点都必须 (MUST) 发送SETTINGS帧,并且在连接的生命周期内,任一端点都可以 (MAY) 在任何其他时间发送。实现必须 (MUST) 支持本规范定义的所有设置。

SETTINGS帧中的每个参数都会替换该参数的任何现有值。设置按其出现的顺序处理,SETTINGS帧的接收方不需要维护除每个设置的当前值之外的任何状态。因此,SETTINGS参数的值是接收方看到的最后一个值。

SETTINGS帧由接收对等方确认。为了实现这一点,SETTINGS帧定义了ACK标志:

ACK (0x01): 设置时,ACK标志表示此帧确认接收和应用对等方的SETTINGS帧。设置此位时,SETTINGS帧的帧有效载荷必须 (MUST) 为空。接收到设置了ACK标志且长度字段值不是0的SETTINGS帧必须 (MUST) 被视为类型为FRAME_SIZE_ERROR的连接错误 (Section 5.4.1)。有关更多信息,请参见第6.5.3节("设置同步")。

SETTINGS帧始终应用于连接,从不应用于单个流。SETTINGS帧的流标识符必须 (MUST) 为零(0x00)。如果端点接收到Stream Identifier字段不是0x00的SETTINGS帧,端点必须 (MUST) 以类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1) 响应。

SETTINGS帧影响连接状态。格式错误或不完整的SETTINGS帧必须 (MUST) 被视为类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1)。

长度不是6个八位字节的倍数的SETTINGS帧必须 (MUST) 被视为类型为FRAME_SIZE_ERROR的连接错误 (Section 5.4.1)。

6.5.1. SETTINGS Format (SETTINGS格式)

SETTINGS帧的帧有效载荷由零个或多个设置组成,每个设置由无符号16位设置标识符和无符号32位值组成。

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),
}

图7: SETTINGS帧格式

Length、Type、Unused Flag(s)、Reserved和Stream Identifier字段在第4节中描述。SETTINGS帧的帧有效载荷包含任意数量的Setting字段,每个字段由以下组成:

Identifier (标识符): 16位设置标识符;参见第6.5.2节。

Value (值): 设置的32位值。

6.5.2. Defined Settings (已定义的设置)

定义了以下设置:

SETTINGS_HEADER_TABLE_SIZE (0x01): 此设置允许发送方通知远程端点用于解码字段块的压缩表的最大大小,单位为八位字节。编码器可以通过在字段块内使用特定于压缩格式的信令(参见 [COMPRESSION])来选择等于或小于此值的任何大小。初始值为4,096个八位字节。

SETTINGS_ENABLE_PUSH (0x02): 此设置可用于启用或禁用服务器推送。如果服务器接收到此参数设置为0的值,则禁止 (MUST NOT) 发送PUSH_PROMISE帧;参见第8.4节。已将此参数设置为0并已确认的客户端必须 (MUST) 将接收PUSH_PROMISE帧视为类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1)。

SETTINGS_ENABLE_PUSH的初始值为1。对于客户端,此值表示它愿意接收PUSH_PROMISE帧。 对于服务器,此初始值无效,等同于值0。任何非0或1的值必须 (MUST) 被视为类型为 PROTOCOL_ERROR的连接错误 (Section 5.4.1)。

服务器禁止 (MUST NOT) 显式将此值设置为1。服务器可以 (MAY) 在发送SETTINGS帧时 选择省略此设置,但如果服务器确实包含值,则必须 (MUST) 为0。客户端必须 (MUST) 将接收到SETTINGS_ENABLE_PUSH设置为1的SETTINGS帧视为类型为PROTOCOL_ERROR的 连接错误 (Section 5.4.1)。

SETTINGS_MAX_CONCURRENT_STREAMS (0x03): 此设置指示发送方将允许的最大并发流数。此限制是定向的:它适用于发送方允许接收方创建的流数。最初,此值没有限制。建议此值不小于100,以免不必要地限制并行性。

SETTINGS_MAX_CONCURRENT_STREAMS的值为0不应该 (SHOULD NOT) 被端点视为特殊。 零值确实会阻止创建新流;但是,对于任何因活动流而耗尽的限制,这也可能发生。 服务器应该 (SHOULD) 仅在短时间内设置零值;如果服务器不希望接受请求,关闭连接 更合适。

SETTINGS_INITIAL_WINDOW_SIZE (0x04): 此设置指示发送方用于流级流量控制的初始窗口大小(单位为八位字节)。初始值为2^16-1(65,535)个八位字节。

此设置影响所有流的窗口大小(参见第6.9.2节)。

超过最大流量控制窗口大小2^31-1的值必须 (MUST) 被视为类型为FLOW_CONTROL_ERROR 的连接错误 (Section 5.4.1)。

SETTINGS_MAX_FRAME_SIZE (0x05): 此设置指示发送方愿意接收的最大帧有效载荷的大小,单位为八位字节。

初始值为2^14(16,384)个八位字节。端点通告的值必须 (MUST) 在此初始值和最大 允许帧大小(2^24-1或16,777,215个八位字节)之间(包括两端)。此范围之外的值 必须 (MUST) 被视为类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1)。

SETTINGS_MAX_HEADER_LIST_SIZE (0x06): 此建议性设置通知对等方发送方准备接受的最大字段段大小,单位为八位字节。该值基于字段行的未压缩大小,包括名称和值的长度(单位为八位字节)加上每个字段行32个八位字节的开销。

对于任何给定的请求,可以 (MAY) 强制执行低于通告值的限制。此设置的初始值是无限的。

接收到包含任何未知或不支持的标识符的SETTINGS帧的端点必须 (MUST) 忽略该设置。

6.5.3. Settings Synchronization (设置同步)

SETTINGS中的大多数值受益于或需要了解对等方何时接收并应用了更改的参数值。为了提供这样的同步时间点,未设置ACK标志的SETTINGS帧的接收方必须 (MUST) 在收到后尽快应用更新的设置。SETTINGS帧按其接收的顺序确认。

SETTINGS帧中的值必须 (MUST) 按其出现的顺序处理,值之间不进行其他帧处理。必须 (MUST) 忽略不支持的设置。一旦所有值都已处理,接收方必须 (MUST) 立即发出设置了ACK标志的SETTINGS帧。收到设置了ACK标志的SETTINGS帧后,更改设置的发送方可以依赖于最旧未确认的SETTINGS帧中的值已被应用。

如果SETTINGS帧的发送方在合理的时间内未收到确认,它可以 (MAY) 发出类型为SETTINGS_TIMEOUT的连接错误 (Section 5.4.1)。在设置超时时,需要为对等方的处理延迟留出一些余地;仅基于端点之间往返时间的超时可能会导致虚假错误。

6.6. PUSH_PROMISE

PUSH_PROMISE帧(type=0x05)用于提前通知对等端点发送方打算发起的流。PUSH_PROMISE帧包括端点计划创建的流的无符号31位标识符以及为流提供附加上下文的字段段。第8.4节包含对PUSH_PROMISE帧使用的全面描述。

PUSH_PROMISE Frame {
Length (24),
Type (8) = 0x05,

Unused Flags (4),
PADDED Flag (1),
END_HEADERS Flag (1),
Unused Flags (2),

Reserved (1),
Stream Identifier (31),

[Pad Length (8)],
Reserved (1),
Promised Stream ID (31),
Field Block Fragment (..),
Padding (..2040),
}

图8: PUSH_PROMISE帧格式

Length、Type、Unused Flag(s)、Reserved和Stream Identifier字段在第4节中描述。PUSH_PROMISE帧有效载荷具有以下附加字段:

Pad Length (填充长度): 一个8位字段,包含帧填充的长度,单位为八位字节。此字段仅在设置了PADDED标志时才存在。

Promised Stream ID (承诺的流ID): 无符号31位整数,标识由PUSH_PROMISE保留的流。承诺的流标识符必须 (MUST) 是发送方发送的下一个流的有效选择(参见第5.1.1节中的"新流标识符")。

Field Block Fragment (字段块片段): 包含请求控制数据和头部段的字段块片段 (Section 4.3)。

Padding (填充): 不包含应用语义值的填充八位字节。发送时填充八位字节必须 (MUST) 设置为零。接收方没有义务验证填充,但可以 (MAY) 将非零填充视为类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1)。

PUSH_PROMISE帧定义以下标志:

PADDED (0x08): 设置时,PADDED标志表示存在Pad Length字段及其描述的任何填充。

END_HEADERS (0x04): 设置时,END_HEADERS标志表示此帧包含整个字段块 (Section 4.3),并且后面没有任何CONTINUATION帧。

未设置END_HEADERS标志的PUSH_PROMISE帧必须 (MUST) 后跟同一流的CONTINUATION帧。 接收方必须 (MUST) 将接收任何其他类型的帧或不同流上的帧视为类型为PROTOCOL_ERROR 的连接错误 (Section 5.4.1)。

PUSH_PROMISE帧只能 (MUST) 在处于"open"或"half-closed (remote)"状态的对等方发起的流上发送。PUSH_PROMISE帧的流标识符指示它关联的流。如果Stream Identifier字段指定值0x00,接收方必须 (MUST) 以类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1) 响应。

承诺的流不需要按其承诺的顺序使用。PUSH_PROMISE仅为稍后使用保留流标识符。

如果对等端点的SETTINGS_ENABLE_PUSH设置设置为0,则禁止 (MUST NOT) 发送PUSH_PROMISE。已设置此设置并已接收确认的端点必须 (MUST) 将接收PUSH_PROMISE帧视为类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1)。

PUSH_PROMISE帧的接收方可以通过将引用承诺的流标识符的RST_STREAM返回给PUSH_PROMISE的发送方来选择拒绝承诺的流。

PUSH_PROMISE帧以两种方式修改连接状态。首先,包含字段块 (Section 4.3) 可能会修改为字段段压缩维护的状态。其次,PUSH_PROMISE还为以后使用保留流,导致承诺的流进入"reserved (local)"或"reserved (remote)"状态。发送方禁止 (MUST NOT) 在流上发送PUSH_PROMISE,除非该流处于"open"或"half-closed (remote)"状态;发送方必须 (MUST) 确保承诺的流是新流标识符的有效选择 (Section 5.1.1)(即,承诺的流必须 (MUST) 处于"idle"状态)。

由于PUSH_PROMISE保留流,忽略PUSH_PROMISE帧会导致流状态变得不确定。接收方必须 (MUST) 将在既不是"open"也不是"half-closed (local)"状态的流上接收PUSH_PROMISE视为类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1)。但是,在关联流上发送了RST_STREAM的端点必须 (MUST) 处理可能在接收和处理RST_STREAM帧之前创建的PUSH_PROMISE帧。

接收方必须 (MUST) 将接收承诺非法流标识符 (Section 5.1.1) 的PUSH_PROMISE视为类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1)。请注意,非法流标识符是当前不处于"idle"状态的流的标识符。

填充八位字节的总数由Pad Length字段的值确定。如果填充的长度等于或大于帧有效载荷的长度,接收方必须 (MUST) 将其视为类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1)。

| 注意:通过包含值为零的Pad Length字段,可以将帧的大小增加一个八位字节。

6.7. PING

PING帧(type=0x06)是一种用于测量发送方的最小往返时间以及确定空闲连接是否仍然正常运行的机制。PING帧可以从任何端点发送。

PING Frame {
Length (24) = 0x08,
Type (8) = 0x06,

Unused Flags (7),
ACK Flag (1),

Reserved (1),
Stream Identifier (31) = 0,

Opaque Data (64),
}

图9: PING帧格式

Length、Type、Unused Flag(s)、Reserved和Stream Identifier字段在第4节中描述。

除了帧头部外,PING帧必须 (MUST) 在帧有效载荷中包含8个八位字节的不透明数据。发送方可以包含它选择的任何值,并以任何方式使用这些八位字节。

不包含ACK标志的PING帧的接收方必须 (MUST) 发送设置了ACK标志的PING帧作为响应,具有相同的帧有效载荷。PING响应应该 (SHOULD) 获得比任何其他帧更高的优先级。

PING帧定义以下标志:

ACK (0x01): 设置时,ACK标志表示此PING帧是PING响应。端点必须 (MUST) 在PING响应中设置此标志。端点禁止 (MUST NOT) 响应包含此标志的PING帧。

PING帧不与任何单个流关联。如果接收到Stream Identifier字段值不是0x00的PING帧,接收方必须 (MUST) 以类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1) 响应。

接收长度字段值不是8的PING帧必须 (MUST) 被视为类型为FRAME_SIZE_ERROR的连接错误 (Section 5.4.1)。

6.8. GOAWAY

GOAWAY帧(type=0x07)用于启动连接关闭或发出严重错误情况的信号。GOAWAY允许端点优雅地停止接受新流,同时仍完成对先前建立的流的处理。这使得能够进行管理操作,如服务器维护。

端点启动新流和远程对等方发送GOAWAY帧之间存在固有的竞争条件。为了处理这种情况,GOAWAY包含在此连接上在发送端点上已处理或可能已处理的最后一个对等方发起的流的流标识符。例如,如果服务器发送GOAWAY帧,标识的流是客户端发起的编号最高的流。

一旦发送GOAWAY,如果流具有高于所包含的最后流标识符的标识符,发送方将忽略接收方发起的流上发送的帧。GOAWAY帧的接收方禁止 (MUST NOT) 在连接上打开额外的流,尽管可以为新流建立新连接。

如果GOAWAY的接收方已在流标识符高于GOAWAY帧中指示的流上发送数据,则这些流不会或将不会被处理。GOAWAY帧的接收方可以将流视为从未创建过,从而允许稍后在新连接上重试这些流。

端点应该 (SHOULD) 在关闭连接之前始终发送GOAWAY帧,以便远程对等方可以知道流是否已部分处理。例如,如果HTTP客户端在服务器关闭连接的同时发送POST,如果服务器不发送GOAWAY帧来指示它可能已处理哪些流,客户端无法知道服务器是否开始处理该POST请求。

端点可能会选择在不发送GOAWAY的情况下关闭连接以应对行为不当的对等方。

GOAWAY帧可能不会立即在关闭连接之前;接收到GOAWAY且对连接不再有用的接收方仍应该 (SHOULD) 在终止连接之前发送GOAWAY帧。

GOAWAY Frame {
Length (24),
Type (8) = 0x07,

Unused Flags (8),

Reserved (1),
Stream Identifier (31) = 0,

Reserved (1),
Last-Stream-ID (31),
Error Code (32),
Additional Debug Data (..),
}

图10: GOAWAY帧格式

Length、Type、Unused Flag(s)、Reserved和Stream Identifier字段在第4节中描述。

GOAWAY帧不定义任何标志。

GOAWAY帧适用于连接,而不是特定流。端点必须 (MUST) 将流标识符不是0x00的GOAWAY帧视为类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1)。

GOAWAY帧中的最后流标识符包含GOAWAY帧的发送方可能已对其采取某些操作或可能尚未采取操作的编号最高的流标识符。直到并包括标识的流的所有流都可能已以某种方式处理。如果没有处理任何流,则最后流标识符可以设置为0。

| 注意:在此上下文中,"已处理"意味着来自流的某些数据已传递给可能因此采取 | 某些操作的某些更高层软件。

如果连接在没有GOAWAY帧的情况下终止,则最后流标识符实际上是可能的最高流标识符。

在编号较低或相等且在连接关闭之前未完全关闭的流上,除了像HTTP GET、PUT或DELETE这样的幂等操作外,不可能重新尝试请求、事务或任何协议活动。使用编号较高的流的任何协议活动都可以使用新连接安全地重试。

编号低于或等于最后流标识符的流上的活动可能仍会成功完成。GOAWAY帧的发送方可以通过发送GOAWAY帧来优雅地关闭连接,在所有进行中的流完成之前将连接保持在"open"状态。

如果情况发生变化,端点可以 (MAY) 发送多个GOAWAY帧。例如,在优雅关闭期间使用NO_ERROR发送GOAWAY的端点随后可能会遇到需要立即终止连接的情况。从最后接收的GOAWAY帧中的最后流标识符指示哪些流可能已被处理。端点禁止 (MUST NOT) 增加它们在最后流标识符中发送的值,因为对等方可能已经在另一个连接上重试了未处理的请求。

无法重试请求的客户端在服务器关闭连接时会丢失所有正在传输的请求。对于可能不使用HTTP/2为客户端提供服务的中介尤其如此。尝试优雅关闭连接的服务器应该 (SHOULD) 发送初始GOAWAY帧,将最后流标识符设置为2^31-1和NO_ERROR代码。这向客户端发出关闭即将到来的信号,并且禁止发起进一步的请求。在允许任何传输中的流创建的时间(至少一个往返时间)之后,服务器可以 (MAY) 发送另一个具有更新的最后流标识符的GOAWAY帧。这确保可以在不丢失请求的情况下干净地关闭连接。

发送GOAWAY帧后,发送方可以丢弃接收方发起的标识符高于标识的最后流的流的帧。但是,不能完全忽略改变连接状态的任何帧。例如,HEADERS、PUSH_PROMISE和CONTINUATION帧必须 (MUST) 进行最低限度的处理,以确保为字段段压缩维护的状态保持一致(参见第4.3节);类似地,DATA帧必须 (MUST) 计入连接流量控制窗口。未能处理这些帧可能导致流量控制或字段段压缩状态变得不同步。

GOAWAY帧还包含一个32位错误码 (Section 7),其中包含关闭连接的原因。

端点可以 (MAY) 将不透明数据附加到任何GOAWAY帧的帧有效载荷。附加调试数据仅用于诊断目的,不携带语义值。调试信息可能包含安全或隐私敏感数据。记录或以其他方式持久存储的调试数据必须 (MUST) 具有足够的保护措施以防止未经授权的访问。

6.9. WINDOW_UPDATE

WINDOW_UPDATE帧(type=0x08)用于实现流量控制;参见第5.2节以获取概述。

流量控制在两个层面上运行:在每个单独的流上和在整个连接上。

两种类型的流量控制都是逐跳的,即仅在两个端点之间。中介不在依赖连接之间转发WINDOW_UPDATE帧。但是,任何接收方对数据传输的限制都可以间接导致流量控制信息向原始发送方传播。

流量控制仅适用于被标识为受流量控制的帧。在本文档中定义的帧类型中,这仅包括DATA帧。免于流量控制的帧必须 (MUST) 被接受和处理,除非接收方无法分配资源来处理帧。如果接收方无法接受帧,则可以 (MAY) 以类型为FLOW_CONTROL_ERROR的流错误 (Section 5.4.2) 或连接错误 (Section 5.4.1) 响应。

WINDOW_UPDATE Frame {
Length (24) = 0x04,
Type (8) = 0x08,

Unused Flags (8),

Reserved (1),
Stream Identifier (31),

Reserved (1),
Window Size Increment (31),
}

图11: WINDOW_UPDATE帧格式

Length、Type、Unused Flag(s)、Reserved和Stream Identifier字段在第4节中描述。WINDOW_UPDATE帧的帧有效载荷是一个保留位加上一个无符号31位整数,指示发送方除了现有流量控制窗口之外可以传输的八位字节数。流量控制窗口增量的合法范围是1到2^31-1(2,147,483,647)个八位字节。

WINDOW_UPDATE帧不定义任何标志。

WINDOW_UPDATE帧可以特定于流或整个连接。在前一种情况下,帧的流标识符指示受影响的流;在后一种情况下,值"0"表示整个连接是帧的主题。

接收方必须 (MUST) 将接收到流量控制窗口增量为0的WINDOW_UPDATE帧视为类型为PROTOCOL_ERROR的流错误 (Section 5.4.2);连接流量控制窗口上的错误必须 (MUST) 被视为连接错误 (Section 5.4.1)。

WINDOW_UPDATE可以由已发送设置了END_STREAM标志的帧的对等方发送。这意味着接收方可以在"half-closed (remote)"或"closed"状态的流上接收WINDOW_UPDATE帧。接收方禁止 (MUST NOT) 将其视为错误(参见第5.1节)。

接收流量控制帧的接收方必须 (MUST) 始终将其贡献计入连接流量控制窗口,除非接收方将其视为连接错误 (Section 5.4.1)。即使帧有错误,这也是必要的。发送方将帧计入流量控制窗口,但如果接收方不这样做,发送方和接收方的流量控制窗口可能会变得不同。

长度不是4个八位字节的WINDOW_UPDATE帧必须 (MUST) 被视为类型为FRAME_SIZE_ERROR的连接错误 (Section 5.4.1)。

6.9.1. The Flow-Control Window (流量控制窗口)

HTTP/2中的流量控制使用每个发送方在每个流上保持的窗口来实现。流量控制窗口是一个简单的整数值,指示发送方允许传输多少八位字节的数据;因此,其大小是接收方缓冲容量的度量。

有两个适用的流量控制窗口:流流量控制窗口和连接流量控制窗口。发送方禁止 (MUST NOT) 发送长度超过接收方通告的任一流量控制窗口中可用空间的流量控制帧。设置了END_STREAM标志的零长度帧(即空DATA帧)如果两个流量控制窗口中都没有可用空间,则可以 (MAY) 发送。

对于流量控制计算,不计入9个八位字节的帧头部。

发送流量控制帧后,发送方将两个窗口中的可用空间减少所传输帧的长度。

帧的接收方在消耗数据并释放流量控制窗口中的空间时发送WINDOW_UPDATE帧。为流级和连接级流量控制窗口发送单独的WINDOW_UPDATE帧。建议接收方设置机制以避免发送具有非常小增量的WINDOW_UPDATE帧;参见 [RFC1122] 第4.2.3.3节。

接收WINDOW_UPDATE帧的发送方按帧中指定的数量更新相应的窗口。

发送方禁止 (MUST NOT) 允许流量控制窗口超过2^31-1个八位字节。如果发送方接收到导致流量控制窗口超过此最大值的WINDOW_UPDATE,则必须 (MUST) 适当地终止流或连接。对于流,发送方发送错误码为FLOW_CONTROL_ERROR的RST_STREAM;对于连接,发送错误码为FLOW_CONTROL_ERROR的GOAWAY帧。

来自发送方的流量控制帧和来自接收方的WINDOW_UPDATE帧彼此完全异步。此属性允许接收方积极更新发送方保持的窗口大小以防止流停滞。

6.9.2. Initial Flow-Control Window Size (初始流量控制窗口大小)

首次建立HTTP/2连接时,新流以初始流量控制窗口大小65,535个八位字节创建。连接流量控制窗口也是65,535个八位字节。两个端点都可以通过在SETTINGS帧中包含SETTINGS_INITIAL_WINDOW_SIZE的值来调整新流的初始窗口大小。只能使用WINDOW_UPDATE帧更改连接流量控制窗口。

在接收到设置SETTINGS_INITIAL_WINDOW_SIZE值的SETTINGS帧之前,端点在发送流量控制帧时只能使用默认初始窗口大小。类似地,在接收到WINDOW_UPDATE帧之前,连接流量控制窗口基于默认初始窗口大小设置。

除了更改尚未活动的流的流量控制窗口外,SETTINGS帧还可以更改具有活动流量控制窗口的流的初始流量控制窗口大小(即处于"open"或"half-closed (remote)"状态的流)。当SETTINGS_INITIAL_WINDOW_SIZE的值发生变化时,接收方必须 (MUST) 通过新值和旧值之间的差异来调整它维护的所有流流量控制窗口的大小。

SETTINGS_INITIAL_WINDOW_SIZE的更改可能导致流量控制窗口中的可用空间变为负数。发送方必须 (MUST) 跟踪负流量控制窗口,并且禁止 (MUST NOT) 发送新的流量控制帧,直到它接收到使流量控制窗口变为正数的WINDOW_UPDATE帧。

例如,如果客户端在连接建立时立即发送60 KB,并且服务器将初始窗口大小设置为16 KB,则客户端在接收到SETTINGS帧时将重新计算可用流量控制窗口为-44 KB。客户端保持负流量控制窗口,直到WINDOW_UPDATE帧将窗口恢复为正数,然后客户端才能恢复发送。

SETTINGS帧不能更改连接流量控制窗口。

端点必须 (MUST) 将导致任何流量控制窗口超过最大大小的SETTINGS_INITIAL_WINDOW_SIZE更改视为类型为FLOW_CONTROL_ERROR的连接错误 (Section 5.4.1)。

6.9.3. Reducing the Stream Window Size (减少流窗口大小)

希望使用比当前大小更小的流量控制窗口的接收方可以发送新的SETTINGS帧。但是,接收方必须 (MUST) 准备好接收超过此窗口大小的数据,因为发送方可能会在处理SETTINGS帧之前发送超过较低限制的数据。

在发送减少初始流量控制窗口大小的SETTINGS帧后,接收方可以 (MAY) 继续处理超过流量控制限制的流。允许流继续不允许接收方立即减少它为流量控制窗口保留的空间。这些流上的进度也可能停滞,因为需要WINDOW_UPDATE帧才能允许发送方恢复发送。接收方可以 (MAY) 改为为受影响的流发送错误码为FLOW_CONTROL_ERROR的RST_STREAM。

6.10. CONTINUATION

CONTINUATION帧(type=0x09)用于继续字段块片段序列 (Section 4.3)。只要前一个帧在同一流上并且是未设置END_HEADERS标志的HEADERS、PUSH_PROMISE或CONTINUATION帧,就可以发送任意数量的CONTINUATION帧。

CONTINUATION Frame {
Length (24),
Type (8) = 0x09,

Unused Flags (5),
END_HEADERS Flag (1),
Unused Flags (2),

Reserved (1),
Stream Identifier (31),

Field Block Fragment (..),
}

图12: CONTINUATION帧格式

Length、Type、Unused Flag(s)、Reserved和Stream Identifier字段在第4节中描述。CONTINUATION帧有效载荷包含字段块片段 (Section 4.3)。

CONTINUATION帧定义以下标志:

END_HEADERS (0x04): 设置时,END_HEADERS标志表示此帧结束字段块 (Section 4.3)。

如果未设置END_HEADERS标志,则此帧必须 (MUST) 后跟另一个CONTINUATION帧。 接收方必须 (MUST) 将接收任何其他类型的帧或不同流上的帧视为类型为 PROTOCOL_ERROR的连接错误 (Section 5.4.1)。

CONTINUATION帧按第4.3节中定义的方式更改连接状态。

CONTINUATION帧必须 (MUST) 与流关联。如果接收到Stream Identifier字段为0x00的CONTINUATION帧,接收方必须 (MUST) 以类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1) 响应。

CONTINUATION帧必须 (MUST) 在未设置END_HEADERS标志的HEADERS、PUSH_PROMISE或CONTINUATION帧之前。观察到违反此规则的接收方必须 (MUST) 以类型为PROTOCOL_ERROR的连接错误 (Section 5.4.1) 响应。


第6章完成!