perf(relay): reduce encrypted binary traffic overhead (#2480)

Preserve application frame identity through encryption so negotiated binary traffic avoids base64 expansion while mixed-version peers remain compatible.
This commit is contained in:
Mohamed Boudra
2026-07-27 12:12:02 +02:00
committed by GitHub
parent 1a1ff8828f
commit 1d1132de9c
22 changed files with 397 additions and 113 deletions
@@ -83,10 +83,7 @@ function decodeCiphertext(text: string): ArrayBuffer {
}
function parseEncryptedJson(sharedKey: Uint8Array, text: string): unknown {
const plaintext = decrypt(sharedKey, decodeCiphertext(text));
if (typeof plaintext !== "string") {
throw new Error("Expected encrypted relay frame to contain UTF-8 JSON");
}
const plaintext = new TextDecoder().decode(decrypt(sharedKey, decodeCiphertext(text)));
return JSON.parse(plaintext);
}
@@ -291,8 +288,13 @@ async function waitForRelayWebSocketReady(port: number, timeout = 60000): Promis
onerror: null,
};
ws.on("message", (data) => {
transport.onmessage?.(typeof data === "string" ? data : data.toString());
ws.on("message", (data, isBinary) => {
transport.onmessage?.({
data: isBinary
? data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength)
: data.toString(),
isBinary,
});
});
ws.on("close", (code, reason) => {
transport.onclose?.(code, reason.toString());
@@ -434,8 +436,13 @@ async function waitForRelayWebSocketReady(port: number, timeout = 60000): Promis
onerror: null,
};
ws.on("message", (data) => {
transport.onmessage?.(typeof data === "string" ? data : data.toString());
ws.on("message", (data, isBinary) => {
transport.onmessage?.({
data: isBinary
? data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength)
: data.toString(),
isBinary,
});
});
ws.on("close", (code, reason) => {
transport.onclose?.(code, reason.toString());
@@ -484,7 +484,8 @@ function createRelayTransportAdapter(
};
socket.on("message", (data, isBinary) => {
relayTransport.onmessage?.(normalizeMessageData(data, isBinary === true));
const binary = isBinary === true;
relayTransport.onmessage?.({ data: normalizeMessageData(data, binary), isBinary: binary });
});
socket.on("close", (code, reason) => {
const closeCode = typeof code === "number" ? code : 1006;
@@ -26,12 +26,18 @@ class BlockingChannel implements EncryptedRelayChannel {
this.closes.push({ code, reason });
}
outboundWireByteLength(data: string | ArrayBuffer): number {
const plaintextBytes =
typeof data === "string" ? new TextEncoder().encode(data).byteLength : data.byteLength;
return plaintextBytes + 40;
}
drain(): void {
this.resolveSend?.();
}
}
test("the encrypted send queue terminates its physical transport at the hard bound", async () => {
test("negotiated binary ciphertext accepts the exact hard bound and rejects one byte over", async () => {
const channel = new BlockingChannel();
let terminations = 0;
const socket = createEncryptedRelaySocket({
@@ -43,11 +49,11 @@ test("the encrypted send queue terminates its physical transport at the hard bou
},
});
socket.send(new Uint8Array(5 * 1024 * 1024));
socket.send(new Uint8Array(MAX_PHYSICAL_SOCKET_BUFFERED_BYTES - 40));
expect(channel.sent).toHaveLength(1);
expect(socket.bufferedAmount).toBeGreaterThan(5 * 1024 * 1024);
expect(socket.bufferedAmount).toBe(MAX_PHYSICAL_SOCKET_BUFFERED_BYTES);
socket.send(new Uint8Array(2 * 1024 * 1024));
socket.send(new Uint8Array(1));
expect(channel.sent).toHaveLength(1);
expect(terminations).toBe(1);
@@ -93,7 +99,7 @@ test("pending encryption and underlying relay backpressure share one hard bound"
socket.send(new Uint8Array(3 * 1024 * 1024));
expect(channel.sent).toHaveLength(1);
transportBufferedAmount = 4 * 1024 * 1024;
transportBufferedAmount = 6 * 1024 * 1024;
socket.send(new Uint8Array(1));
expect(channel.sent).toHaveLength(1);
@@ -1,12 +1,10 @@
import { EventEmitter } from "node:events";
import { MAX_PHYSICAL_SOCKET_BUFFERED_BYTES, outboundFrameByteLength } from "./physical-socket.js";
// NaCl adds a 24-byte nonce and 16-byte authenticator before base64 encoding.
const ENCRYPTED_FRAME_OVERHEAD_BYTES = 40;
import { MAX_PHYSICAL_SOCKET_BUFFERED_BYTES } from "./physical-socket.js";
export interface EncryptedRelayChannel {
setState: (state: "open") => void;
send: (data: string | ArrayBuffer) => Promise<void>;
outboundWireByteLength: (data: string | ArrayBuffer) => number;
close: (code?: number, reason?: string) => void;
}
@@ -58,7 +56,7 @@ export function createEncryptedRelaySocket(params: {
send: (data) => {
if (readyState !== 1) return;
const outbound = normalizeRelaySendPayload(data);
const outboundBytes = encryptedRelayFrameByteLength(outbound);
const outboundBytes = channel.outboundWireByteLength(outbound);
const queuedBytes = pendingEncryptedBytes + (getTransportBufferedAmount() ?? 0);
if (queuedBytes + outboundBytes > MAX_PHYSICAL_SOCKET_BUFFERED_BYTES) {
terminate();
@@ -93,8 +91,3 @@ function normalizeRelaySendPayload(data: string | Uint8Array | ArrayBuffer): str
out.set(view);
return out.buffer;
}
function encryptedRelayFrameByteLength(data: string | ArrayBuffer): number {
const encryptedBytes = outboundFrameByteLength(data) + ENCRYPTED_FRAME_OVERHEAD_BYTES;
return 4 * Math.ceil(encryptedBytes / 3);
}