Skip to main content

9. Connection Management (连接管理)

HTTP 消息传递独立于底层传输层或会话层连接协议。HTTP 只假定可靠的传输,具有请求的有序传递和相应响应的有序传递。HTTP 请求和响应结构到底层传输协议的数据单元的映射超出了本规范的范围。

如 [HTTP] 第 7.3 节所述,用于 HTTP 交互的特定连接协议由客户端配置和目标 URI 确定。例如,"http" URI 方案 ([HTTP] 第 4.2.1 节) 表示通过 IP 的 TCP 的默认连接,默认 TCP 端口为 80,但客户端可能被配置为通过其他连接、端口或协议使用代理。

HTTP 实现应该进行连接管理,包括维护当前连接的状态、建立新连接或重用现有连接、处理在连接上接收的消息、检测连接故障以及关闭每个连接。大多数客户端并行维护多个连接,包括每个服务器端点的多个连接。大多数服务器被设计为维护数千个并发连接,同时控制请求队列以实现公平使用并检测拒绝服务攻击。

9.1. Establishment (建立)

通过各种传输层或会话层协议建立连接的描述超出了本规范的范围。每个 HTTP 连接映射到一个底层传输连接。

9.2. Associating a Response to a Request (关联响应与请求)

HTTP/1.1 不包括用于将给定请求消息与其相应的一个或多个响应消息关联的请求标识符。因此,它依赖于响应到达的顺序与同一连接上发出请求的顺序完全对应。每个请求的多个响应消息仅在一个或多个信息响应 (1xx; 参见 [HTTP] 第 15.2 节) 在同一请求的最终响应之前时发生。

在连接上有多个未完成请求的客户端必须 (MUST) 按发送顺序维护未完成请求的列表,并且必须 (MUST) 将该连接上收到的每个响应消息与尚未收到最终 (非 1xx) 响应的第一个未完成请求相关联。

如果客户端在没有未完成请求的连接上接收数据,客户端禁止 (MUST NOT) 将该数据视为有效响应; 客户端应该 (SHOULD) 关闭连接,因为消息界定现在是模糊的,除非数据仅由一个或多个 CRLF 组成(可以根据第 2.2 节丢弃)。

9.3. Persistence (持久性)

HTTP/1.1 默认使用"持久连接" (persistent connections),允许在单个连接上承载多个请求和响应。HTTP 实现应该 (SHOULD) 支持持久连接。

接收方根据最近收到的消息(如果有)中的协议版本和 Connection 头字段 ([HTTP] 第 7.6.1 节) 确定连接是否持久:

  • 如果存在 "close" 连接选项 (第 9.6 节),则连接在当前响应后将不会持久; 否则,

  • 如果收到的协议是 HTTP/1.1(或更高版本),则连接将在当前响应后持久; 否则,

  • 如果收到的协议是 HTTP/1.0,存在 "keep-alive" 连接选项,接收方不是代理或消息是响应,并且接收方希望遵守 HTTP/1.0 "keep-alive" 机制,则连接将在当前响应后持久; 否则,

  • 连接将在当前响应后关闭。

不支持持久连接的客户端必须 (MUST) 在每个请求消息中发送 "close" 连接选项。

不支持持久连接的服务器必须 (MUST) 在每个不具有 1xx (Informational) 状态码的响应消息中发送 "close" 连接选项。

客户端可以 (MAY) 在持久连接上发送额外的请求,直到它发送或接收 "close" 连接选项或接收到没有 "keep-alive" 连接选项的 HTTP/1.0 响应。

为了保持持久,连接上的所有消息都需要具有自定义的消息长度(即不是由连接关闭定义的长度),如第 6 节所述。服务器必须 (MUST) 读取整个请求消息体或在发送响应后关闭连接; 否则,持久连接上的剩余数据将被误解为下一个请求。同样,如果客户端打算将同一连接重用于后续请求,则必须 (MUST) 读取整个响应消息体。

代理服务器禁止 (MUST NOT) 与 HTTP/1.0 客户端维护持久连接(有关许多 HTTP/1.0 客户端实现的 Keep-Alive 头字段的问题的信息和讨论,请参见附录 C.2.2)。

有关与 HTTP/1.0 客户端的向后兼容性的更多信息,请参见附录 C.2.2。

9.3.1. Retrying Requests (重试请求)

连接可以在任何时候关闭,无论是否有意。实现应该预见到从异步关闭事件中恢复的需要。客户端可以自动重试一系列未完成请求的条件在 [HTTP] 第 9.2.2 节中定义。

9.3.2. Pipelining (管道化)

