web/moq: the WebSocket binding's capsules, WebTransport and the binding behind one transport interface (WebTransport first, offering moqt-22 and the relay's certificate hash; the binding as the fallback, or forced), a MOQT session (SETUP with the token, requests, every data stream read concurrently, GOAWAY), and the payload framings a WAI track carries. web/room: the room client (WebCodecs publishing with receipts, verified subscriptions, a late joiner's FETCH, the composed canvas and the mixed AudioWorklet, the events timeline and captions with their origin, the move after GOAWAY). web/compose: the composition corpus in the page. e2e: the relay on loopback from a test configuration, real Chrome through Playwright with a fake camera and microphone, each browser under memguard. Co-Authored-By: Claude Opus 5.5 <[email protected]>
55 lines
2.8 KiB
JavaScript
55 lines
2.8 KiB
JavaScript
// The WebSocket binding's capsules against the bytes joule-moq-relay's README states
|
|
// (`node --test web/moq/capsule.test.mjs`).
|
|
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import * as cap from "./capsule.js";
|
|
|
|
const bytes = (s) => Uint8Array.from(s.trim().split(/\s+/).map((x) => parseInt(x, 16)));
|
|
|
|
test("the relay's first capsules are its stream credit", () => {
|
|
assert.deepEqual(cap.maxStreamsCapsule(true, 256), bytes("99 0b 4d 3f 02 41 00"));
|
|
assert.deepEqual(cap.maxStreamsCapsule(false, 1024), bytes("99 0b 4d 40 02 44 00"));
|
|
assert.deepEqual(cap.decodeCapsule(bytes("99 0b 4d 3f 02 41 00")), { kind: "max_streams", bidi: true, max: 256 });
|
|
assert.deepEqual(cap.decodeCapsule(bytes("99 0b 4d 40 02 44 00")), { kind: "max_streams", bidi: false, max: 1024 });
|
|
});
|
|
|
|
test("a session closed at the GOAWAY timeout", () => {
|
|
const b = bytes("68 43 12 00 00 00 10 47 4f 41 57 41 59 20 74 69 6d 65 6f 75 74");
|
|
assert.deepEqual(cap.decodeCapsule(b), { kind: "close", code: 0x10, message: "GOAWAY timeout" });
|
|
assert.deepEqual(cap.closeSessionCapsule(0x10, "GOAWAY timeout"), b);
|
|
});
|
|
|
|
test("stream data, FIN, reset and stop round-trip", () => {
|
|
const d = new TextEncoder().encode("moqt");
|
|
for (const fin of [false, true]) {
|
|
const c = cap.decodeCapsule(cap.streamCapsule(6, d, fin));
|
|
assert.equal(c.kind, "stream");
|
|
assert.equal(c.id, 6);
|
|
assert.equal(c.fin, fin);
|
|
assert.deepEqual(c.data, d);
|
|
}
|
|
assert.deepEqual(cap.decodeCapsule(cap.resetStreamCapsule(3, 2)), { kind: "reset", id: 3, code: 2 });
|
|
assert.deepEqual(cap.decodeCapsule(cap.stopSendingCapsule(4, 1)), { kind: "stop", id: 4, code: 1 });
|
|
assert.deepEqual(cap.decodeCapsule(cap.streamsBlockedCapsule(false, 9)), { kind: "blocked", bidi: false, max: 9 });
|
|
});
|
|
|
|
test("QUIC varints at each length, and a message that is not one whole capsule", () => {
|
|
for (const v of [0, 63, 64, 16383, 16384, 2 ** 30 - 1, 2 ** 30, 2 ** 53 - 1]) {
|
|
const b = new Uint8Array(8);
|
|
const n = cap.putQvarint(b, 0, v);
|
|
assert.deepEqual(cap.getQvarint(b.subarray(0, n)), [v, n]);
|
|
}
|
|
const m = cap.maxStreamsCapsule(true, 5);
|
|
assert.throws(() => cap.decodeCapsule(Uint8Array.from([...m, 0])));
|
|
assert.throws(() => cap.decodeCapsule(m.subarray(0, m.length - 1)));
|
|
assert.deepEqual(cap.decodeCapsule(Uint8Array.from([0x80, 0x00, 0x7a, 0xbc, 0x00])), { kind: "ignored", type: 0x7abc });
|
|
});
|
|
|
|
test("stream IDs are numbered as QUIC numbers them", () => {
|
|
assert.equal(cap.streamId.nth(0, true, true), 0);
|
|
assert.equal(cap.streamId.nth(0, false, true), 1);
|
|
assert.equal(cap.streamId.nth(0, true, false), 2);
|
|
assert.equal(cap.streamId.nth(3, false, false), 15);
|
|
assert.ok(cap.streamId.clientInitiated(4) && cap.streamId.bidi(4) && !cap.streamId.bidi(6));
|
|
assert.equal(cap.streamId.index(15), 3);
|
|
});
|