fix(bootstrap): resume downloads without discarding a valid archive
Resuming a partial download leaned entirely on the remote size from a HEAD request. When that size could not be determined the code played it safe by deleting the partial file and starting over, so a flaky HEAD (or an offline mirror) threw away gigabytes that were fine to resume. It also could not tell a real size from an error page, and if the server answered a ranged request with the whole file (200, not 206) libcurl could append it to the partial and silently corrupt the archive. - The HEAD now returns transport status, HTTP code, and size together, and a size counts as authoritative only on a 2xx reply with a positive Content-Length. - A pure helper decides what to do from the existing bytes and that authoritative flag: start fresh, truncate a stale oversized file, reuse a complete one, or resume with a ranged GET. When the size is not authoritative the partial is kept and a ranged GET is attempted rather than discarded. - A ranged request that comes back as 200 instead of 206 now restarts cleanly from scratch instead of appending to the partial. - The free-space pre-check runs only when the size is authoritative, and a failed download reports how many bytes were kept for the retry. Tests cover the resume-decision helper across the fresh, stale, complete, resuming, and non-authoritative cases. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -141,6 +141,10 @@ public:
|
||||
/// expectedHexLen hex chars; "" otherwise (an HTTP error page is never a checksum).
|
||||
static std::string parseChecksumFile(const std::string& content, std::size_t expectedHexLen);
|
||||
|
||||
enum class ResumeAction { Fresh, TruncateAndFresh, ReuseComplete, RangedGet };
|
||||
/// Pure, no-I/O. authoritative = HEAD was 2xx with a positive Content-Length.
|
||||
static ResumeAction decideResume(long long existingBytes, bool authoritative, long long remoteSize);
|
||||
|
||||
private:
|
||||
mutable std::mutex mutex_;
|
||||
Progress progress_;
|
||||
@@ -173,9 +177,9 @@ private:
|
||||
/// Verify downloaded zip against remote checksums. Returns true if valid.
|
||||
bool verifyChecksums(const std::string& zipPath, const std::string& baseUrl);
|
||||
|
||||
/// Perform a HEAD request to get remote file size.
|
||||
/// Returns -1 on failure.
|
||||
long long getRemoteFileSize(const std::string& url);
|
||||
struct RemoteInfo { bool transportOk = false; long httpCode = 0; long long size = -1; };
|
||||
/// HEAD the remote file. transportOk=false on any curl error; size=-1 when absent.
|
||||
RemoteInfo getRemoteFileSize(const std::string& url);
|
||||
|
||||
/// Byte offset of the partial file when resuming a download.
|
||||
/// Added to dlnow in progress reports so the bar reflects true total.
|
||||
|
||||
Reference in New Issue
Block a user