支持持久连接的客户端可以 (MAY) "管道化"其请求(即在不等待每个响应的情况下发送多个请求)。如果所有请求都具有安全方法 ([HTTP] 第 9.2.1 节),服务器可以 (MAY) 并行处理一系列管道化请求,但它必须 (MUST) 按接收请求的相同顺序发送相应的响应。

如果连接在收到所有相应响应之前关闭,管道化请求的客户端应该 (SHOULD) 重试未应答的请求。在失败连接后重试管道化请求时(服务器在其最后完整响应中未明确关闭的连接),客户端禁止 (MUST NOT) 在连接建立后立即管道化,因为先前管道中的第一个剩余请求可能已导致错误响应,如果在过早关闭的连接上发送多个请求,该错误响应可能会再次丢失(参见第 9.6 节中描述的 TCP 重置问题)。

幂等方法 ([HTTP] 第 9.2.2 节) 对管道化很重要,因为它们可以在连接失败后自动重试。用户代理不应该 (SHOULD NOT) 在非幂等方法之后管道化请求,直到收到该方法的最终响应状态码,除非用户代理有办法检测和恢复涉及管道化序列的部分失败条件。

收到管道化请求的中介在向入站转发时可以 (MAY) 管道化这些请求,因为它可以依赖出站用户代理确定哪些请求可以安全地管道化。如果入站连接在收到响应之前失败,管道化中介可以 (MAY) 尝试重试尚未收到响应的请求序列,如果请求都具有幂等方法; 否则,管道化中介应该 (SHOULD) 转发任何收到的响应,然后关闭相应的出站连接,以便出站用户代理可以相应地恢复。

9.4. Concurrency (并发)

客户端应该限制它维护到给定服务器的同时打开连接的数量。

HTTP 的先前修订版给出了特定数量的连接作为上限,但这被发现对许多应用程序不切实际。因此,本规范不强制要求特定的最大连接数,而是鼓励客户端在打开多个连接时保持保守。

通常使用多个连接来避免"队头阻塞"问题,其中需要大量服务器端处理和/或传输非常大内容的请求会阻塞同一连接上的后续请求。但是,每个连接都会消耗服务器资源。

此外,在拥塞网络中使用多个连接可能会导致不良副作用。使用大量连接也可能在其他未拥塞的网络中导致副作用,因为它们的聚合和初始同步发送行为可能会导致拥塞,如果使用较少的并行连接则不会出现这种拥塞。

请注意,服务器可能会拒绝它认为滥用或具有拒绝服务攻击特征的流量,例如来自单个客户端的过多打开连接数。

9.5. Failures and Timeouts (失败与超时)

服务器通常会有一些超时值,超过该值它们将不再维护非活动连接。代理服务器可能会使这个值更高,因为客户端很可能会通过同一代理服务器建立更多连接。使用持久连接对客户端或服务器的此超时的长度(或存在)没有要求。

希望超时的客户端或服务器应该 (SHOULD) 在连接上发出优雅关闭。实现应该 (SHOULD) 持续监视打开的连接以获取接收到的关闭信号并适当地响应它,因为连接双方的及时关闭使分配的系统资源能够被回收。

客户端、服务器或代理可以 (MAY) 在任何时候关闭传输连接。例如,客户端可能在服务器决定关闭"空闲"连接的同时开始发送新请求。从服务器的角度来看,连接在空闲时关闭,但从客户端的角度来看,请求正在进行中。

服务器应该 (SHOULD) 在可能的情况下维持持久连接,并允许底层传输的流量控制机制解决临时过载,而不是终止连接并期望客户端重试。后一种技术会加剧网络拥塞或服务器负载。

发送消息体的客户端应该 (SHOULD) 在传输请求时监视网络连接以获取错误响应。如果客户端看到指示服务器不希望接收消息体并正在关闭连接的响应,客户端应该 (SHOULD) 立即停止传输消息体并关闭其连接端。

9.6. Tear-down (拆除)

"close" 连接选项被定义为发送方将在完成响应后关闭此连接的信号。当打算关闭连接时,发送方应该 (SHOULD) 发送包含 "close" 连接选项的 Connection 头字段 ([HTTP] 第 7.6.1 节)。例如:

Connection: close

作为请求头字段表示这是客户端将在此连接上发送的最后一个请求,而在响应中,相同的字段表示服务器将在响应消息完成后关闭此连接。

请注意,字段名 "Close" 是保留的,因为将该名称用作头字段可能与 "close" 连接选项冲突。

发送 "close" 连接选项的客户端禁止 (MUST NOT) 在该连接上发送进一步的请求(在包含 "close" 的请求之后),并且必须 (MUST) 在读取与此请求对应的最终响应消息后关闭连接。

