Skip to content

Commit 3a7e09d

Browse files
linesightclaude
andcommitted
Retry download on truncated response in download_cef.py
response.read() silently returns b"" on a dropped connection, causing the loop to exit normally with a partial file that then fails SHA1. Fix: after each attempt compare downloaded bytes against Content-Length; treat a short read as failure, delete the partial file, and retry up to 3 times (configurable via max_retries). Also retries on connection errors. The error message now names the root cause instead of surfacing only the SHA1 mismatch. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 140859a commit 3a7e09d

1 file changed

Lines changed: 46 additions & 27 deletions

File tree

tools/download_cef.py

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -195,41 +195,60 @@ def find_in_index(cef_version, cef_postfix2):
195195
sys.exit(1)
196196

197197

198-
def download(url, dest_path, expected_size=0):
199-
"""Download url to dest_path with a progress bar."""
198+
def download(url, dest_path, expected_size=0, max_retries=3):
199+
"""Download url to dest_path with a progress bar, retrying on failure."""
200200
try:
201201
from urllib.request import urlopen
202202
except ImportError:
203203
from urllib2 import urlopen
204204

205205
log("Downloading: {}".format(url))
206-
try:
207-
response = urlopen(url, timeout=120)
208-
except Exception as exc:
209-
log("ERROR: Download failed: {}".format(exc))
210-
sys.exit(1)
211-
212-
total = int(response.headers.get("Content-Length") or expected_size or 0)
213-
downloaded = 0
214-
chunk_size = 1024 * 1024 # 1 MB
206+
for attempt in range(1, max_retries + 1):
207+
if attempt > 1:
208+
log("Retry {}/{}: {}".format(attempt, max_retries, url))
209+
try:
210+
response = urlopen(url, timeout=120)
211+
except Exception as exc:
212+
if attempt == max_retries:
213+
log("ERROR: Download failed: {}".format(exc))
214+
sys.exit(1)
215+
log("WARNING: Connection error (attempt {}): {}".format(attempt, exc))
216+
continue
215217

216-
try:
217-
with open(dest_path, "wb") as fp:
218-
while True:
219-
chunk = response.read(chunk_size)
220-
if not chunk:
221-
break
222-
fp.write(chunk)
223-
downloaded += len(chunk)
224-
_print_progress(downloaded, total)
225-
except Exception as exc:
226-
if os.path.isfile(dest_path):
227-
os.remove(dest_path)
228-
log("\nERROR: Download failed: {}".format(exc))
229-
sys.exit(1)
218+
total = int(response.headers.get("Content-Length") or expected_size or 0)
219+
downloaded = 0
220+
chunk_size = 1024 * 1024 # 1 MB
221+
error = None
222+
223+
try:
224+
with open(dest_path, "wb") as fp:
225+
while True:
226+
chunk = response.read(chunk_size)
227+
if not chunk:
228+
break
229+
fp.write(chunk)
230+
downloaded += len(chunk)
231+
_print_progress(downloaded, total)
232+
except Exception as exc:
233+
error = exc
234+
235+
print() # end progress line
236+
237+
if error is None and total and downloaded < total:
238+
error = "short read: got {:.1f} MB of {:.1f} MB".format(
239+
downloaded / (1024 * 1024), total / (1024 * 1024))
240+
241+
if error is not None:
242+
if os.path.isfile(dest_path):
243+
os.remove(dest_path)
244+
if attempt == max_retries:
245+
log("ERROR: Download failed: {}".format(error))
246+
sys.exit(1)
247+
log("WARNING: Download incomplete (attempt {}): {}".format(attempt, error))
248+
continue
230249

231-
print() # end progress line
232-
log("Saved: {}".format(os.path.basename(dest_path)))
250+
log("Saved: {}".format(os.path.basename(dest_path)))
251+
return
233252

234253

235254
def _print_progress(downloaded, total):

0 commit comments

Comments
 (0)