协议升级机制
HTTP/1.1 协议 提供了一种特殊的机制,可用于使用 Upgrade
头字段将已建立的连接升级到不同的协议。
此机制是可选的;它不能用于坚持协议更改。即使实现支持新协议,也可以选择不利用升级,并且在实践中,此机制主要用于引导 WebSocket 连接。
另请注意,HTTP/2 明确禁止使用此机制;它特定于 HTTP/1.1。
升级 HTTP/1.1 连接
客户端使用 Upgrade
头字段邀请服务器切换到列出的协议之一,优先级从高到低。
由于 Upgrade
是一个逐跳头,因此也需要在 Connection
头字段中列出。这意味着包含 Upgrade 的典型请求看起来像这样
GET /index.html HTTP/1.1
Host: www.example.com
Connection: upgrade
Upgrade: example/1, foo/2
根据请求的协议,可能需要其他标头;例如,WebSocket 升级允许使用其他标头来配置有关 WebSocket 连接的详细信息,以及在打开连接时提供一定程度的安全保障。有关更多详细信息,请参阅 升级到 WebSocket 连接。
如果服务器决定升级连接,它将发送回 101 Switching Protocols
响应状态,其中包含一个指定正在切换到的协议的 Upgrade 头。如果它不升级(或无法升级)连接,则它将忽略 Upgrade
头并发送回常规响应(例如,200 OK
)。
发送 101
状态代码后,服务器就可以开始使用新协议进行通信,并根据需要执行任何其他特定于协议的握手。实际上,一旦升级响应完成,连接就会变成一个双向管道,并且可以在新协议上完成启动升级的请求。
此机制的常见用途
在这里,我们查看 Upgrade
标头的最常见用例。
升级到 WebSocket 连接
到目前为止,升级 HTTP 连接的最常见用例是使用 WebSockets,WebSockets 始终通过升级 HTTP 或 HTTPS 连接来实现。请记住,如果您使用 WebSocket API 或任何执行 WebSockets 的库打开新连接,则大部分或全部操作都将为您完成。例如,打开 WebSocket 连接就像这样简单
webSocket = new WebSocket("ws://destination.server.ext", "optionalProtocol");
WebSocket()
构造函数会完成创建初始 HTTP/1.1 连接以及为您处理握手和升级过程的所有工作。
注意:您还可以使用 "wss://"
URL 方案打开安全的 WebSocket 连接。
如果您需要从头开始创建 WebSocket 连接,则必须自己处理握手过程。创建初始 HTTP/1.1 会话后,需要通过向标准请求添加 Upgrade
和 Connection
头来请求升级,如下所示
Connection: Upgrade
Upgrade: websocket
WebSocket 特定标头
以下标头参与 WebSocket 升级过程。除了 Upgrade
和 Connection
标头之外,其余标头通常是可选的,或者在浏览器和服务器相互通信时由它们为您处理。
Sec-WebSocket-Extensions
指定一个或多个协议级 WebSocket 扩展,以要求服务器使用。在一个请求中使用多个 Sec-WebSocket-Extension
标头是允许的;结果与您在一个此类标头中包含所有列出的扩展相同。
Sec-WebSocket-Extensions: extensions
extensions
-
要请求(或同意支持)的扩展的逗号分隔列表。这些应从 IANA WebSocket 扩展名称注册表 中选择。带参数的扩展使用分号分隔符进行参数化。
例如
Sec-WebSocket-Extensions: superspeed, colormode; depth=16
Sec-WebSocket-Key
向服务器提供确认客户端有权请求升级到 WebSocket 所需的信息。当不安全的(HTTP)客户端希望升级时,可以使用此标头,以便提供一定程度的防范滥用保护。密钥的值是使用 WebSocket 规范中定义的算法计算的,因此这不会提供安全性。相反,它有助于防止非 WebSocket 客户端无意中或通过误用请求 WebSocket 连接。因此,从本质上讲,此密钥确认“是的,我确实打算打开一个 WebSocket 连接。”
此标头由选择使用它的客户端自动添加;它无法使用 fetch()
或 XMLHttpRequest.setRequestHeader()
方法添加。
Sec-WebSocket-Key: key
key
-
此升级请求的密钥。如果客户端希望执行此操作,则会添加此密钥,服务器将在响应中包含其自己的密钥,客户端将在将升级响应传递给您之前验证该密钥。
服务器响应的 Sec-WebSocket-Accept
标头的值将根据指定的 key
计算得出。
Sec-WebSocket-Protocol
Sec-WebSocket-Protocol
标头指定一个或多个您希望使用的 WebSocket 协议,优先级从高到低。服务器支持的第一个协议将被选中,并由服务器在响应中包含的 Sec-WebSocket-Protocol
标头中返回。您可以在标头中多次使用它,结果与您在一个标头中使用子协议标识符的逗号分隔列表相同。
Sec-WebSocket-Protocol: subprotocols
subprotocols
-
子协议名称的逗号分隔列表,按优先级排序。子协议可以从 IANA WebSocket 子协议名称注册表 中选择,也可以是客户端和服务器共同理解的自定义名称。
Sec-WebSocket-Version
请求头
指定客户端希望使用的 WebSocket 协议版本,以便服务器可以确认其端点是否支持该版本。
Sec-WebSocket-Version: version
version
-
客户端在与服务器通信时希望使用的 WebSocket 协议版本。此数字应为 IANA WebSocket 版本号注册表 中列出的最新版本。WebSocket 协议的最新最终版本是版本 13。
响应头
如果服务器无法使用指定的 WebSocket 协议版本进行通信,它将响应错误(例如 426 Upgrade Required),并在其标头中包含一个 Sec-WebSocket-Version
标头,其中包含支持的协议版本的逗号分隔列表。如果服务器支持请求的协议版本,则响应中不包含 Sec-WebSocket-Version
标头。
Sec-WebSocket-Version: supportedVersions
supportedVersions
-
服务器支持的 WebSocket 协议版本的逗号分隔列表。
仅响应标头
服务器的响应可能包含这些。
Sec-WebSocket-Accept
在服务器在打开握手过程中发送的响应消息中包含,当服务器愿意启动 WebSocket 连接时。它在响应标头中最多出现一次。
Sec-WebSocket-Accept: hash
hash
-
如果提供了
Sec-WebSocket-Key
标头,则此标头的值是通过获取密钥的值、将字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”连接到它、获取该连接字符串的 SHA-1 哈希值(生成一个 20 字节的值)来计算的。然后对该值进行 base64 编码以获取此属性的值。
参考
- WebSocket API
- HTTP
- 规范和 RFC