Files
beads-group/docs/ws-server-report.md

54 lines
5.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WebSocket 服务端交付报告
## 协议
新增 `GET /api/ws`。浏览器使用当前后台同源的 `wss://.../api/ws`;令牌不出现在 URL,任何 query string 均拒绝。Origin 必须精确匹配请求 Host(含端口),或来自 CORS_ORIGINS 明确配置的源;拒绝缺失、null、路径、userinfo 等异常 Origin。跨域后台必须把实际后台 HTTPS Origin 配入 CORS_ORIGINS。WebSocket 协议升级返回 101;Origin/升级错误允许真实 HTTP 错误状态,既有 JSON 契约保持原样。
客户端连接后 5 秒内发送首个文本 JSON:
```json
{"type":"auth","token":"后台登录返回的token"}
```
首帧与后续应用消息均限制 4096 字节。服务端直接查询 Redis 会话,必须有合法 user ID、storeId 与 admin/staff 角色;访客无法订阅。鉴权成功发送 `{"type":"ready"}`。服务请求新增和状态变更在 PostgreSQL 事务成功提交后,向对应 storeId 的员工连接发送 `{"type":"tasks.changed"}`。消息仅提示重新 GET /api/service-requests,没有任务正文、顾客信息、桌码或图纸内容。相同 requestId 重试、内容冲突和失败的状态变更均不广播。
每 20 秒服务端重验 Redis 会话,并发送 WebSocket ping 控制帧与 `{"type":"heartbeat"}`。浏览器协议栈自动回应 pong;前端无需发送应用层消息,也允许 `{"type":"pong"}`。heartbeat 用于前端判活,tasks.changed 用于刷新列表,再由前端选择播报。JSON 通知不是可靠事件队列;就绪与重连后必须主动拉取最新任务。
关闭码:4403 鉴权失败、5 秒首帧超时、会话过期/撤销或身份/门店变更;4400 非约定应用消息;1001 服务关闭;消息过大也可能由协议库以 1009 关闭。Redis 故障按失效会话关闭,客户端应重新验证登录并采用重连退避。
## 实现约束
- 连接读期限 45 秒,由协议 pong 延长;写操作最多 5 秒,Redis 查询为独立最多 3 秒 context。WS 升级请求不套用常规 HTTP 的 15 秒业务 context,因此连接可长期存活。
- 每个连接一个 reader 和一个 writer。应用 JSON、ping/pong 与正常关闭均通过 writer 发出;鉴权阶段尚未启动 writer,由握手处理同步完成。
- 单实例、按门店分组的轻量 hub。每连接容量 1 的变更通道合并失效通知,广播不等待浏览器写入,慢连接不会阻塞业务事务。控制 pong 使用容量 4 的独立小通道。
- 服务关闭显式通知已鉴权的 hijacked WS 连接,HTTP graceful shutdown 不依赖隐式关闭 WS。
- 当前广播是进程内的。未来多实例需要 Redis PubSub(门店主题)或可靠消息设施,否则其他实例上的连接不会收到更新。业务真源仍为 PostgreSQL。
Gorilla WebSocket 锁定 `v1.5.3`,使用前查询了[官方发布页](https://github.com/gorilla/websocket/releases)。该页当前标记 v1.5.3 为 latest;Go 官方数据库列出的弱 mask key 报告影响 v1.5.3 之前的版本。[Go 漏洞报告](https://pkg.go.dev/vuln/GO-2026-6278)
## 代理与构建
仅修改指定的 `deploy/nginx-https.example.conf` 和 `perler-beads-webui/nginx.conf`:新增精确 `/api/ws` 代理,HTTP/1.1 + Upgrade/Connection,75 秒读期限,10 秒发送期限,禁用代理缓冲。WS 路由使用 `Host $http_host` 保留原始 Host 与非标准端口(例如 Compose :8088),以便正确校验同源。生产 DNS、TLS、frp 和 Nginx 二进制未在本次 WS 测试运行,需在真实部署验证隧道。
新二进制:`/tmp/perler-ws-server`,主任务已接管启动本机 8080,并完成真实浏览器 WebSocket 与 TTS 联调。
## 真实执行验证
先写实际 Redis 鉴权测试,缺少端点时观察到预期失败:`websocket: bad handshake (status 200)`。新增/更新推送测试在接入提交钩子前观察到预期失败:`committed task did not push notification`,然后接入提交后广播。
使用真实本地 PostgreSQL 独立 `perler_test` 库(127.0.0.1:55432)与 Redis 专用测试 DB15(127.0.0.1:56379)。Redis session key 和 PostgreSQL fixture 使用随机 ID,仅删除测试自己创建的数据,不清空共享库。
实际执行通过:
```sh
export GOPATH=/tmp/perler-go GOCACHE=/tmp/perler-go-cache GOMODCACHE=/tmp/perler-go-mod
export TEST_DATABASE_URL='独立测试 PostgreSQL 连接串,数据库名需包含 test'
export TEST_REDIS_URL='redis://127.0.0.1:56379/15'
GIN_MODE=release go test ./... -count=1
GIN_MODE=release go test -race ./... -count=1
go vet ./...
go build -o /tmp/perler-ws-server ./cmd/server
```
普通测试约 22 秒,race 测试约 23 秒,均通过。覆盖管理员/员工认证、访客/无效令牌/空门店拒绝、同源/白名单/恶意 Origin、消息上限、未知协议、5 秒首帧超时、20 秒 heartbeat(证明超过 15 秒 HTTP context)、会话撤销关闭、真实事务提交后通知、跨门店不通知、幂等重放及冲突静默、越权状态变更静默、失效消息无私有字段,以及慢订阅者通知合并和广播不阻塞。原有 PostgreSQL 单连接池回归测试同时通过。