fix: bound browser library downloads while streaming

This commit is contained in:
MrMha committed 2026-10-17 18:42:20 +04:00
1 parent 82f0c9aec9
commit fcea0e6edb
2 files changed
+72 -3

No files matched your search

+50
View File
@@ -150,6 +150,56 @@ for (const directories of [[], parents, [...parents].reverse()]) {
} finally { await archive.close(); }
}
{
const originalFetch = globalThis.fetch;
const originalSelf = globalThis.self;
globalThis.self = { postMessage() {} };
try {
const { downloadLibraries } = await import(pathToFileURL(path.join(process.argv[2], "worker.mjs")));
const bytes = Uint8Array.from({ length: 32 }, (_, index) => index);
const sha256 = [...new Uint8Array(await crypto.subtle.digest("SHA-256", bytes))].map(byte => byte.toString(16).padStart(2, "0")).join("");
const asset = { path: "libraries/linux-1234.zip", size: bytes.length, expandedBytes: 100, sha256 };
let cancelled = false;
let pulled = 0;
const configureDownload = (chunks, size = bytes.length) => {
cancelled = false;
pulled = 0;
globalThis.fetch = async url => {
if (url === "./release.json") return Response.json({ tag: "v1", platforms: { linux: { ...asset, size } } });
if (url.startsWith("https://api.github.com/")) return Response.json({ tag_name: "v1" });
return new Response(new ReadableStream({
pull(controller) {
if (pulled < chunks.length) controller.enqueue(chunks[pulled++]);
else controller.close();
},
cancel() { cancelled = true; }
}));
};
};
configureDownload(Array.from({ length: 8 }, () => bytes));
await assert.rejects(downloadLibraries("linux"), /System library download is incomplete/);
assert.equal(cancelled, true);
assert(pulled < 8);
for (const chunks of [[], [bytes.subarray(0, 31)]]) {
configureDownload(chunks);
await assert.rejects(downloadLibraries("linux"), /System library download is incomplete/);
}
configureDownload([bytes.subarray(0, 3), bytes.subarray(3)]);
const downloaded = await downloadLibraries("linux");
assert.deepEqual(new Uint8Array(await downloaded.file.arrayBuffer()), bytes);
assert.equal(downloaded.expandedBytes, 100);
for (const size of [-1, 1.5, 512 * 1024 * 1024 + 1]) {
configureDownload([bytes], size);
await assert.rejects(downloadLibraries("linux"), /Invalid release manifest/);
assert.equal(pulled, 0);
}
} finally {
globalThis.fetch = originalFetch;
if (originalSelf === undefined) delete globalThis.self;
else globalThis.self = originalSelf;
}
}
const folder = new Archive([{ path: "outer/inner/title/main.elf", file: new Blob([header]) }]);
try { assert.equal((await folder.inspect(() => {})).root, "outer/inner/title/"); }
finally { await folder.close(); }
+22 -3
View File
@@ -21,7 +21,7 @@ export async function downloadLibraries(target) {
if (!response.ok) throw new Error(`Cannot load release manifest (${response.status})`);
const release = await response.json();
const asset = release.platforms[target];
if (!asset || !/^libraries\/(windows|linux)-[a-f0-9]+\.zip$/u.test(asset.path) || !/^[a-f0-9]{64}$/u.test(asset.sha256)) throw new Error("Invalid release manifest");
if (!asset || !Number.isSafeInteger(asset.size) || asset.size < 0 || asset.size > 512 * 1024 * 1024 || !/^libraries\/(windows|linux)-[a-f0-9]+\.zip$/u.test(asset.path) || !/^[a-f0-9]{64}$/u.test(asset.sha256)) throw new Error("Invalid release manifest");
const latestResponse = await fetch("https://api.github.com/repos/boykopovar/AnyPS5/releases/latest", { headers: { Accept: "application/vnd.github+json" } });
if (!latestResponse.ok) {
const rateLimited = latestResponse.status === 429 || (latestResponse.status === 403 && latestResponse.headers.get("x-ratelimit-remaining") === "0");
@@ -33,8 +33,27 @@ export async function downloadLibraries(target) {
}
const download = await fetch(asset.path);
if (!download.ok) throw new Error(`Cannot download system libraries (${download.status})`);
const bytes = await download.arrayBuffer();
if (bytes.byteLength !== asset.size) throw new Error("System library download is incomplete");
const reader = download.body.getReader();
const chunks = [];
let size = 0;
try {
for (;;) {
const { value, done } = await reader.read();
if (done) break;
size += value.byteLength;
if (size > asset.size) throw new Error("System library download is incomplete");
chunks.push(value);
}
if (size !== asset.size) throw new Error("System library download is incomplete");
} catch (error) {
await reader.cancel(error).catch(() => {});
throw error;
} finally {
reader.releaseLock();
}
const bytes = new Uint8Array(size);
let offset = 0;
for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.byteLength; }
const digest = [...new Uint8Array(await crypto.subtle.digest("SHA-256", bytes))].map(byte => byte.toString(16).padStart(2, "0")).join("");
if (digest !== asset.sha256) throw new Error("System library checksum mismatch");
return { file: new Blob([bytes]), release, expandedBytes: asset.expandedBytes };