Skip to content

Commit 2e84e1b

Browse files
SNOW-2176524 bump up vendored urllib3 to 2.5.0 and requests to v2.32.5
1 parent 53e0165 commit 2e84e1b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+6965
-6575
lines changed

MANIFEST.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ include LICENSE.txt
44
include NOTICE
55
include pyproject.toml
66
include src/snowflake/connector/nanoarrow_cpp/ArrowIterator/LICENSE.txt
7-
recursive-include src/snowflake/connector py.typed *.py *.pyx
7+
recursive-include src/snowflake/connector py.typed *.py *.pyx *.js
88
recursive-include src/snowflake/connector/vendored LICENSE*
99

1010
recursive-include src/snowflake/connector/nanoarrow_cpp *.cpp *.hpp

src/snowflake/connector/vendored/requests/__init__.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@
4040

4141
import warnings
4242

43-
from .. import urllib3
43+
import urllib3
44+
4445
from .exceptions import RequestsDependencyWarning
4546

4647
try:
@@ -82,7 +83,11 @@ def check_compatibility(urllib3_version, chardet_version, charset_normalizer_ver
8283
# charset_normalizer >= 2.0.0 < 4.0.0
8384
assert (2, 0, 0) <= (major, minor, patch) < (4, 0, 0)
8485
else:
85-
raise Exception("You need either charset_normalizer or chardet installed")
86+
warnings.warn(
87+
"Unable to find acceptable character detection dependency "
88+
"(chardet or charset_normalizer).",
89+
RequestsDependencyWarning,
90+
)
8691

8792

8893
def _check_cryptography(cryptography_version):
@@ -113,27 +118,37 @@ def _check_cryptography(cryptography_version):
113118
RequestsDependencyWarning,
114119
)
115120

116-
# Attempt to enable urllib3's SNI support, if possible
121+
# Attempt to enable urllib3's fallback for SNI support
122+
# if the standard library doesn't support SNI or the
123+
# 'ssl' library isn't available.
117124
try:
118-
from ..urllib3.contrib import pyopenssl
119-
pyopenssl.inject_into_urllib3()
125+
try:
126+
import ssl
127+
except ImportError:
128+
ssl = None
129+
130+
if not getattr(ssl, "HAS_SNI", False):
131+
from urllib3.contrib import pyopenssl
132+
133+
pyopenssl.inject_into_urllib3()
134+
135+
# Check cryptography version
136+
from cryptography import __version__ as cryptography_version
120137

121-
# Check cryptography version
122-
from cryptography import __version__ as cryptography_version
123-
_check_cryptography(cryptography_version)
138+
_check_cryptography(cryptography_version)
124139
except ImportError:
125140
pass
126141

127142
# urllib3's DependencyWarnings should be silenced.
128-
from ..urllib3.exceptions import DependencyWarning
143+
from urllib3.exceptions import DependencyWarning
129144

130145
warnings.simplefilter("ignore", DependencyWarning)
131146

132147
# Set default logging handler to avoid "No handler found" warnings.
133148
import logging
134149
from logging import NullHandler
135150

