Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
adapters.py749 linesDownload Raw Back to requests
1"""2requests.adapters3~~~~~~~~~~~~~~~~~4 5This module contains the transport adapters that Requests uses to define6and maintain connections.7"""8 9from __future__ import annotations10 11import os.path12import socket  # noqa: F401  # type: ignore[reportUnusedImport]13import typing14import warnings15from typing import Any16 17from urllib3.exceptions import (18    ClosedPoolError,19    ConnectTimeoutError,20    LocationValueError,21    MaxRetryError,22    NewConnectionError,23    ProtocolError,24    ReadTimeoutError,25    ResponseError,26)27from urllib3.exceptions import HTTPError as _HTTPError28from urllib3.exceptions import InvalidHeader as _InvalidHeader29from urllib3.exceptions import ProxyError as _ProxyError30from urllib3.exceptions import SSLError as _SSLError31from urllib3.poolmanager import PoolManager, proxy_from_url32from urllib3.util import Timeout as TimeoutSauce33from urllib3.util import parse_url34from urllib3.util.retry import Retry35 36from .auth import _basic_auth_str  # type: ignore[reportPrivateUsage]37from .compat import basestring, urlparse38from .cookies import extract_cookies_to_jar39from .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 Response52from .structures import CaseInsensitiveDict53from .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 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 urllib3.connectionpool import HTTPConnectionPool72    from urllib3.poolmanager import PoolManager as _PoolManager73 74    from . import _types as _t75    from .models import PreparedRequest76 77from ._types import is_prepared as _is_prepared78 79DEFAULT_POOLBLOCK = False80DEFAULT_POOLSIZE = 1081DEFAULT_RETRIES = 082DEFAULT_POOL_TIMEOUT = None83 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.port96 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"] = verify103        else:104            pool_kwargs["ca_cert_dir"] = verify105    pool_kwargs["cert_reqs"] = cert_reqs106    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 client112            # cert path113            pool_kwargs["cert_file"] = client_cert114    host_params = {115        "scheme": scheme,116        "host": parsed_request_url.hostname,117        "port": port,118    }119    return host_params, pool_kwargs120 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 send142            data before giving up, as a float, or a :ref:`(connect timeout,143            read timeout) <timeouts>` tuple.144        :type timeout: float or tuple145        :param verify: (optional) Either a boolean, in which case it controls whether we verify146            the server's TLS certificate, or a string, in which case it must be a path147            to a CA bundle to use148        :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 NotImplementedError152 153    def close(self) -> None:154        """Cleans up adapter specific items."""155        raise NotImplementedError156 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 and162    HTTPS urls by implementing the Transport Adapter interface. This class will163    usually be created by the :class:`Session <Session>` class under the164    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 connection169        should attempt. Note, this applies only to failed DNS lookups, socket170        connections and connection timeouts, never to requests where data has171        made it to the server. By default, Requests does not retry failed172        connections. If you need granular control over the conditions under173        which we retry a request, import urllib3's ``Retry`` class and pass174        that instead.175    :param pool_block: Whether the connection pool should block for connections.176 177    Usage::178 179      >>> import requests180      >>> 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: Retry194    config: dict[str, Any]195    proxy_manager: dict[str, Any]196    _pool_connections: int197    _pool_maxsize: int198    _pool_block: bool199    poolmanager: _PoolManager200 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_connections218        self._pool_maxsize = pool_maxsize219        self._pool_block = pool_block220 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__ because228        # 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_block237        )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 only249        exposed for use when subclassing the250        :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 pickling258        self._pool_connections = connections259        self._pool_maxsize = maxsize260        self._pool_block = block261 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 only273        exposed for use when subclassing the274        :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: ProxyManager279        :rtype: urllib3.ProxyManager280        """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 manager306 307    def cert_verify(308        self, conn: Any, url: str, verify: _t.VerifyType, cert: _t.CertType309    ) -> None:310        """Verify a SSL certificate. This method should not be called from user311        code, and is only exposed for use when subclassing the312        :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 verify317            the server's TLS certificate, or a string, in which case it must be a path318            to a CA bundle to use319        :param cert: The SSL certificate to verify.320        """321        if url.lower().startswith("https") and verify:322            cert_loc = None323 324            # Allow self-specified cert location.325            if verify is not True:326                cert_loc = verify327 328            if not cert_loc:329                cert_loc = DEFAULT_CA_BUNDLE_PATH330 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_loc341            else:342                conn.ca_cert_dir = cert_loc343        else:344            conn.cert_reqs = "CERT_NONE"345            conn.ca_certs = None346            conn.ca_cert_dir = None347 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 = cert354                conn.key_file = None355            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 urllib3367        response. This should not be called from user code, and is only exposed368        for use when subclassing the369        :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.Response374        """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 = resp387        response.reason = response.raw.reason388 389        if isinstance(req.url, bytes):390            response.url = req.url.decode("utf-8")391        else:392            response.url = req.url393 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 = req399        response.connection = self400 401        return response402 403    def build_connection_pool_key_attributes(404        self, request: PreparedRequest, verify: _t.VerifyType, cert: _t.CertType = None405    ) -> 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 values410        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 of413        this writing, use the following to determine what keys may be in that414        dictionary:415 416        * If ``verify`` is ``True``, ``"ssl_context"`` will be set and will be the417          default Requests SSL Context418        * If ``verify`` is ``False``, ``"ssl_context"`` will not be set but419          ``"cert_reqs"`` will be set420        * 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 recognized422          by :py:func:`os.path.isdir`, otherwise ``"ca_cert_dir"`` will be423          set.424        * If ``"cert"`` is specified, ``"cert_file"`` will always be set. If425          ``"cert"`` is a tuple with a second item, ``"key_file"`` will also426          be present427 428        To override these settings, one may subclass this class, call this429        method and use the above logic to change parameters as desired. For430        example, if one wishes to use a custom :py:class:`ssl.SSLContext` one431        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 whether440            we verify the server's TLS certificate, or a string, in which case it441            must be a path to a CA bundle to use.442        :param cert:443            (optional) Any user-provided SSL certificate for client444            authentication (a.k.a., mTLS). This may be a string (i.e., just445            the path to a file which holds both certificate and key) or a446            tuple of length 2 with the certificate file path and key file447            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. The451            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 use464        when subclassing the :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.465 466        :param request:467            The :class:`PreparedRequest <PreparedRequest>` object to be sent468            over the connection.469        :param verify:470            Either a boolean, in which case it controls whether we verify the471            server's TLS certificate, or a string, in which case it must be a472            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 client477            authentication (a.k.a., mTLS).478        :rtype:479            urllib3.HTTPConnectionPool480        """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_kwargs503            )504        else:505            # Only scheme should be lower case506            conn = self.poolmanager.connection_from_host(507                **host_params, pool_kwargs=pool_kwargs508            )509 510        return conn511 512    def get_connection(513        self, url: str, proxies: dict[str, str] | None = None514    ) -> 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 be519        called from user code, and is only exposed for use when subclassing the520        :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.HTTPConnectionPool525        """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 case549            parsed = urlparse(url)550            url = parsed.geturl()551            conn = self.poolmanager.connection_from_url(url)552 553        return conn554 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] | None567    ) -> 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 to571        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 use574        when subclassing the575        :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: str580        """581        assert _is_prepared(request)582 583        proxy = select_proxy(request.url, proxies)584        scheme = urlparse(request.url).scheme585 586        is_proxied_http_request = proxy and scheme != "https"587        using_socks_proxy = False588        if proxy:589            proxy_scheme = urlparse(proxy).scheme.lower()590            using_socks_proxy = proxy_scheme.startswith("socks")591 592        url = request.path_url593 594        if is_proxied_http_request and not using_socks_proxy:595            url = urldefragauth(request.url)596 597        return url598 599    def add_headers(self, request: PreparedRequest, **kwargs: Any) -> None:600        """Add any headers needed by the connection. As of v2.0 this does601        nothing by default, but is left for overriding by users that subclass602        the :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.603 604        This should not be called from user code, and is only exposed for use605        when subclassing the606        :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        pass612 613    def proxy_headers(self, proxy: str) -> dict[str, str]:614        """Returns a dictionary of the headers to add to any request sent615        through a proxy. This works with urllib3 magic to ensure that they are616        correctly sent to the proxy, rather than in a tunnelled request if617        CONNECT is being used.618 619        This should not be called from user code, and is only exposed for use620        when subclassing the621        :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.622 623        :param proxy: The url of the proxy being used for this request.624        :rtype: dict625        """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 headers633 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 send648            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 object651        :param verify: (optional) Either a boolean, in which case it controls whether652            we verify the server's TLS certificate, or a string, in which case it653            must be a path to a CA bundle to use654        :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.Response657        """658 659        assert _is_prepared(request)660 661        try:662            conn = self.get_connection_with_tls_context(663                request, verify, proxies=proxies, cert=cert664            )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 = timeout684                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 = timeout692        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#3072701                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 #2811716                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.22740                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                raise747 748        return self.build_response(request, resp)749 
codekingpro/portable-devtools · Team Ai