协议升级机制

HTTP/1.1 协议 提供了一种特殊的机制,可用于使用 Upgrade 头字段将已建立的连接升级到不同的协议。

此机制是可选的;它不能用于坚持协议更改。即使实现支持新协议,也可以选择不利用升级,并且在实践中,此机制主要用于引导 WebSocket 连接。

另请注意,HTTP/2 明确禁止使用此机制;它特定于 HTTP/1.1。

升级 HTTP/1.1 连接

客户端使用 Upgrade 头字段邀请服务器切换到列出的协议之一,优先级从高到低。

由于 Upgrade 是一个逐跳头,因此也需要在 Connection 头字段中列出。这意味着包含 Upgrade 的典型请求看起来像这样

http
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 连接就像这样简单

js
webSocket = new WebSocket("ws://destination.server.ext", "optionalProtocol");

WebSocket() 构造函数会完成创建初始 HTTP/1.1 连接以及为您处理握手和升级过程的所有工作。

注意:您还可以使用 "wss://" URL 方案打开安全的 WebSocket 连接。

如果您需要从头开始创建 WebSocket 连接,则必须自己处理握手过程。创建初始 HTTP/1.1 会话后,需要通过向标准请求添加 UpgradeConnection 头来请求升级,如下所示

http
Connection: Upgrade
Upgrade: websocket

WebSocket 特定标头

以下标头参与 WebSocket 升级过程。除了 UpgradeConnection 标头之外,其余标头通常是可选的,或者在浏览器和服务器相互通信时由它们为您处理。

Sec-WebSocket-Extensions

指定一个或多个协议级 WebSocket 扩展,以要求服务器使用。在一个请求中使用多个 Sec-WebSocket-Extension 标头是允许的;结果与您在一个此类标头中包含所有列出的扩展相同。

http
Sec-WebSocket-Extensions: extensions
extensions

要请求(或同意支持)的扩展的逗号分隔列表。这些应从 IANA WebSocket 扩展名称注册表 中选择。带参数的扩展使用分号分隔符进行参数化。

例如

http
Sec-WebSocket-Extensions: superspeed, colormode; depth=16

Sec-WebSocket-Key

向服务器提供确认客户端有权请求升级到 WebSocket 所需的信息。当不安全的(HTTP)客户端希望升级时,可以使用此标头,以便提供一定程度的防范滥用保护。密钥的值是使用 WebSocket 规范中定义的算法计算的,因此这不会提供安全性。相反,它有助于防止非 WebSocket 客户端无意中或通过误用请求 WebSocket 连接。因此,从本质上讲,此密钥确认“是的,我确实打算打开一个 WebSocket 连接。”

此标头由选择使用它的客户端自动添加;它无法使用 fetch()XMLHttpRequest.setRequestHeader() 方法添加。

http
Sec-WebSocket-Key: key
key

此升级请求的密钥。如果客户端希望执行此操作,则会添加此密钥,服务器将在响应中包含其自己的密钥,客户端将在将升级响应传递给您之前验证该密钥。

服务器响应的 Sec-WebSocket-Accept 标头的值将根据指定的 key 计算得出。

Sec-WebSocket-Protocol

Sec-WebSocket-Protocol 标头指定一个或多个您希望使用的 WebSocket 协议,优先级从高到低。服务器支持的第一个协议将被选中,并由服务器在响应中包含的 Sec-WebSocket-Protocol 标头中返回。您可以在标头中多次使用它,结果与您在一个标头中使用子协议标识符的逗号分隔列表相同。

http
Sec-WebSocket-Protocol: subprotocols
subprotocols

子协议名称的逗号分隔列表,按优先级排序。子协议可以从 IANA WebSocket 子协议名称注册表 中选择,也可以是客户端和服务器共同理解的自定义名称。

Sec-WebSocket-Version

请求头

指定客户端希望使用的 WebSocket 协议版本,以便服务器可以确认其端点是否支持该版本。

http
Sec-WebSocket-Version: version
version

客户端在与服务器通信时希望使用的 WebSocket 协议版本。此数字应为 IANA WebSocket 版本号注册表 中列出的最新版本。WebSocket 协议的最新最终版本是版本 13。

响应头

如果服务器无法使用指定的 WebSocket 协议版本进行通信,它将响应错误(例如 426 Upgrade Required),并在其标头中包含一个 Sec-WebSocket-Version 标头,其中包含支持的协议版本的逗号分隔列表。如果服务器支持请求的协议版本,则响应中不包含 Sec-WebSocket-Version 标头。

http
Sec-WebSocket-Version: supportedVersions
supportedVersions

服务器支持的 WebSocket 协议版本的逗号分隔列表。

仅响应标头

服务器的响应可能包含这些。

Sec-WebSocket-Accept

在服务器在打开握手过程中发送的响应消息中包含,当服务器愿意启动 WebSocket 连接时。它在响应标头中最多出现一次。

http
Sec-WebSocket-Accept: hash
hash

如果提供了 Sec-WebSocket-Key 标头,则此标头的值是通过获取密钥的值、将字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”连接到它、获取该连接字符串的 SHA-1 哈希值(生成一个 20 字节的值)来计算的。然后对该值进行 base64 编码以获取此属性的值。

参考