@@ -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
9893def 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
111104def 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: {}\n Calculated checksum: {}' .format (checksum , calc_checksum .hexdigest ()))
204188 if not size_ok :
205189 free_space = sizeof_fmt (diskspace ())
206190 log (4 , 'Expected filesize: {}\n Real filesize: {}\n Remaining 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