使用 Proxyman Scripting 修改 WebSocket 消息的方法
阅读约 6 分钟
Proxyman 现在可以实时修改 WebSocket 消息 🎉
只需几行 JavaScript,你就能编辑一条消息、丢弃它,或发送一条新消息。它同时支持 WS 和 WSS,并且两个方向都适用:从客户端到服务器,以及从服务器到客户端。
本文内容包括:
- 测试 WebSocket 应用的难点。
- 新增的 Modify WebSocket Messages 选项的作用。
- 一个可自行运行的本地 WebSocket 服务器,附带逐步演示。
- 现成的 WebSocket 模板,以及消息没有变化时的排查技巧。
Modify WebSocket Messages 是 Proxyman for macOS 的新功能。请从下载页面更新到最新版本,再按照本教程操作。
下面是最终效果。服务器发送的是真实的 BTC 价格,但应用收到的却是 $1。当应用发送密码时,Proxyman 会丢弃该消息,并向应用返回一条警告:
1. 问题所在
聊天、交易、游戏和实时仪表盘等应用都通过 WebSocket 获取实时数据。测试这类应用并不容易:
- 你无法控制服务器。 它发送的是真实数据。要让它发送边界情况,例如价格为 0 或错误消息,就必须修改服务器代码。
- 有些情况很难复现。 丢包、异常回复或连接断开,往往不会在你需要的时候出现。
- 以前,Proxyman 只能读取 WebSocket 消息。 Scripting 中的
onWebsocket()函数可以记录消息,但无法修改。
2. “Modify WebSocket Messages”是什么?
这是 Scripting 规则中的一个新复选框。开启后,Proxyman 会暂存每一条 WebSocket 消息,直到你的 onWebsocket() 函数返回。函数的返回值决定后续操作:
onWebsocket() 中的写法 | 结果 |
|---|---|
return message | 发送该消息(包含你的修改) |
return null | 丢弃该消息,对方不会收到 |
| 不返回任何值 | 发送原始消息 |
abort() | 关闭两端的 WebSocket |
sendToClient(data) / sendToServer(data) | 发送一条新消息。String 发送文本消息,Uint8Array 发送二进制消息 |
message 对象包含以下字段:
| 字段 | 值 |
|---|---|
message.from | "client" 或 "server" |
message.type | "text"、"binary"、"ping"、"pong" 或 "close" |
message.data | 消息的文本内容。修改它即可编辑文本消息 |
message.rawData | 二进制消息的字节,类型为 Uint8Array |
该选项默认关闭。关闭时,onWebsocket() 与之前一样,只读取消息,不会延迟它们。
3. 分步操作:修改实时 WebSocket
我们将搭建视频中的演示:一个发送 BTC 价格的小型服务器,以及一个通过 Proxyman 与其通信的客户端。
3.1 启动 WebSocket 服务器
创建一个新文件夹,并安装 ws 包:
mkdir websocket-demo && cd websocket-demo
npm init -y
npm install ws
将以下代码保存为 server.js。它每秒发送一次价格,并回显每一条聊天消息:
// server.js: a WebSocket server on ws://localhost:3456
const WebSocket = require('ws');
const server = new WebSocket.Server({ port: 3456 });
server.on('connection', (socket) => {
// Send a BTC price every second
const timer = setInterval(() => {
const price = Number((67000 + Math.random() * 1000).toFixed(2));
socket.send(JSON.stringify({ type: 'price', symbol: 'BTC', price: price }));
}, 1000);
// Print what the client sends, then echo it back
socket.on('message', (data) => {
console.log('SERVER gets:', data.toString());
socket.send(JSON.stringify({ type: 'echo', text: JSON.parse(data).text }));
});
socket.on('close', () => clearInterval(timer));
});
console.log('WebSocket server on ws://localhost:3456');
在终端中运行:
node server.js
3.2 通过 Proxyman 连接客户端
Node.js 不会使用 macOS 的代理设置,因此客户端需要将 WebSocket 握手请求直接发送给 Proxyman。将以下代码保存为 client.js:
// client.js: connects to the server through Proxyman
const http = require('http');
const WebSocket = require('ws');
// Proxyman's port. You can see it in the Proxyman toolbar
const PROXYMAN_PORT = 9090;
// Sends the WebSocket handshake to Proxyman, like an app that uses an HTTP proxy
class ProxymanAgent extends http.Agent {
addRequest(request, options) {
request.path = 'http://' + options.host + ':' + options.port + request.path;
super.addRequest(request, { ...options, host: '127.0.0.1', port: PROXYMAN_PORT });
}
}
const socket = new WebSocket('ws://localhost:3456/prices', { agent: new ProxymanAgent() });
socket.on('open', () => {
setTimeout(() => socket.send(JSON.stringify({ type: 'chat', text: 'Hello' })), 2000);
setTimeout(() => socket.send(JSON.stringify({ type: 'chat', text: 'my password is hunter2' })), 4000);
});
socket.on('message', (data) => console.log('CLIENT gets:', data.toString()));
打开 Proxyman,然后在第二个终端窗口中运行客户端:
node client.js
你会在 Proxyman 中看到 ws://localhost:3456/prices。打开它的 Websocket 标签页即可查看消息。此时尚未做任何修改。按 Ctrl+C 停止客户端。
3.3 创建 Scripting 规则
- 在 Proxyman 中,右键点击
ws://localhost:3456/prices会话,依次选择 Tools → Scripting...。Proxyman 会创建一条已填入该 URL 的规则。 - 保持 Request 勾选,并勾选 Modify WebSocket Messages。
- 将脚本替换为下面的代码,然后点击 Save & Activate(⌘S)。

