Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 22 additions & 12 deletions src/snowflake/connector/session_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from .vendored import requests
from .vendored.requests import Response, Session
from .vendored.requests.adapters import BaseAdapter, HTTPAdapter
from .vendored.requests.exceptions import InvalidProxyURL
from .vendored.requests.exceptions import InvalidProxyURL, InvalidURL
from .vendored.requests.utils import prepend_scheme_if_needed, select_proxy
from .vendored.urllib3 import PoolManager, Retry
from .vendored.urllib3.poolmanager import ProxyManager
Expand All @@ -23,7 +23,6 @@
if TYPE_CHECKING:
from .vendored.urllib3.connectionpool import HTTPConnectionPool, HTTPSConnectionPool


logger = logging.getLogger(__name__)
REQUESTS_RETRY = 1 # requests library builtin retry

Expand Down Expand Up @@ -60,19 +59,25 @@ def wrapper(self, *args, **kwargs):
class ProxySupportAdapter(HTTPAdapter):
"""This Adapter creates proper headers for Proxy CONNECT messages."""

def get_connection(
self, url: str, proxies: dict | None = None
def get_connection_with_tls_context(
self, request, verify, proxies=None, cert=None
) -> HTTPConnectionPool | HTTPSConnectionPool:
proxy = select_proxy(url, proxies)
parsed_url = urlparse(url)

proxy = select_proxy(request.url, proxies)
try:
host_params, pool_kwargs = self.build_connection_pool_key_attributes(
request,
verify,
cert,
)
except ValueError as e:
raise InvalidURL(e, request=request)
if proxy:
proxy = prepend_scheme_if_needed(proxy, "http")
proxy_url = parse_url(proxy)
if not proxy_url.host:
raise InvalidProxyURL(
"Please check proxy URL. It is malformed"
" and could be missing the host."
"Please check proxy URL. It is malformed "
"and could be missing the host."
)
proxy_manager = self.proxy_manager_for(proxy)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The piece of logic below is the custom one we introduced. Can we override proxy_manager_for instead and avoid isinstance call?

Expand All @@ -85,17 +90,22 @@ def get_connection(
# Note: netloc also keeps user-info (user:pass@host) if present in URL. The driver never sends
# URLs with embedded credentials, so we leave them unhandled — for full support
# we’d need to manually concatenate hostname with optional port and IPv6 brackets.
parsed_url = urlparse(request.url)
proxy_manager.proxy_headers["Host"] = parsed_url.netloc
else:
logger.debug(
f"Unable to set 'Host' to proxy manager of type {type(proxy_manager)} as"
f" it does not have attribute 'proxy_headers'."
)
conn = proxy_manager.connection_from_url(url)

conn = proxy_manager.connection_from_host(
**host_params, pool_kwargs=pool_kwargs
)
else:
# Only scheme should be lower case
url = parsed_url.geturl()
conn = self.poolmanager.connection_from_url(url)
conn = self.poolmanager.connection_from_host(
**host_params, pool_kwargs=pool_kwargs
)

return conn

Expand Down
6 changes: 3 additions & 3 deletions src/snowflake/connector/vendored/requests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@

import warnings

import urllib3
from .. import urllib3

from .exceptions import RequestsDependencyWarning

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Below in this file we have 2 more imports from urllib3 (line 131 and 143) - did we deliberately leave those unchanged?

Expand Down Expand Up @@ -128,7 +128,7 @@ def _check_cryptography(cryptography_version):
ssl = None

if not getattr(ssl, "HAS_SNI", False):
from urllib3.contrib import pyopenssl
from ..urllib3.contrib import pyopenssl

pyopenssl.inject_into_urllib3()

Expand All @@ -140,7 +140,7 @@ def _check_cryptography(cryptography_version):
pass

# urllib3's DependencyWarnings should be silenced.
from urllib3.exceptions import DependencyWarning
from ..urllib3.exceptions import DependencyWarning

warnings.simplefilter("ignore", DependencyWarning)

Expand Down
24 changes: 12 additions & 12 deletions src/snowflake/connector/vendored/requests/adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,22 @@
import typing
import warnings

from urllib3.exceptions import ClosedPoolError, ConnectTimeoutError
from urllib3.exceptions import HTTPError as _HTTPError
from urllib3.exceptions import InvalidHeader as _InvalidHeader
from urllib3.exceptions import (
from ..urllib3.exceptions import ClosedPoolError, ConnectTimeoutError
from ..urllib3.exceptions import HTTPError as _HTTPError
from ..urllib3.exceptions import InvalidHeader as _InvalidHeader
from ..urllib3.exceptions import (
LocationValueError,
MaxRetryError,
NewConnectionError,
ProtocolError,
)
from urllib3.exceptions import ProxyError as _ProxyError
from urllib3.exceptions import ReadTimeoutError, ResponseError
from urllib3.exceptions import SSLError as _SSLError
from urllib3.poolmanager import PoolManager, proxy_from_url
from urllib3.util import Timeout as TimeoutSauce
from urllib3.util import parse_url
from urllib3.util.retry import Retry
from ..urllib3.exceptions import ProxyError as _ProxyError
from ..urllib3.exceptions import ReadTimeoutError, ResponseError
from ..urllib3.exceptions import SSLError as _SSLError
from ..urllib3.poolmanager import PoolManager, proxy_from_url
from ..urllib3.util import Timeout as TimeoutSauce
from ..urllib3.util import parse_url
from ..urllib3.util.retry import Retry

from .auth import _basic_auth_str
from .compat import basestring, urlparse
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In line 59 we have from urllib3.contrib.socks import SOCKSProxyManager - was it intentional?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also have:

  1. one in line 24 of help.py
  2. one in contrib/emscripten/init.py -> import urllib3.connection

Expand Down Expand Up @@ -56,7 +56,7 @@
)

try:
from urllib3.contrib.socks import SOCKSProxyManager
from ..urllib3.contrib.socks import SOCKSProxyManager
except ImportError:

def SOCKSProxyManager(*args, **kwargs):
Expand Down
2 changes: 1 addition & 1 deletion src/snowflake/connector/vendored/requests/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# -------
# urllib3
# -------
from urllib3 import __version__ as urllib3_version
from ..urllib3 import __version__ as urllib3_version

# Detect which major version of urllib3 is being used.
try:
Expand Down
2 changes: 1 addition & 1 deletion src/snowflake/connector/vendored/requests/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

This module contains the set of Requests' exceptions.
"""
from urllib3.exceptions import HTTPError as BaseHTTPError
from ..urllib3.exceptions import HTTPError as BaseHTTPError

from .compat import JSONDecodeError as CompatJSONDecodeError

Expand Down
4 changes: 2 additions & 2 deletions src/snowflake/connector/vendored/requests/help.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import sys

import idna
import urllib3
from .. import urllib3

from . import __version__ as requests_version

Expand All @@ -21,7 +21,7 @@
chardet = None

try:
from urllib3.contrib import pyopenssl
from ..urllib3.contrib import pyopenssl
except ImportError:
pyopenssl = None
OpenSSL = None
Expand Down
8 changes: 4 additions & 4 deletions src/snowflake/connector/vendored/requests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@
import encodings.idna # noqa: F401
from io import UnsupportedOperation

from urllib3.exceptions import (
from ..urllib3.exceptions import (
DecodeError,
LocationParseError,
ProtocolError,
ReadTimeoutError,
SSLError,
)
from urllib3.fields import RequestField
from urllib3.filepost import encode_multipart_formdata
from urllib3.util import parse_url
from ..urllib3.fields import RequestField
from ..urllib3.filepost import encode_multipart_formdata
from ..urllib3.util import parse_url

from ._internal_utils import to_native_string, unicode_is_ascii
from .auth import HTTPBasicAuth
Expand Down
2 changes: 1 addition & 1 deletion src/snowflake/connector/vendored/requests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import zipfile
from collections import OrderedDict

from urllib3.util import make_headers, parse_url
from ..urllib3.util import make_headers, parse_url

from . import certs
from .__version__ import __version__
Expand Down
13 changes: 11 additions & 2 deletions src/snowflake/connector/vendored/urllib3/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,13 +338,22 @@ def connect(self) -> None:
if self._has_connected_to_proxy:
self.proxy_is_verified = False

# See issue for more context: https://github.com/urllib3/urllib3/issues/1878
# the maintainers know that this issue can be resolve using the change below but
# they have not merged this change because they need to root-cause it. See
# comment: https://github.com/urllib3/urllib3/issues/1878#issuecomment-641548977
# adding the fix in our vendored code so our users get unblocked
def _is_closed_patch_for_invalid_socket_descriptor(self):
if getattr(self.sock, "fileno", lambda _: None)() == -1:
return True

@property
def is_closed(self) -> bool:
return self.sock is None
return self.sock is None or self._is_closed_patch_for_invalid_socket_descriptor()

@property
def is_connected(self) -> bool:
if self.sock is None:
if self.sock is None or self._is_closed_patch_for_invalid_socket_descriptor():
return False
return not wait_for_read(self.sock, timeout=0.0)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

import urllib3.connection
import snowflake.connector.vendored.urllib3.connection

from ...connectionpool import HTTPConnectionPool, HTTPSConnectionPool
from .connection import EmscriptenHTTPConnection, EmscriptenHTTPSConnection
Expand All @@ -12,5 +12,5 @@ def inject_into_urllib3() -> None:
# if it isn't ignored
HTTPConnectionPool.ConnectionCls = EmscriptenHTTPConnection
HTTPSConnectionPool.ConnectionCls = EmscriptenHTTPSConnection
urllib3.connection.HTTPConnection = EmscriptenHTTPConnection # type: ignore[misc,assignment]
urllib3.connection.HTTPSConnection = EmscriptenHTTPSConnection # type: ignore[misc,assignment]
snowflake.connector.vendored.urllib3.connection.HTTPConnection = EmscriptenHTTPConnection # type: ignore[misc,assignment]
snowflake.connector.vendored.urllib3.connection.HTTPSConnection = EmscriptenHTTPSConnection # type: ignore[misc,assignment]
3 changes: 3 additions & 0 deletions test/unit/test_proxies.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ def __init__(self):
def connection_from_url(self, url):
pass

def connection_from_host(self, host, *args, **kwargs):
pass

def mock_proxy_manager_for_url_no_header(*args, **kwargs):
return MockSOCKSProxyManager()

Expand Down
Loading