| Internet Engineering Task Force (IETF) | P. McManus |
| 请求评注: 8441 | Mozilla |
| 更新: 6455 | 2018年9月 |
| 类别: Standards Track | |
| ISSN: 2070-1721 |
使用 HTTP/2 引导 WebSockets
摘要
本文档定义了一种机制,用以在单个 HTTP/2 连接的流上运行 WebSocket 协议(RFC 6455)。
本备忘录的状态
这是一个互联网标准轨道文档。
本文档是互联网工程任务组 (IETF) 的产物,代表 IETF 社区的共识。它已接受公开审查并已获得互联网工程指导组 (IESG) 批准发布。有关互联网标准的更多信息,请参阅 RFC 7841 的第 2 节。
有关本文档当前状态、任何勘误以及如何提供反馈的信息,可在 https://www.rfc-editor.org/info/rfc8441 获取。
Copyright Notice
Copyright (c) 2018 IETF Trust and the persons identified as the document authors. All rights reserved.
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.
1. 介绍
The Hypertext Transfer Protocol (HTTP) [RFC7230] 在不同版本之间提供了兼容的资源级语义,但在连接管理层面并不提供兼容性。依赖于 HTTP 连接管理细节的其他协议(例如 WebSockets)必须针对 HTTP 的新版本进行更新。
The WebSocket Protocol [RFC6455] 使用 HTTP/1.1 的 Upgrade 机制(RFC7230 第 6.7 节) 将 TCP 连接从 HTTP 切换为 WebSocket 连接。对于 HTTP/2 [RFC7540] 必须采取不同的方法。由于其多路复用特性,HTTP/2 不允许连接范围的头字段或状态码(例如 Upgrade 和 Connection 请求头字段或 101 (Switching Protocols) 响应码)。这些在 [RFC6455] 握手中都是必需的。
能够从 HTTP/2 引导 WebSockets 允许两个协议共享同一个 TCP 连接,并将 HTTP/2 更高效的网络利用扩展到 WebSockets。
本文档扩展了 HTTP/2 中为 CONNECT 方法规定的用法(参见 RFC7540 第 8.3 节)。该扩展允许替代 CONNECT 通常用于连接外部主机的目标,而改为使用一个新的协议名称。其结果是在单个 HTTP/2 流上创建隧道,该隧道可以承载 WebSockets(或任何其他协议)的数据。连接上的其他流可以承载更多扩展的 CONNECT 隧道、传统的 HTTP/2 数据,或两者的混合。
该隧道化流将与连接上的其他常规流一起被多路复用,并享有 HTTP/2 的常规优先级、取消和流量控制特性。
成功使用隧道流并按照本文档对打开握手所做的修改建立 WebSocket 连接的流,随后使用传统的 WebSocket 协议,将该流视为本规范中所指的 TCP 连接。
2. 术语
3. SETTINGS_ENABLE_CONNECT_PROTOCOL SETTINGS 参数
本文档向 [RFC7540] 定义的 SETTINGS 参数集合(参见 RFC7540 第 6.5.2 节)添加了一个新的 SETTINGS 参数。
新参数名为 SETTINGS_ENABLE_CONNECT_PROTOCOL。该参数的值 MUST 为 0 或 1。
在接收到值为 1 的 SETTINGS_ENABLE_CONNECT_PROTOCOL 后,客户端 MAY 在创建新流时使用本文档定义的扩展 CONNECT。服务器接收到此参数不会产生影响。
发送者 MUST NOT 在先前发送过值为 1 之后再次发送值为 0 的 SETTINGS_ENABLE_CONNECT_PROTOCOL 参数。
使用 SETTINGS 参数来选择加入本来不兼容的协议更改属于 RFC7540 第 5.5 节 定义的 “扩展 HTTP/2” 的用法。具体来说,本规范中新伪头字段 “:protocol” 的添加,以及在第 4 节 中对 :authority 伪头字段含义的更改,都需要通过可选协商来启用。如果客户端在未先接收到 SETTINGS_ENABLE_CONNECT_PROTOCOL 参数的情况下使用本文档定义的扩展 CONNECT 的条款,则不支持的对端会检测到格式错误的请求并生成流错误(参见 RFC7540 第 8.1.2.6 节)。
4. 扩展的 CONNECT 方法
HTTP/2 中 CONNECT 方法的使用由 RFC7540 第 8.3 节 定义。本扩展对该方法作如下修改:
- 请求 HEADERS 中可以包含一个新的伪头字段 :protocol,用来指示希望在由 CONNECT 创建的隧道上使用的协议。该伪头字段为单值,包含来自 “Hypertext Transfer Protocol (HTTP) Upgrade Token Registry” 的值,注册表地址为 <https://www.iana.org/assignments/http-upgrade-tokens/>。
- 在包含 :protocol 伪头字段的请求上,目标 URI 的 :scheme 和 :path 伪头字段(参见第 5 节)也 MUST 一并包含。
- 在带有 :protocol 伪头字段的请求中,:authority 伪头字段的解释依照 RFC7540 第 8.1.2.3 节 而非该文档的 第 8.3 节。特别地,服务器在此扩展下不得像处理未被扩展修改的 CONNECT 请求那样,基于 :authority 指示的主机创建到该主机的隧道。
在接收到包含 :protocol 伪头字段的 CONNECT 请求后,服务器建立到伪头字段所指示协议类型的另一项服务的隧道。该服务可以与服务器同址,也可以不在同一位置。
5. 使用扩展的 CONNECT 引导 WebSocket 协议
CONNECT 请求 MUST 包含 :protocol 伪头字段,其值 MUST 为 websocket,以便在 HTTP/2 流上启动 WebSocket 连接。其他用于操作 cookie 的 HTTP 请求和响应头字段可以像往常一样随 CONNECT 方法一起包含在 HEADERS 中。该请求取代了 [RFC6455] 中基于 GET 的请求,并用于处理 WebSockets 的打开握手。
目标 URI 的 scheme(参见 RFC7230 第 5.1 节)在用于 “wss” 的 WebSockets 时 MUST 为 “https”,在用于 “ws” 的 WebSockets 时 MUST 为 “http”。目标 URI 的其余部分与 WebSocket URI 相同。WebSocket URI 仍用于代理自动配置。本文档所用的 HTTP/2 连接的安全要求由 [RFC7540] 为 https 请求规定,并由 [RFC8164] 为 http 请求规定。
[RFC6455] 要求使用 Connection 和 Upgrade 头字段,这些头字段不属于 HTTP/2。它们 MUST NOT 被包含在此处定义的 CONNECT 请求中。
[RFC6455] 要求使用 Host 头字段,而该头字段也不属于 HTTP/2。Host 信息由 :authority 伪头字段传递,该伪头字段在每次 HTTP/2 事务中都是必需的。
使用此扩展的 CONNECT 来引导 WebSockets 的实现不再处理 [RFC6455] 中的 Sec-WebSocket-Key 和 Sec-WebSocket-Accept 头字段,因为这些功能已被 :protocol 伪头字段取代。
Origin ([RFC6454]), Sec-WebSocket-Version, Sec-WebSocket-Protocol, 和 Sec-WebSocket-Extensions 头字段在 CONNECT 请求和响应头字段中按 [RFC6455] 的定义使用。注意 HTTP/1 的头字段名不区分大小写,而 HTTP/2 要求它们以小写编码。
在成功处理打开握手后,对等方应继续使用 WebSocket 协议 [RFC6455],并将 CONNECT 事务使用的 HTTP/2 流视为该规范中所指的 TCP 连接。此时 WebSocket 连接的状态为 OPEN(参见 [RFC6455] 第 4.1 节)。
HTTP/2 流的关闭也类似于 [RFC6455] 中的 TCP 连接关闭。有序的 TCP 级关闭由 END_STREAM 标志表示(参见 [RFC7540] 第 6.1 节)。RST 异常由 RST_STREAM 帧表示(参见 [RFC7540] 第 6.4 节),错误码使用 CANCEL(参见 [RFC7540] 第 7 节)。
5.1. 示例
[[ From Client ]] [[ From Server ]]
SETTINGS
SETTINGS_ENABLE_CONNECT_[..] = 1
HEADERS + END_HEADERS
:method = CONNECT
:protocol = websocket
:scheme = https
:path = /chat
:authority = server.example.com
sec-websocket-protocol = chat, superchat
sec-websocket-extensions = permessage-deflate
sec-websocket-version = 13
origin = http://www.example.com
HEADERS + END_HEADERS
:status = 200
sec-websocket-protocol = chat
DATA
WebSocket Data
DATA + END_STREAM
WebSocket Data
DATA + END_STREAM
WebSocket Data
6. 设计注意事项
通过对 HTTP/2 做更大改动实现与之更原生的集成当然是可行的。本设计的选择旨在在最小化解决方案复杂性的同时,仍然解决在同一连接上同时运行 HTTP/2 与 WebSockets 的主要问题。
7. 关于中间节点
本文档并未改变 WebSockets 与 HTTP 正向代理的交互方式。如果希望通过 HTTP/2 与 HTTP 代理建立 WebSockets 的客户端,应继续使用传统的 CONNECT(即不带 :protocol 伪头字段)通过该代理将隧道建立到 WebSocket 服务器。
隧道上使用的 HTTP 版本决定了 WebSockets 是直接启动还是通过本文档描述的修改后的 CONNECT 请求启动。
8. 安全注意事项
[RFC6455] 确保非 WebSockets 客户端,特别是基于 XMLHttpRequest 的客户端,无法建立 WebSocket 连接。其主要机制是使用以 Sec- 前缀的请求头字段,这些字段不能由基于 XMLHttpRequest 的客户端创建。本文档通过两种方式处理该问题:
- XMLHttpRequest 还禁止使用 CONNECT 方法,除了禁止 Sec- 前缀的请求头字段外。
- 伪头字段的使用属于连接专属,且 HTTP/2 不允许在协议栈之外创建伪头字段。
当使用本文档时,[RFC6455] 第 10 节中关于安全的注意事项仍然适用,但第 10.8 节除外。第 10.8 节与本文件所改变的引导握手特定相关,因此在此处不适用。
9. IANA 注意事项
9.1. 新的 HTTP/2 设置
本文档在由 RFC7540 第 11.3 节 建立的 “HTTP/2 Settings” 注册表中注册了一项条目。
- Code:
- 0x8
- Name:
- SETTINGS_ENABLE_CONNECT_PROTOCOL
- Initial Value:
- 0
- Specification:
- This document
9.2. 新的 HTTP Upgrade 令牌
本文档在由 [RFC7230] 建立的 “HTTP Upgrade Tokens” 注册表中注册了一项条目。
- Value:
- websocket
- Description:
- Web Socket 协议
- Expected Version Tokens:
- References:
- [RFC6455] [RFC8441]
10. 规范性参考文献
- [RFC2119]
- Bradner, S., “Key words for use in RFCs to Indicate Requirement Levels”, BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, <https://www.rfc-editor.org/info/rfc2119>.
- [RFC6454]
- Barth, A., “The Web Origin Concept”, RFC 6454, DOI 10.17487/RFC6454, December 2011, <https://www.rfc-editor.org/info/rfc6454>.
- [RFC6455]
- Fette, I. and A. Melnikov, “The WebSocket Protocol”, RFC 6455, DOI 10.17487/RFC6455, December 2011, <https://www.rfc-editor.org/info/rfc6455>.
- [RFC7230]
- Fielding, R., Ed. and J. Reschke, Ed., “Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing”, RFC 7230, DOI 10.17487/RFC7230, June 2014, <https://www.rfc-editor.org/info/rfc7230>.
- [RFC7540]
- Belshe, M., Peon, R., and M. Thomson, Ed., “Hypertext Transfer Protocol Version 2 (HTTP/2)”, RFC 7540, DOI 10.17487/RFC7540, May 2015, <https://www.rfc-editor.org/info/rfc7540>.
- [RFC8164]
- Nottingham, M. and M. Thomson, “Opportunistic Security for HTTP/2”, RFC 8164, DOI 10.17487/RFC8164, May 2017, <https://www.rfc-editor.org/info/rfc8164>.
- [RFC8174]
- Leiba, B., “Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words”, BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, <https://www.rfc-editor.org/info/rfc8174>.
致谢
2017 年 HTTP 研讨会中进行了一次富有成效的讨论,帮助确定了关键问题和可接受的解决方案复杂度。