136-
from . import utils
151+
from . import packages, utils
137152
from .__version__ import (
138153
__author__,
139154
__author_email__,

src/snowflake/connector/vendored/requests/__version__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
__title__ = "requests"
66
__description__ = "Python HTTP for Humans."
77
__url__ = "https://requests.readthedocs.io"
8-
__version__ = "2.31.0"
9-
__build__ = 0x023100
8+
__version__ = "2.32.5"
9+
__build__ = 0x023205
1010
__author__ = "Kenneth Reitz"
1111
__author_email__ = "[email protected]"
12-
__license__ = "Apache 2.0"
12+
__license__ = "Apache-2.0"
1313
__copyright__ = "Copyright Kenneth Reitz"
1414
__cake__ = "\u2728 \U0001f370 \u2728"

src/snowflake/connector/vendored/requests/adapters.py

Lines changed: 174 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,26 @@
88

99
import os.path
1010
import socket # noqa: F401
11+
import typing
12+
import warnings
1113

12-
from ..urllib3.exceptions import ClosedPoolError, ConnectTimeoutError
13-
from ..urllib3.exceptions import HTTPError as _HTTPError
14-
from ..urllib3.exceptions import InvalidHeader as _InvalidHeader
15-
from ..urllib3.exceptions import (
14+
from urllib3.exceptions import ClosedPoolError, ConnectTimeoutError
15+
from urllib3.exceptions import HTTPError as _HTTPError
16+
from urllib3.exceptions import InvalidHeader as _InvalidHeader
17+
from urllib3.exceptions import (
1618
LocationValueError,
1719
MaxRetryError,
1820
NewConnectionError,
1921
ProtocolError,
2022
)
21-
from ..urllib3.exceptions import ProxyError as _ProxyError
22-
from ..urllib3.exceptions import ReadTimeoutError, ResponseError
23-
from ..urllib3.exceptions import SSLError as _SSLError
24-
from ..urllib3.poolmanager import PoolManager, proxy_from_url
25-
from ..urllib3.util import Timeout as TimeoutSauce
26-
from ..urllib3.util import parse_url
27-
from ..urllib3.util.retry import Retry
23+
from urllib3.exceptions import ProxyError as _ProxyError
24+
from urllib3.exceptions import ReadTimeoutError, ResponseError
25+
from urllib3.exceptions import SSLError as _SSLError
26+
from urllib3.poolmanager import PoolManager, proxy_from_url
27+
from urllib3.util import Timeout as TimeoutSauce
28+
from urllib3.util import parse_url
29+
from urllib3.util.retry import Retry
30+
2831
from .auth import _basic_auth_str
2932
from .compat import basestring, urlparse
3033
from .cookies import extract_cookies_to_jar
@@ -53,19 +56,60 @@
5356
)
5457

5558
try:
56-
from ..urllib3.contrib.socks import SOCKSProxyManager
59+
from urllib3.contrib.socks import SOCKSProxyManager
5760
except ImportError:
5861

5962
def SOCKSProxyManager(*args, **kwargs):
6063
raise InvalidSchema("Missing dependencies for SOCKS support.")
6164

6265

66+
if typing.TYPE_CHECKING:
67+
from .models import PreparedRequest
68+
69+
6370
DEFAULT_POOLBLOCK = False
6471
DEFAULT_POOLSIZE = 10
6572
DEFAULT_RETRIES = 0
6673
DEFAULT_POOL_TIMEOUT = None
6774

6875

76+
def _urllib3_request_context(
77+
request: "PreparedRequest",
78+
verify: "bool | str | None",
79+
client_cert: "typing.Tuple[str, str] | str | None",
80+
poolmanager: "PoolManager",
81+
) -> "(typing.Dict[str, typing.Any], typing.Dict[str, typing.Any])":
82+
host_params = {}
83+
pool_kwargs = {}
84+
parsed_request_url = urlparse(request.url)
85+
scheme = parsed_request_url.scheme.lower()
86+
port = parsed_request_url.port
87+
88+
cert_reqs = "CERT_REQUIRED"
89+
if verify is False:
90+
cert_reqs = "CERT_NONE"
91+
elif isinstance(verify, str):
92+
if not os.path.isdir(verify):
93+
pool_kwargs["ca_certs"] = verify
94+
else:
95+
pool_kwargs["ca_cert_dir"] = verify
96+
pool_kwargs["cert_reqs"] = cert_reqs
97+
if client_cert is not None:
98+
if isinstance(client_cert, tuple) and len(client_cert) == 2:
99+
pool_kwargs["cert_file"] = client_cert[0]
100+
pool_kwargs["key_file"] = client_cert[1]
101+
else:
102+
# According to our docs, we allow users to specify just the client
103+
# cert path
104+
pool_kwargs["cert_file"] = client_cert
105+
host_params = {
106+
"scheme": scheme,
107+
"host": parsed_request_url.hostname,
108+
"port": port,
109+
}
110+
return host_params, pool_kwargs
111+
112+
69113
class BaseAdapter:
70114
"""The Base Transport Adapter"""
71115

@@ -246,7 +290,6 @@ def cert_verify(self, conn, url, verify, cert):
246290
:param cert: The SSL certificate to verify.
247291
"""
248292
if url.lower().startswith("https") and verify:
249-
250293
cert_loc = None
251294

252295
# Allow self-specified cert location.
@@ -327,15 +370,126 @@ def build_response(self, req, resp):
327370

328371
return response
329372

373+
def build_connection_pool_key_attributes(self, request, verify, cert=None):
374+
"""Build the PoolKey attributes used by urllib3 to return a connection.
375+
376+
This looks at the PreparedRequest, the user-specified verify value,
377+
and the value of the cert parameter to determine what PoolKey values
378+
to use to select a connection from a given urllib3 Connection Pool.
379+
380+
The SSL related pool key arguments are not consistently set. As of
381+
this writing, use the following to determine what keys may be in that
382+
dictionary:
383+
384+
* If ``verify`` is ``True``, ``"ssl_context"`` will be set and will be the
385+
default Requests SSL Context
386+
* If ``verify`` is ``False``, ``"ssl_context"`` will not be set but
387+
``"cert_reqs"`` will be set
388+
* If ``verify`` is a string, (i.e., it is a user-specified trust bundle)
389+
``"ca_certs"`` will be set if the string is not a directory recognized
390+
by :py:func:`os.path.isdir`, otherwise ``"ca_cert_dir"`` will be
391+
set.
392+
* If ``"cert"`` is specified, ``"cert_file"`` will always be set. If
393+
``"cert"`` is a tuple with a second item, ``"key_file"`` will also
394+
be present
395+
396+
To override these settings, one may subclass this class, call this
397+
method and use the above logic to change parameters as desired. For
398+
example, if one wishes to use a custom :py:class:`ssl.SSLContext` one
399+
must both set ``"ssl_context"`` and based on what else they require,
400+
alter the other keys to ensure the desired behaviour.
401+
402+
:param request:
403+
The PreparedReqest being sent over the connection.
404+
:type request:
405+
:class:`~requests.models.PreparedRequest`
406+
:param verify:
407+
Either a boolean, in which case it controls whether
408+
we verify the server's TLS certificate, or a string, in which case it
409+
must be a path to a CA bundle to use.
410+
:param cert:
411+
(optional) Any user-provided SSL certificate for client
412+
authentication (a.k.a., mTLS). This may be a string (i.e., just
413+
the path to a file which holds both certificate and key) or a
414+
tuple of length 2 with the certificate file path and key file
415+
path.
416+
:returns:
417+
A tuple of two dictionaries. The first is the "host parameters"
418+
portion of the Pool Key including scheme, hostname, and port. The
419+
second is a dictionary of SSLContext related parameters.
420+
"""
421+
return _urllib3_request_context(request, verify, cert, self.poolmanager)
422+
423+
def get_connection_with_tls_context(self, request, verify, proxies=None, cert=None):
424+
"""Returns a urllib3 connection for the given request and TLS settings.
425+
This should not be called from user code, and is only exposed for use
426+
when subclassing the :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
427+
428+
:param request:
429+
The :class:`PreparedRequest <PreparedRequest>` object to be sent
430+
over the connection.
431+
:param verify:
432+
Either a boolean, in which case it controls whether we verify the
433+
server's TLS certificate, or a string, in which case it must be a
434+
path to a CA bundle to use.
435+
:param proxies:
436+
(optional) The proxies dictionary to apply to the request.
437+
:param cert:
438+
(optional) Any user-provided SSL certificate to be used for client
439+
authentication (a.k.a., mTLS).
440+
:rtype:
441+
urllib3.ConnectionPool
442+
"""
443+
proxy = select_proxy(request.url, proxies)
444+
try:
445+
host_params, pool_kwargs = self.build_connection_pool_key_attributes(
446+
request,
447+
verify,
448+
cert,
449+
)
450+
except ValueError as e:
451+
raise InvalidURL(e, request=request)
452+
if proxy:
453+
proxy = prepend_scheme_if_needed(proxy, "http")
454+
proxy_url = parse_url(proxy)
455+
if not proxy_url.host:
456+
raise InvalidProxyURL(
457+
"Please check proxy URL. It is malformed "
458+
"and could be missing the host."
459+
)
460+
proxy_manager = self.proxy_manager_for(proxy)
461+
conn = proxy_manager.connection_from_host(
462+
**host_params, pool_kwargs=pool_kwargs
463+
)
464+
else:
465+
# Only scheme should be lower case
466+
conn = self.poolmanager.connection_from_host(
467+
**host_params, pool_kwargs=pool_kwargs
468+
)
469+
470+
return conn
471+
330472
def get_connection(self, url, proxies=None):
331-
"""Returns a urllib3 connection for the given URL. This should not be
473+
"""DEPRECATED: Users should move to `get_connection_with_tls_context`
474+
for all subclasses of HTTPAdapter using Requests>=2.32.2.
475+
476+
Returns a urllib3 connection for the given URL. This should not be
332477
called from user code, and is only exposed for use when subclassing the
333478
:class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
334479
335480
:param url: The URL to connect to.
336481
:param proxies: (optional) A Requests-style dictionary of proxies used on this request.
337482
:rtype: urllib3.ConnectionPool
338483
"""
484+
warnings.warn(
485+
(
486+
"`get_connection` has been deprecated in favor of "
487+
"`get_connection_with_tls_context`. Custom HTTPAdapter subclasses "
488+
"will need to migrate for Requests>=2.32.2. Please see "
489+
"https://github.com/psf/requests/pull/6710 for more details."
490+
),
491+
DeprecationWarning,
492+
)
339493
proxy = select_proxy(url, proxies)
340494

341495
if proxy:
@@ -390,6 +544,9 @@ def request_url(self, request, proxies):
390544
using_socks_proxy = proxy_scheme.startswith("socks")
391545

392546
url = request.path_url
547+
if url.startswith("//"): # Don't confuse urllib3
548+
url = f"/{url.lstrip('/')}"
549+
393550
if is_proxied_http_request and not using_socks_proxy:
394551
url = urldefragauth(request.url)
395552

@@ -450,7 +607,9 @@ def send(
450607
"""
451608

452609
try:
453-
conn = self.get_connection(request.url, proxies)
610+
conn = self.get_connection_with_tls_context(
611+
request, verify, proxies=proxies, cert=cert
612+
)
454613
except LocationValueError as e:
455614
raise InvalidURL(e, request=request)
456615

src/snowflake/connector/vendored/requests/api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def request(method, url, **kwargs):
2525
:param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
2626
:param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload.
2727
``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')``
28-
or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string
28+
or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content_type'`` is a string
2929
defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers
3030
to add for the file.
3131
:param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth.

src/snowflake/connector/vendored/requests/auth.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,6 @@ def handle_401(self, r, **kwargs):
258258
s_auth = r.headers.get("www-authenticate", "")
259259

260260
if "digest" in s_auth.lower() and self._thread_local.num_401_calls < 2:
261-
262261
self._thread_local.num_401_calls += 1
263262
pat = re.compile(r"digest ", flags=re.IGNORECASE)
264263
self._thread_local.chal = parse_dict_header(pat.sub("", s_auth, count=1))

0 commit comments

Comments
 (0)