收到 "close" 连接选项的服务器必须 (MUST) 在发送包含 "close" 连接选项的请求的最终响应后启动连接关闭(见下文)。服务器应该 (SHOULD) 在其在该连接上的最终响应中发送 "close" 连接选项。服务器禁止 (MUST NOT) 处理在该连接上收到的任何进一步请求。

发送 "close" 连接选项的服务器必须 (MUST) 在发送包含 "close" 连接选项的响应后启动连接关闭(见下文)。服务器禁止 (MUST NOT) 处理在该连接上收到的任何进一步请求。

收到 "close" 连接选项的客户端必须 (MUST) 停止在该连接上发送请求,并在读取包含 "close" 连接选项的响应消息后关闭连接; 如果在连接上已发送额外的管道化请求,客户端不应该 (SHOULD NOT) 假设它们将被服务器处理。

如果服务器执行 TCP 连接的立即关闭,则客户端可能无法读取最后一个 HTTP 响应的风险很大。如果服务器在完全关闭的连接上从客户端接收到额外数据,例如客户端在收到服务器的响应之前发送的另一个请求,服务器的 TCP 堆栈将向客户端发送重置数据包; 不幸的是,重置数据包可能会在客户端的 HTTP 解析器读取和解释未确认的输入缓冲区之前擦除它们。

为避免 TCP 重置问题,服务器通常分阶段关闭连接。首先,服务器通过仅关闭读/写连接的写端来执行半关闭。然后,服务器继续从连接读取,直到它收到客户端的相应关闭,或者直到服务器合理确信其自己的 TCP 堆栈已收到客户端对包含服务器最后响应的数据包的确认。最后,服务器完全关闭连接。

目前尚不清楚重置问题是否仅限于 TCP 或是否也可能在其他传输连接协议中发现。

请注意,客户端半关闭的 TCP 连接不会界定请求消息,也不意味着客户端不再对响应感兴趣。通常,不能依赖传输信号来发出边缘情况信号,因为 HTTP/1.1 独立于传输。

9.7. TLS Connection Initiation (TLS连接初始化)

从概念上讲,HTTP/TLS 只是通过 TLS [TLS13] 保护的连接发送 HTTP 消息。

HTTP 客户端也充当 TLS 客户端。它在适当的端口上启动到服务器的连接,并发送 TLS ClientHello 以开始 TLS 握手。当 TLS 握手完成时,客户端可以启动第一个 HTTP 请求。所有 HTTP 数据必须 (MUST) 作为 TLS "应用数据"发送,但在其他方面像 HTTP 的正常连接一样处理(包括可能重用作为持久连接)。

9.8. TLS Connection Closure (TLS连接关闭)

TLS 在(非错误)连接关闭之前使用关闭警报交换来提供安全的连接关闭; 参见 [TLS13] 第 6.1 节。当收到有效的关闭警报时,实现可以确保不会在该连接上接收到进一步的数据。

当实现知道它已发送或接收了它关心的所有消息数据时,通常通过检测 HTTP 消息边界,它可能会通过发送关闭警报然后关闭连接而不等待从其对等方接收相应的关闭警报来生成"不完整关闭"。

不完整关闭不会质疑已接收数据的安全性,但它可能表明后续数据可能已被截断。由于 TLS 不直接知道 HTTP 消息帧,因此有必要检查 HTTP 数据本身以确定消息是否完整。不完整消息的处理在第 8 节中定义。

当遇到不完整关闭时,如果客户端已收到以下任一项,则应该 (SHOULD) 将所有请求视为已完成:

  1. Content-Length 头字段中指定的数据量,或

  2. 终端零长度块(当使用分块的 Transfer-Encoding 时)。

既没有分块传输编码也没有 Content-Length 的响应仅在收到有效关闭警报时才是完整的。将不完整消息视为完整可能会使实现暴露于攻击。

检测到不完整关闭的客户端应该 (SHOULD) 优雅地恢复。

客户端必须 (MUST) 在关闭连接之前发送关闭警报。不期望接收更多数据的客户端可以 (MAY) 选择不等待服务器的关闭警报而只是关闭连接,从而在服务器端生成不完整关闭。

服务器应该 (SHOULD) 准备好从客户端接收不完整关闭,因为客户端通常可以定位服务器数据的结束。

服务器必须 (MUST) 尝试在关闭连接之前与客户端启动关闭警报交换。服务器可以 (MAY) 在发送关闭警报后关闭连接,从而在客户端生成不完整关闭。