Release response streams used by cache metadata probes

This commit is contained in:
Jonathan Sykes
2026-10-09 10:43:05 +08:00
parent f359e5dd39
commit 2fc6d5d4dc
2 changed files with 20 additions and 4 deletions

View File

@@ -49,10 +49,19 @@
return response ? response.json() : null;
}
function verifiedProbe(response, hash) {
if (!response) return false;
const valid = response.headers.get('X-Asset-Hash') === hash;
// Metadata checks must release Blob/data-pipe readers. They never consume
// asset bytes, and cancelling this response does not remove its cache entry.
response.body?.cancel().catch(() => {});
return valid;
}
async function status(manifest, cache, { previous, activeLayout, offline = true } = {}) {
const found = await Promise.all(blocking(manifest, previous, activeLayout).map(async path => {
const response = await cache.match(url(path, manifest.files[path]));
return !!response && response.headers.get('X-Asset-Hash') === manifest.files[path].h;
return verifiedProbe(response, manifest.files[path].h);
}));
const missing = found.filter(value => !value).length;
return { ready: missing === 0, missing, version: manifest.buildTag, ...(offline ? await completeness(manifest, cache) : {}) };
@@ -85,7 +94,7 @@
for (const path of selected) {
const key = url(path, manifest.files[path]);
const response = await cache.match(key);
if (!response || response.headers.get('X-Asset-Hash') !== manifest.files[path].h) missing.push(key);
if (!verifiedProbe(response, manifest.files[path].h)) missing.push(key);
}
const count = missing.length;
let failure;
@@ -94,7 +103,7 @@
const key = missing.shift();
try { await sharedDownload(key, async () => {
const held = await cache.match(key);
if (held?.headers.get('X-Asset-Hash') === key.split('v=')[1]) return;
if (verifiedProbe(held, key.split('v=')[1])) return;
let error;
for (let attempt = 0; attempt < Math.min(3, attempts); attempt++) {
const controller = new AbortController();
@@ -127,7 +136,7 @@
const files = paths(manifest);
const missingFiles = (await Promise.all(files.map(async path => {
const response = await cache.match(url(path, manifest.files[path]));
return !response || response.headers.get('X-Asset-Hash') !== manifest.files[path].h ? path : null;
return verifiedProbe(response, manifest.files[path].h) ? null : path;
}))).filter(Boolean);
const total = files.length;
return { offlineReady: missingFiles.length === 0, cached: total - missingFiles.length, total, missingFiles };