fix(downloads): resume Deepbrid open-ended ranges safely
Accept Deepbrid's open-ended 206 Content-Range response when the total is numeric, derive the missing end byte safely, and prefer byte-exact HTTP totals over rounded provider metadata. Preserve complete archive files on standard HTTP 416 responses when provider metadata is slightly smaller. Add end-to-end resume regressions that separate the final eight bytes and prove no retry, truncation, deletion, or second request occurs.
This commit is contained in:
@@ -510,13 +510,13 @@ function parseContentRange(contentRange: string | null): ParsedContentRange | nu
|
||||
if (!contentRange) {
|
||||
return null;
|
||||
}
|
||||
const match = contentRange.match(/^bytes\s+(\d+)-(\d+)\/(\d+|\*)$/i);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
const start = Number(match[1]);
|
||||
const end = Number(match[2]);
|
||||
const total = match[3] === "*" ? null : Number(match[3]);
|
||||
const match = contentRange.match(/^bytes\s+(\d+)-(\d*)\/(\d+|\*)$/i);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
const start = Number(match[1]);
|
||||
const total = match[3] === "*" ? null : Number(match[3]);
|
||||
const end = match[2] ? Number(match[2]) : total === null ? NaN : total - 1;
|
||||
if (!Number.isFinite(start) || !Number.isFinite(end) || start < 0 || end < start) {
|
||||
return null;
|
||||
}
|
||||
@@ -526,9 +526,18 @@ function parseContentRange(contentRange: string | null): ParsedContentRange | nu
|
||||
return { start, end, total };
|
||||
}
|
||||
|
||||
function parseContentRangeTotal(contentRange: string | null): number | null {
|
||||
return parseContentRange(contentRange)?.total ?? null;
|
||||
}
|
||||
function parseContentRangeTotal(contentRange: string | null): number | null {
|
||||
const parsed = parseContentRange(contentRange)?.total;
|
||||
if (parsed !== null && parsed !== undefined) {
|
||||
return parsed;
|
||||
}
|
||||
const match = contentRange?.match(/^bytes\s+\*\/(\d+)$/i);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
const total = Number(match[1]);
|
||||
return Number.isSafeInteger(total) && total > 0 ? total : null;
|
||||
}
|
||||
|
||||
function parseContentDispositionFilename(contentDisposition: string | null): string {
|
||||
if (!contentDisposition) {
|
||||
@@ -11132,11 +11141,11 @@ export class DownloadManager extends EventEmitter {
|
||||
contentLength,
|
||||
totalFromRange
|
||||
});
|
||||
} else if (knownTotal && knownTotal > 0) {
|
||||
item.totalBytes = knownTotal;
|
||||
} else if (totalFromRange) {
|
||||
item.totalBytes = totalFromRange;
|
||||
} else if (contentLength > 0) {
|
||||
} else if (totalFromRange) {
|
||||
item.totalBytes = totalFromRange;
|
||||
} else if (knownTotal && knownTotal > 0) {
|
||||
item.totalBytes = knownTotal;
|
||||
} else if (contentLength > 0) {
|
||||
item.totalBytes = response.status === 206 ? existingBytes + contentLength : contentLength;
|
||||
}
|
||||
const completionPlan = planDownloadCompletion({
|
||||
|
||||
Reference in New Issue
Block a user