HTTP range requests: safely resume a generated media download
·
HTTP range requests can recover an interrupted media transfer, but appending the next response blindly can corrupt it. Range support belongs to the output host and must be observed for the actual resource. Genace’s output object provides a URL, media type, and metadata; it does not guarantee that every output URL supports resumable range retrieval.
Preserve partial-transfer evidence
Store the source job ID, output index, local staging path, downloaded byte count, and any strong object validator returned by the host. Keep this manifest private because source URLs can contain signed query parameters. Do not mark the artifact available merely because a partial file has a video extension.
Before resuming, revalidate the destination and any redirects. A source URL may expire or point to a changed representation. If the resource cannot be confirmed as the same object, restart into a new staging file rather than combining bytes from potentially different outputs.
Require a matching partial response
For a saved prefix of N bytes, request the remaining bytes starting at N. Use If-Range with a suitable strong validator when available. The following local decision table describes the response handling your downloader must implement; it is not a new API contract.
206 + Content-Range starts at saved byte count
-> validate object identity, append bounded bytes
200 full representation
-> replace staging file from byte zero; never append
416 range unsatisfied
-> inspect total length and existing file; do not assume success
redirect or HTTP error
-> apply destination/retry policy, preserve manifest
Check the response’s Content-Range syntax, starting offset, and total length where supplied. A server can ignore Range and return a normal full response, so a successful HTTP status alone is not permission to append. Accept-Ranges can be useful evidence but does not replace checking the response you actually received.
Execute a conservative decision before writing
For a positive saved prefix, send one Range: bytes=N- request. This example chooses only an identity-encoded representation with a saved strong ETag, using that same tag as If-Range. Do not send a weak ETag as If-Range. Keep request construction, redirect validation, timeouts, bounded reading, and file writes outside this pure helper:
export function resumeDecision(input) {
if (input.status === 200) return 'restart';
if (input.status !== 206) return 'inspect';
const strongTag = /^"[\x21\x23-\x7e\x80-\xff]*"$/;
if (
!Number.isSafeInteger(input.savedBytes) || input.savedBytes <= 0 ||
!Number.isSafeInteger(input.bodyBytes) || input.bodyBytes < 0 ||
typeof input.savedEtag !== 'string' ||
!strongTag.test(input.savedEtag) || input.etag !== input.savedEtag ||
input.savedEncoding !== 'identity' ||
(input.encoding && input.encoding !== 'identity') ||
(typeof input.contentType === 'string' &&
input.contentType.toLowerCase().startsWith('multipart/'))
) return 'inspect';
const range = /^bytes (\d+)-(\d+)\/(\d+)$/.exec(input.contentRange ?? '');
if (!range) return 'inspect';
const [start, end, total] = range.slice(1).map(Number);
if (
![start, end, total].every(Number.isSafeInteger) ||
start !== input.savedBytes || end < start || end >= total ||
end !== total - 1 || input.bodyBytes !== end - start + 1
) return 'inspect';
return 'append';
}
Pass actual received byte count as bodyBytes, not the Content-Length header. Map contentRange, etag, encoding, and contentType from the response headers; absent Content-Encoding means identity for this policy. The saved encoding and tag must describe the prefix already on disk, not a newly observed replacement object.
For example, a four-byte saved prefix, unchanged strong tag, bytes 4-9/10, and six received bytes yields append. The same input with a 200 status yields restart: write a fresh staging object, never append its full body. A 416 yields inspect even if a reported total equals your local file size; complete-file validation still has to establish whether that file is usable.
This is deliberately a complete-tail, single-range policy. It declines unknown totals, multipart responses, compressed representations, or a valid shorter range that does not yet reach the end. Those are not all HTTP protocol errors; they require a more capable downloader than this example. The helper neither downloads nor writes files, checks a media signature, or proves prefix integrity. Enforce size and elapsed-time limits before buffering, append only the validated bytes, and update your saved offset and validator consistently after storage succeeds.
Keep transfer limits across attempts
Bound total artifact size, total elapsed delivery time, and number of resume attempts. A retry loop should not accumulate unlimited temporary files or keep extending a user’s delivery deadline indefinitely. Account for content encoding and representation changes; do not combine byte offsets calculated for different encodings.
After collection finishes, validate the complete media file and compute its own digest. A correctly formatted 206 does not prove the resulting video is playable or matches the intended duration. If a trusted expected digest is unavailable, your digest supports later storage comparison rather than proving source authenticity by itself.
Test with a controllable origin
Use a local HTTP server that can return correct ranges, ignore the header, return a mismatched offset, change its validator, or truncate a response. Verify the downloader never appends a 200 body and refuses mismatched identity. Test an expired URL without destroying the saved operation reference.
Keep the remote generation succeeded throughout these delivery failures. A new render is a separate budgeted action; range recovery should operate only on the known output and staging manifest.
See redirect policy, upload integrity, and media corruption tests.
Continue with production operations
Where these facts come from
- Bing autocomplete exact match on 2026-10-30; search volume and site performance not measured
- https://www.rfc-editor.org/rfc/rfc9110.html#name-range
- https://www.rfc-editor.org/rfc/rfc9110.html#name-if-range
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Range_requests
- codebase: src/ai/providers/types.ts