mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-28 13:32:23 +00:00
Co-authored-by: Steven Enamakel <31011319+senamakel@users.noreply.github.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
158 lines
3.8 KiB
JavaScript
158 lines
3.8 KiB
JavaScript
import crypto from "node:crypto";
|
|
import { WebSocketServer } from "ws";
|
|
|
|
const wsServer = new WebSocketServer({ noServer: true });
|
|
|
|
function isWsLibrarySocket(socket) {
|
|
return socket && typeof socket.send === "function";
|
|
}
|
|
|
|
export function socketIsOpen(socket) {
|
|
if (!socket) return false;
|
|
if (isWsLibrarySocket(socket)) return socket.readyState === 1;
|
|
return !socket.destroyed;
|
|
}
|
|
|
|
export function closeWebSocket(socket) {
|
|
if (!socket) return;
|
|
try {
|
|
if (isWsLibrarySocket(socket)) {
|
|
socket.close();
|
|
socket.terminate?.();
|
|
return;
|
|
}
|
|
socket.end?.();
|
|
socket.destroy?.();
|
|
} catch {
|
|
// noop
|
|
}
|
|
}
|
|
|
|
export function sendWsFrame(socket, opcode, payload) {
|
|
if (!socket || socket.destroyed) return;
|
|
|
|
const len = payload.length;
|
|
let header;
|
|
if (len < 126) {
|
|
header = Buffer.alloc(2);
|
|
header[0] = 0x80 | opcode;
|
|
header[1] = len;
|
|
} else if (len < 65536) {
|
|
header = Buffer.alloc(4);
|
|
header[0] = 0x80 | opcode;
|
|
header[1] = 126;
|
|
header.writeUInt16BE(len, 2);
|
|
} else {
|
|
header = Buffer.alloc(10);
|
|
header[0] = 0x80 | opcode;
|
|
header[1] = 127;
|
|
header.writeBigUInt64BE(BigInt(len), 2);
|
|
}
|
|
|
|
try {
|
|
socket.write(header);
|
|
socket.write(payload);
|
|
} catch {
|
|
// noop
|
|
}
|
|
}
|
|
|
|
export function sendWsText(socket, text) {
|
|
if (isWsLibrarySocket(socket)) {
|
|
if (socket.readyState === 1) socket.send(String(text));
|
|
return;
|
|
}
|
|
sendWsFrame(socket, 0x01, Buffer.from(text, "utf-8"));
|
|
}
|
|
|
|
export function upgradeWebSocket(req, socket, head, onConnection) {
|
|
if (!Buffer.isBuffer(head)) return false;
|
|
wsServer.handleUpgrade(req, socket, head, (ws) => {
|
|
onConnection(ws);
|
|
});
|
|
return true;
|
|
}
|
|
|
|
export function acceptWebSocket(req, socket) {
|
|
const rawKey = req.headers["sec-websocket-key"];
|
|
const key = Array.isArray(rawKey) ? rawKey[0]?.trim() : rawKey?.trim();
|
|
if (!key) {
|
|
socket.destroy();
|
|
return false;
|
|
}
|
|
const acceptKey = crypto
|
|
.createHash("sha1")
|
|
.update(key + "258EAFA5-E914-47DA-95CA-5AB5DC085B11")
|
|
.digest("base64");
|
|
socket.write(
|
|
"HTTP/1.1 101 Switching Protocols\r\n" +
|
|
"Upgrade: websocket\r\n" +
|
|
"Connection: Upgrade\r\n" +
|
|
`Sec-WebSocket-Accept: ${acceptKey}\r\n` +
|
|
"\r\n",
|
|
);
|
|
return true;
|
|
}
|
|
|
|
export function decodeWebSocketFrames(socket, onText) {
|
|
if (isWsLibrarySocket(socket)) {
|
|
socket.on("message", (data) => {
|
|
onText(Buffer.isBuffer(data) ? data.toString("utf-8") : String(data));
|
|
});
|
|
return;
|
|
}
|
|
|
|
let buffer = Buffer.alloc(0);
|
|
|
|
socket.on("data", (chunk) => {
|
|
buffer = Buffer.concat([buffer, chunk]);
|
|
while (buffer.length >= 2) {
|
|
const firstByte = buffer[0];
|
|
const opcode = firstByte & 0x0f;
|
|
const secondByte = buffer[1];
|
|
const masked = (secondByte & 0x80) !== 0;
|
|
let payloadLen = secondByte & 0x7f;
|
|
let offset = 2;
|
|
|
|
if (payloadLen === 126) {
|
|
if (buffer.length < 4) return;
|
|
payloadLen = buffer.readUInt16BE(2);
|
|
offset = 4;
|
|
} else if (payloadLen === 127) {
|
|
if (buffer.length < 10) return;
|
|
payloadLen = Number(buffer.readBigUInt64BE(2));
|
|
offset = 10;
|
|
}
|
|
|
|
const maskSize = masked ? 4 : 0;
|
|
const totalLen = offset + maskSize + payloadLen;
|
|
if (buffer.length < totalLen) return;
|
|
|
|
let payload = buffer.subarray(offset + maskSize, totalLen);
|
|
if (masked) {
|
|
const mask = buffer.subarray(offset, offset + 4);
|
|
payload = Buffer.from(payload);
|
|
for (let i = 0; i < payload.length; i += 1) {
|
|
payload[i] ^= mask[i % 4];
|
|
}
|
|
}
|
|
|
|
buffer = buffer.subarray(totalLen);
|
|
|
|
if (opcode === 0x08) {
|
|
socket.end();
|
|
return;
|
|
}
|
|
|
|
if (opcode === 0x09) {
|
|
sendWsFrame(socket, 0x0a, payload);
|
|
continue;
|
|
}
|
|
|
|
if (opcode === 0x01) {
|
|
onText(payload.toString("utf-8"));
|
|
}
|
|
}
|
|
});
|
|
}
|