fix: treat a chunked cut as a failed attempt

This commit is contained in:
Abdessamad Derraz committed 2026-10-18 16:24:03 +02:00
1 parent 19f02312ce
commit eeeaa22579
4 files changed
+43 -6

No files matched your search

+1 -1
View File
@@ -8,7 +8,7 @@ param(
$ErrorActionPreference = "Stop"
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
$defaultInstallSha256 = "ad00604f027af67f44fbb7dbe49513a870b6ba722263cf6a514df53cfe7ec13d"
$defaultInstallSha256 = "0c3089832c2ee7e61b50a49edcd9a810814be8b5465238755b3954ff895c5e18"
$maximumInstallerBytes = 2MB
$installerExit = 0
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
+9 -4
View File
@@ -1753,8 +1753,10 @@ def _fetch_json(url: str, limit: int, label: str) -> object:
if exc.code < 500 and exc.code != 429 or attempt == MAX_RETRIES:
raise
delay = _retry_delay(attempt, exc.headers.get("Retry-After") if exc.headers else None)
except (urllib.error.URLError, OSError):
except (urllib.error.URLError, OSError, http.client.HTTPException) as exc:
if attempt == MAX_RETRIES:
if isinstance(exc, http.client.HTTPException):
raise ConnectionError(f"connection cut: {exc!r}") from exc
raise
delay = _retry_delay(attempt, None)
time.sleep(delay)
@@ -2397,7 +2399,10 @@ def _download_one(
tmp_path.unlink() # the partial copy no longer fits the file
delay = _retry_delay(attempt, exc.headers.get("Retry-After") if exc.headers else None)
except (urllib.error.URLError, OSError, ValueError) as exc:
# http.client raises IncompleteRead (an HTTPException, not an
# OSError) when a chunked transfer is cut, as a proxy re-encoding
# the stream does: it is a dropped connection like any other.
except (urllib.error.URLError, OSError, ValueError, http.client.HTTPException) as exc:
if (isinstance(exc, OSError) and not isinstance(exc, urllib.error.URLError)
and _write_refused(exc)):
# No attempt can change a refused write.
@@ -2749,9 +2754,9 @@ def _fetch_archive(url: str, directory: Path) -> Path:
if exc.code < 500 and exc.code != 429:
raise
reason = exc
except (urllib.error.URLError, OSError) as exc:
except (urllib.error.URLError, OSError, http.client.HTTPException) as exc:
Path(handle.name).unlink(missing_ok=True)
reason = exc
reason = exc if isinstance(exc, OSError) else ConnectionError(repr(exc))
except BaseException:
Path(handle.name).unlink(missing_ok=True)
raise
+1 -1
View File
@@ -18,7 +18,7 @@ esac
TEMP_INSTALLER=""
TEMP_DIRECTORY=""
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
DEFAULT_INSTALL_SHA256="ad00604f027af67f44fbb7dbe49513a870b6ba722263cf6a514df53cfe7ec13d"
DEFAULT_INSTALL_SHA256="0c3089832c2ee7e61b50a49edcd9a810814be8b5465238755b3954ff895c5e18"
MAX_INSTALLER_BYTES=2097152
cleanup() {
+32
View File
@@ -2586,5 +2586,37 @@ class DataDirectoriesAreInstalled(unittest.TestCase):
)
class AChunkedCutIsADownloadFailure(unittest.TestCase):
"""Behind a proxy that re-encodes as chunked, a cut raised
http.client.IncompleteRead, which no handler caught: the run ended on a
traceback with no summary."""
def test_the_file_fails_and_the_run_goes_on(self):
import http.client
class Cut:
headers = {}
status = 200
def __enter__(self):
return self
def __exit__(self, *exc):
return False
def read(self, *_args):
raise http.client.IncompleteRead(b"abc", 10)
entry = {"dest": "a.bin", "repo_path": "bios/a.bin", "size": 10,
"sha1": "0" * 40, "sha256": "0" * 64}
with unittest.mock.patch.object(install.urllib.request, "urlopen",
lambda *a, **k: Cut()), \
unittest.mock.patch.object(install, "_retry_delay", return_value=0):
dest, ok, reason = install._download_one(entry, Path(_tempdir(self)))
self.assertFalse(ok)
self.assertIn("IncompleteRead", reason)
if __name__ == "__main__":
unittest.main()