Summary
In flash_from_url (src/fls/from_url.rs), when a connection is interrupted and the download is retried, start_download is called with resume_from = Some(progress.bytes_received), which sends a Range: bytes=<offset>- header. However:
- The HTTP response status code is never checked — if the server returns
200 OK (full content) instead of 206 Partial Content, the data stream restarts from byte 0 and gets silently stitched to the previously-received bytes, corrupting the output.
- The
Content-Range response header is not validated — even for a 206 response, the actual start offset returned by the server is never confirmed to match progress.bytes_received.
Additionally, the content-length calculation response.content_length().map(|len| len + offset) assumes the server honored the Range request; if it returned 200 instead, this produces an inflated and incorrect total length.
Impact
- For uncompressed files, data would be silently corrupted.
- For compressed files (gzip, xz, etc.): if the server returns
200 with a full restart, the decompressor receives a valid but restarted stream stitched mid-stream, potentially causing subtle corruption that survives decompression (depending on format/block boundaries).
Suggested Fix
After calling start_download with a non-None resume_from:
- Check that the response status is
206 Partial Content; if 200, either restart from scratch (reset bytes_received) or return an error indicating the server does not support resumable downloads.
- Parse the
Content-Range header and verify the start offset matches resume_from.
References
/cc @mangelajo
Summary
In
flash_from_url(src/fls/from_url.rs), when a connection is interrupted and the download is retried,start_downloadis called withresume_from = Some(progress.bytes_received), which sends aRange: bytes=<offset>-header. However:200 OK(full content) instead of206 Partial Content, the data stream restarts from byte 0 and gets silently stitched to the previously-received bytes, corrupting the output.Content-Rangeresponse header is not validated — even for a206response, the actual start offset returned by the server is never confirmed to matchprogress.bytes_received.Additionally, the content-length calculation
response.content_length().map(|len| len + offset)assumes the server honored the Range request; if it returned200instead, this produces an inflated and incorrect total length.Impact
200with a full restart, the decompressor receives a valid but restarted stream stitched mid-stream, potentially causing subtle corruption that survives decompression (depending on format/block boundaries).Suggested Fix
After calling
start_downloadwith a non-Noneresume_from:206 Partial Content; if200, either restart from scratch (resetbytes_received) or return an error indicating the server does not support resumable downloads.Content-Rangeheader and verify the start offset matchesresume_from.References
/cc @mangelajo