@@ -73,26 +73,44 @@ def SOCKSProxyManager(*args, **kwargs):
7373DEFAULT_RETRIES = 0
7474DEFAULT_POOL_TIMEOUT = None
7575
76- _preloaded_ssl_context = create_urllib3_context ()
77- _preloaded_ssl_context .load_verify_locations (
78- extract_zipped_paths (DEFAULT_CA_BUNDLE_PATH )
79- )
76+
77+ try :
78+ import ssl # noqa: F401
79+
80+ _preloaded_ssl_context = create_urllib3_context ()
81+ _preloaded_ssl_context .load_verify_locations (
82+ extract_zipped_paths (DEFAULT_CA_BUNDLE_PATH )
83+ )
84+ except ImportError :
85+ # Bypass default SSLContext creation when Python
86+ # interpreter isn't built with the ssl module.
87+ _preloaded_ssl_context = None
8088
8189
8290def _urllib3_request_context (
8391 request : "PreparedRequest" ,
8492 verify : "bool | str | None" ,
8593 client_cert : "typing.Tuple[str, str] | str | None" ,
94+ poolmanager : "PoolManager" ,
8695) -> "(typing.Dict[str, typing.Any], typing.Dict[str, typing.Any])" :
8796 host_params = {}
8897 pool_kwargs = {}
8998 parsed_request_url = urlparse (request .url )
9099 scheme = parsed_request_url .scheme .lower ()
91100 port = parsed_request_url .port
101+
102+ # Determine if we have and should use our default SSLContext
103+ # to optimize performance on standard requests.
104+ poolmanager_kwargs = getattr (poolmanager , "connection_pool_kw" , {})
105+ has_poolmanager_ssl_context = poolmanager_kwargs .get ("ssl_context" )
106+ should_use_default_ssl_context = (
107+ _preloaded_ssl_context is not None and not has_poolmanager_ssl_context
108+ )
109+
92110 cert_reqs = "CERT_REQUIRED"
93111 if verify is False :
94112 cert_reqs = "CERT_NONE"
95- elif verify is True :
113+ elif verify is True and should_use_default_ssl_context :
96114 pool_kwargs ["ssl_context" ] = _preloaded_ssl_context
97115 elif isinstance (verify , str ):
98116 if not os .path .isdir (verify ):
@@ -375,23 +393,83 @@ def build_response(self, req, resp):
375393
376394 return response
377395
396+ def build_connection_pool_key_attributes (self , request , verify , cert = None ):
397+ """Build the PoolKey attributes used by urllib3 to return a connection.
398+
399+ This looks at the PreparedRequest, the user-specified verify value,
400+ and the value of the cert parameter to determine what PoolKey values
401+ to use to select a connection from a given urllib3 Connection Pool.
402+
403+ The SSL related pool key arguments are not consistently set. As of
404+ this writing, use the following to determine what keys may be in that
405+ dictionary:
406+
407+ * If ``verify`` is ``True``, ``"ssl_context"`` will be set and will be the
408+ default Requests SSL Context
409+ * If ``verify`` is ``False``, ``"ssl_context"`` will not be set but
410+ ``"cert_reqs"`` will be set
411+ * If ``verify`` is a string, (i.e., it is a user-specified trust bundle)
412+ ``"ca_certs"`` will be set if the string is not a directory recognized
413+ by :py:func:`os.path.isdir`, otherwise ``"ca_certs_dir"`` will be
414+ set.
415+ * If ``"cert"`` is specified, ``"cert_file"`` will always be set. If
416+ ``"cert"`` is a tuple with a second item, ``"key_file"`` will also
417+ be present
418+
419+ To override these settings, one may subclass this class, call this
420+ method and use the above logic to change parameters as desired. For
421+ example, if one wishes to use a custom :py:class:`ssl.SSLContext` one
422+ must both set ``"ssl_context"`` and based on what else they require,
423+ alter the other keys to ensure the desired behaviour.
424+
425+ :param request:
426+ The PreparedReqest being sent over the connection.
427+ :type request:
428+ :class:`~requests.models.PreparedRequest`
429+ :param verify:
430+ Either a boolean, in which case it controls whether
431+ we verify the server's TLS certificate, or a string, in which case it
432+ must be a path to a CA bundle to use.
433+ :param cert:
434+ (optional) Any user-provided SSL certificate for client
435+ authentication (a.k.a., mTLS). This may be a string (i.e., just
436+ the path to a file which holds both certificate and key) or a
437+ tuple of length 2 with the certificate file path and key file
438+ path.
439+ :returns:
440+ A tuple of two dictionaries. The first is the "host parameters"
441+ portion of the Pool Key including scheme, hostname, and port. The
442+ second is a dictionary of SSLContext related parameters.
443+ """
444+ return _urllib3_request_context (request , verify , cert , self .poolmanager )
445+
378446 def get_connection_with_tls_context (self , request , verify , proxies = None , cert = None ):
379447 """Returns a urllib3 connection for the given request and TLS settings.
380448 This should not be called from user code, and is only exposed for use
381449 when subclassing the :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
382450
383- :param request: The :class:`PreparedRequest <PreparedRequest>` object
384- to be sent over the connection.
385- :param verify: Either a boolean, in which case it controls whether
386- we verify the server's TLS certificate, or a string, in which case it
387- must be a path to a CA bundle to use.
388- :param proxies: (optional) The proxies dictionary to apply to the request.
389- :param cert: (optional) Any user-provided SSL certificate to be trusted.
390- :rtype: urllib3.ConnectionPool
451+ :param request:
452+ The :class:`PreparedRequest <PreparedRequest>` object to be sent
453+ over the connection.
454+ :param verify:
455+ Either a boolean, in which case it controls whether we verify the
456+ server's TLS certificate, or a string, in which case it must be a
457+ path to a CA bundle to use.
458+ :param proxies:
459+ (optional) The proxies dictionary to apply to the request.
460+ :param cert:
461+ (optional) Any user-provided SSL certificate to be used for client
462+ authentication (a.k.a., mTLS).
463+ :rtype:
464+ urllib3.ConnectionPool
391465 """
392466 proxy = select_proxy (request .url , proxies )
393467 try :
394- host_params , pool_kwargs = _urllib3_request_context (request , verify , cert )
468+ host_params , pool_kwargs = self .build_connection_pool_key_attributes (
469+ request ,
470+ verify ,
471+ cert ,
472+ )
395473 except ValueError as e :
396474 raise InvalidURL (e , request = request )
397475 if proxy :
0 commit comments