File size: 2,530 Bytes
87cb242 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | import WebSocket from 'ws';
// ββ Inbound message types (client β server) ββββββββββββββββββββββββββββββββββ
export type ClientMessageType =
| 'heartbeat'
| 'offer'
| 'answer'
| 'ice-candidate'
| 'message'
| 'get-upload-url'
| 'relay'
| 'friend';
// ββ Outbound message types (server β client) ββββββββββββββββββββββββββββββββββ
export type ServerMessageType =
| 'connected'
| 'ice-servers'
| 'offer'
| 'answer'
| 'ice-candidate'
| 'message'
| 'upload-url'
| 'relay'
| 'peer-online'
| 'peer-offline'
| 'offline-flush'
| 'error';
export interface ClientMessage {
type: ClientMessageType;
payload: Record<string, unknown>;
// requestId allows client to correlate responses
requestId?: string;
}
export interface ServerMessage {
type: ServerMessageType;
payload: Record<string, unknown>;
requestId?: string;
ts: number; // epoch ms
}
/**
* Parse and validate an incoming raw WS message.
* Returns null if the data is malformed β caller should silently drop.
*/
export function parseClientMessage(raw: WebSocket.RawData): ClientMessage | null {
try {
const str = raw.toString('utf8');
if (str.length > 65536) return null; // 64 KB hard cap β protects against flood
const obj = JSON.parse(str) as unknown;
if (
typeof obj !== 'object' ||
obj === null ||
typeof (obj as Record<string, unknown>).type !== 'string'
) {
return null;
}
return obj as ClientMessage;
} catch {
return null;
}
}
/**
* Serialize a server message.
* Using a single Buffer allocation avoids repeated JSON.stringify on hot paths.
*/
export function buildServerMessage(
type: ServerMessageType,
payload: Record<string, unknown>,
requestId?: string
): Buffer {
const msg: ServerMessage = { type, payload, requestId, ts: Date.now() };
return Buffer.from(JSON.stringify(msg), 'utf8');
}
/**
* Send a message to a WebSocket, catching send errors silently.
* Avoids crashing the process when a socket closes mid-send.
*/
export function safeSend(
ws: WebSocket,
type: ServerMessageType,
payload: Record<string, unknown>,
requestId?: string
): void {
if (ws.readyState !== WebSocket.OPEN) return;
try {
ws.send(buildServerMessage(type, payload, requestId));
} catch {
// Socket probably closed between the readyState check and send β ignore
}
}
|