|
|
@ -6,6 +6,7 @@ from hashlib import sha256 |
|
|
|
from urllib3 import PoolManager |
|
|
|
from urllib3 import PoolManager |
|
|
|
from urllib3.util import Timeout |
|
|
|
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 typing import Optional |
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
@ -25,9 +26,12 @@ class URLFileException(Exception): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class URLFile: |
|
|
|
class URLFile: |
|
|
|
_tlocal = threading.local() |
|
|
|
_pid: Optional[int] = None |
|
|
|
|
|
|
|
_pool_manager: Optional[PoolManager] = None |
|
|
|
|
|
|
|
_pool_manager_lock = threading.Lock() |
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, url, debug=False, cache=None): |
|
|
|
def __init__(self, url, debug=False, cache=None): |
|
|
|
|
|
|
|
self._pool_manager = None |
|
|
|
self._url = url |
|
|
|
self._url = url |
|
|
|
self._pos = 0 |
|
|
|
self._pos = 0 |
|
|
|
self._length = None |
|
|
|
self._length = None |
|
|
@ -41,11 +45,6 @@ class URLFile: |
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
@ -55,10 +54,20 @@ class URLFile: |
|
|
|
self._local_file.close() |
|
|
|
self._local_file.close() |
|
|
|
self._local_file = None |
|
|
|
self._local_file = None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _http_client(self) -> PoolManager: |
|
|
|
|
|
|
|
if self._pool_manager is None: |
|
|
|
|
|
|
|
pid = os.getpid() |
|
|
|
|
|
|
|
with URLFile._pool_manager_lock: |
|
|
|
|
|
|
|
if URLFile._pid != pid or URLFile._pool_manager is None: # unsafe to share after fork |
|
|
|
|
|
|
|
URLFile._pid = pid |
|
|
|
|
|
|
|
URLFile._pool_manager = PoolManager(num_pools=10, maxsize=10) |
|
|
|
|
|
|
|
self._pool_manager = URLFile._pool_manager |
|
|
|
|
|
|
|
return self._pool_manager |
|
|
|
|
|
|
|
|
|
|
|
@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): |
|
|
|
timeout = Timeout(connect=50.0, read=500.0) |
|
|
|
timeout = Timeout(connect=50.0, read=500.0) |
|
|
|
response = self._http_client.request('HEAD', self._url, timeout=timeout, preload_content=False) |
|
|
|
response = self._http_client().request('HEAD', self._url, timeout=timeout, preload_content=False) |
|
|
|
if not (200 <= response.status <= 299): |
|
|
|
if not (200 <= response.status <= 299): |
|
|
|
return -1 |
|
|
|
return -1 |
|
|
|
length = response.headers.get('content-length', 0) |
|
|
|
length = response.headers.get('content-length', 0) |
|
|
@ -131,7 +140,7 @@ class URLFile: |
|
|
|
t1 = time.time() |
|
|
|
t1 = time.time() |
|
|
|
|
|
|
|
|
|
|
|
timeout = Timeout(connect=50.0, read=500.0) |
|
|
|
timeout = Timeout(connect=50.0, read=500.0) |
|
|
|
response = self._http_client.request('GET', self._url, timeout=timeout, preload_content=False, headers=headers) |
|
|
|
response = self._http_client().request('GET', self._url, timeout=timeout, preload_content=False, headers=headers) |
|
|
|
ret = response.data |
|
|
|
ret = response.data |
|
|
|
|
|
|
|
|
|
|
|
if self._debug: |
|
|
|
if self._debug: |
|
|
|