8. 连接 (Connections)
8 Connections
8.1 持久连接
8.1.1 目的
在持久连接出现之前, 获取每个 URL 都要建立一个单独 TCP connection, 这增加了 HTTP server 的负载并导致 Internet 拥塞. inline image 和其他关联数据的使用, 往往要求 client 在很短时间内向同一 server 发出多个 request. 对这些性能问题的分析以及原型实现结果可见 [26] [30]. 实现经验和实际 HTTP/1.1 (RFC 2068) 实现的测量显示结果良好 [39]. 也探索过替代方案, 例如 T/TCP [27].
持久 HTTP connection 有多项优点:
- 通过减少 TCP connection 的打开和关闭次数, 可节省 router 和 host
(client, server, proxy, gateway, tunnel 或 cache) 中的 CPU 时间,
并可在 host 中节省 TCP protocol control block 使用的内存.
- HTTP request 和 response 可以在一个 connection 上 pipeline.
pipelining 允许 client 在不等待每个 response 的情况下发出多个 request,
从而让单个 TCP connection 得到更高效使用, 并显著降低经过时间.
- 通过减少 TCP 打开造成的 packet 数量, 并允许 TCP 有足够时间判断网络拥塞状态, 可降低网络拥塞.
- 后续 request 的 latency 会降低, 因为不再花时间进行 TCP connection 打开握手.
- HTTP 能更平滑地演进, 因为可报告错误而不必付出关闭 TCP connection 的代价.
使用 HTTP 未来版本的 client 可能乐观地尝试某项新功能,
但如果正在与较旧 server 通信, 可在报告错误后用旧语义重试.
HTTP 实现 SHOULD 实现持久连接.
8.1.2 总体操作
HTTP/1.1 与 HTTP 早期版本之间的一个重要差异是, 持久连接是任何 HTTP connection 的默认行为. 也就是说, 除非另有指示, client SHOULD 假定 server 将维护持久连接, 即使 server 返回错误 response 之后也是如此.
持久连接提供一种机制, 使 client 和 server 能够发出关闭 TCP connection 的信号. 该信号通过 Connection header field (第 14.10 节) 传递. 一旦发出 close 信号, client MUST NOT 在该 connection 上发送任何更多 request.
8.1.2.1 协商
HTTP/1.1 server MAY 假定 HTTP/1.1 client 打算维护持久连接, 除非 request 中发送了包含 connection-token "close" 的 Connection header. 如果 server 选择在发送 response 后立即关闭 connection, 它 SHOULD 发送包含 connection-token close 的 Connection header.
HTTP/1.1 client MAY 期望 connection 保持打开, 但会基于 server 的 response 是否包含带 connection-token close 的 Connection header 来决定是否保持打开. 如果 client 不希望在该 request 之后继续维护 connection, 它 SHOULD 发送包含 connection-token close 的 Connection header.
如果 client 或 server 任一方在 Connection header 中发送 close token, 该 request 就成为该 connection 上的最后一个 request.
client 和 server SHOULD NOT 假定低于 1.1 的 HTTP 版本会维护持久连接, 除非显式发出了信号. 关于与 HTTP/1.0 client 向后兼容的更多信息见第 19.6.2 节.
为保持持久, connection 上的所有 message MUST 具有自定义 message length (即不是由 connection 关闭来定义的长度), 如第 4.4 节所述.
8.1.2.2 Pipelining
支持持久连接的 client MAY 对其 request 进行 "pipeline" (即不等待每个 response 就发送多个 request). server MUST 按收到这些 request 的相同顺序发送 response.
假定使用持久连接并在 connection 建立后立即 pipeline 的 client, SHOULD 准备在第一次 pipelined 尝试失败时重试其 connection. 如果 client 这样重试, 在知道该 connection 是持久连接之前 MUST NOT pipeline. 如果 server 在发送所有相应 response 之前关闭 connection, client MUST 也准备重新发送其 request.
client SHOULD NOT 使用非幂等 method 或非幂等 method 序列 pipeline request (见第 9.1.2 节). 否则, transport connection 过早终止可能导致不确定结果. 希望发送非幂等 request 的 client SHOULD 等到收到前一个 request 的 response status 后再发送该 request.
8.1.3 Proxy Server
proxy 正确实现第 14.10 节规定的 Connection header field 属性尤其重要.
proxy server MUST 分别向其 client 和它所连接的 origin server (或其他 proxy server) 发出持久连接信号. 每个持久连接只适用于一个 transport link.
proxy server MUST NOT 与 HTTP/1.0 client 建立 HTTP/1.1 持久连接 (但关于许多 HTTP/1.0 client 实现的 Keep-Alive header 的信息和问题讨论见 RFC 2068 [33]).
8.1.4 实际考虑
server 通常会有某个 time-out 值, 超过该值后不再维护非活动 connection. proxy server 可能把该值设置得更高, 因为 client 很可能通过同一 server 建立更多 connection. 使用持久连接并不对 client 或 server 的此 time-out 长度 (或是否存在) 提出要求.
当 client 或 server 希望 time-out 时, 它 SHOULD 对 transport connection 发起 graceful close. client 和 server SHOULD 都持续监视 transport 另一端的关闭, 并作出适当响应. 如果 client 或 server 没有及时检测到另一端关闭, 可能在网络上造成不必要的资源消耗.
client, server 或 proxy MAY 随时关闭 transport connection. 例如, 当 server 已决定关闭 "idle" connection 时, client 可能同时已经开始发送新的 request. 从 server 角度看, connection 在空闲时被关闭; 但从 client 角度看, request 正在进行.
这意味着 client, server 和 proxy MUST 能够从异步关闭事件中恢复. 只要 request 序列是幂等的 (见第 9.1.2 节), client 软件 SHOULD 在无需用户交互的情况下重新打开 transport connection 并重传被中止的 request 序列. 非幂等 method 或序列 MUST NOT 自动重试, 尽管 user agent MAY 向人工操作员提供重试 request 的选择. 具有应用语义理解的 user-agent 软件所作确认 MAY 替代用户确认. 如果第二个 request 序列失败, SHOULD NOT 重复自动重试.
如果可能, server SHOULD 始终对每个 connection 至少响应一个 request. 除非怀疑发生网络或 client 故障, server SHOULD NOT 在传输 response 中途关闭 connection.
使用持久连接的 client SHOULD 限制它们为给定 server 维护的同时 connection 数量. 单用户 client SHOULD NOT 与任何 server 或 proxy 保持超过 2 个 connection. proxy SHOULD 使用至多 2*N 个到另一 server 或 proxy 的 connection, 其中 N 是同时活动用户数量. 这些指导旨在改善 HTTP response time 并避免拥塞.
8.2 消息传输要求
8.2.1 持久连接和流量控制
HTTP/1.1 server SHOULD 维护持久连接并使用 TCP 的 flow control 机制解决临时过载, 而不是以 client 会重试为预期来终止 connection. 后一种技术可能加剧网络拥塞.
8.2.2 监视 Connection 以获取错误状态消息
正在发送 message-body 的 HTTP/1.1 (或更高版本) client SHOULD 在传输 request 时监视 network connection 是否出现 error status. 如果 client 看到 error status, 它 SHOULD 立即停止传输 body. 如果 body 使用 "chunked" encoding (第 3.6 节) 发送, MAY 使用零长度 chunk 和空 trailer 提前标记 message 结束. 如果 body 前有 Content-Length header, client MUST 关闭 connection.
8.2.3 使用 100 (Continue) Status
100 (Continue) status (见第 10.1.1 节) 的目的, 是允许正在发送带 request body 的 request message 的 client, 在发送 request body 之前判断 origin server 是否愿意接受该 request (基于 request header). 在某些情况下, 如果 server 会在不查看 body 的情况下拒绝 message, client 发送 body 可能不合适或效率很低.
HTTP/1.1 client 的要求:
- 如果 client 会在发送 request body 前等待 100 (Continue) response,
它 MUST 发送带有 "100-continue" expectation 的 Expect request-header field (第 14.20 节).
- 如果 client 不打算发送 request body, 它 MUST NOT 发送带有 "100-continue" expectation 的
Expect request-header field (第 14.20 节).
由于存在较旧实现, 协议允许一些模糊情况: client 可能发送 "Expect: 100-continue", 却既收不到 417 (Expectation Failed) status, 也收不到 100 (Continue) status. 因此, 当 client 向从未见过其发送 100 (Continue) status 的 origin server (可能经由 proxy) 发送此 header field 时, client SHOULD NOT 在发送 request body 前无限期等待.
HTTP/1.1 origin server 的要求:
- 收到包含带 "100-continue" expectation 的 Expect request-header field 的 request 时,
origin server MUST 要么以 100 (Continue) status 响应并继续从 input stream 读取,
要么以 final status code 响应. origin server MUST NOT 在发送 100 (Continue) response 前等待 request body.
如果它以 final status code 响应, 它 MAY 关闭 transport connection, 或 MAY 继续读取并丢弃 request 的其余部分.
如果返回 final status code, 它 MUST NOT 执行所请求 method.
- 如果 request message 不包含带 "100-continue" expectation 的 Expect request-header field,
origin server SHOULD NOT 发送 100 (Continue) response; 如果此类 request 来自 HTTP/1.0
(或更早版本) client, 则 MUST NOT 发送 100 (Continue) response.
此规则有一个例外: 为兼容 RFC 2068, server MAY 针对不包含带 "100-continue" expectation 的
Expect request-header field 的 HTTP/1.1 PUT 或 POST request 发送 100 (Continue) status.
该例外的目的是最小化与未声明等待 100 (Continue) status 相关的任何 client 处理延迟,
且只适用于 HTTP/1.1 request, 不适用于任何其他 HTTP-version 值的 request.
- 如果 origin server 已经收到相应 request body 的一部分或全部, 它 MAY 省略 100 (Continue) response.
- 发送 100 (Continue) response 的 origin server 在收到并处理 request body 后,
MUST 最终发送 final status code, 除非它过早终止 transport connection.
- 如果 origin server 收到的 request 不包含带 "100-continue" expectation 的 Expect request-header field,
该 request 包含 request body, 且 server 在从 transport connection 读取完整 request body 前就用 final status code 响应,
则 server SHOULD NOT 关闭 transport connection, 直到读取完整 request, 或直到 client 关闭 connection.
否则, client 可能无法可靠收到 response message. 但是, 不应将此要求解释为阻止 server
防御 denial-of-service attack, 或防御严重损坏的 client 实现.
HTTP/1.1 proxy 的要求:
- 如果 proxy 收到包含带 "100-continue" expectation 的 Expect request-header field 的 request,
且 proxy 要么知道 next-hop server 符合 HTTP/1.1 或更高版本,
要么不知道 next-hop server 的 HTTP 版本, 则它 MUST 转发该 request, 包括 Expect header field.
- 如果 proxy 知道 next-hop server 的版本为 HTTP/1.0 或更低, 它 MUST NOT 转发该 request,
并且 MUST 以 417 (Expectation Failed) status 响应.
- proxy SHOULD 维护一个 cache, 记录最近引用的 next-hop server 所收到的 HTTP version number.
- 如果 request message 来自 HTTP/1.0 (或更早版本) client, 且不包含带 "100-continue" expectation 的
Expect request-header field, proxy MUST NOT 转发 100 (Continue) response.
该要求覆盖转发 1xx response 的一般规则 (见第 10.1 节).
8.2.4 如果 Server 过早关闭 Connection 时的 Client 行为
如果 HTTP/1.1 client 发送包含 request body 但不包含带 "100-continue" expectation 的 Expect request-header field 的 request, 且该 client 未直接连接到 HTTP/1.1 origin server, 并且 client 在收到来自 server 的任何 status 之前看到 connection 关闭, 则 client SHOULD 重试该 request. 如果 client 重试此 request, 它 MAY 使用以下 "binary exponential backoff" 算法, 以确保获得可靠 response:
1. 发起到 server 的新 connection
2. 传输 request-header
3. 将变量 R 初始化为到 server 的估计 round-trip time
(例如, 基于建立 connection 所花时间), 如果 round-trip time 不可用, 则初始化为常量值 5 秒.
4. 计算 T = R * (2**N), 其中 N 是该 request 先前重试次数.
5. 等待来自 server 的 error response, 或等待 T 秒 (以先发生者为准)
6. 如果没有收到 error response, 在 T 秒后传输 request body.
7. 如果 client 看到 connection 过早关闭, 从步骤 1 开始重复,
直到 request 被接受, 收到 error response, 或用户失去耐心并终止重试过程.
如果任何时刻收到 error status, client
- SHOULD NOT 继续, 并且
- 如果尚未完成 request message 发送, SHOULD 关闭 connection.