Skip to content

SNOW-694457: env-vars-proxy-leaking #2451

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 113 commits into from
Aug 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
113 commits
Select commit Hold shift + click to select a range
11d88e9
SNOW-2183023: Session manager refactored
sfc-gh-fpawlowski Jul 7, 2025
142fb68
Merge branch 'main' into SNOW-2183023-Python-WIF-in-Driver-SDK-boto3-…
sfc-gh-fpawlowski Jul 7, 2025
c375164
SNOW-2183023: Added adapter factory
sfc-gh-fpawlowski Jul 7, 2025
e820b4d
SNOW-2183023: Adapters fixed in tests
sfc-gh-fpawlowski Jul 7, 2025
965786a
SNOW-2183023: Removed boto
sfc-gh-fpawlowski Jul 7, 2025
f8b0944
SNOW-2183023: Removed boto
sfc-gh-fpawlowski Jul 7, 2025
f872fd2
SNOW-2183023: SEssions map access fixed
sfc-gh-fpawlowski Jul 7, 2025
95ace1f
SNOW-2183023: use pooling
sfc-gh-fpawlowski Jul 7, 2025
53087d4
SNOW-2183023: refactored
sfc-gh-fpawlowski Jul 7, 2025
23e338b
SNOW-2183023: fixed fake env
sfc-gh-fpawlowski Jul 7, 2025
3faa942
Revert "SNOW-2183023: refactored"
sfc-gh-fpawlowski Jul 7, 2025
43f7868
Reapply "SNOW-2183023: refactored"
sfc-gh-fpawlowski Jul 7, 2025
2241969
SNOW-2183023: fixed csp_helper
sfc-gh-fpawlowski Jul 7, 2025
4f0785c
Revert "Reapply "SNOW-2183023: refactored""
sfc-gh-fpawlowski Jul 7, 2025
e603c1c
Reapply "Reapply "SNOW-2183023: refactored""
sfc-gh-fpawlowski Jul 7, 2025
fde7556
SNOW-2183023: fixed csp_helper and added boto as dev-dep
sfc-gh-fpawlowski Jul 8, 2025
023f0ae
SNOW-2183023: added arn fetch
sfc-gh-fpawlowski Jul 8, 2025
3fef4e7
Revert "Reapply "Reapply "SNOW-2183023: refactored"""
sfc-gh-fpawlowski Jul 8, 2025
2e23fe3
Reapply "Reapply "Reapply "SNOW-2183023: refactored"""
sfc-gh-fpawlowski Jul 8, 2025
59a3373
SNOW-2183023: fixed missing prepare args
sfc-gh-fpawlowski Jul 8, 2025
093b5b5
SNOW-2183023: fixed missing prepare args
sfc-gh-fpawlowski Jul 8, 2025
219e8c6
SNOW-2183023: fixed patching
sfc-gh-fpawlowski Jul 8, 2025
b486239
SNOW-2183023: fixed tests
sfc-gh-fpawlowski Jul 8, 2025
af2f4a3
SNOW-2183023: fixed tests
sfc-gh-fpawlowski Jul 8, 2025
88ccb83
SNOW-2183023: fixed tests
sfc-gh-fpawlowski Jul 8, 2025
9fbda91
SNOW-2183023: removed http_client for now
sfc-gh-fpawlowski Jul 8, 2025
5e921d8
SNOW-2183023: fixed test proxies
sfc-gh-fpawlowski Jul 8, 2025
a3faefd
SNOW-2183023: fixed user id
sfc-gh-fpawlowski Jul 8, 2025
a2c0295
SNOW-2183023: fixed mock
sfc-gh-fpawlowski Jul 8, 2025
3c6df39
Merge branch 'fpawlowski/Copy/boto-before-merge-wif2' into SNOW-22030…
sfc-gh-fpawlowski Jul 13, 2025
1b2dfd7
Merge branch 'main' into SNOW-2203079-HTTP-traffic-without-intercepti…
sfc-gh-fpawlowski Jul 19, 2025
b663f8b
SNOW-2203079: Added Session Manager to the connection and SFRest
sfc-gh-fpawlowski Jul 24, 2025
c4550c8
Merge branch 'main' into SNOW-2203079-HTTP-traffic-without-intercepti…
sfc-gh-fpawlowski Jul 24, 2025
f25ac57
SNOW-2203079: Added session manager to metadata calls in workload ide…
sfc-gh-fpawlowski Jul 24, 2025
43d0750
SNOW-2203079: Cleaned comments
sfc-gh-fpawlowski Jul 24, 2025
53723c2
SNOW-2203079: Boto revert
sfc-gh-fpawlowski Jul 24, 2025
b40e1df
SNOW-2203079: Keep csp helpers untouched
sfc-gh-fpawlowski Jul 24, 2025
afc5c50
SNOW-2203079: Session manager initialized after connection config setup
sfc-gh-fpawlowski Jul 24, 2025
113a72f
SNOW-2203079: renamed use_Requests_session
sfc-gh-fpawlowski Jul 24, 2025
fc4e4f2
SNOW-2203079: added comments
sfc-gh-fpawlowski Jul 24, 2025
b858e25
SNOW-2203079: Fixed sf rest not having session manager as own - when …
sfc-gh-fpawlowski Jul 24, 2025
771aeeb
SNOW-2203079: Unit tests made work
sfc-gh-fpawlowski Jul 27, 2025
a811807
SNOW-2203079: Added session manager unit tests
sfc-gh-fpawlowski Jul 27, 2025
18fe68f
SNOW-2203079: Made sessions tests mock session_manager
sfc-gh-fpawlowski Jul 27, 2025
6c66cff
Merge branch 'main' into SNOW-2203079-HTTP-traffic-without-intercepti…
sfc-gh-fpawlowski Jul 27, 2025
93516a3
SNOW-2203079: Allow sending through mock adapter for SSL tests
sfc-gh-fpawlowski Jul 27, 2025
c1f0921
SNOW-2203079: OCSP and Telemetry refactored
sfc-gh-fpawlowski Jul 27, 2025
1d25aaf
SNOW-2203079: timeout_sec renamed. Added todos for connection_diagnos…
sfc-gh-fpawlowski Jul 27, 2025
7a0d778
SNOW-2203079: added helpers methods
sfc-gh-fpawlowski Jul 27, 2025
2ba7a06
SNOW-2203079: Extracted mixin
sfc-gh-fpawlowski Jul 27, 2025
eb1a8ab
SNOW-2203079: Storage client to session_manager
sfc-gh-fpawlowski Jul 27, 2025
ee5e2c9
SNOW-2203079: fixed tests
sfc-gh-fpawlowski Jul 27, 2025
8218746
SNOW-2203079: added result_batch support
sfc-gh-fpawlowski Jul 27, 2025
5cc0628
SNOW-2203079: fixed tests
sfc-gh-fpawlowski Jul 27, 2025
e74c40a
SNOW-2203079: Make Session Manager pickleable
sfc-gh-fpawlowski Jul 28, 2025
66b7008
SNOW-2203079: Added auth todos as security validation could prolong P…
sfc-gh-fpawlowski Jul 28, 2025
954a890
Revert "SNOW-2203079: Make Session Manager pickleable"
sfc-gh-fpawlowski Jul 28, 2025
0fb8e5b
Revert "SNOW-2203079: added result_batch support"
sfc-gh-fpawlowski Jul 28, 2025
e168e02
SNOW-2203079: Batch approach fixed - only when connection supplied do…
sfc-gh-fpawlowski Jul 28, 2025
34efeda
SNOW-2203079: Cleaned up comments and methods calls
sfc-gh-fpawlowski Jul 28, 2025
04eb0c5
SNOW-2203079: Cleaned up comments and methods calls
sfc-gh-fpawlowski Jul 28, 2025
7b06573
SNOW-2203079: Fix in old approach - this way we will only download us…
sfc-gh-fpawlowski Jul 28, 2025
251c50b
Revert "Revert "SNOW-2203079: added result_batch support""
sfc-gh-fpawlowski Jul 28, 2025
8d24676
Revert "Revert "SNOW-2203079: Make Session Manager pickleable""
sfc-gh-fpawlowski Jul 28, 2025
40cf50e
SNOW-2203079: Batch sessionManager is cloned to isolate sessions
sfc-gh-fpawlowski Jul 28, 2025
02c80ce
SNOW-2203079: OCSP migrated missing path
sfc-gh-fpawlowski Jul 28, 2025
00526c7
SNOW-2203079: Connection diagnostics added
sfc-gh-fpawlowski Jul 28, 2025
90dde31
SNOW-2203079: Connection diagnostics added
sfc-gh-fpawlowski Jul 28, 2025
0104495
SNOW-2203079: fix ocsp
sfc-gh-fpawlowski Jul 28, 2025
80605be
Merge branch 'main' into SNOW-2203079-HTTP-traffic-without-intercepti…
sfc-gh-fpawlowski Jul 28, 2025
62507a2
Snow-2203079: http config, no cloning and ocsp propagation fail-safe …
sfc-gh-fpawlowski Aug 4, 2025
fc93f47
Merge branch 'main' into SNOW-2203079-HTTP-traffic-without-intercepti…
sfc-gh-fpawlowski Aug 4, 2025
05e7077
SNOW-694457: env-vars-proxy-leaking cherry-pick
sfc-gh-fpawlowski Aug 4, 2025
aed91ff
SNOW-694457: cherrypick from Kerberos branch - wiremock fixtures refa…
sfc-gh-fpawlowski Aug 5, 2025
6341156
SNOW-694457: fixed hostname for proxy CONNECT messages without port. …
sfc-gh-fpawlowski Aug 5, 2025
f656b40
SNOW-694457: working tests for proxy flow
sfc-gh-fpawlowski Aug 5, 2025
e9f2e71
SNOW-694457: wiremock fixtures refactor
sfc-gh-fpawlowski Aug 5, 2025
b9cf0a7
SNOW-694457: wiremock fixtures refactor
sfc-gh-fpawlowski Aug 5, 2025
fd44be6
SNOW-694457: wiremock fixtures refactor
sfc-gh-fpawlowski Aug 5, 2025
6ba4020
SNOW-694457: large request tests
sfc-gh-fpawlowski Aug 5, 2025
3d17bb9
SNOW-694457: large request tests
sfc-gh-fpawlowski Aug 5, 2025
dbfc580
SNOW-694457: add mapping with default params
sfc-gh-fpawlowski Aug 6, 2025
262ce15
SNOW-694457: fix old test
sfc-gh-fpawlowski Aug 6, 2025
a512f74
SNOW-694457: working simple check with headers
sfc-gh-fpawlowski Aug 6, 2025
75b692c
SNOW-694457: working simple check with headers
sfc-gh-fpawlowski Aug 6, 2025
9e9a780
SNOW-694457: test oauth with headers
sfc-gh-fpawlowski Aug 6, 2025
db2f37a
SNOW-694457: tests to ensure support for env vars - detected that OAu…
sfc-gh-fpawlowski Aug 6, 2025
837f4e6
SNOW-694457: Reverted tests conflicting with the web browser. Finished
sfc-gh-fpawlowski Aug 6, 2025
d01e28a
Merge branch 'main' into SNOW-2203079-HTTP-traffic-without-intercepti…
sfc-gh-mmishchenko Aug 6, 2025
854a89b
Merge branch 'SNOW-2203079-HTTP-traffic-without-interception-with-ada…
sfc-gh-fpawlowski Aug 6, 2025
8ebdc95
Merge branch 'main' into SNOW-2203079-HTTP-traffic-without-intercepti…
sfc-gh-fpawlowski Aug 7, 2025
f3bf98e
Merge branch 'SNOW-2203079-HTTP-traffic-without-interception-with-ada…
sfc-gh-fpawlowski Aug 7, 2025
dc40ec2
Merge branch 'main' into SNOW-2203079-HTTP-traffic-without-intercepti…
sfc-gh-fpawlowski Aug 11, 2025
2943fcd
NO-SNOW: add precommit checks on legacy native http calls (#2464)
sfc-gh-fpawlowski Aug 12, 2025
f2c4234
Merge branch 'main' into SNOW-2203079-HTTP-traffic-without-intercepti…
sfc-gh-fpawlowski Aug 12, 2025
d820b44
SNOW-2203079: Made mixin abstract
sfc-gh-fpawlowski Aug 12, 2025
8b6ea81
Merge branch 'SNOW-2203079-HTTP-traffic-without-interception-with-ada…
sfc-gh-fpawlowski Aug 12, 2025
afd73d8
Merge branch 'main' into SNOW-2203079-HTTP-traffic-without-intercepti…
sfc-gh-fpawlowski Aug 12, 2025
03865c0
Merge branch 'SNOW-2203079-HTTP-traffic-without-interception-with-ada…
sfc-gh-fpawlowski Aug 12, 2025
e7eb02c
SNOW-2203079: Fixed attribute rest access for session manager
sfc-gh-fpawlowski Aug 12, 2025
52a8dec
SNOW-2203079: Renamed shallow_clone to clone
sfc-gh-fpawlowski Aug 12, 2025
9e15f72
Merge branch 'SNOW-2203079-HTTP-traffic-without-interception-with-ada…
sfc-gh-fpawlowski Aug 12, 2025
f23eb04
SNOW-2203079: improved comments
sfc-gh-fpawlowski Aug 12, 2025
13e5080
Merge branch 'SNOW-2203079-HTTP-traffic-without-interception-with-ada…
sfc-gh-fpawlowski Aug 12, 2025
39ce780
Merge branch 'main' into SNOW-2203079-HTTP-traffic-without-intercepti…
sfc-gh-fpawlowski Aug 12, 2025
ad9ef20
Merge branch 'SNOW-2203079-HTTP-traffic-without-interception-with-ada…
sfc-gh-fpawlowski Aug 12, 2025
2588a9c
Merge branch 'main' into SNOW-694457-env-vars-proxy-leaking
sfc-gh-fpawlowski Aug 13, 2025
0aa531d
SNOW-694457: Changelog update
sfc-gh-fpawlowski Aug 13, 2025
fd962a0
NO-SNOW: olddriver fix (#2474)
sfc-gh-fpawlowski Aug 13, 2025
10697b0
Merge branch 'main' into SNOW-694457-env-vars-proxy-leaking
sfc-gh-fpawlowski Aug 13, 2025
2bf8743
Merge branch 'main' into SNOW-694457-env-vars-proxy-leaking
sfc-gh-fpawlowski Aug 13, 2025
15cfad6
SNOW-694457: Trigger locked jobs
sfc-gh-fpawlowski Aug 13, 2025
547fc8b
SNOW-694457: Solved Jenkins issues with no proxy env var to delete
sfc-gh-fpawlowski Aug 13, 2025
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
1 change: 1 addition & 0 deletions DESCRIPTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Source code is also available at: https://github.com/snowflakedb/snowflake-conne
- Fix case-sensitivity of `Oauth` and `programmatic_access_token` authenticator values.
- Relaxed `pyarrow` version constraint, versions >= 19 can now be used.
- Populate type_code in ResultMetadata for interval types.
- Proxy setup with connection parameters added.

- v3.16.0(July 04,2025)
- Bumped numpy dependency from <2.1.0 to <=2.2.4.
Expand Down
39 changes: 37 additions & 2 deletions src/snowflake/connector/auth/_oauth_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@
)
from ..errors import Error, ProgrammingError
from ..network import OAUTH_AUTHENTICATOR
from ..proxy import get_proxy_url
from ..secret_detector import SecretDetector
from ..token_cache import TokenCache, TokenKey, TokenType
from ..vendored import urllib3
from ..vendored.requests.utils import get_environ_proxies, select_proxy
from ..vendored.urllib3.poolmanager import ProxyManager
from .by_plugin import AuthByPlugin, AuthType

if TYPE_CHECKING:
Expand Down Expand Up @@ -319,7 +322,13 @@ def _get_refresh_token_response(
fields["scope"] = self._scope
try:
# TODO(SNOW-2229411) Session manager should be used here. It may require additional security validation (since we would transition from PoolManager to requests.Session) and some parameters would be passed implicitly. OAuth token exchange must NOT reuse pooled HTTP sessions. We should create a fresh SessionManager with use_pooling=False for each call.
return urllib3.PoolManager().request_encode_body(
proxy_url = self._resolve_proxy_url(conn, self._token_request_url)
http_client = (
ProxyManager(proxy_url=proxy_url)
if proxy_url
else urllib3.PoolManager()
)
return http_client.request_encode_body(
"POST",
self._token_request_url,
encode_multipart=False,
Expand Down Expand Up @@ -359,7 +368,11 @@ def _get_request_token_response(
fields: dict[str, str],
) -> (str | None, str | None):
# TODO(SNOW-2229411) Session manager should be used here. It may require additional security validation (since we would transition from PoolManager to requests.Session) and some parameters would be passed implicitly. Token request must bypass HTTP connection pools.
resp = urllib3.PoolManager().request_encode_body(
proxy_url = self._resolve_proxy_url(connection, self._token_request_url)
http_client = (
ProxyManager(proxy_url=proxy_url) if proxy_url else urllib3.PoolManager()
)
resp = http_client.request_encode_body(
"POST",
self._token_request_url,
headers=self._create_token_request_headers(),
Expand Down Expand Up @@ -400,3 +413,25 @@ def _create_token_request_headers(self) -> dict[str, str]:
"Accept": "application/json",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
}

@staticmethod
def _resolve_proxy_url(
connection: SnowflakeConnection, request_url: str
) -> str | None:
# TODO(SNOW-2229411) Session manager should be used instead. It may require additional security validation.
"""Resolve proxy URL from explicit config first, then environment variables."""
# First try explicit proxy configuration from connection parameters
proxy_url = get_proxy_url(
connection.proxy_host,
connection.proxy_port,
connection.proxy_user,
connection.proxy_password,
)

if proxy_url:
return proxy_url

# Fall back to environment variables (HTTP_PROXY, HTTPS_PROXY)
# Use proper proxy selection that considers the URL scheme
proxies = get_environ_proxies(request_url)
return select_proxy(request_url, proxies)
1 change: 1 addition & 0 deletions src/snowflake/connector/auth/oauth_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ def _do_authorization_request(
"login. If you can't see it, check existing browser windows, "
"or your OS settings. Press CTRL+C to abort and try again..."
)
# TODO(SNOW-2229411) Investigate if Session manager / Http Config should be used here.
code, state = (
self._receive_authorization_callback(callback_server, connection)
if webbrowser.open(authorization_request)
Expand Down
10 changes: 5 additions & 5 deletions src/snowflake/connector/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey

from . import errors, proxy
from . import errors
from ._query_context_cache import QueryContextCache
from ._utils import (
_DEFAULT_VALUE_SERVER_DOP_CAP_FOR_FILE_TRANSFER,
Expand Down Expand Up @@ -924,6 +924,10 @@ def connect(self, **kwargs) -> None:
self._http_config = HttpConfig(
adapter_factory=ProxySupportAdapterFactory(),
use_pooling=(not self.disable_request_pooling),
proxy_host=self.proxy_host,
proxy_port=self.proxy_port,
proxy_user=self.proxy_user,
proxy_password=self.proxy_password,
)
self._session_manager = SessionManager(self._http_config)

Expand Down Expand Up @@ -1125,10 +1129,6 @@ def __open_connection(self):
use_numpy=self._numpy, support_negative_year=self._support_negative_year
)

proxy.set_proxies(
self.proxy_host, self.proxy_port, self.proxy_user, self.proxy_password
)

self._rest = SnowflakeRestful(
host=self.host,
port=self.port,
Expand Down
51 changes: 18 additions & 33 deletions src/snowflake/connector/proxy.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,28 @@
#!/usr/bin/env python
from __future__ import annotations

import os


def set_proxies(
def get_proxy_url(
proxy_host: str | None,
proxy_port: str | None,
proxy_user: str | None = None,
proxy_password: str | None = None,
) -> dict[str, str] | None:
"""Sets proxy dict for requests."""
PREFIX_HTTP = "http://"
PREFIX_HTTPS = "https://"
proxies = None
) -> str | None:
http_prefix = "http://"
https_prefix = "https://"

if proxy_host and proxy_port:
if proxy_host.startswith(PREFIX_HTTP):
proxy_host = proxy_host[len(PREFIX_HTTP) :]
elif proxy_host.startswith(PREFIX_HTTPS):
proxy_host = proxy_host[len(PREFIX_HTTPS) :]
if proxy_user or proxy_password:
proxy_auth = "{proxy_user}:{proxy_password}@".format(
proxy_user=proxy_user if proxy_user is not None else "",
proxy_password=proxy_password if proxy_password is not None else "",
)
if proxy_host.startswith(http_prefix):
host = proxy_host[len(http_prefix) :]
elif proxy_host.startswith(https_prefix):
host = proxy_host[len(https_prefix) :]
else:
proxy_auth = ""
proxies = {
"http": "http://{proxy_auth}{proxy_host}:{proxy_port}".format(
proxy_host=proxy_host,
proxy_port=str(proxy_port),
proxy_auth=proxy_auth,
),
"https": "http://{proxy_auth}{proxy_host}:{proxy_port}".format(
proxy_host=proxy_host,
proxy_port=str(proxy_port),
proxy_auth=proxy_auth,
),
}
os.environ["HTTP_PROXY"] = proxies["http"]
os.environ["HTTPS_PROXY"] = proxies["https"]
return proxies
host = proxy_host
auth = (
f"{proxy_user or ''}:{proxy_password or ''}@"
if proxy_user or proxy_password
else ""
)
return f"{http_prefix}{auth}{host}:{proxy_port}"

return None
23 changes: 22 additions & 1 deletion src/snowflake/connector/result_batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from .options import installed_pandas
from .options import pyarrow as pa
from .secret_detector import SecretDetector
from .session_manager import SessionManager
from .session_manager import HttpConfig, SessionManager
from .time_util import TimerContextManager

logger = getLogger(__name__)
Expand Down Expand Up @@ -261,6 +261,8 @@ def __init__(
[s._to_result_metadata_v1() for s in schema] if schema is not None else None
)
self._use_dict_result = use_dict_result
# Passed to contain the configured Http behavior in case the connectio is no longer active for the download
# Can be overridden with setters if needed.
self._session_manager = session_manager
self._metrics: dict[str, int] = {}
self._data: str | list[tuple[Any, ...]] | None = None
Expand Down Expand Up @@ -300,6 +302,25 @@ def uncompressed_size(self) -> int | None:
def column_names(self) -> list[str]:
return [col.name for col in self._schema]

@property
def session_manager(self) -> SessionManager | None:
return self._session_manager

@session_manager.setter
def session_manager(self, session_manager: SessionManager | None) -> None:
self._session_manager = session_manager

@property
def http_config(self):
return self._session_manager.config

@http_config.setter
def http_config(self, config: HttpConfig) -> None:
if self._session_manager:
self._session_manager.config = config
else:
self._session_manager = SessionManager(config=config)

def __iter__(
self,
) -> Iterator[dict | Exception] | Iterator[tuple | Exception]:
Expand Down
35 changes: 30 additions & 5 deletions src/snowflake/connector/session_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from typing import TYPE_CHECKING, Any, Callable, Generator, Mapping

from .compat import urlparse
from .proxy import get_proxy_url
from .vendored import requests
from .vendored.requests import Response, Session
from .vendored.requests.adapters import BaseAdapter, HTTPAdapter
Expand Down Expand Up @@ -76,8 +77,15 @@ def get_connection(
proxy_manager = self.proxy_manager_for(proxy)

if isinstance(proxy_manager, ProxyManager):
# Add Host to proxy header SNOW-232777
proxy_manager.proxy_headers["Host"] = parsed_url.hostname
# Add Host to proxy header SNOW-232777 and SNOW-694457

# RFC 7230 / 5.4 – a proxy’s Host header must repeat the request authority
# verbatim: <hostname>[:<port>] with IPv6 still in [brackets]. We take that
# straight from urlparse(url).netloc, which preserves port and brackets (and case-sensitive hostname).
# 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.
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"
Expand Down Expand Up @@ -112,6 +120,10 @@ class HttpConfig:
)
use_pooling: bool = True
max_retries: int | None = REQUESTS_RETRY
proxy_host: str | None = None
proxy_port: str | None = None
proxy_user: str | None = None
proxy_password: str | None = None

def copy_with(self, **overrides: Any) -> HttpConfig:
"""Return a new HttpConfig with overrides applied."""
Expand Down Expand Up @@ -293,13 +305,13 @@ class SessionManager(_RequestVerbsUsingSessionMixin):
**Two Operating Modes**:
- use_pooling=False: One-shot sessions (create, use, close) - suitable for infrequent requests
- use_pooling=True: Per-hostname session pools - reuses TCP connections, avoiding handshake
and SSL/TLS negotiation overhead for repeated requests to the same host
and SSL/TLS negotiation overhead for repeated requests to the same host.

**Key Benefits**:
- Centralized HTTP configuration management and easy propagation across the codebase
- Consistent proxy setup (SNOW-694457) and headers customization (SNOW-2043816)
- HTTPAdapter customization for connection-level request manipulation
- Performance optimization through connection reuse for high-traffic scenarios
- Performance optimization through connection reuse for high-traffic scenarios.

**Usage**: Create the base session manager, then use clone() for derived managers to ensure
proper config propagation. Pre-commit checks enforce usage to prevent code drift back to
Expand All @@ -315,7 +327,6 @@ def __init__(self, config: HttpConfig | None = None, **http_config_kwargs) -> No
logger.debug("Creating a config for the SessionManager")
config = HttpConfig(**http_config_kwargs)
self._cfg: HttpConfig = config

self._sessions_map: dict[str | None, SessionPool] = collections.defaultdict(
lambda: SessionPool(self)
)
Expand All @@ -338,6 +349,19 @@ def from_config(cls, cfg: HttpConfig, **overrides: Any) -> SessionManager:
def config(self) -> HttpConfig:
return self._cfg

@config.setter
def config(self, cfg: HttpConfig) -> None:
self._cfg = cfg

@property
def proxy_url(self) -> str:
return get_proxy_url(
self._cfg.proxy_host,
self._cfg.proxy_port,
self._cfg.proxy_user,
self._cfg.proxy_password,
)

@property
def use_pooling(self) -> bool:
return self._cfg.use_pooling
Expand Down Expand Up @@ -395,6 +419,7 @@ def _mount_adapters(self, session: requests.Session) -> None:
def make_session(self) -> Session:
session = requests.Session()
self._mount_adapters(session)
session.proxies = {"http": self.proxy_url, "https": self.proxy_url}
return session

@contextlib.contextmanager
Expand Down
2 changes: 2 additions & 0 deletions test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from contextlib import contextmanager
from logging import getLogger
from pathlib import Path
from test.test_utils.cross_module_fixtures.http_fixtures import * # NOQA
from test.test_utils.cross_module_fixtures.wiremock_fixtures import * # NOQA
from typing import Generator

import pytest
Expand Down
60 changes: 60 additions & 0 deletions test/data/wiremock/mappings/auth/password/successful_flow.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"mappings": [
{
"request": {
"urlPathPattern": "/session/v1/login-request.*",
"method": "POST",
"bodyPatterns": [
{
"equalToJson" : {
"data": {
"LOGIN_NAME": "testUser",
"PASSWORD": "testPassword"
}
},
"ignoreExtraElements" : true
}
]
},
"response": {
"status": 200,
"jsonBody": {
"data": {
"masterToken": "master token",
"token": "session token",
"validityInSeconds": 3600,
"masterValidityInSeconds": 14400,
"displayUserName": "TEST_USER",
"serverVersion": "8.48.0 b2024121104444034239f05",
"firstLogin": false,
"remMeToken": null,
"remMeValidityInSeconds": 0,
"healthCheckInterval": 45,
"newClientForUpgrade": "3.12.3",
"sessionId": 1172562260498,
"parameters": [
{
"name": "CLIENT_PREFETCH_THREADS",
"value": 4
}
],
"sessionInfo": {
"databaseName": "TEST_DB",
"schemaName": "TEST_GO",
"warehouseName": "TEST_XSMALL",
"roleName": "ANALYST"
},
"idToken": null,
"idTokenValidityInSeconds": 0,
"responseData": null,
"mfaToken": null,
"mfaTokenValidityInSeconds": 0
},
"code": null,
"message": null,
"success": true
}
}
}
]
}
12 changes: 12 additions & 0 deletions test/data/wiremock/mappings/generic/proxy_forward_all.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"request": {
"urlPattern": "/.*",
"method": "ANY"
},
"response": {
"proxyBaseUrl": "{{TARGET_HTTP_HOST_WITH_PORT}}",
"additionalProxyRequestHeaders": {
"Via": "1.1 wiremock-proxy"
}
}
}
Loading
Loading