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:
@@ -3961,6 +3961,19 @@ void testBootstrapParseChecksum()
|
||||
EXPECT_EQ(Bootstrap::parseChecksumFile("", 64), std::string());
|
||||
}
|
||||
|
||||
void testBootstrapDecideResume()
|
||||
{
|
||||
using R = dragonx::util::Bootstrap::ResumeAction;
|
||||
using dragonx::util::Bootstrap;
|
||||
EXPECT_TRUE(Bootstrap::decideResume(0, false, -1) == R::Fresh);
|
||||
EXPECT_TRUE(Bootstrap::decideResume(0, true, 100) == R::Fresh);
|
||||
EXPECT_TRUE(Bootstrap::decideResume(5, true, 3) == R::TruncateAndFresh);
|
||||
EXPECT_TRUE(Bootstrap::decideResume(5, true, 5) == R::ReuseComplete);
|
||||
EXPECT_TRUE(Bootstrap::decideResume(3, true, 5) == R::RangedGet);
|
||||
EXPECT_TRUE(Bootstrap::decideResume(3, false, -1) == R::RangedGet); // offline: keep + try range
|
||||
EXPECT_TRUE(Bootstrap::decideResume(3, false, 999) == R::RangedGet); // bogus error-page length ignored
|
||||
}
|
||||
|
||||
void testBootstrapSpaceSufficient()
|
||||
{
|
||||
using dragonx::util::Bootstrap;
|
||||
@@ -18448,6 +18461,7 @@ int main()
|
||||
testXmrigProvisionPolicy();
|
||||
testBootstrapSpaceSufficient();
|
||||
testBootstrapParseChecksum();
|
||||
testBootstrapDecideResume();
|
||||
testBootstrapPlanArchive();
|
||||
#ifndef _WIN32
|
||||
testBootstrapReclaimable();
|
||||
|
||||
Reference in New Issue
Block a user