johnandersen777.bsky.social/atlibs-webrtc
WebRTC transport for atlibs: com.fedfork.exec.webrtc.connect in the browser, and webrtc-peer, its guest side
0
0
// node --test js/http-datachannel.test.mjs
// httpChannel against Node's HTTP server, a fake datachannel bridged to a TCP socket (as webrtc-peer and
// the guest agent bridge a tag): content-length, chunked and close-delimited bodies, keep-alive on one
// connection, Connection: close ending it, and the size cap.
import assert from "node:assert/strict";
import { createServer } from "node:http";
import { connect, createServer as createTcp } from "node:net";
import { after, test } from "node:test";
import { BODY_MAX, httpChannel } from "./http-datachannel.js";
const connections = [];
const http = createServer((req, res) => {
let body = "";
req.on("data", (d) => (body += d)).on("end", () => {
if (req.url === "/length") return res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ got: body, host: req.headers.host }));
if (req.url === "/chunked") return (res.write("a".repeat(20000)), res.write("é"), res.end("z"));
if (req.url === "/close") return res.writeHead(200, { connection: "close" }).end("bye");
if (req.url === "/empty") return res.writeHead(202).end();
if (req.url === "/huge") return res.writeHead(200, { "content-length": BODY_MAX + 1 }).end();
});
}).on("connection", (s) => connections.push(s)).listen(0, "127.0.0.1");
// A raw server for a response delimited by the close (HTTP/1.0 style, no length).
const raw = createTcp((s) => s.once("data", () => s.end("HTTP/1.0 200 OK\r\ncontent-type: text/plain\r\n\r\nuntil close"))).listen(0, "127.0.0.1");
await Promise.all([http, raw].map((s) => new Promise((r) => s.once("listening", r))));
after(() => (http.close(), raw.close(), connections.forEach((s) => s.destroy())));
// A datachannel whose other end is 127.0.0.1:port.
async function channel(server) {
const socket = connect(server.address().port, "127.0.0.1");
await new Promise((r) => socket.once("connect", r));
const dc = {
readyState: "open",
send: (bytes) => socket.write(bytes),
close: () => socket.end(),
};
socket.on("data", (d) => dc.onmessage?.({ data: d.buffer.slice(d.byteOffset, d.byteOffset + d.length) }));
socket.on("close", () => ((dc.readyState = "closed"), dc.onclose?.()));
return dc;
}
const text = (r) => new TextDecoder().decode(r.body);
test("keep-alive: several requests on one channel, content-length and chunked bodies", async () => {
const before = connections.length;
const conn = httpChannel(await channel(http));
const big = "x".repeat(50000);
const one = await conn.request({ path: "/length", headers: { "content-type": "application/json" }, body: big });
assert.deepEqual([one.status, JSON.parse(text(one))], [200, { got: big, host: "guest" }]);
assert.equal(conn.reusable, true);
const two = await conn.request({ path: "/chunked" });
assert.equal(two.headers["transfer-encoding"], "chunked");
assert.equal(text(two), `${"a".repeat(20000)}éz`);
const three = await conn.request({ path: "/empty" });
assert.deepEqual([three.status, three.body.length, conn.reusable], [202, 0, true]);
assert.equal(connections.length - before, 1, "one connection");
});
test("Connection: close and a close-delimited body end the channel", async () => {
const conn = httpChannel(await channel(http));
assert.equal(text(await conn.request({ path: "/close" })), "bye");
assert.equal(conn.reusable, false);
const old = httpChannel(await channel(raw));
assert.equal(text(await old.request({ method: "GET", path: "/" })), "until close");
assert.equal(old.reusable, false);
});
test("a body over BODY_MAX is refused and the channel closed", async () => {
const conn = httpChannel(await channel(http));
await assert.rejects(conn.request({ path: "/huge" }), /over 4194304 bytes/);
assert.equal(conn.reusable, false);
});