Skip to content

Commit 7eb3a46

Browse files
update http download logic
1 parent 6076c8d commit 7eb3a46

File tree

1 file changed

+32
-49
lines changed

1 file changed

+32
-49
lines changed

lib/inputstreamhelper/utils.py

Lines changed: 32 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -72,41 +72,34 @@ def download_path(url):
7272

7373
return os.path.join(temp_path(), filename)
7474

75-
76-
77-
def _http_request(url, headers=None, time_out=10):
78-
"""Perform an HTTP request and return the response and content."""
79-
headers = headers or {}
80-
81-
log(0, 'Request URL: {url}', url=url)
82-
request = Request(url, headers=headers)
83-
75+
def _http_request(url, headers=None, time_out=30):
76+
"""Make a robust HTTP request handling redirections."""
8477
try:
85-
with urlopen(request, timeout=time_out) as response:
86-
log(0, 'Response code: {code}', code=response.getcode())
87-
if 400 <= response.getcode() < 600:
88-
raise HTTPError(url, response.getcode(), f'HTTP {response.getcode()} Error for url: {url}', response.headers, None)
89-
# Read the content inside the `with` block
90-
content = response.read()
91-
return response, content
78+
with urlopen(url, timeout=time_out) as response:
79+
if response.status in [301, 302, 303, 307, 308]: # Handle redirections
80+
new_url = response.getheader('Location')
81+
log(1, f"Redirecting to {new_url}")
82+
return _http_request(new_url, time_out)
83+
return response
9284
except (HTTPError, URLError) as err:
9385
log(2, 'Download failed with error {}'.format(err))
9486
if yesno_dialog(localize(30004), '{line1}\n{line2}'.format(line1=localize(30063), line2=localize(30065))): # Internet down, try again?
9587
return _http_request(url, headers, time_out)
9688
return None
89+
except timeout as e:
90+
log(2, f"HTTP request timed out: {e}")
91+
return None
9792

9893
def http_get(url):
99-
"""Perform an HTTP GET request and return content."""
100-
response, content = _http_request(url)
101-
if response is None or content is None:
94+
"""Perform an HTTP GET request and return content"""
95+
req = _http_request(url)
96+
if req is None:
10297
return None
10398

104-
try:
105-
decoded_content = content.decode("utf-8")
106-
return decoded_content
107-
except UnicodeDecodeError as error:
108-
log(2, 'Failed to decode content. Error: {error}', error=str(error))
109-
return None
99+
content = req.read()
100+
# NOTE: Do not log reponse (as could be large)
101+
# log(0, 'Response: {response}', response=content)
102+
return content.decode("utf-8")
110103

111104
def http_head(url):
112105
"""Perform an HTTP HEAD request and return status code."""
@@ -140,10 +133,13 @@ def http_download(url, message=None, checksum=None, hash_alg='sha1', dl_size=Non
140133
if not message: # display "downloading [filename]"
141134
message = localize(30015, filename=filename) # Downloading file
142135

143-
total_length = int(req.info().get('content-length'))
136+
total_length = int(req.info().get('content-length', 0))
137+
if total_length == 0:
138+
log(2, 'No content-length header available, download may not progress as expected.')
139+
144140
if dl_size and dl_size != total_length:
145141
log(2, 'The given file size does not match the request!')
146-
dl_size = total_length # Otherwise size check at end would fail even if dl succeeded
142+
dl_size = total_length
147143

148144
if background:
149145
progress = bg_progress_dialog()
@@ -153,38 +149,27 @@ def http_download(url, message=None, checksum=None, hash_alg='sha1', dl_size=Non
153149

154150
starttime = time()
155151
chunk_size = 32 * 1024
152+
size = 0
156153
with open(compat_path(dl_path), 'wb') as image:
157-
size = 0
158-
while size < total_length:
159-
try:
160-
chunk = req.read(chunk_size)
161-
except (timeout, SSLError):
162-
req.close()
163-
if not yesno_dialog(localize(30004), '{line1}\n{line2}'.format(line1=localize(30064),
164-
line2=localize(30065))): # Could not finish dl. Try again?
165-
progress.close()
166-
return False
167-
168-
headers = {'Range': 'bytes={}-{}'.format(size, total_length)}
169-
req = _http_request(url, headers=headers)
170-
if req is None:
171-
return None
172-
continue
154+
while True:
155+
chunk = req.read(chunk_size)
156+
if not chunk:
157+
break
173158

174159
image.write(chunk)
175160
if checksum:
176161
calc_checksum.update(chunk)
177162
size += len(chunk)
178-
percent = int(round(size * 100 / total_length))
163+
percent = int(round(size * 100 / total_length)) if total_length > 0 else 0
179164
if not background and progress.iscanceled():
180165
progress.close()
181166
req.close()
182167
return False
183-
if time() - starttime > 5:
168+
if time() - starttime > 5 and size > 0:
184169
time_left = int(round((total_length - size) * (time() - starttime) / size))
185170
prog_message = '{line1}\n{line2}'.format(
186171
line1=message,
187-
line2=localize(30058, mins=time_left // 60, secs=time_left % 60)) # Time remaining
172+
line2=localize(30058, mins=time_left // 60, secs=time_left % 60))
188173
else:
189174
prog_message = message
190175

@@ -197,15 +182,13 @@ def http_download(url, message=None, checksum=None, hash_alg='sha1', dl_size=Non
197182
size_ok = (not dl_size or stat_file(dl_path).st_size() == dl_size)
198183

199184
if not all((checksum_ok, size_ok)):
200-
free_space = sizeof_fmt(diskspace())
201185
log(4, 'Something may be wrong with the downloaded file.')
202186
if not checksum_ok:
203187
log(4, 'Provided checksum: {}\nCalculated checksum: {}'.format(checksum, calc_checksum.hexdigest()))
204188
if not size_ok:
205189
free_space = sizeof_fmt(diskspace())
206190
log(4, 'Expected filesize: {}\nReal filesize: {}\nRemaining diskspace: {}'.format(dl_size, stat_file(dl_path).st_size(), free_space))
207-
208-
if yesno_dialog(localize(30003), localize(30070, filename=filename)): # file maybe broken. Continue anyway?
191+
if yesno_dialog(localize(30003), localize(30070, filename=filename)):
209192
log(4, 'Continuing despite possibly corrupt file!')
210193
else:
211194
return False

0 commit comments

Comments
 (0)