mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-02 22:52:58 +00:00
Negotiate shared binary chunk framing below application messages and routing, with bounded logical assembly and paced writes that respect encrypted relay limits. Preserve legacy admission, reconnect state, sibling route isolation, and native single-frame upload behavior.
39 lines
1.9 KiB
TypeScript
39 lines
1.9 KiB
TypeScript
import { MAX_WEBSOCKET_MESSAGE_BYTES } from "../websocket-message-limit.js";
|
|
/** Routing adds a bounded header; the enclosed terminal/file frame is never re-encoded. */
|
|
export const HOST_ROUTE_OPCODE = 0x30;
|
|
export const MAX_HOST_ROUTE_ID_BYTES = 256;
|
|
export const MAX_HOST_ROUTE_HEADER_BYTES = 3 + MAX_HOST_ROUTE_ID_BYTES;
|
|
// A logical host channel crosses the 32 MiB route admission boundary. Text can
|
|
// expand sixfold when enclosed in JSON; binary adds the bounded route header.
|
|
export const MAX_HOST_CHANNEL_FRAME_BYTES = Math.floor(
|
|
(32 * 1024 * 1024 - MAX_HOST_ROUTE_HEADER_BYTES) / 6,
|
|
);
|
|
export const MAX_ROUTED_PAYLOAD_BYTES = MAX_WEBSOCKET_MESSAGE_BYTES - MAX_HOST_ROUTE_HEADER_BYTES;
|
|
export interface HostRouteFrame {
|
|
routeId: string;
|
|
payload: Uint8Array;
|
|
}
|
|
export function encodeHostRouteFrame(routeId: string, payload: Uint8Array): Uint8Array {
|
|
const id = new TextEncoder().encode(routeId);
|
|
if (!id.length || id.length > MAX_HOST_ROUTE_ID_BYTES) throw new Error("Invalid host route ID");
|
|
if (payload.byteLength > MAX_ROUTED_PAYLOAD_BYTES) throw new Error("Host frame is too large");
|
|
const frame = new Uint8Array(3 + id.length + payload.byteLength);
|
|
frame[0] = HOST_ROUTE_OPCODE;
|
|
new DataView(frame.buffer).setUint16(1, id.length);
|
|
frame.set(id, 3);
|
|
frame.set(payload, 3 + id.length);
|
|
return frame;
|
|
}
|
|
export function decodeHostRouteFrame(bytes: Uint8Array): HostRouteFrame | null {
|
|
if (bytes[0] !== HOST_ROUTE_OPCODE || bytes.length < 3) return null;
|
|
const length = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint16(1);
|
|
if (!length || length > MAX_HOST_ROUTE_ID_BYTES || bytes.length < 3 + length) return null;
|
|
if (bytes.length - 3 - length > MAX_ROUTED_PAYLOAD_BYTES) return null;
|
|
try {
|
|
const routeId = new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(3, 3 + length));
|
|
return { routeId, payload: bytes.subarray(3 + length) };
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|