边缘函数

处理WebSocket连接

如何在边缘函数中处理WebSocket连接


边缘函数支持托管 WebSocket 服务器,可实现与浏览器客户端的双向通信。

您还可以从边缘函数建立到其他服务器的出站 WebSocket 客户端连接(例如 OpenAI 实时 API)。在 supabase-community GitHub 账号 上可以找到一个 OpenAI 实时中继服务器的实现示例。

编写 WebSocket 服务器

以下是使用 Deno 和 Node.js API 设置 WebSocket 服务器的基础示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Deno.serve((req) => { const upgrade = req.headers.get('upgrade') || '' if (upgrade.toLowerCase() != 'WebSocket') { return new Response("请求未尝试升级为 WebSocket。", { status: 400 }) } const { socket, response } = Deno.upgradeWebSocket(req) socket.onopen = () => console.log('socket 已打开') socket.onmessage = (e) => { console.log('socket 消息:', e.data) socket.send(new Date().toString()) } socket.onerror = (e) => console.log('socket 错误:', e.message) socket.onclose = () => console.log('socket 已关闭') return response})

出站 WebSocket 连接

您还可以从边缘函数建立到其他服务器的出站 WebSocket 连接。

结合入站 WebSocket 服务器,可以将边缘函数用作 WebSocket 代理,例如作为 OpenAI 实时 API中继服务器

认证

WebSocket 浏览器客户端无法发送自定义头部信息。因此,边缘函数无法执行常规的授权头部检查来验证 JWT。

您可以通过在运行和部署函数时显式提供 --no-verify-jwt 参数来跳过默认的授权头部检查。

要对发起 WebSocket 请求的用户进行认证,可以通过 URL 查询参数或自定义协议传递 JWT。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import { createClient } from "jsr:@supabase/supabase-js@2";const supabase = createClient(Deno.env.get("SUPABASE_URL"),Deno.env.get("SUPABASE_SERVICE_ROLE_KEY"),);Deno.serve(req => {const upgrade = req.headers.get("upgrade") || ""; if (upgrade.toLowerCase() != "WebSocket") { return new Response("request isn't trying to upgrade to WebSocket.", { status: 400 }); }// 请注意查询参数可能会被某些日志系统记录const url = new URL(req.url);const jwt = url.searchParams.get("jwt");if (!jwt) {console.error("Auth token not provided");return new Response("Auth token not provided", { status: 403 });}const { error, data } = await supabase.auth.getUser(jwt);if (error) {console.error(error);return new Response("Invalid token provided", { status: 403 });}if (!data.user) {console.error("user is not authenticated");return new Response("User is not authenticated", { status: 403 });} const { socket, response } = Deno.upgradeWebSocket(req); socket.onopen = () => console.log("socket opened"); socket.onmessage = (e) => { console.log("socket message:", e.data); socket.send(new Date().toString()); }; socket.onerror = e => console.log("socket errored:", e.message); socket.onclose = () => console.log("socket closed"); return response;});

限制条件

最大执行时长受限于实际时间、CPU和内存限制。当函数达到这些限制条件中的任意一项时,将会被终止。

本地测试WebSocket

使用Supabase CLI在本地测试边缘函数时,实例会在请求完成后自动终止。这将导致无法保持WebSocket连接处于打开状态。

要解决这个问题,您可以在supabase/config.toml中更新以下配置:

1
2
[edge_runtime]policy = "per_worker"

当使用per_worker策略运行时,函数在编辑后不会自动重新加载。您需要通过运行supabase functions serve命令手动重启函数。