协议升级机制
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,它总是通过升级 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
扩展
-
以逗号分隔的请求(或同意支持)扩展列表。这些应从 IANA WebSocket 扩展名称注册表中选择。带参数的扩展使用分号分隔。
例如,此头部指示两个自定义扩展:superspeed
和 colormode
(还包含参数 depth=16
)
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
子协议
-
以逗号分隔的子协议名称列表,按偏好顺序排列。子协议可以从 IANA WebSocket 子协议名称注册表中选择,也可以是客户端和服务器共同理解的自定义名称。
Sec-WebSocket-Version
请求头
指定客户端希望使用的 WebSocket 协议版本,以便服务器可以确认其端是否支持该版本。
Sec-WebSocket-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
哈希
-
如果提供了
Sec-WebSocket-Key
头部,则此头部的值是通过获取密钥的值,将其与字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”连接,然后对连接后的字符串进行 SHA-1 哈希计算,得到一个 20 字节的值。然后将该值进行 base64 编码以获取此属性的值。
规范
规范 |
---|
WebSocket 协议 |
超文本传输协议 (HTTP/1.1): 消息语法和路由 |
超文本传输协议版本 2 (HTTP/2) |