|
9 | 9 | # and added OCSP validator on the top. |
10 | 10 | import logging |
11 | 11 | import time |
| 12 | +import weakref |
| 13 | +from contextvars import ContextVar |
12 | 14 | from functools import wraps |
13 | 15 | from inspect import getfullargspec as get_args |
14 | 16 | from socket import socket |
15 | | -from typing import Any |
| 17 | +from typing import TYPE_CHECKING, Any |
16 | 18 |
|
17 | 19 | import certifi |
18 | 20 | import OpenSSL.SSL |
19 | 21 |
|
20 | 22 | from .constants import OCSPMode |
21 | 23 | from .errorcode import ER_OCSP_RESPONSE_CERT_STATUS_REVOKED |
22 | 24 | from .errors import OperationalError |
| 25 | + |
| 26 | +if TYPE_CHECKING: |
| 27 | + from .session_manager import SessionManager |
| 28 | + |
23 | 29 | from .vendored.urllib3 import connection as connection_ |
24 | 30 | from .vendored.urllib3.contrib.pyopenssl import PyOpenSSLContext, WrappedSocket |
25 | 31 | from .vendored.urllib3.util import ssl_ as ssl_ |
|
35 | 41 | log = logging.getLogger(__name__) |
36 | 42 |
|
37 | 43 |
|
| 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 | + |
38 | 82 | def inject_into_urllib3() -> None: |
39 | 83 | """Monkey-patch urllib3 with PyOpenSSL-backed SSL-support and OCSP.""" |
40 | 84 | log.debug("Injecting ssl_wrap_socket_with_ocsp") |
|
0 commit comments