# WS 断、HTTP 通的窄代理(`devtools/ws-block-proxy/`) 一个**只掐 `/ws/chat`、其余 HTTP 一律放行**的窄代理:用来把「WebSocket 断了,但 REST 还通」 这个场景变成可复现、可取证的状态。纯 Python 3 标准库,零第三方依赖。 它**不是**产品的一部分,也**不是**后端行为的权威来源。它只做一件事:把发往 `/ws/chat` 的请求就地回 `503`(`ws blocked by test proxy`),别的请求原样转发给真后端,并把 **每一行的去向**打到 stdout。 它**不做什么**:不鉴权、不改写请求体、不落盘、不重试、不实现任何业务端点。 转发时逐行丢掉 hop-by-hop 头(`Connection` / `Upgrade` / `Transfer-Encoding` 等), 请求体按 `Content-Length` 读全再转发——**只要真后端还听得懂,它就照转**。 --- ## 1. 用途 W41 的「三级阶梯」(WS → 健康探测 → REST 回落)只在**WS 连不上**时才会走。真实的断线 不可控也不好复现,用这个代理把 WS 单独掐掉即可:客户端应该表现为「重连中…」(而不是「离线」), 然后按阶梯回落 `POST /api/v1/chat` 把消息发出去。 排查时的用处同样直接:**想知道 App 到底发了什么、发没发出去**,看代理日志就行。 ## 2. 用法 ```bash python ws_block_proxy.py ``` - ``:代理自己监听的端口(**App 要连的那个口**,见下面的 `adb reverse`) - ``:真后端在本机监听的端口 典型用法(真后端在 10942,代理听 18098): ```bash # 终端 A:代理听 18098,转发给 127.0.0.1:10942 cd /d/Project/Code/Uni/Cyrene-For-Android/devtools/ws-block-proxy python ws_block_proxy.py 18098 10942 # 终端 B:把设备上的 10942 引到宿主机的 18098(即代理) adb reverse tcp:10942 tcp:18098 ``` App 侧不用改任何配置:它照旧连 `127.0.0.1:10942`,被 `adb reverse` 送到代理。 日志(stdout)长这样: ``` proxy 18098 -> 127.0.0.1:10942, blocking /ws/chat # 启动 BLOCKED GET /ws/chat?token=... # WS 被掐(App 会退避重试) PASS GET /api/v1/sessions/web/messages?limit=50 -> 200 PASS POST /api/v1/chat -> 200 ERR GET /api/v1/health -> ConnectionRefusedError(...) # 真后端没起来时 ``` 收尾:撤掉端口映射(`adb reverse --remove tcp:10942`)、Ctrl-C 停代理。 **用完记得撤**——留着的话别的会话连真后端也会经过这个「掐 WS」的口。 ## 3. 关键性质(它最有价值的一条) **调试期间 Web 前端是直连后端(`127.0.0.1:10942`)的,不经过 `adb reverse`。** 所以设备侧的口只有 App 会用,**代理日志里的每一行都只可能来自 App** —— 不存在「这条请求到底是谁发的」这个问题。这是分辨「App 到底发了什么」最干净的判据: 要确认某次发送走的是 WS 还是 REST 回落,看日志里有没有对应的 `PASS POST /api/v1/chat` 就够了, 不需要在客户端加任何日志。 ## 4. 注意 - **日志里会带查询串**(例如 `/ws/chat?token=...`),贴给别人之前先抹掉凭据。 脚本本身**不硬编码任何凭据**,也不写任何文件。 - 它**不是**后端行为的事实来源:要判断真后端怎么回,去看真后端本身。 - 端口随便挑,但**别让代理和真后端抢同一个口**;`10942` 是真后端的常规端口。