7
7
8
8
# standard library
9
9
import logging
10
+ import ssl
10
11
import warnings
11
12
from os import getenv
12
13
from pathlib import Path
13
14
14
15
# 3rd party
15
16
import truststore
16
17
from requests import Response , Session
18
+ from requests .adapters import HTTPAdapter
17
19
from requests .exceptions import ConnectionError , HTTPError
18
20
from requests .utils import requote_uri
19
21
from urllib3 .exceptions import InsecureRequestWarning
31
33
# logs
32
34
logger = logging .getLogger (__name__ )
33
35
34
- if str2bool (getenv ("QDT_SSL_USE_SYSTEM_STORES" , False )):
35
- truststore .inject_into_ssl ()
36
- logger .debug ("Option to use native system certificates stores is enabled." )
37
36
if not str2bool (getenv ("QDT_SSL_VERIFY" , True )):
38
37
warnings .filterwarnings ("ignore" , category = InsecureRequestWarning )
39
38
logger .warning (
43
42
"See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings"
44
43
)
45
44
45
+
46
+ # ############################################################################
47
+ # ########## CLASSES #############
48
+ # ################################
49
+
50
+
51
+ class TruststoreAdapter (HTTPAdapter ):
52
+ """Custom HTTP transport adapter made to use local trust store.
53
+
54
+ Source: <https://stackoverflow.com/a/78265028/2556577>
55
+ Documentation: <https://requests.readthedocs.io/en/latest/user/advanced/#transport-adapters>
56
+ """
57
+
58
+ def init_poolmanager (
59
+ self , connections : int , maxsize : int , block : bool = False
60
+ ) -> None :
61
+ """Initializes a urllib3 PoolManager.
62
+
63
+ Args:
64
+ connections (int): number of urllib3 connection pools to cache.
65
+ maxsize (int): maximum number of connections to save in the pool.
66
+ block (bool, optional): Block when no free connections are available.. Defaults to False.
67
+
68
+ """
69
+ ctx = truststore .SSLContext (ssl .PROTOCOL_TLS_CLIENT )
70
+ return super ().init_poolmanager (connections , maxsize , block , ssl_context = ctx )
71
+
72
+
46
73
# ############################################################################
47
74
# ########## FUNCTIONS ###########
48
75
# ################################
@@ -68,8 +95,10 @@ def download_remote_file_to_local(
68
95
content_type (str | None, optional): HTTP content-type. Defaults to None.
69
96
chunk_size (int, optional): size of each chunk to read and write in bytes. \
70
97
Defaults to 8192.
71
- timeout (tuple[int, int], optional): custom timeout (request, response). Defaults to (800, 800).
72
- use_stream (bool, optional): Option to enable/disable streaming download. Defaults to True.
98
+ timeout (tuple[int, int], optional): custom timeout (request, response). \
99
+ Defaults to (800, 800).
100
+ use_stream (bool, optional): Option to enable/disable streaming download. \
101
+ Defaults to True.
73
102
74
103
Returns:
75
104
Path: path to the local file (should be the same as local_file_path)
@@ -93,6 +122,13 @@ def download_remote_file_to_local(
93
122
dl_session .proxies .update (get_proxy_settings ())
94
123
dl_session .verify = str2bool (getenv ("QDT_SSL_VERIFY" , True ))
95
124
125
+ # handle local system certificates store
126
+ if str2bool (getenv ("QDT_SSL_USE_SYSTEM_STORES" , False )):
127
+ logger .debug (
128
+ "Option to use native system certificates stores is enabled."
129
+ )
130
+ dl_session .mount ("https://" , TruststoreAdapter ())
131
+
96
132
with dl_session .get (
97
133
url = requote_uri (remote_url_to_download ), stream = True , timeout = timeout
98
134
) as req :
0 commit comments