| 
						
						
						
					 | 
					 | 
					@ -1,10 +1,11 @@ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import os | 
					 | 
					 | 
					 | 
					import os | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import time | 
					 | 
					 | 
					 | 
					import time | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import threading | 
					 | 
					 | 
					 | 
					import threading | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import pycurl | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					from hashlib import sha256 | 
					 | 
					 | 
					 | 
					from hashlib import sha256 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					from io import BytesIO | 
					 | 
					 | 
					 | 
					from urllib3 import PoolManager, Retry | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					from urllib3.util import Timeout | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					from tenacity import retry, wait_random_exponential, stop_after_attempt | 
					 | 
					 | 
					 | 
					from tenacity import retry, wait_random_exponential, stop_after_attempt | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					from openpilot.common.file_helpers import atomic_write_in_dir | 
					 | 
					 | 
					 | 
					from openpilot.common.file_helpers import atomic_write_in_dir | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					from openpilot.system.hardware.hw import Paths | 
					 | 
					 | 
					 | 
					from openpilot.system.hardware.hw import Paths | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#  Cache chunk size | 
					 | 
					 | 
					 | 
					#  Cache chunk size | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -35,13 +36,14 @@ class URLFile: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if cache is not None: | 
					 | 
					 | 
					 | 
					    if cache is not None: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      self._force_download = not cache | 
					 | 
					 | 
					 | 
					      self._force_download = not cache | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    try: | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      self._curl = self._tlocal.curl | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    except AttributeError: | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      self._curl = self._tlocal.curl = pycurl.Curl() | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if not self._force_download: | 
					 | 
					 | 
					 | 
					    if not self._force_download: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      os.makedirs(Paths.download_cache_root(), exist_ok=True) | 
					 | 
					 | 
					 | 
					      os.makedirs(Paths.download_cache_root(), exist_ok=True) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    try: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      self._http_client = URLFile._tlocal.http_client | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    except AttributeError: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      self._http_client = URLFile._tlocal.http_client = PoolManager() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  def __enter__(self): | 
					 | 
					 | 
					 | 
					  def __enter__(self): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return self | 
					 | 
					 | 
					 | 
					    return self | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -53,17 +55,10 @@ class URLFile: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  @retry(wait=wait_random_exponential(multiplier=1, max=5), stop=stop_after_attempt(3), reraise=True) | 
					 | 
					 | 
					 | 
					  @retry(wait=wait_random_exponential(multiplier=1, max=5), stop=stop_after_attempt(3), reraise=True) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  def get_length_online(self): | 
					 | 
					 | 
					 | 
					  def get_length_online(self): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    c = self._curl | 
					 | 
					 | 
					 | 
					    timeout = Timeout(connect=50.0, read=500.0) | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    c.reset() | 
					 | 
					 | 
					 | 
					    response = self._http_client.request('HEAD', self._url, timeout=timeout, preload_content=False) | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    c.setopt(pycurl.NOSIGNAL, 1) | 
					 | 
					 | 
					 | 
					    length = response.headers.get('content-length', 0) | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    c.setopt(pycurl.TIMEOUT_MS, 500000) | 
					 | 
					 | 
					 | 
					    return int(length) | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    c.setopt(pycurl.FOLLOWLOCATION, True) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    c.setopt(pycurl.URL, self._url) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    c.setopt(c.NOBODY, 1) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    c.perform() | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    length = int(c.getinfo(c.CONTENT_LENGTH_DOWNLOAD)) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    c.reset() | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return length | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  def get_length(self): | 
					 | 
					 | 
					 | 
					  def get_length(self): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if self._length is not None: | 
					 | 
					 | 
					 | 
					    if self._length is not None: | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -117,7 +112,7 @@ class URLFile: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  @retry(wait=wait_random_exponential(multiplier=1, max=5), stop=stop_after_attempt(3), reraise=True) | 
					 | 
					 | 
					 | 
					  @retry(wait=wait_random_exponential(multiplier=1, max=5), stop=stop_after_attempt(3), reraise=True) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  def read_aux(self, ll=None): | 
					 | 
					 | 
					 | 
					  def read_aux(self, ll=None): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    download_range = False | 
					 | 
					 | 
					 | 
					    download_range = False | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    headers = ["Connection: keep-alive"] | 
					 | 
					 | 
					 | 
					    headers = {'Connection': 'keep-alive'} | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if self._pos != 0 or ll is not None: | 
					 | 
					 | 
					 | 
					    if self._pos != 0 or ll is not None: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if ll is None: | 
					 | 
					 | 
					 | 
					      if ll is None: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        end = self.get_length() - 1 | 
					 | 
					 | 
					 | 
					        end = self.get_length() - 1 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -125,50 +120,29 @@ class URLFile: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        end = min(self._pos + ll, self.get_length()) - 1 | 
					 | 
					 | 
					 | 
					        end = min(self._pos + ll, self.get_length()) - 1 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if self._pos >= end: | 
					 | 
					 | 
					 | 
					      if self._pos >= end: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        return b"" | 
					 | 
					 | 
					 | 
					        return b"" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      headers.append(f"Range: bytes={self._pos}-{end}") | 
					 | 
					 | 
					 | 
					      headers['Range'] = f"bytes={self._pos}-{end}" | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      download_range = True | 
					 | 
					 | 
					 | 
					      download_range = True | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    dats = BytesIO() | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    c = self._curl | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    c.setopt(pycurl.URL, self._url) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    c.setopt(pycurl.WRITEDATA, dats) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    c.setopt(pycurl.NOSIGNAL, 1) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    c.setopt(pycurl.TIMEOUT_MS, 500000) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    c.setopt(pycurl.HTTPHEADER, headers) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    c.setopt(pycurl.FOLLOWLOCATION, True) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if self._debug: | 
					 | 
					 | 
					 | 
					    if self._debug: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      print("downloading", self._url) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      def header(x): | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        if b'MISS' in x: | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          print(x.strip()) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      c.setopt(pycurl.HEADERFUNCTION, header) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      def test(debug_type, debug_msg): | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					       print("  debug(%d): %s" % (debug_type, debug_msg.strip())) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      c.setopt(pycurl.VERBOSE, 1) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      c.setopt(pycurl.DEBUGFUNCTION, test) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      t1 = time.time() | 
					 | 
					 | 
					 | 
					      t1 = time.time() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    c.perform() | 
					 | 
					 | 
					 | 
					    timeout = Timeout(connect=50.0, read=500.0) | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    response = self._http_client.request('GET', self._url, timeout=timeout, preload_content=False, headers=headers) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    ret = response.data | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if self._debug: | 
					 | 
					 | 
					 | 
					    if self._debug: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      t2 = time.time() | 
					 | 
					 | 
					 | 
					      t2 = time.time() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if t2 - t1 > 0.1: | 
					 | 
					 | 
					 | 
					      if t2 - t1 > 0.1: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        print(f"get {self._url} {headers!r} {t2 - t1:.f} slow") | 
					 | 
					 | 
					 | 
					        print(f"get {self._url} {headers!r} {t2 - t1:.3f} slow") | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    response_code = c.getinfo(pycurl.RESPONSE_CODE) | 
					 | 
					 | 
					 | 
					    response_code = response.status | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if response_code == 416:  # Requested Range Not Satisfiable | 
					 | 
					 | 
					 | 
					    if response_code == 416:  # Requested Range Not Satisfiable | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      raise URLFileException(f"Error, range out of bounds {response_code} {headers} ({self._url}): {repr(dats.getvalue())[:500]}") | 
					 | 
					 | 
					 | 
					      raise URLFileException(f"Error, range out of bounds {response_code} {headers} ({self._url}): {repr(ret)[:500]}") | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if download_range and response_code != 206:  # Partial Content | 
					 | 
					 | 
					 | 
					    if download_range and response_code != 206:  # Partial Content | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      raise URLFileException(f"Error, requested range but got unexpected response {response_code} {headers} ({self._url}): {repr(dats.getvalue())[:500]}") | 
					 | 
					 | 
					 | 
					      raise URLFileException(f"Error, requested range but got unexpected response {response_code} {headers} ({self._url}): {repr(ret)[:500]}") | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (not download_range) and response_code != 200:  # OK | 
					 | 
					 | 
					 | 
					    if (not download_range) and response_code != 200:  # OK | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      raise URLFileException(f"Error {response_code} {headers} ({self._url}): {repr(dats.getvalue())[:500]}") | 
					 | 
					 | 
					 | 
					      raise URLFileException(f"Error {response_code} {headers} ({self._url}): {repr(ret)[:500]}") | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    ret = dats.getvalue() | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    self._pos += len(ret) | 
					 | 
					 | 
					 | 
					    self._pos += len(ret) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return ret | 
					 | 
					 | 
					 | 
					    return ret | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
					 | 
					
  |