164 lines
5.1 KiB
JavaScript
164 lines
5.1 KiB
JavaScript
import assert from "node:assert/strict";
|
||
import { createHash } from "node:crypto";
|
||
import { test } from "node:test";
|
||
import { verifyNormalRecords } from "./http3-capture-assertions.mjs";
|
||
|
||
function connectionFactsFixture() {
|
||
const body = Buffer.from([0, 255, 128, 1]);
|
||
const view = (bytes) => ({
|
||
base64: bytes.toString("base64"),
|
||
bytes: bytes.length,
|
||
sha256: createHash("sha256").update(bytes).digest("hex"),
|
||
});
|
||
const producer = {
|
||
id: "flow-id",
|
||
error: null,
|
||
request: { http_version: "HTTP/3", content: view(body) },
|
||
response: { http_version: "HTTP/3", content: view(body) },
|
||
client_conn: {
|
||
id: "client-id",
|
||
transport_protocol: "udp",
|
||
tls_version: "QUICv1",
|
||
timestamp_tls_setup: 1_700_000_000.125678,
|
||
error: null,
|
||
},
|
||
server_conn: {
|
||
id: "server-id",
|
||
transport_protocol: "udp",
|
||
tls_version: "QUICv1",
|
||
timestamp_tls_setup: 1_700_000_000.987654,
|
||
error: null,
|
||
},
|
||
};
|
||
const reported = structuredClone(producer);
|
||
const requestFields = {
|
||
method: "POST",
|
||
scheme: "https",
|
||
authority: "lab.test:18443",
|
||
path: "/echo?case=readable",
|
||
};
|
||
Object.assign(reported.request, requestFields);
|
||
const headers = [
|
||
[Buffer.from("x-repeat"), Buffer.from("first-α")],
|
||
[Buffer.from("x-repeat"), Buffer.from("second-β")],
|
||
[Buffer.from("x-binary"), Buffer.from([255])],
|
||
];
|
||
for (const side of ["request", "response"])
|
||
reported[side].headers = [
|
||
["x-repeat", "first-α"],
|
||
["x-repeat", "second-β"],
|
||
["x-binary", null],
|
||
];
|
||
reported.client_conn.alpn = "h3";
|
||
reported.server_conn.alpn = "h3";
|
||
reported.response.status_code = 200;
|
||
const binary_fields = [
|
||
["/request/content", body],
|
||
["/response/content", body],
|
||
["/client_conn/alpn", Buffer.from("h3")],
|
||
["/server_conn/alpn", Buffer.from("h3")],
|
||
...Object.entries(requestFields).map(([name, value]) => [
|
||
`/request/${name}`,
|
||
Buffer.from(value),
|
||
]),
|
||
...["request", "response"].flatMap((side) =>
|
||
headers.flatMap((pair, index) =>
|
||
pair.map((value, part) => [`/${side}/headers/${index}/${part}`, value]),
|
||
),
|
||
),
|
||
].map(([pointer, bytes]) => {
|
||
const { base64, ...facts } = view(bytes);
|
||
return {
|
||
pointer,
|
||
representation: "producer-bytes",
|
||
state: "retained",
|
||
content_base64: base64,
|
||
...facts,
|
||
};
|
||
});
|
||
return {
|
||
normal: { records: [{ reported, binary_fields }] },
|
||
manifest: {
|
||
requests: {
|
||
curl: {
|
||
headers: [
|
||
...Object.entries(requestFields).map(([name, value]) => [
|
||
view(Buffer.from(`:${name}`)),
|
||
view(Buffer.from(value)),
|
||
]),
|
||
...headers.map((pair) => pair.map(view)),
|
||
],
|
||
body: view(body),
|
||
},
|
||
},
|
||
responses: {
|
||
curl: {
|
||
headers: headers.map((pair) => pair.map(view)),
|
||
body: view(body),
|
||
},
|
||
},
|
||
},
|
||
producer: [producer],
|
||
};
|
||
}
|
||
|
||
test("positive assertions retain exact producer connection facts on both legs", () => {
|
||
const { normal, manifest, producer } = connectionFactsFixture();
|
||
verifyNormalRecords(normal, manifest, producer);
|
||
});
|
||
|
||
for (const [field, value] of [
|
||
["method", "GET"],
|
||
["scheme", "http"],
|
||
["authority", "other.test:18443"],
|
||
["path", "/different"],
|
||
])
|
||
test(`positive assertions reject changed readable request ${field}`, () => {
|
||
const { normal, manifest, producer } = connectionFactsFixture();
|
||
normal.records[0].reported.request[field] = value;
|
||
assert.throws(() => verifyNormalRecords(normal, manifest, producer), {
|
||
code: "ERR_ASSERTION",
|
||
});
|
||
});
|
||
|
||
for (const side of ["client_conn", "server_conn"])
|
||
test(`positive assertions reject null readable ${side} ALPN`, () => {
|
||
const { normal, manifest, producer } = connectionFactsFixture();
|
||
normal.records[0].reported[side].alpn = null;
|
||
assert.throws(() => verifyNormalRecords(normal, manifest, producer), {
|
||
code: "ERR_ASSERTION",
|
||
});
|
||
});
|
||
|
||
for (const side of ["request", "response"])
|
||
for (const [index, part, value] of [
|
||
[0, 0, "x-changed"],
|
||
[0, 1, "changed-first"],
|
||
[1, 1, "changed-second"],
|
||
[2, 1, "<22>"],
|
||
])
|
||
test(`positive assertions reject changed readable ${side} header ${index}/${part}`, () => {
|
||
const { normal, manifest, producer } = connectionFactsFixture();
|
||
normal.records[0].reported[side].headers[index][part] = value;
|
||
assert.throws(() => verifyNormalRecords(normal, manifest, producer), {
|
||
code: "ERR_ASSERTION",
|
||
});
|
||
});
|
||
|
||
for (const side of ["client_conn", "server_conn"])
|
||
for (const [field, value] of [
|
||
["tls_version", null],
|
||
["tls_version", "changed-producer-label"],
|
||
["error", "changed-connection-error"],
|
||
["error", undefined],
|
||
["timestamp_tls_setup", 0],
|
||
["timestamp_tls_setup", 1_700_000_000],
|
||
])
|
||
test(`positive assertions reject ${side}.${field} changed to ${String(value)}`, () => {
|
||
const { normal, manifest, producer } = connectionFactsFixture();
|
||
normal.records[0].reported[side][field] = value;
|
||
assert.throws(() => verifyNormalRecords(normal, manifest, producer), {
|
||
code: "ERR_ASSERTION",
|
||
});
|
||
});
|