← requests  /  src/requests/adapters.py

1
"""
2
requests.adapters
3
~~~~~~~~~~~~~~~~~
4
5
This module contains the transport adapters that Requests uses to define
6
and maintain connections.
7
"""
8
9
from __future__ import annotations
10
11
import os.path
12
import socket  # noqa: F401  # type: ignore[reportUnusedImport]
13
import typing
14
import warnings
15
from typing import Any
16
17
from urllib3.exceptions import (
18
    ClosedPoolError,
19
    ConnectTimeoutError,
20
    LocationValueError,
21
    MaxRetryError,
22
    NewConnectionError,
23
    ProtocolError,
24
    ReadTimeoutError,
25
    ResponseError,
26
)
27
from urllib3.exceptions import HTTPError as _HTTPError
28
from urllib3.exceptions import InvalidHeader as _InvalidHeader
29
from urllib3.exceptions import ProxyError as _ProxyError
30
from urllib3.exceptions import SSLError as _SSLError
31
from urllib3.poolmanager import PoolManager, proxy_from_url
32
from urllib3.util import Timeout as TimeoutSauce
33
from urllib3.util import parse_url
34
from urllib3.util.retry import Retry
35
36
from .auth import _basic_auth_str  # type: ignore[reportPrivateUsage]
37
from .compat import basestring, urlparse
38
from .cookies import extract_cookies_to_jar
39
from .exceptions import (
40
    ConnectionError,
41
    ConnectTimeout,
42
    InvalidHeader,
43
    InvalidProxyURL,
44
    InvalidSchema,
45
    InvalidURL,
46
    ProxyError,
47
    ReadTimeout,
48
    RetryError,
49
    SSLError,
50
)
51
from .models import Response
52
from .structures import CaseInsensitiveDict
53
from .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
62
try:
63
    from urllib3.contrib.socks import SOCKSProxyManager  # type: ignore[assignment]
64
except ImportError:
65
66
    def SOCKSProxyManager(*args: Any, **kwargs: Any) -> None:
67
        raise InvalidSchema("Missing dependencies for SOCKS support.")
68
69
70
if typing.TYPE_CHECKING:
71
    from urllib3.connectionpool import HTTPConnectionPool
72
    from urllib3.poolmanager import PoolManager as _PoolManager
73
74
    from . import _types as _t
75
    from .models import PreparedRequest
76
77
from ._types import is_prepared as _is_prepared
78
79
DEFAULT_POOLBLOCK = False
80
DEFAULT_POOLSIZE = 10
81
DEFAULT_RETRIES = 0
82
DEFAULT_POOL_TIMEOUT = None
83
84
85
def _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
122
class 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
158
class 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)
749