one
Version:
One is a React framework for web, iOS, and Android apps.
106 lines (104 loc) • 3.26 kB
JavaScript
async function withRange(request, response) {
const range = request.headers.get("range");
if (!range || request.method !== "GET" || response.status !== 200 || !response.body || response.headers.has("content-range") || response.headers.has("content-encoding") || !ifRangeMatches(request.headers.get("if-range"), response.headers)) {
return response;
}
const match = /^bytes=(\d*)-(\d*)$/.exec(range.trim());
if (!match || !match[1] && !match[2]) return response;
const length = response.headers.get("content-length");
const known = length && /^\d+$/.test(length) ? Number(length) : void 0;
const headers = new Headers(response.headers);
headers.set("accept-ranges", "bytes");
headers.delete("content-length");
const { size, kept, keptStart } = await readRange(response.body, match, known);
const span = resolve(match, size);
if (!span) {
headers.set("content-range", `bytes */${size}`);
return new Response(null, {
status: 416,
headers
});
}
headers.set("content-range", `bytes ${span.start}-${span.end}/${size}`);
headers.set("content-length", String(span.end - span.start + 1));
return new Response(kept.subarray(span.start - keptStart, span.end - keptStart + 1), {
status: 206,
headers
});
}
function ifRangeMatches(ifRange, headers) {
if (!ifRange) return true;
const value = ifRange.trim();
if (value.startsWith("\"")) {
const etag = headers.get("etag");
return !!etag && !etag.startsWith("W/") && etag === value;
}
if (value.startsWith("W/")) return false;
return headers.get("last-modified") === value;
}
function resolve(match, size) {
let start;
let end;
if (!match[1]) {
start = Math.max(0, size - Number(match[2]));
end = size - 1;
} else {
start = Number(match[1]);
end = match[2] ? Math.min(Number(match[2]), size - 1) : size - 1;
}
if (start >= size || start > end) return;
return {
start,
end
};
}
async function readRange(body, match, known) {
const span = known === void 0 ? void 0 : resolve(match, known) ?? {
start: known,
end: -1
};
const suffix = !span && !match[1] ? Number(match[2]) : 0;
const from = span ? span.start : match[1] ? Number(match[1]) : 0;
const to = span ? span.end : match[2] && match[1] ? Number(match[2]) : Number.POSITIVE_INFINITY;
const chunks = [];
let keptBytes = 0;
let size = 0;
const reader = body.getReader();
while (known === void 0 || size <= to) {
const { done, value } = await reader.read();
if (done) break;
const chunkStart = size;
size += value.byteLength;
if (suffix) {
chunks.push(value);
keptBytes += value.byteLength;
while (chunks.length > 1 && keptBytes - chunks[0].byteLength >= suffix) {
keptBytes -= chunks.shift().byteLength;
}
continue;
}
const lo = Math.max(from, chunkStart);
const hi = Math.min(to + 1, size);
if (hi > lo) {
chunks.push(value.subarray(lo - chunkStart, hi - chunkStart));
keptBytes += hi - lo;
}
}
if (known !== void 0) {
await reader.cancel();
size = known;
}
const kept = new Uint8Array(keptBytes);
let at = 0;
for (const chunk of chunks) {
kept.set(chunk, at);
at += chunk.byteLength;
}
return {
size,
kept,
keptStart: suffix ? size - keptBytes : from
};
}
export { withRange };
//# sourceMappingURL=assetRange.mjs.map