6. Connection Management (连接管理)
HTTP 消息传递独立于底层传输或会话层连接协议。HTTP 只假设存在一个可靠的传输, 按顺序传递请求和响应, 以便通过读取连接流来确定请求和响应的边界。"连接" 一词用作通用术语, 表示两个程序之间用于通信目的的传输层虚拟电路。
本规范不要求 HTTP 的特定传输协议, 因为其他协议可能会在特定情况下使用或出现。HTTP 依赖于在概念上类似于 TCP/IP 的映射到连接的概念, 因此本规范包括关于 TCP 的连接管理的指导和要求, 以及识别什么构成连接的多路复用策略。
6.1. Connection (Connection)
"Connection" 头部字段允许发送方指示特定连接的所需控制选项。
Connection = 1#connection-option
connection-option = token
连接选项是不区分大小写的标记, 表示一个选项名称。
当头部字段除了 Connection 之外仅对立即连接有意义时, 发送方必须在 Connection 头部字段内列出该头部字段的相应标记。如果接收方知道某个头部字段应该被转发, 但该字段在接收到的消息的 Connection 头部字段中作为连接选项列出, 则接收方必须在转发消息之前从消息中删除该头部字段。
Connection 头部字段的值可以包含一个令牌 "close", 以指示发送方希望在完成当前请求/响应后关闭连接。例如:
Connection: close
发送方绝对不能发送连接选项对应于一个由 Proxy-Connection 字段命名的头部字段。
中间方如果接收到包含头部字段的消息, 该头部字段通过 Connection 头部字段中的连接选项被标识, 则中间方必须从该消息中解析和删除该连接选项, 然后再转发消息到下一跳。这将删除该头部字段以便它不会被不正确地转发并确保该连接选项不会应用于除第一跳之外的任何后续连接。
当收到消息中有 Connection 头部字段时, 接收方必须在确定该消息是否为持久性连接之前解析并应用所有连接选项 (见 Section 6.3)。
HTTP/1.1 定义了 "close" 连接选项供发送方用于信号该连接在当前请求/响应完成后将被关闭。例如:
Connection: close
在请求或响应头部字段中表示发送方将在完成响应后关闭该连接。请注意, "close" 连接选项仅用于表示即将关闭的连接以供信息目的, 不影响连接的持久性。
发送方不应该在 HTTP/1.0 消息中发送 Connection 头部字段。
6.2. Establishment (建立)
客户端负责建立对服务器的连接。HTTP 在其消息框架中对建立或识别连接本身没有任何要求, 只要求它是可靠的传输, 有序的, 有流控制并能传递请求和响应。因此, HTTP 可以在任何可靠的传输层协议上传递, 包括 TCP、SCTP, 或 TLS 会话内的任何协议。
HTTP/1.1 使用序列号来识别请求和响应对, 在建立连接后, 客户端可能会立即发送其第一个请求。服务器应该延迟处理该请求, 直到它成功读取整个请求消息。
大多数 HTTP 实现设计为使用 TCP 作为传输协议。默认 TCP 端口是用于 "http" URI 的 80, 和用于 "https" URI 的 443。
实现可以设计为使用其他传输协议, 但这样做需要将 HTTP 的请求/响应语义映射到底层传输的某些等效概念。例如, SCTP [RFC4960] 提供了多个流上的可靠的按顺序传递, 如果这样的实现被配置为一次只使用一个流用于单个连接, 那么它可能会满足 HTTP/1.1 的所有要求。
6.3. Persistence (持久性)
HTTP/1.1 默认使用"持久连接" (persistent connections), 允许多个请求和响应通过单个连接进行传输。HTTP 实现应该支持持久连接。
接收方通过测量自最后一个请求或响应以来经过的时间来确定消息何时完成。关闭信号是通过底层连接关闭来提供的。
6.3.1. Retrying Requests (重试请求)
当自动重试请求时, 连接可能会在发送请求消息的过程中失败 (或者在等待响应消息的过程中失败)。客户端不应该自动重试请求, 除非它可以确定请求是幂等的 (见 [RFC7231], [Section 4.2.2]) 或者已经收到了来自源服务器的消息, 该消息指示服务器不会在连接失败之前处理该请求。
例如, 客户端如果发送了 POST 请求并且在看到任何响应之前连接失败, 则客户端不应该自动重试该 POST 请求, 因为无法知道服务器是否在连接失败之前收到并开始处理该请求。但是, 如果连接在客户端收到了状态码为 401 (Unauthorized) 的完整响应后失败, 并且客户端然后生成了第二个请求 (包含凭据), 客户端可以自动重试第二个请求, 即使它是 POST, 因为它知道第一个 POST 已经由服务器处理。
用户代理不应该自动重试包含非幂等方法的请求, 除非它有其他方法知道请求语义实际上是幂等的 (与方法定义无关) 或者服务器明确指示它希望重试请求。例如, 用户代理可以自动重试 POST 请求, 其中包含用户代理知道可以安全重试的唯一交易标识符。
用户代理应该让用户控制是否重试非幂等请求。
6.3.2. Pipelining (管道化)
支持持久连接的客户端可以"管道化" (pipeline) 它们的请求 (即, 发送多个请求而不等待每个响应)。服务器必须以接收到请求的相同顺序发送它的响应到这些管道化的请求。
客户端如果管道化请求, 应该对那些不会有问题如果必须重试的请求进行管道化, 如果连接在响应接收之前关闭的话。
例如, GET 方法是安全的, 因此客户端可以管道化一系列的 GET 请求。但是, 如果客户端在管道中混合了 GET 和 POST 请求并且连接在发送 GET 响应之前关闭, 客户端可能会需要知道哪些请求已经被服务器处理, 以避免在重新建立连接后不正确地重复发送那些已经应用的请求。
幂等方法 ([RFC7231], [Section 4.2.2]) 对于管道化是重要的, 因为它们可以自动重试如果连接在响应收到之前关闭。用户代理不应该在没有幂等方法的情况下管道化请求或者在请求需要大的或不可预测的延迟之后, 除非用户代理知道服务器支持管道化。
中间方如果接收到管道化的请求, 不应该在转发它们到入站服务器之前假设服务器支持管道化, 除非中间方以前已经从该入站服务器接收到管道化响应。
6.4. Concurrency (并发性)
客户端应该限制它到服务器的并发连接数量。
之前的 HTTP 修订版建议最多两个连接。许多用户代理使用多于两个连接以实现更快的传输。使用更多连接可以增加拥塞并且对网络和服务器的性能产生负面影响。此外, 使用过多的连接可能会触发反 DoS 保护机制, 导致其他客户端的性能下降。
服务器可能会拒绝来自客户端的流量如果它检测到该客户端打开过多的连接。
6.5. Failures and Timeouts (故障和超时)
服务器通常会有某种超时值, 超过该值它将不再维护非活动连接。代理服务器可能会使较短的超时值以节省资源。源服务器应该根据各种因素配置超时, 包括请求的数量和连接的繁忙程度。
客户端不应该假设持久连接将保持打开状态。即使客户端打算重用连接, 服务器也可能决定在发送响应后关闭连接, 或者在接收到后续请求之前关闭连接。如果客户端打算重用连接, 它应该准备好处理连接在任何时候都可能被服务器关闭的情况, 并在必要时重新建立连接。
6.6. Tear-down (拆除)
Connection 头部字段 (见 Section 6.1) 提供了一个 "close" 连接选项, 发送方使用它来信号在当前请求/响应完成后该连接将被关闭。
客户端如果不希望进一步的通信通过该连接, 应该在其最后一个请求上发送 "close" 连接选项。
服务器如果不希望进一步的通信通过该连接, 或者检测到它的客户端将在接收最终响应后关闭连接, 应该在其最后一个响应上发送 "close" 连接选项。
如果服务器或客户端在发送消息时知道这是该连接上的最后一条消息, 则应该通过包含 "close" 连接选项来指示这一点。
服务器在发送包含 "close" 连接选项的响应后应该保持连接打开, 直到它接收到客户端发送的相应请求或者客户端关闭连接。这使得客户端能够确定关闭连接的原因并且不会错误地将服务器端关闭解释为超时或失败。
发送 "close" 连接选项的客户端不应该在发送该请求后在该连接上发起任何进一步的请求。
6.6.1. Retrying Requests (重试请求)
客户端应该重试幂等请求如果连接在发送请求后关闭, 即使客户端之前已经在该连接上发送了请求。
客户端不应该自动重试非幂等请求, 除非它有其他方法来知道请求语义实际上是幂等的, 与方法定义无关, 或者服务器明确指示希望重试请求。
6.6.2. Closure (关闭)
如果服务器执行立即关闭的 TCP 连接, 存在客户端不能读取最后的 HTTP 响应的重大风险。如果服务器接收到额外的数据从客户端在发送响应后, 某些 TCP 栈会发送一个重置包给客户端; 不幸的是, 该重置包可能会在客户端读取到 HTTP 响应之前清除客户端的未确认输入缓冲区。
为了避免 TCP 重置问题, 服务器应该关闭写连接同时继续从连接读取直到它接收到相应的客户端关闭, 或者直到服务器相当确定它自己的 TCP 栈已经接收到客户端确认关闭包。此处 "相当确定" 的标准是模糊的, 但是设计来防止不必要的关闭网络连接, 假设在预期的请求的末尾接收到关闭包或者等待已知的或可配置的超时对于大多数情况来说是足够的。
服务器如果知道客户端在发送请求时会关闭连接, 应该立即关闭其读写连接, 因为那种情况下继续读取不会产生任何有用的信息。
当立即关闭时, 实现应该关闭连接的两端以发送和接收任何额外的数据。实现应该继续监控连接的关闭状态并响应任何关闭信号, 直到该连接不再使用任何网络资源 (即套接字被关闭)。实现不应该中止连接, 除非存在特殊情况, 例如收到可疑的攻击, 因为中止会导致数据丢失。
6.7. Upgrade (升级)
"Upgrade" 头部字段提供了一个简单的机制用于转换从 HTTP/1.1 到某些其他不兼容的协议。一个服务器可以通过发送 101 (Switching Protocols, 切换协议) 响应来切换协议; 见 [RFC7231], [Section 6.2.2]。
Upgrade 头部字段仅适用于切换立即连接上的应用层协议。因此, Upgrade 仅用于在需要时提供信号机制, 以便部署不兼容的协议。
Upgrade = 1#protocol
protocol = protocol-name ["/" protocol-version]
protocol-name = token
protocol-version = token
服务器可以发送 Upgrade 头部字段在任何未指示 101 (Switching Protocols) 响应的响应中以指示它愿意切换到列出的协议之一, 按偏好递减顺序, 如果客户端愿意的话。
客户端可以发送 Upgrade 头部字段在任何请求中以指示它更喜欢使用列出的协议之一进行通信 (按偏好递减顺序) 如果服务器愿意切换的话。服务器可以忽略接收到的 Upgrade 头部字段, 如果它不希望或不能升级到客户端列出的任何协议。然而, 服务器如果发送 101 (Switching Protocols) 响应, 则服务器同意升级到客户端列出的协议之一。
Upgrade 头部字段不能被用于坚持协议更改; 它的接受和使用由接收方自行决定。升级协议的能力和性质可能受到 HTTP 之外的各种因素的限制, 例如客户端或服务器的配置以及连接的性质 (即该连接是对客户端私有的还是通过共享代理)。
发送 Upgrade 头部字段的客户端必须也发送一个包含 "upgrade" 的 Connection 头部字段 (见 Section 6.1) 来表示该连接选项是为了提供与 HTTP/1.0 代理的向后兼容性。
服务器不应该切换协议除非客户端的请求消息中的 Connection 头部字段包括 "upgrade" 连接选项, 因为没有 Connection 头部字段的 Upgrade 请求可能已经被中间方误解或错误地转发。
服务器可以发送 Upgrade 头部字段作为任何响应的一部分, 但服务器发送 Upgrade 到 426 (Upgrade Required, 需要升级) 响应是合适的以指示服务器拒绝使用当前协议执行请求, 但可能会接受该请求如果客户端升级到指定协议之一的话。服务器可以发送 Upgrade 头部字段到任何 5xx (Server Error, 服务器错误) 响应以指示服务器愿意尝试用不同的协议处理请求。
如果一个响应通告了多个升级选项, 服务器应该按照服务器的偏好顺序列出它们。如果一个响应没有通告升级选项, 服务器不应该假设客户端支持某个特定的协议, 除非客户端已经通过某些其他方式 (例如, 之前的请求或配置信息) 表明了支持。
Upgrade 头部字段仅适用于切换立即连接上的应用层协议。客户端不应该发送 Upgrade 头部字段在 CONNECT 请求中, 因为 Upgrade 被设计为仅在已建立的连接上使用。
📍 翻译进度: Section 6 完成
下一章节: Section 7, 8, 9
请稍候,继续翻译剩余章节...