协议升级机制

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,它总是通过升级 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
扩展

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

例如,此头部指示两个自定义扩展:superspeedcolormode(还包含参数 depth=16

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
子协议

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

Sec-WebSocket-Version

请求头

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

http
Sec-WebSocket-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
哈希

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

规范

规范
WebSocket 协议
超文本传输协议 (HTTP/1.1): 消息语法和路由
超文本传输协议版本 2 (HTTP/2)

另见