mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-06 16:40:32 +00:00
140 lines
3.7 KiB
JavaScript
140 lines
3.7 KiB
JavaScript
const payloadSize = 262144;
|
|
const chunkSize = 4096;
|
|
const payloadText = 'x'.repeat(payloadSize);
|
|
|
|
function delayedPayloadStream(offset = 0, end = -1) {
|
|
const limit = end >= 0 ? Math.min(end + 1, payloadSize) : payloadSize;
|
|
let pos = offset;
|
|
return new ReadableStream({
|
|
async pull(controller) {
|
|
if (pos >= limit) {
|
|
controller.close();
|
|
return;
|
|
}
|
|
const size = Math.min(chunkSize, limit - pos);
|
|
const chunk = new Uint8Array(size);
|
|
chunk.fill(120);
|
|
pos += size;
|
|
controller.enqueue(chunk);
|
|
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
},
|
|
});
|
|
}
|
|
|
|
function fastPayloadStream(offset = 0, end = -1) {
|
|
const limit = end >= 0 ? Math.min(end + 1, payloadSize) : payloadSize;
|
|
let pos = offset;
|
|
return new ReadableStream({
|
|
pull(controller) {
|
|
if (pos >= limit) {
|
|
controller.close();
|
|
return;
|
|
}
|
|
const size = Math.min(chunkSize, limit - pos);
|
|
const chunk = new Uint8Array(size);
|
|
chunk.fill(120);
|
|
pos += size;
|
|
controller.enqueue(chunk);
|
|
},
|
|
});
|
|
}
|
|
|
|
async function createBlobSourceURL(delayed) {
|
|
const blob = new Blob([payloadText], { type: 'application/octet-stream' });
|
|
const originalSlice = blob.slice.bind(blob);
|
|
blob.slice = function (offset = 0, end) {
|
|
if (!delayed) {
|
|
return originalSlice(offset, end);
|
|
}
|
|
const sliceEnd = Number.isFinite(Number(end)) ? Number(end) - 1 : -1;
|
|
return {
|
|
stream() {
|
|
return delayedPayloadStream(Number(offset) || 0, sliceEnd);
|
|
},
|
|
};
|
|
};
|
|
return await gopeed.runtime.blob.createObjectURL(blob);
|
|
}
|
|
|
|
async function createOpenerSourceURL(delayed) {
|
|
const open = async () => delayed ? delayedPayloadStream(0, -1) : fastPayloadStream(0, -1);
|
|
return await gopeed.runtime.blob.createObjectURL(open, {
|
|
size: payloadSize,
|
|
contentType: 'application/octet-stream',
|
|
});
|
|
}
|
|
|
|
async function createRangeSourceURL(delayed) {
|
|
const open = async ({ offset = 0, end = -1 }) => delayed ? delayedPayloadStream(offset, end) : fastPayloadStream(offset, end);
|
|
return await gopeed.runtime.blob.createObjectURL(open, {
|
|
size: payloadSize,
|
|
range: true,
|
|
contentType: 'application/octet-stream',
|
|
});
|
|
}
|
|
|
|
function modeFromURL(url) {
|
|
if (url.includes('/pause-rebuild-blob')) {
|
|
return 'blob';
|
|
}
|
|
if (url.includes('/pause-rebuild-opener')) {
|
|
return 'opener';
|
|
}
|
|
if (url.includes('/pause-rebuild-range')) {
|
|
return 'range';
|
|
}
|
|
return '';
|
|
}
|
|
|
|
async function createPayloadURL(mode, delayed) {
|
|
switch (mode) {
|
|
case 'blob':
|
|
return await createBlobSourceURL(delayed);
|
|
case 'opener':
|
|
return await createOpenerSourceURL(delayed);
|
|
case 'range':
|
|
return await createRangeSourceURL(delayed);
|
|
default:
|
|
throw new Error('unsupported pause rebuild mode: ' + mode);
|
|
}
|
|
}
|
|
|
|
gopeed.events.onResolve(async function (ctx) {
|
|
const mode = modeFromURL(ctx.req.url);
|
|
if (!mode) {
|
|
return;
|
|
}
|
|
|
|
ctx.res = {
|
|
name: 'blob-pause-rebuild-' + mode,
|
|
range: mode !== 'opener',
|
|
files: [
|
|
{
|
|
name: 'pause-rebuild-' + mode + '.bin',
|
|
size: payloadSize,
|
|
req: {
|
|
url: await createPayloadURL(mode, true),
|
|
rawUrl: ctx.req.rawUrl || ctx.req.url,
|
|
labels: {
|
|
mode: 'pause-rebuild',
|
|
source: mode,
|
|
},
|
|
},
|
|
},
|
|
],
|
|
};
|
|
});
|
|
|
|
gopeed.events.onError(async function (ctx) {
|
|
const req = ctx.task?.meta?.req;
|
|
const source = req?.labels?.source || modeFromURL(req?.rawUrl || '');
|
|
if (!req || req.labels?.mode !== 'pause-rebuild' || req.labels?.rebuilt === 'true' || !source) {
|
|
return;
|
|
}
|
|
|
|
await ctx.task.setUrl(await createPayloadURL(source, false));
|
|
await req.putLabel('started', 'true');
|
|
await req.putLabel('rebuilt', 'true');
|
|
await ctx.task.continue();
|
|
});
|