外观
WebSocket(双向通信)
WebSocket 插件为页面提供一个由 Go 建立连接的 WebSocket,其 API 与 浏览器的一致。与 webview 自带的那个不同,它:
- 在打开握手时发送任何请求头,例如
Authorization、Cookie或Origin,以满足希望在请求头中携带令牌(server)的服务器; - 不受页面的 Content Security Policy 限制;
- 当页面落后时,把快速服务器的消息保存在 Go 中,而不是页面的内存里。
设置
在 Go 中使用该插件:
go
import (
"github.com/egoist/mygo"
"github.com/egoist/mygo/plugins/websocket"
)
func main() {
mygo.Use(websocket.Plugin)
// ...
}并在前端添加它的包:
sh
bun add @mygo-plugins/websocket在一个未使用其 Go 侧组件的应用中打开连接会失败,并且控制台会提示要 调用 mygo.Use。
建立连接
从包中导入 WebSocket,像使用浏览器的那个一样使用它:
ts
import { WebSocket } from "@mygo-plugins/websocket";
const ws = new WebSocket("wss://example.com/live", ["v1"], {
headers: { authorization: `Bearer ${token}` },
});
ws.binaryType = "arraybuffer";
ws.onopen = () => ws.send(JSON.stringify({ subscribe: "prices" }));
ws.onmessage = (e) => console.log(e.data);
ws.onclose = (e) => console.log(e.code, e.reason, e.wasClean);前两个参数与浏览器的 WebSocket 相同:URL,ws: 或 wss:(http: 和 https: 会转换为它们,相对 URL 相对于页面的 URL 解析),以及子协议, 服务器会从中选择一个,连接打开后即为 ws.protocol。第三个是插件的: headers,打开握手的请求头,可以是 Headers、对象或键值对。
其余就是浏览器的 API:readyState 及其常量,send 字符串、 ArrayBuffer、类型化数组和 Blob,close(code, reason),binaryType (默认为 "blob",或 "arraybuffer"),以及 open、message、 error 和 close 事件,既可作为 on… 属性,也可用 addEventListener。
接受 WebSocket 类的库——GraphQL 和实时客户端常常如此——用两个参数 创建连接;给它们一个负责添加请求头的类:
ts
class AuthWebSocket extends WebSocket {
constructor(url: string | URL, protocols?: string | string[]) {
super(url, protocols, { headers: { authorization: `Bearer ${token}` } });
}
}发送与接收
send在连接仍在打开时抛出异常,连接关闭后发送的消息会被丢弃,与 浏览器的一致。消息按发送顺序发出。bufferedAmount统计已传给send但 Go 尚未写出的字节数:大量发送 的页面可以等待它下降。- 当页面还有超过 1 MiB 的消息尚未处理时,来自服务器的消息在 Go 中 等待,因此比页面更快的服务器不会填满页面的内存。大于
MaxMessageSize(默认 64 MiB)的消息会使连接失败。 - Go 会响应服务器的 ping。
关闭
close(code, reason) 接受 1000 或 3000 到 4999 之间的状态码,以及最多 123 字节的原因说明,与浏览器的一致。随后 Go 等待服务器完成关闭握手: 完成时,close 事件带有服务器的状态码和原因说明,wasClean 为 true; 超过 CloseTimeout(默认 5 秒)仍未完成时,Go 断开连接,事件的状态码 为 1006。
当页面导航离开或其窗口关闭时,连接会自行关闭,并告知服务器它正在离开 (状态码 1001)。
当连接失败——因为无法到达服务器、服务器应答握手时没有升级、重定向了 握手或破坏了协议——页面会收到一个 error 事件,随后是状态码为 1006 的 close 事件,并且控制台会说明原因,与浏览器的一致。
与浏览器的 WebSocket 的区别
- 构造函数接受第三个参数,即握手的请求头。
- 页面的 Content Security Policy(
connect-src)不适用,webview 的 Cookie 也不适用:适用的是 Go 侧http.Client的 Cookie(参见 选项)。 - 不协商任何扩展,例如 permessage-deflate:
extensions始终为空。
选项
websocket.Plugin 通过系统的代理设置向任何 URL 打开连接(参见 代理)。websocket.New 则接受选项:
go
mygo.Use(websocket.New(websocket.Options{
Client: &http.Client{Transport: transport},
Allow: func(r *http.Request) bool {
return r.URL.Hostname() == "live.example.com"
},
MaxMessageSize: 16 << 20, // 16 MiB; default 64 MiB
CloseTimeout: 2 * time.Second, // default 5 seconds
}))Client发出打开握手,因此它的Transport(代理、TLS 设置)和Jar(Cookie)会生效。它的Timeout和CheckRedirect会被忽略: 连接会长期保持,而重定向会使连接失败,与浏览器中一样。Allow看到每个连接的握手请求,带着它的 URL 和请求头,并决定页面 是否可以打开它;被它拒绝的连接会失败。MaxMessageSize是服务器的单条消息最多可拥有的字节数;更大的消息会 使连接失败。CloseTimeout是页面关闭的连接等待服务器完成关闭握手的时长。
只有应用自己的页面可以调用该插件,与每个绑定方法一样(参见谁可以 调用)。