function onWebsocket(context, url, request, response, message) {
// Only change text messages. Binary, ping, pong, and close messages pass through
if (message.type !== "text") {
return message;
}
const json = JSON.parse(message.data);
// 1. Server → client: every BTC price becomes $1
if (message.from === "server" && json.type === "price") {
json.price = 1;
json.note = "edited by Proxyman";
message.data = JSON.stringify(json);
return message;
}
// 2. Client → server: drop a message with a password, and warn the client
if (message.from === "client" && json.text.includes("password")) {
sendToClient(JSON.stringify({ type: "warning", text: "Proxyman blocked a password" }));
return null;
}
// 3. Everything else: send it as it is
return message;
}
3.4 再次运行并查看结果
再次运行 node client.js。Proxyman 会在 WebSocket 连接时决定是否启用该选项,因此保存规则后,请务必重新连接。
现在,客户端收到的是带有备注 "edited by Proxyman" 的 "price": 1。包含密码的消息永远不会到达服务器:服务器终端只会打印 Hello,而客户端则会收到脚本发出的警告。

在 Proxyman 的 Websocket 标签页中,可以看到已修改的价格、脚本发送的警告,以及被丢弃的密码消息。

4. 更多示例
双向替换文本,例如切换主机名:
const FIND_REGEX = /staging\.example\.com/g;
const REPLACE_WITH = "api.example.com";
function onWebsocket(context, url, request, response, message) {
if (message.type === "text") {
message.data = message.data.replace(FIND_REGEX, REPLACE_WITH);
}
return message;
}
模拟连接断开:收到 10 条消息后断开连接,用于测试应用的重连逻辑。函数外部的代码会在每个连接中运行一次,因此每个连接都会单独计数:
const CLOSE_AFTER_MESSAGES = 10;
let messageCount = 0;
function onWebsocket(context, url, request, response, message) {
messageCount += 1;
if (messageCount === CLOSE_AFTER_MESSAGES) {
console.log("Closing the WebSocket after " + messageCount + " messages");
abort();
}
return message;
}
修改二进制消息:设置新的字节内容:
function onWebsocket(context, url, request, response, message) {
if (message.type === "binary" && message.from === "server") {
message.rawData = new Uint8Array([1, 2, 3]);
}
return message;
}
5. 使用 WebSocket 模板快速上手
无需从零编写脚本。在脚本编辑器中点击 Templates,然后搜索 “websocket”:
- Log WebSocket Messages:在 Console Log 中打印每一条消息。它只读取消息,不做修改。
- Modify JSON WebSocket Messages:修改服务器发送的 JSON 消息中的某个字段。
- Replace Text in WebSocket Messages:双向查找并替换消息中的文本。
- Drop WebSocket Messages:丢弃匹配正则表达式的消息,例如埋点事件。
- Reply to a WebSocket Message:使用
sendToClient()回复客户端的消息。服务器不会收到这条消息。 - Simulate a WebSocket Disconnect:收到若干条消息后关闭 WebSocket。
点击 Replace 即可使用模板。Proxyman 还会自动勾选所需的复选框,包括 Modify WebSocket Messages。

6. 常见问题与解决方法
消息没有变化
- 勾选 Modify WebSocket Messages,然后点击 Save & Activate(⌘S)。
- 重新连接 WebSocket。该选项在 WebSocket 连接时读取,而不是对每条消息都读取。
- 保持 Request 或 Response 至少一项被勾选。两项都未勾选的规则根本不会运行,包括
onWebsocket()。 - 在脚本编辑器中打开 Console Log。如果脚本抛出错误,Proxyman 会发送原始消息,并在此处打印错误信息。
在 Proxyman 中看不到我的 WebSocket
- 对于
wss://,请像处理 HTTPS 一样,为该域名启用 SSL Proxying。 - 许多应用和库会跳过
localhost的代理。请像client.js那样将流量发送到 Proxyman,或者按照你所用平台的 WebSocket 文档 进行设置。
注意事项
- 只有匹配规则的 WebSocket 会等待脚本处理,其他流量照常传输。
- 超过 16 MB 的消息会原样发送,不经过脚本处理。
- 如果你使用 AI 智能体,Proxyman MCP 可以创建相同的规则:
create_scripting_rule接受modify_websocket_messages: true参数。
延伸阅读
