mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 00:22:57 -05:00
fix: bound browser library downloads while streaming
This commit is contained in:
1 parent
82f0c9aec9
commit
fcea0e6edb
2 files changed
+72
-3
No files matched your search
@@ -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
@@ -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 };
|
||||
|
||||
Reference in new issue
Block a user