* Copyright (C) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const WebSocket = require('ws');
const { LONG_TRACE_TEXT_ENCODING_UTF8 } = require('./traceConstants.js');
* 发送 JSON 控制帧,通信异常在会话层统一兜底,不在此处抛出。
* @param {WebSocket} ws - 目标 WebSocket 连接。
* @param {Object} obj - 待发送的 JSON 控制帧对象。
*/
function sendJsonFrame(ws, obj) {
try {
ws.send(JSON.stringify(obj));
} catch (e) {}
}
* 按固定块大小发送二进制补丁,避免单帧过大造成浏览器或 ws 缓冲压力。
* @param {WebSocket} ws - 目标 WebSocket 连接。
* @param {Buffer} buffer - 待发送的二进制补丁数据。
* @param {number} chunkSize - 每个块的字节数。
*/
function sendBinaryInChunks(ws, buffer, chunkSize) {
let offset = 0;
while (offset < buffer.byteLength) {
const end = Math.min(offset + chunkSize, buffer.byteLength);
ws.send(buffer.subarray(offset, end));
offset = end;
}
}
* 回收单个会话占用的上下行连接与定时器资源,防止超时连接长期驻留。
* @param {Map} sessionStore - 会话存储,键为会话 ID,值为会话状态。
* @param {string} sessionId - 要回收的会话 ID。
*/
function cleanupLongTraceSession(sessionStore, sessionId) {
const session = sessionStore.get(sessionId);
if (!session) {
return;
}
sessionStore.delete(sessionId);
try {
if (session.uploadWs && session.uploadWs.readyState === WebSocket.OPEN) {
session.uploadWs.close();
}
} catch (e) {}
try {
if (session.downloadWs && session.downloadWs.readyState === WebSocket.OPEN) {
session.downloadWs.close();
}
} catch (e) {}
try {
if (session.bindTimer) {
clearTimeout(session.bindTimer);
}
} catch (e) {}
try {
if (session.idleTimer) {
clearTimeout(session.idleTimer);
}
} catch (e) {}
}
* 创建 longtrace 会话的初始运行时状态,供双通道绑定完成前后共享。
* @param {string} sessionId - 会话 ID。
* @param {number} bindTimer - 绑定超时器 ID。
*/
function createLongTraceSessionState(sessionId, bindTimer) {
return {
sessionId,
uploadWs: undefined,
downloadWs: undefined,
bound: false,
bindTimer,
idleTimer: undefined,
patchEvents: [],
pendingQueue: [],
processing: false,
inflight: undefined,
minN: undefined,
nextN: undefined,
noPatchDone: new Set(),
};
}
* 统一把 string、Buffer、ArrayBuffer 三类消息载体解码为 JSON 对象。
* @param {string|Buffer|ArrayBuffer} data - 待解码的消息数据。
* @returns {Object|undefined} - 解码后的 JSON 对象或 undefined。
*/
function decodeTextMessage(data) {
let text = '';
if (typeof data === 'string') {
text = data;
} else if (Buffer.isBuffer(data)) {
text = data.toString(LONG_TRACE_TEXT_ENCODING_UTF8);
} else if (data instanceof ArrayBuffer) {
text = Buffer.from(data).toString(LONG_TRACE_TEXT_ENCODING_UTF8);
} else {
return undefined;
}
try {
return JSON.parse(text);
} catch (e) {
return undefined;
}
}
module.exports = {
cleanupLongTraceSession,
createLongTraceSessionState,
decodeTextMessage,
sendBinaryInChunks,
sendJsonFrame,
};