1"""
2requests.adapters
3~~~~~~~~~~~~~~~~~
4
5This module contains the transport adapters that Requests uses to define
6and maintain connections.
7"""
8
9from __future__ import annotations
10
11import os.path
12import socket # noqa: F401 # type: ignore[reportUnusedImport]
13import typing
14import warnings
15from typing import Any
16
17from pip._vendor.urllib3.exceptions import (
18 ClosedPoolError,
19 ConnectTimeoutError,
20 LocationValueError,
21 MaxRetryError,
22 NewConnectionError,
23 ProtocolError,
24 ReadTimeoutError,
25 ResponseError,
26)
27from pip._vendor.urllib3.exceptions import HTTPError as _HTTPError
28from pip._vendor.urllib3.exceptions import InvalidHeader as _InvalidHeader
29from pip._vendor.urllib3.exceptions import ProxyError as _ProxyError
30from pip._vendor.urllib3.exceptions import SSLError as _SSLError
31from pip._vendor.urllib3.poolmanager import PoolManager, proxy_from_url
32from pip._vendor.urllib3.util import Timeout as TimeoutSauce
33from pip._vendor.urllib3.util import parse_url
34from pip._vendor.urllib3.util.retry import Retry
35
36from .auth import _basic_auth_str # type: ignore[reportPrivateUsage]
37from .compat import basestring, urlparse
38from .cookies import extract_cookies_to_jar
39from .exceptions import (
40 ConnectionError,
41 ConnectTimeout,
42 InvalidHeader,
43 InvalidProxyURL,
44 InvalidSchema,
45 InvalidURL,
46 ProxyError,
47 ReadTimeout,
48 RetryError,
49 SSLError,
50)
51from .models import Response
52from .structures import CaseInsensitiveDict
53from .utils import (
54 DEFAULT_CA_BUNDLE_PATH,
55 get_auth_from_url,
56 get_encoding_from_headers,
57 prepend_scheme_if_needed,
58 select_proxy,
59 urldefragauth,
60)
61
62try:
63 from pip._vendor.urllib3.contrib.socks import SOCKSProxyManager # type: ignore[assignment]
64except ImportError:
65
66 def SOCKSProxyManager(*args: Any, **kwargs: Any) -> None:
67 raise InvalidSchema("Missing dependencies for SOCKS support.")
68
69
70if typing.TYPE_CHECKING:
71 from pip._vendor.urllib3.connectionpool import HTTPConnectionPool
72 from pip._vendor.urllib3.poolmanager import PoolManager as _PoolManager
73
74 from . import _types as _t
75 from .models import PreparedRequest
76
77from ._types import is_prepared as _is_prepared
78
79DEFAULT_POOLBLOCK = False
80DEFAULT_POOLSIZE = 10
81DEFAULT_RETRIES = 0
82DEFAULT_POOL_TIMEOUT = None
83
84
85def _urllib3_request_context(
86 request: PreparedRequest,
87 verify: bool | str | None,
88 client_cert: tuple[str, str] | str | None,
89 poolmanager: PoolManager,
90) -> tuple[dict[str, Any], dict[str, Any]]:
91 host_params: dict[str, Any] = {}
92 pool_kwargs: dict[str, Any] = {}
93 parsed_request_url = urlparse(request.url)
94 scheme = parsed_request_url.scheme.lower()
95 port = parsed_request_url.port
96
97 cert_reqs = "CERT_REQUIRED"
98 if verify is False:
99 cert_reqs = "CERT_NONE"
100 elif isinstance(verify, str):
101 if not os.path.isdir(verify):
102 pool_kwargs["ca_certs"] = verify
103 else:
104 pool_kwargs["ca_cert_dir"] = verify
105 pool_kwargs["cert_reqs"] = cert_reqs
106 if client_cert is not None:
107 if isinstance(client_cert, tuple) and len(client_cert) == 2:
108 pool_kwargs["cert_file"] = client_cert[0]
109 pool_kwargs["key_file"] = client_cert[1]
110 else:
111 # According to our docs, we allow users to specify just the client
112 # cert path
113 pool_kwargs["cert_file"] = client_cert
114 host_params = {
115 "scheme": scheme,
116 "host": parsed_request_url.hostname,
117 "port": port,
118 }
119 return host_params, pool_kwargs
120
121
122class BaseAdapter:
123 """The Base Transport Adapter"""
124
125 def __init__(self) -> None:
126 super().__init__()
127
128 def send(
129 self,
130 request: PreparedRequest,
131 stream: bool = False,
132 timeout: _t.TimeoutType = None,
133 verify: _t.VerifyType = True,
134 cert: _t.CertType = None,
135 proxies: dict[str, str] | None = None,
136 ) -> Response:
137 """Sends PreparedRequest object. Returns Response object.
138
139 :param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
140 :param stream: (optional) Whether to stream the request content.
141 :param timeout: (optional) How long to wait for the server to send
142 data before giving up, as a float, or a :ref:`(connect timeout,
143 read timeout) <timeouts>` tuple.
144 :type timeout: float or tuple
145 :param verify: (optional) Either a boolean, in which case it controls whether we verify
146 the server's TLS certificate, or a string, in which case it must be a path
147 to a CA bundle to use
148 :param cert: (optional) Any user-provided SSL certificate to be trusted.
149 :param proxies: (optional) The proxies dictionary to apply to the request.
150 """
151 raise NotImplementedError
152
153 def close(self) -> None:
154 """Cleans up adapter specific items."""
155 raise NotImplementedError
156
157
158class HTTPAdapter(BaseAdapter):
159 """The built-in HTTP Adapter for urllib3.
160
161 Provides a general-case interface for Requests sessions to contact HTTP and
162 HTTPS urls by implementing the Transport Adapter interface. This class will
163 usually be created by the :class:`Session <Session>` class under the
164 covers.
165
166 :param pool_connections: The number of urllib3 connection pools to cache.
167 :param pool_maxsize: The maximum number of connections to save in the pool.
168 :param max_retries: The maximum number of retries each connection
169 should attempt. Note, this applies only to failed DNS lookups, socket
170 connections and connection timeouts, never to requests where data has
171 made it to the server. By default, Requests does not retry failed
172 connections. If you need granular control over the conditions under
173 which we retry a request, import urllib3's ``Retry`` class and pass
174 that instead.
175 :param pool_block: Whether the connection pool should block for connections.
176
177 Usage::
178
179 >>> import requests
180 >>> s = requests.Session()
181 >>> a = requests.adapters.HTTPAdapter(max_retries=3)
182 >>> s.mount('http://', a)
183 """
184
185 __attrs__: list[str] = [
186 "max_retries",
187 "config",
188 "_pool_connections",
189 "_pool_maxsize",
190 "_pool_block",
191 ]
192
193 max_retries: Retry
194 config: dict[str, Any]
195 proxy_manager: dict[str, Any]
196 _pool_connections: int
197 _pool_maxsize: int
198 _pool_block: bool
199 poolmanager: _PoolManager
200
201 def __init__(
202 self,
203 pool_connections: int = DEFAULT_POOLSIZE,
204 pool_maxsize: int = DEFAULT_POOLSIZE,
205 max_retries: int | Retry = DEFAULT_RETRIES,
206 pool_block: bool = DEFAULT_POOLBLOCK,
207 ) -> None:
208 if max_retries == DEFAULT_RETRIES:
209 self.max_retries = Retry(0, read=False)
210 else:
211 self.max_retries = Retry.from_int(max_retries)
212 self.config = {}
213 self.proxy_manager = {}
214
215 super().__init__()
216
217 self._pool_connections = pool_connections
218 self._pool_maxsize = pool_maxsize
219 self._pool_block = pool_block
220
221 self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block)
222
223 def __getstate__(self) -> dict[str, Any]:
224 return {attr: getattr(self, attr, None) for attr in self.__attrs__}
225
226 def __setstate__(self, state: dict[str, Any]) -> None:
227 # Can't handle by adding 'proxy_manager' to self.__attrs__ because
228 # self.poolmanager uses a lambda function, which isn't pickleable.
229 self.proxy_manager = {}
230 self.config = {}
231
232 for attr, value in state.items():
233 setattr(self, attr, value)
234
235 self.init_poolmanager(
236 self._pool_connections, self._pool_maxsize, block=self._pool_block
237 )
238
239 def init_poolmanager(
240 self,
241 connections: int,
242 maxsize: int,
243 block: bool = DEFAULT_POOLBLOCK,
244 **pool_kwargs: Any,
245 ) -> None:
246 """Initializes a urllib3 PoolManager.
247
248 This method should not be called from user code, and is only
249 exposed for use when subclassing the
250 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
251
252 :param connections: The number of urllib3 connection pools to cache.
253 :param maxsize: The maximum number of connections to save in the pool.
254 :param block: Block when no free connections are available.
255 :param pool_kwargs: Extra keyword arguments used to initialize the Pool Manager.
256 """
257 # save these values for pickling
258 self._pool_connections = connections
259 self._pool_maxsize = maxsize
260 self._pool_block = block
261
262 self.poolmanager = PoolManager(
263 num_pools=connections,
264 maxsize=maxsize,
265 block=block,
266 **pool_kwargs,
267 )
268
269 def proxy_manager_for(self, proxy: str, **proxy_kwargs: Any) -> Any:
270 """Return urllib3 ProxyManager for the given proxy.
271
272 This method should not be called from user code, and is only
273 exposed for use when subclassing the
274 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
275
276 :param proxy: The proxy to return a urllib3 ProxyManager for.
277 :param proxy_kwargs: Extra keyword arguments used to configure the Proxy Manager.
278 :returns: ProxyManager
279 :rtype: urllib3.ProxyManager
280 """
281 if proxy in self.proxy_manager:
282 manager = self.proxy_manager[proxy]
283 elif proxy.lower().startswith("socks"):
284 username, password = get_auth_from_url(proxy)
285 manager = self.proxy_manager[proxy] = SOCKSProxyManager(
286 proxy,
287 username=username,
288 password=password,
289 num_pools=self._pool_connections,
290 maxsize=self._pool_maxsize,
291 block=self._pool_block,
292 **proxy_kwargs,
293 )
294 else:
295 proxy_headers = self.proxy_headers(proxy)
296 manager = self.proxy_manager[proxy] = proxy_from_url(
297 proxy,
298 proxy_headers=proxy_headers,
299 num_pools=self._pool_connections,
300 maxsize=self._pool_maxsize,
301 block=self._pool_block,
302 **proxy_kwargs,
303 )
304
305 return manager
306
307 def cert_verify(
308 self, conn: Any, url: str, verify: _t.VerifyType, cert: _t.CertType
309 ) -> None:
310 """Verify a SSL certificate. This method should not be called from user
311 code, and is only exposed for use when subclassing the
312 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
313
314 :param conn: The urllib3 connection object associated with the cert.
315 :param url: The requested URL.
316 :param verify: Either a boolean, in which case it controls whether we verify
317 the server's TLS certificate, or a string, in which case it must be a path
318 to a CA bundle to use
319 :param cert: The SSL certificate to verify.
320 """
321 if url.lower().startswith("https") and verify:
322 cert_loc = None
323
324 # Allow self-specified cert location.
325 if verify is not True:
326 cert_loc = verify
327
328 if not cert_loc:
329 cert_loc = DEFAULT_CA_BUNDLE_PATH
330
331 if not cert_loc or not os.path.exists(cert_loc):
332 raise OSError(
333 f"Could not find a suitable TLS CA certificate bundle, "
334 f"invalid path: {cert_loc}"
335 )
336
337 conn.cert_reqs = "CERT_REQUIRED"
338
339 if not os.path.isdir(cert_loc):
340 conn.ca_certs = cert_loc
341 else:
342 conn.ca_cert_dir = cert_loc
343 else:
344 conn.cert_reqs = "CERT_NONE"
345 conn.ca_certs = None
346 conn.ca_cert_dir = None
347
348 if cert:
349 if not isinstance(cert, basestring):
350 conn.cert_file = cert[0]
351 conn.key_file = cert[1]
352 else:
353 conn.cert_file = cert
354 conn.key_file = None
355 if conn.cert_file and not os.path.exists(conn.cert_file):
356 raise OSError(
357 f"Could not find the TLS certificate file, "
358 f"invalid path: {conn.cert_file}"
359 )
360 if conn.key_file and not os.path.exists(conn.key_file):
361 raise OSError(
362 f"Could not find the TLS key file, invalid path: {conn.key_file}"
363 )
364
365 def build_response(self, req: PreparedRequest, resp: Any) -> Response:
366 """Builds a :class:`Response <requests.Response>` object from a urllib3
367 response. This should not be called from user code, and is only exposed
368 for use when subclassing the
369 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`
370
371 :param req: The :class:`PreparedRequest <PreparedRequest>` used to generate the response.
372 :param resp: The urllib3 response object.
373 :rtype: requests.Response
374 """
375 assert _is_prepared(req)
376 response = Response()
377
378 # Fallback to None if there's no status_code, for whatever reason.
379 response.status_code = getattr(resp, "status", None) # type: ignore[assignment]
380
381 # Make headers case-insensitive.
382 response.headers = CaseInsensitiveDict(getattr(resp, "headers", {}))
383
384 # Set encoding.
385 response.encoding = get_encoding_from_headers(response.headers)
386 response.raw = resp
387 response.reason = response.raw.reason
388
389 if isinstance(req.url, bytes):
390 response.url = req.url.decode("utf-8")
391 else:
392 response.url = req.url
393
394 # Add new cookies from the server.
395 extract_cookies_to_jar(response.cookies, req, resp)
396
397 # Give the Response some context.
398 response.request = req
399 response.connection = self
400
401 return response
402
403 def build_connection_pool_key_attributes(
404 self, request: PreparedRequest, verify: _t.VerifyType, cert: _t.CertType = None
405 ) -> tuple[dict[str, Any], dict[str, Any]]:
406 """Build the PoolKey attributes used by urllib3 to return a connection.
407
408 This looks at the PreparedRequest, the user-specified verify value,
409 and the value of the cert parameter to determine what PoolKey values
410 to use to select a connection from a given urllib3 Connection Pool.
411
412 The SSL related pool key arguments are not consistently set. As of
413 this writing, use the following to determine what keys may be in that
414 dictionary:
415
416 * If ``verify`` is ``True``, ``"ssl_context"`` will be set and will be the
417 default Requests SSL Context
418 * If ``verify`` is ``False``, ``"ssl_context"`` will not be set but
419 ``"cert_reqs"`` will be set
420 * If ``verify`` is a string, (i.e., it is a user-specified trust bundle)
421 ``"ca_certs"`` will be set if the string is not a directory recognized
422 by :py:func:`os.path.isdir`, otherwise ``"ca_cert_dir"`` will be
423 set.
424 * If ``"cert"`` is specified, ``"cert_file"`` will always be set. If
425 ``"cert"`` is a tuple with a second item, ``"key_file"`` will also
426 be present
427
428 To override these settings, one may subclass this class, call this
429 method and use the above logic to change parameters as desired. For
430 example, if one wishes to use a custom :py:class:`ssl.SSLContext` one
431 must both set ``"ssl_context"`` and based on what else they require,
432 alter the other keys to ensure the desired behaviour.
433
434 :param request:
435 The PreparedRequest being sent over the connection.
436 :type request:
437 :class:`~requests.models.PreparedRequest`
438 :param verify:
439 Either a boolean, in which case it controls whether
440 we verify the server's TLS certificate, or a string, in which case it
441 must be a path to a CA bundle to use.
442 :param cert:
443 (optional) Any user-provided SSL certificate for client
444 authentication (a.k.a., mTLS). This may be a string (i.e., just
445 the path to a file which holds both certificate and key) or a
446 tuple of length 2 with the certificate file path and key file
447 path.
448 :returns:
449 A tuple of two dictionaries. The first is the "host parameters"
450 portion of the Pool Key including scheme, hostname, and port. The
451 second is a dictionary of SSLContext related parameters.
452 """
453 return _urllib3_request_context(request, verify, cert, self.poolmanager)
454
455 def get_connection_with_tls_context(
456 self,
457 request: PreparedRequest,
458 verify: _t.VerifyType,
459 proxies: dict[str, str] | None = None,
460 cert: _t.CertType = None,
461 ) -> HTTPConnectionPool:
462 """Returns a urllib3 connection for the given request and TLS settings.
463 This should not be called from user code, and is only exposed for use
464 when subclassing the :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
465
466 :param request:
467 The :class:`PreparedRequest <PreparedRequest>` object to be sent
468 over the connection.
469 :param verify:
470 Either a boolean, in which case it controls whether we verify the
471 server's TLS certificate, or a string, in which case it must be a
472 path to a CA bundle to use.
473 :param proxies:
474 (optional) The proxies dictionary to apply to the request.
475 :param cert:
476 (optional) Any user-provided SSL certificate to be used for client
477 authentication (a.k.a., mTLS).
478 :rtype:
479 urllib3.HTTPConnectionPool
480 """
481 assert _is_prepared(request)
482
483 proxy = select_proxy(request.url, proxies)
484 try:
485 host_params, pool_kwargs = self.build_connection_pool_key_attributes(
486 request,
487 verify,
488 cert,
489 )
490 except ValueError as e:
491 raise InvalidURL(e, request=request)
492 if proxy:
493 proxy = prepend_scheme_if_needed(proxy, "http")
494 proxy_url = parse_url(proxy)
495 if not proxy_url.host:
496 raise InvalidProxyURL(
497 "Please check proxy URL. It is malformed "
498 "and could be missing the host."
499 )
500 proxy_manager = self.proxy_manager_for(proxy)
501 conn = proxy_manager.connection_from_host(
502 **host_params, pool_kwargs=pool_kwargs
503 )
504 else:
505 # Only scheme should be lower case
506 conn = self.poolmanager.connection_from_host(
507 **host_params, pool_kwargs=pool_kwargs
508 )
509
510 return conn
511
512 def get_connection(
513 self, url: str, proxies: dict[str, str] | None = None
514 ) -> HTTPConnectionPool:
515 """DEPRECATED: Users should move to `get_connection_with_tls_context`
516 for all subclasses of HTTPAdapter using Requests>=2.32.2.
517
518 Returns a urllib3 connection for the given URL. This should not be
519 called from user code, and is only exposed for use when subclassing the
520 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
521
522 :param url: The URL to connect to.
523 :param proxies: (optional) A Requests-style dictionary of proxies used on this request.
524 :rtype: urllib3.HTTPConnectionPool
525 """
526 warnings.warn(
527 (
528 "`get_connection` has been deprecated in favor of "
529 "`get_connection_with_tls_context`. Custom HTTPAdapter subclasses "
530 "will need to migrate for Requests>=2.32.2. Please see "
531 "https://github.com/psf/requests/pull/6710 for more details."
532 ),
533 DeprecationWarning,
534 )
535 proxy = select_proxy(url, proxies)
536
537 if proxy:
538 proxy = prepend_scheme_if_needed(proxy, "http")
539 proxy_url = parse_url(proxy)
540 if not proxy_url.host:
541 raise InvalidProxyURL(
542 "Please check proxy URL. It is malformed "
543 "and could be missing the host."
544 )
545 proxy_manager = self.proxy_manager_for(proxy)
546 conn = proxy_manager.connection_from_url(url)
547 else:
548 # Only scheme should be lower case
549 parsed = urlparse(url)
550 url = parsed.geturl()
551 conn = self.poolmanager.connection_from_url(url)
552
553 return conn
554
555 def close(self) -> None:
556 """Disposes of any internal state.
557
558 Currently, this closes the PoolManager and any active ProxyManager,
559 which closes any pooled connections.
560 """
561 self.poolmanager.clear()
562 for proxy in self.proxy_manager.values():
563 proxy.clear()
564
565 def request_url(
566 self, request: PreparedRequest, proxies: dict[str, str] | None
567 ) -> str:
568 """Obtain the url to use when making the final request.
569
570 If the message is being sent through a HTTP proxy, the full URL has to
571 be used. Otherwise, we should only use the path portion of the URL.
572
573 This should not be called from user code, and is only exposed for use
574 when subclassing the
575 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
576
577 :param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
578 :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs.
579 :rtype: str
580 """
581 assert _is_prepared(request)
582
583 proxy = select_proxy(request.url, proxies)
584 scheme = urlparse(request.url).scheme
585
586 is_proxied_http_request = proxy and scheme != "https"
587 using_socks_proxy = False
588 if proxy:
589 proxy_scheme = urlparse(proxy).scheme.lower()
590 using_socks_proxy = proxy_scheme.startswith("socks")
591
592 url = request.path_url
593
594 if is_proxied_http_request and not using_socks_proxy:
595 url = urldefragauth(request.url)
596
597 return url
598
599 def add_headers(self, request: PreparedRequest, **kwargs: Any) -> None:
600 """Add any headers needed by the connection. As of v2.0 this does
601 nothing by default, but is left for overriding by users that subclass
602 the :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
603
604 This should not be called from user code, and is only exposed for use
605 when subclassing the
606 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
607
608 :param request: The :class:`PreparedRequest <PreparedRequest>` to add headers to.
609 :param kwargs: The keyword arguments from the call to send().
610 """
611 pass
612
613 def proxy_headers(self, proxy: str) -> dict[str, str]:
614 """Returns a dictionary of the headers to add to any request sent
615 through a proxy. This works with urllib3 magic to ensure that they are
616 correctly sent to the proxy, rather than in a tunnelled request if
617 CONNECT is being used.
618
619 This should not be called from user code, and is only exposed for use
620 when subclassing the
621 :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
622
623 :param proxy: The url of the proxy being used for this request.
624 :rtype: dict
625 """
626 headers: dict[str, str] = {}
627 username, password = get_auth_from_url(proxy)
628
629 if username:
630 headers["Proxy-Authorization"] = _basic_auth_str(username, password)
631
632 return headers
633
634 def send(
635 self,
636 request: PreparedRequest,
637 stream: bool = False,
638 timeout: _t.TimeoutType = None,
639 verify: _t.VerifyType = True,
640 cert: _t.CertType = None,
641 proxies: dict[str, str] | None = None,
642 ) -> Response:
643 """Sends PreparedRequest object. Returns Response object.
644
645 :param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
646 :param stream: (optional) Whether to stream the request content.
647 :param timeout: (optional) How long to wait for the server to send
648 data before giving up, as a float, or a :ref:`(connect timeout,
649 read timeout) <timeouts>` tuple.
650 :type timeout: float or tuple or urllib3 Timeout object
651 :param verify: (optional) Either a boolean, in which case it controls whether
652 we verify the server's TLS certificate, or a string, in which case it
653 must be a path to a CA bundle to use
654 :param cert: (optional) Any user-provided SSL certificate to be trusted.
655 :param proxies: (optional) The proxies dictionary to apply to the request.
656 :rtype: requests.Response
657 """
658
659 assert _is_prepared(request)
660
661 try:
662 conn = self.get_connection_with_tls_context(
663 request, verify, proxies=proxies, cert=cert
664 )
665 except LocationValueError as e:
666 raise InvalidURL(e, request=request)
667
668 self.cert_verify(conn, request.url, verify, cert)
669 url = self.request_url(request, proxies)
670 self.add_headers(
671 request,
672 stream=stream,
673 timeout=timeout,
674 verify=verify,
675 cert=cert,
676 proxies=proxies,
677 )
678
679 chunked = not (request.body is None or "Content-Length" in request.headers)
680
681 if isinstance(timeout, tuple):
682 try:
683 connect, read = timeout
684 resolved_timeout = TimeoutSauce(connect=connect, read=read)
685 except ValueError:
686 raise ValueError(
687 f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, "
688 f"or a single float to set both timeouts to the same value."
689 )
690 elif isinstance(timeout, TimeoutSauce):
691 resolved_timeout = timeout
692 else:
693 resolved_timeout = TimeoutSauce(connect=timeout, read=timeout)
694
695 try:
696 resp = conn.urlopen(
697 method=request.method,
698 url=url,
699 body=request.body, # type: ignore[arg-type] # urllib3 stubs don't accept Iterable[bytes | str]
700 headers=request.headers, # type: ignore[arg-type] # urllib3#3072
701 redirect=False,
702 assert_same_host=False,
703 preload_content=False,
704 decode_content=False,
705 retries=self.max_retries,
706 timeout=resolved_timeout,
707 chunked=chunked,
708 )
709
710 except (ProtocolError, OSError) as err:
711 raise ConnectionError(err, request=request)
712
713 except MaxRetryError as e:
714 if isinstance(e.reason, ConnectTimeoutError):
715 # TODO: Remove this in 3.0.0: see #2811
716 if not isinstance(e.reason, NewConnectionError):
717 raise ConnectTimeout(e, request=request)
718
719 if isinstance(e.reason, ResponseError):
720 raise RetryError(e, request=request)
721
722 if isinstance(e.reason, _ProxyError):
723 raise ProxyError(e, request=request)
724
725 if isinstance(e.reason, _SSLError):
726 # This branch is for urllib3 v1.22 and later.
727 raise SSLError(e, request=request)
728
729 raise ConnectionError(e, request=request)
730
731 except ClosedPoolError as e:
732 raise ConnectionError(e, request=request)
733
734 except _ProxyError as e:
735 raise ProxyError(e)
736
737 except (_SSLError, _HTTPError) as e:
738 if isinstance(e, _SSLError):
739 # This branch is for urllib3 versions earlier than v1.22
740 raise SSLError(e, request=request)
741 elif isinstance(e, ReadTimeoutError):
742 raise ReadTimeout(e, request=request)
743 elif isinstance(e, _InvalidHeader):
744 raise InvalidHeader(e, request=request)
745 else:
746 raise
747
748 return self.build_response(request, resp)