100 lines
4.4 KiB
JavaScript
100 lines
4.4 KiB
JavaScript
// ============================================================
|
||
// WebSocket 流量捕获(页面内 hook,传输仍由浏览器发起)
|
||
// ============================================================
|
||
// 目的:在不改变「服务端看到的连接」的前提下,把浏览器真实的 ws 帧抓下来,
|
||
// 用于与「中继转存」逐帧对比,定位「直连能进、中继不能进」的差异。
|
||
//
|
||
// 挂载:serve.js 提供 /ws_hook.js;web/index.html 在主脚本之前引入(先于游戏创建 WebSocket)。
|
||
// 开关:**默认不启用**;URL hash 加 #hook 才抓(#nohook 可强制关)。
|
||
// 产物:
|
||
// window.__ws_capture__ —— 内存里的抓包(connections[].messages[] = {d,t,n,b},
|
||
// d=c2s/s2c,n=字节数,b=base64 原始字节,顺序即收发顺序)
|
||
// window.__ws_save__() —— 手动保存;离开页面时自动 sendBeacon 再存一份
|
||
// 落盘位置:serve.js 的 POST /__dump__/<name> → 3DTank/names/<name>
|
||
// ============================================================
|
||
(function () {
|
||
if (window.__ws_hooked__) return;
|
||
if (!/#hook/.test(location.hash) || /#nohook/.test(location.hash)) {
|
||
console.log('[WS Hook] 未启用(默认关闭;URL 加 #hook 开启抓包)');
|
||
// 兜底:即使没启用也定义,调用时提示怎么开,避免"__ws_save__ 不存在"
|
||
window.__ws_save__ = function () {
|
||
console.warn('[WS Hook] 未启用:请在 URL 末尾加 #hook(如 http://localhost:8080/#hook)后整页刷新,再调用 __ws_save__()');
|
||
return Promise.resolve(null);
|
||
};
|
||
return;
|
||
}
|
||
window.__ws_hooked__ = true;
|
||
|
||
var Capture = { startedAt: Date.now(), connections: [] };
|
||
window.__ws_capture__ = Capture;
|
||
var Original = window.WebSocket;
|
||
|
||
function ToBytes(Data) {
|
||
if (Data instanceof ArrayBuffer) return new Uint8Array(Data);
|
||
if (ArrayBuffer.isView(Data)) return new Uint8Array(Data.buffer, Data.byteOffset, Data.byteLength);
|
||
if (typeof Data === 'string') return new TextEncoder().encode(Data);
|
||
return new Uint8Array(0);
|
||
}
|
||
function ToBase64(Bytes) {
|
||
var S = '';
|
||
var Chunk = 0x8000; // 分块,避免超长字符串参数
|
||
for (var I = 0; I < Bytes.length; I += Chunk) {
|
||
S += String.fromCharCode.apply(null, Bytes.subarray(I, I + Chunk));
|
||
}
|
||
return btoa(S);
|
||
}
|
||
|
||
function Hooked(Url, Protocols) {
|
||
var Ws = Protocols ? new Original(Url, Protocols) : new Original(Url);
|
||
var Info = { id: Capture.connections.length, url: String(Url), openTime: null, closeCode: null, messages: [] };
|
||
Capture.connections.push(Info);
|
||
console.log('[WS Hook] #' + Info.id + ' 连接 ' + Info.url);
|
||
|
||
function Record(Dir, Data) {
|
||
var Bytes = ToBytes(Data);
|
||
Info.messages.push({ d: Dir, t: Date.now(), n: Bytes.length, b: ToBase64(Bytes) });
|
||
}
|
||
|
||
var OrigSend = Ws.send;
|
||
Ws.send = function (Data) {
|
||
Record('c2s', Data);
|
||
return OrigSend.apply(Ws, arguments);
|
||
};
|
||
Ws.addEventListener('message', function (Ev) { Record('s2c', Ev.data); });
|
||
Ws.addEventListener('open', function () { Info.openTime = Date.now(); });
|
||
Ws.addEventListener('close', function (Ev) { Info.closeCode = Ev.code; });
|
||
|
||
return Ws;
|
||
}
|
||
Hooked.CONNECTING = Original.CONNECTING;
|
||
Hooked.OPEN = Original.OPEN;
|
||
Hooked.CLOSING = Original.CLOSING;
|
||
Hooked.CLOSED = Original.CLOSED;
|
||
Hooked.prototype = Original.prototype;
|
||
window.WebSocket = Hooked;
|
||
|
||
function Stamp() {
|
||
return new Date().toISOString().replace(/[:.]/g, '-');
|
||
}
|
||
function Save(Prefix) {
|
||
var Json = JSON.stringify(Capture);
|
||
var Url = '/__dump__/' + (Prefix || 'ws_capture_') + Stamp() + '.json';
|
||
return fetch(Url, { method: 'POST', body: Json })
|
||
.then(function (R) {
|
||
console.log('[WS Hook] 已保存 ' + Url + '(' + Json.length + ' 字节,' + Capture.connections.length + ' 个连接)');
|
||
return R;
|
||
})
|
||
.catch(function (E) { console.warn('[WS Hook] 保存失败: ' + (E && E.message)); });
|
||
}
|
||
window.__ws_save__ = function () { return Save('ws_capture_'); };
|
||
|
||
window.addEventListener('beforeunload', function () {
|
||
try {
|
||
var Blob = new Blob([JSON.stringify(Capture)], { type: 'application/json' });
|
||
navigator.sendBeacon('/__dump__/ws_capture_auto_' + Date.now() + '.json', Blob);
|
||
} catch (E) {}
|
||
});
|
||
|
||
console.log('[WS Hook] 已挂载:window.__ws_capture__(内存)/ __ws_save__()(存到 3DTank/names/)');
|
||
})();
|