Skip to content

Commit c1f0921

Browse files
SNOW-2203079: OCSP and Telemetry refactored
1 parent 93516a3 commit c1f0921

File tree

4 files changed

+74
-2
lines changed

4 files changed

+74
-2
lines changed

src/snowflake/connector/connection.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@
121121
)
122122
from .session_manager import SessionManager
123123
from .sqlstate import SQLSTATE_CONNECTION_NOT_EXISTS, SQLSTATE_FEATURE_NOT_SUPPORTED
124+
from .ssl_wrap_socket import (
125+
set_current_session_manager as set_current_session_manager_for_ssl,
126+
)
124127
from .telemetry import TelemetryClient, TelemetryData, TelemetryField
125128
from .time_util import HeartBeatTimer, get_time_millis
126129
from .url_util import extract_top_level_domain_from_hostname
@@ -892,6 +895,15 @@ def connect(self, **kwargs) -> None:
892895
self._session_manager = SessionManager(
893896
use_pooling=(not self.disable_request_pooling),
894897
)
898+
try:
899+
# Set context var for OCSP downloads to use this manager. Can be removed once OCSP is deprecated.
900+
self._ocsp_sm_context_var_old_token = set_current_session_manager_for_ssl(
901+
self._session_manager
902+
)
903+
except Exception:
904+
logger.debug(
905+
"Could not set current SessionManager in ssl_wrap_socket", exc_info=True
906+
)
895907

896908
if self.enable_connection_diag:
897909
exceptions_dict = {}
@@ -932,6 +944,15 @@ def connect(self, **kwargs) -> None:
932944
else:
933945
self.__open_connection()
934946

947+
try:
948+
# Reet context var for OCSP downloads to use this manager. Can be removed once OCSP is deprecated.
949+
from .ssl_wrap_socket import reset_current_session_manager as _reset_sm
950+
951+
if hasattr(self, "_ocsp_sm_context_var_old_token"):
952+
_reset_sm(self._ocsp_sm_context_var_old_token)
953+
except Exception:
954+
logger.debug("Failed to reset OCSP SessionManager context", exc_info=True)
955+
935956
def close(self, retry: bool = True) -> None:
936957
"""Closes the connection."""
937958
# unregister to dereference connection object as it's already closed after the execution

src/snowflake/connector/ocsp_snowflake.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353
)
5454
from snowflake.connector.errors import RevocationCheckError
5555
from snowflake.connector.network import PYTHON_CONNECTOR_USER_AGENT
56+
from snowflake.connector.session_manager import SessionManager
57+
from snowflake.connector.ssl_wrap_socket import get_current_session_manager
5658

5759
from . import constants
5860
from .backoff_policies import exponential_backoff
@@ -546,7 +548,11 @@ def _download_ocsp_response_cache(ocsp, url, do_retry: bool = True) -> bool:
546548
if sf_cache_server_url is not None:
547549
url = sf_cache_server_url
548550

549-
with generic_requests.Session() as session:
551+
# Obtain SessionManager from ssl_wrap_socket context var if available
552+
session_manager = get_current_session_manager().clone(
553+
use_pooling=False
554+
) or SessionManager(use_pooling=False)
555+
with session_manager.use_requests_session() as session:
550556
max_retry = SnowflakeOCSP.OCSP_CACHE_SERVER_MAX_RETRY if do_retry else 1
551557
sleep_time = 1
552558
backoff = exponential_backoff()()

src/snowflake/connector/ssl_wrap_socket.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,23 @@
99
# and added OCSP validator on the top.
1010
import logging
1111
import time
12+
import weakref
13+
from contextvars import ContextVar
1214
from functools import wraps
1315
from inspect import getfullargspec as get_args
1416
from socket import socket
15-
from typing import Any
17+
from typing import TYPE_CHECKING, Any
1618

1719
import certifi
1820
import OpenSSL.SSL
1921

2022
from .constants import OCSPMode
2123
from .errorcode import ER_OCSP_RESPONSE_CERT_STATUS_REVOKED
2224
from .errors import OperationalError
25+
26+
if TYPE_CHECKING:
27+
from .session_manager import SessionManager
28+
2329
from .vendored.urllib3 import connection as connection_
2430
from .vendored.urllib3.contrib.pyopenssl import PyOpenSSLContext, WrappedSocket
2531
from .vendored.urllib3.util import ssl_ as ssl_
@@ -35,6 +41,44 @@
3541
log = logging.getLogger(__name__)
3642

3743

44+
# Store a *weak* reference so that the context variable doesn’t prolong the
45+
# lifetime of the SessionManager. Once all owning connections are GC-ed the
46+
# weakref goes dead and OCSP will fall back to its local manager (but most likely won't be used ever again anyway).
47+
_CURRENT_SESSION_MANAGER: ContextVar[weakref.ref[SessionManager] | None] = ContextVar(
48+
"_CURRENT_SESSION_MANAGER",
49+
default=None,
50+
)
51+
52+
53+
def get_current_session_manager() -> SessionManager | None:
54+
"""Return the SessionManager associated with the current handshake, if any.
55+
56+
If the weak reference is dead or no manager was set, returns ``None``.
57+
"""
58+
ref = _CURRENT_SESSION_MANAGER.get()
59+
return ref() if ref is not None else None
60+
61+
62+
def set_current_session_manager(sm: SessionManager | None) -> Any:
63+
"""Set the SessionManager for the current execution context.
64+
65+
Called from SnowflakeConnection so that OCSP downloads
66+
use the same proxy / header configuration as the initiating connection.
67+
68+
Alternative approach would be moving method inject_into_urllib3() inside connection initialization, but in case this delay (from module import time to connection initialization time) would cause some code to break we stayed with this approach, having in mind soon OCSP deprecation.
69+
"""
70+
return _CURRENT_SESSION_MANAGER.set(weakref.ref(sm) if sm is not None else None)
71+
72+
73+
def reset_current_session_manager(token) -> None:
74+
"""Restore previous SessionManager context stored in *token* (from ContextVar.set)."""
75+
try:
76+
_CURRENT_SESSION_MANAGER.reset(token)
77+
except Exception:
78+
# ignore invalid token errors
79+
pass
80+
81+
3882
def inject_into_urllib3() -> None:
3983
"""Monkey-patch urllib3 with PyOpenSSL-backed SSL-support and OCSP."""
4084
log.debug("Injecting ssl_wrap_socket_with_ocsp")

src/snowflake/connector/telemetry_oob.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@ def _upload_payload(self, payload) -> None:
482482
# This logger guarantees the payload won't be masked. Testing purpose.
483483
rt_plain_logger.debug(f"OOB telemetry data being sent is {payload}")
484484

485+
# TODO: Telemetry OOB is currently disabled. If Telemetry OOB is to be re-enabled, this HTTP call must be routed through the connection_argument.session_manager.use_requests_session(use_pooling) (so the SessionManager instance attached to the connection which initialization's fail most likely triggered this telemetry log). It would allow to pick up proxy configuration & custom headers (see tickets SNOW-694457 and SNOW-2203079).
485486
with requests.Session() as session:
486487
headers = {
487488
"Content-type": "application/json",

0 commit comments

Comments
 (0)