wasm-viewer tests: the cross-origin path without exposed headers
serve.py always exposed Content-Range and ETag, and Node has no CORS, so openUrl's documented cross-origin path (length from a HEAD request, answers checked by body length alone, no validator) was never run. - serve.py /noexpose/ serves ranges without Content-Range, ETag, Last-Modified or Accept-Ranges (what a page sees of a server that does not expose them); /unexposed/ sends them but exposes none, for a real browser. HEAD requests are counted (0 bytes). - test.mjs: every fixture check through /noexpose/ at 1 MiB and 512-byte blocks (one HEAD each, requests and bytes as the server counted them), concurrent reads with cacheSize 0, a short answer still caught, and a server without a HEAD length a clear error. - browser.sh: the page on 127.0.0.1 opens the file from localhost, once with Content-Range exposed and once through /unexposed/, where the server's log must show the HEAD. Checked by breaking the HEAD length in remote.js: the new checks fail. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -254,6 +254,52 @@ async function remoteTests() {
|
||||
checks += 2;
|
||||
}
|
||||
|
||||
// A cross-origin server that does not expose Content-Range, ETag or
|
||||
// Last-Modified (serve.py's /noexpose/): the length comes from a HEAD
|
||||
// request and answers are checked by their length alone. Every fixture
|
||||
// check, calls in flight at once, and what the page counts.
|
||||
for (const opts of [undefined, { blockSize: 512, cacheSize: 1024 }]) {
|
||||
for (const [name, exp] of Object.entries(expected)) {
|
||||
await fetch(`${base}/__reset`);
|
||||
const f = await pkg.openUrl(`${base}/noexpose/fix/${name}`, opts);
|
||||
await checkFile(`${name} (no exposed headers, ${JSON.stringify(opts ?? {})})`, exp, f);
|
||||
const st = f.stats();
|
||||
eq(st.lazy, true, "no exposed headers: read by ranges");
|
||||
eq(st.size, statSync(join(fixDir, name)).size, "no exposed headers: size from HEAD");
|
||||
const server = await serverStats();
|
||||
eq(server.log.filter((l) => l[4] === "HEAD").length, 1, "no exposed headers: one HEAD");
|
||||
eq(st.requests, server.requests, "no exposed headers: requests counted");
|
||||
eq(st.bytesFetched, server.bytes, "no exposed headers: bytes counted");
|
||||
f.free();
|
||||
}
|
||||
}
|
||||
{
|
||||
const f = await pkg.openUrl(`${base}/noexpose/fix/fixture.h5`, { blockSize: 512, cacheSize: 0, parallel: 3 });
|
||||
const exp = expected["fixture.h5"];
|
||||
const paths = ["/grid", "/sensors/temp", "/vlen_str", "/cube"];
|
||||
const got = await Promise.all([...paths, ...paths].map((p) => f.read(p)));
|
||||
got.forEach((r, i) => eq(values(exp.datasets[paths[i % 4]].kind, r.data), exp.datasets[paths[i % 4]].values,
|
||||
`no exposed headers: concurrent ${paths[i % 4]}`));
|
||||
await checkErrors(f);
|
||||
}
|
||||
// Without a validator a changed file cannot be told apart; a short
|
||||
// answer still can.
|
||||
await fails(async () => {
|
||||
const f = await pkg.openUrl(`${base}/noexpose/fix/fixture.h5`, {
|
||||
blockSize: 512,
|
||||
fetch: async (url, init) => {
|
||||
const r = await fetch(url, init);
|
||||
if (init.method === "HEAD" || init.headers.Range === "bytes=0-511") return r;
|
||||
return new Response((await r.arrayBuffer()).slice(1), { status: 206 });
|
||||
},
|
||||
});
|
||||
await f.read("/grid");
|
||||
}, /got \d+/, "no exposed headers: short answer");
|
||||
// No HEAD length either: a clear error.
|
||||
await fails(() => pkg.openUrl(`${base}/noexpose/fix/fixture.h5`, {
|
||||
fetch: async (url, init) => (init.method === "HEAD" ? new Response(null, { status: 405 }) : fetch(url, init)),
|
||||
}), /cannot learn the file's size/, "no exposed headers, no HEAD");
|
||||
|
||||
// A server without range support: downloaded whole (the default), or
|
||||
// refused.
|
||||
const whole = await pkg.openUrl(`${base}/norange/fix/fixture.h5`);
|
||||
|
||||
Reference in New Issue
Block a user