1"""HTTP related errors.""" 
    2 
    3import asyncio 
    4import warnings 
    5from typing import TYPE_CHECKING, Optional, Tuple, Union 
    6 
    7from multidict import MultiMapping 
    8 
    9from .typedefs import StrOrURL 
    10 
    11if TYPE_CHECKING: 
    12    import ssl 
    13 
    14    SSLContext = ssl.SSLContext 
    15else: 
    16    try: 
    17        import ssl 
    18 
    19        SSLContext = ssl.SSLContext 
    20    except ImportError:  # pragma: no cover 
    21        ssl = SSLContext = None  # type: ignore[assignment] 
    22 
    23if TYPE_CHECKING: 
    24    from .client_reqrep import ClientResponse, ConnectionKey, Fingerprint, RequestInfo 
    25    from .http_parser import RawResponseMessage 
    26else: 
    27    RequestInfo = ClientResponse = ConnectionKey = RawResponseMessage = None 
    28 
    29__all__ = ( 
    30    "ClientError", 
    31    "ClientConnectionError", 
    32    "ClientConnectionResetError", 
    33    "ClientOSError", 
    34    "ClientConnectorError", 
    35    "ClientProxyConnectionError", 
    36    "ClientSSLError", 
    37    "ClientConnectorDNSError", 
    38    "ClientConnectorSSLError", 
    39    "ClientConnectorCertificateError", 
    40    "ConnectionTimeoutError", 
    41    "SocketTimeoutError", 
    42    "ServerConnectionError", 
    43    "ServerTimeoutError", 
    44    "ServerDisconnectedError", 
    45    "ServerFingerprintMismatch", 
    46    "ClientResponseError", 
    47    "ClientHttpProxyError", 
    48    "WSServerHandshakeError", 
    49    "ContentTypeError", 
    50    "ClientPayloadError", 
    51    "InvalidURL", 
    52    "InvalidUrlClientError", 
    53    "RedirectClientError", 
    54    "NonHttpUrlClientError", 
    55    "InvalidUrlRedirectClientError", 
    56    "NonHttpUrlRedirectClientError", 
    57    "WSMessageTypeError", 
    58) 
    59 
    60 
    61class ClientError(Exception): 
    62    """Base class for client connection errors.""" 
    63 
    64 
    65class ClientResponseError(ClientError): 
    66    """Base class for exceptions that occur after getting a response. 
    67 
    68    request_info: An instance of RequestInfo. 
    69    history: A sequence of responses, if redirects occurred. 
    70    status: HTTP status code. 
    71    message: Error message. 
    72    headers: Response headers. 
    73    """ 
    74 
    75    def __init__( 
    76        self, 
    77        request_info: RequestInfo, 
    78        history: Tuple[ClientResponse, ...], 
    79        *, 
    80        code: Optional[int] = None, 
    81        status: Optional[int] = None, 
    82        message: str = "", 
    83        headers: Optional[MultiMapping[str]] = None, 
    84    ) -> None: 
    85        self.request_info = request_info 
    86        if code is not None: 
    87            if status is not None: 
    88                raise ValueError( 
    89                    "Both code and status arguments are provided; " 
    90                    "code is deprecated, use status instead" 
    91                ) 
    92            warnings.warn( 
    93                "code argument is deprecated, use status instead", 
    94                DeprecationWarning, 
    95                stacklevel=2, 
    96            ) 
    97        if status is not None: 
    98            self.status = status 
    99        elif code is not None: 
    100            self.status = code 
    101        else: 
    102            self.status = 0 
    103        self.message = message 
    104        self.headers = headers 
    105        self.history = history 
    106        self.args = (request_info, history) 
    107 
    108    def __str__(self) -> str: 
    109        return "{}, message={!r}, url={!r}".format( 
    110            self.status, 
    111            self.message, 
    112            str(self.request_info.real_url), 
    113        ) 
    114 
    115    def __repr__(self) -> str: 
    116        args = f"{self.request_info!r}, {self.history!r}" 
    117        if self.status != 0: 
    118            args += f", status={self.status!r}" 
    119        if self.message != "": 
    120            args += f", message={self.message!r}" 
    121        if self.headers is not None: 
    122            args += f", headers={self.headers!r}" 
    123        return f"{type(self).__name__}({args})" 
    124 
    125    @property 
    126    def code(self) -> int: 
    127        warnings.warn( 
    128            "code property is deprecated, use status instead", 
    129            DeprecationWarning, 
    130            stacklevel=2, 
    131        ) 
    132        return self.status 
    133 
    134    @code.setter 
    135    def code(self, value: int) -> None: 
    136        warnings.warn( 
    137            "code property is deprecated, use status instead", 
    138            DeprecationWarning, 
    139            stacklevel=2, 
    140        ) 
    141        self.status = value 
    142 
    143 
    144class ContentTypeError(ClientResponseError): 
    145    """ContentType found is not valid.""" 
    146 
    147 
    148class WSServerHandshakeError(ClientResponseError): 
    149    """websocket server handshake error.""" 
    150 
    151 
    152class ClientHttpProxyError(ClientResponseError): 
    153    """HTTP proxy error. 
    154 
    155    Raised in :class:`aiohttp.connector.TCPConnector` if 
    156    proxy responds with status other than ``200 OK`` 
    157    on ``CONNECT`` request. 
    158    """ 
    159 
    160 
    161class TooManyRedirects(ClientResponseError): 
    162    """Client was redirected too many times.""" 
    163 
    164 
    165class ClientConnectionError(ClientError): 
    166    """Base class for client socket errors.""" 
    167 
    168 
    169class ClientConnectionResetError(ClientConnectionError, ConnectionResetError): 
    170    """ConnectionResetError""" 
    171 
    172 
    173class ClientOSError(ClientConnectionError, OSError): 
    174    """OSError error.""" 
    175 
    176 
    177class ClientConnectorError(ClientOSError): 
    178    """Client connector error. 
    179 
    180    Raised in :class:`aiohttp.connector.TCPConnector` if 
    181        a connection can not be established. 
    182    """ 
    183 
    184    def __init__(self, connection_key: ConnectionKey, os_error: OSError) -> None: 
    185        self._conn_key = connection_key 
    186        self._os_error = os_error 
    187        super().__init__(os_error.errno, os_error.strerror) 
    188        self.args = (connection_key, os_error) 
    189 
    190    @property 
    191    def os_error(self) -> OSError: 
    192        return self._os_error 
    193 
    194    @property 
    195    def host(self) -> str: 
    196        return self._conn_key.host 
    197 
    198    @property 
    199    def port(self) -> Optional[int]: 
    200        return self._conn_key.port 
    201 
    202    @property 
    203    def ssl(self) -> Union[SSLContext, bool, "Fingerprint"]: 
    204        return self._conn_key.ssl 
    205 
    206    def __str__(self) -> str: 
    207        return "Cannot connect to host {0.host}:{0.port} ssl:{1} [{2}]".format( 
    208            self, "default" if self.ssl is True else self.ssl, self.strerror 
    209        ) 
    210 
    211    # OSError.__reduce__ does too much black magick 
    212    __reduce__ = BaseException.__reduce__ 
    213 
    214 
    215class ClientConnectorDNSError(ClientConnectorError): 
    216    """DNS resolution failed during client connection. 
    217 
    218    Raised in :class:`aiohttp.connector.TCPConnector` if 
    219        DNS resolution fails. 
    220    """ 
    221 
    222 
    223class ClientProxyConnectionError(ClientConnectorError): 
    224    """Proxy connection error. 
    225 
    226    Raised in :class:`aiohttp.connector.TCPConnector` if 
    227        connection to proxy can not be established. 
    228    """ 
    229 
    230 
    231class UnixClientConnectorError(ClientConnectorError): 
    232    """Unix connector error. 
    233 
    234    Raised in :py:class:`aiohttp.connector.UnixConnector` 
    235    if connection to unix socket can not be established. 
    236    """ 
    237 
    238    def __init__( 
    239        self, path: str, connection_key: ConnectionKey, os_error: OSError 
    240    ) -> None: 
    241        self._path = path 
    242        super().__init__(connection_key, os_error) 
    243 
    244    @property 
    245    def path(self) -> str: 
    246        return self._path 
    247 
    248    def __str__(self) -> str: 
    249        return "Cannot connect to unix socket {0.path} ssl:{1} [{2}]".format( 
    250            self, "default" if self.ssl is True else self.ssl, self.strerror 
    251        ) 
    252 
    253 
    254class ServerConnectionError(ClientConnectionError): 
    255    """Server connection errors.""" 
    256 
    257 
    258class ServerDisconnectedError(ServerConnectionError): 
    259    """Server disconnected.""" 
    260 
    261    def __init__(self, message: Union[RawResponseMessage, str, None] = None) -> None: 
    262        if message is None: 
    263            message = "Server disconnected" 
    264 
    265        self.args = (message,) 
    266        self.message = message 
    267 
    268 
    269class ServerTimeoutError(ServerConnectionError, asyncio.TimeoutError): 
    270    """Server timeout error.""" 
    271 
    272 
    273class ConnectionTimeoutError(ServerTimeoutError): 
    274    """Connection timeout error.""" 
    275 
    276 
    277class SocketTimeoutError(ServerTimeoutError): 
    278    """Socket timeout error.""" 
    279 
    280 
    281class ServerFingerprintMismatch(ServerConnectionError): 
    282    """SSL certificate does not match expected fingerprint.""" 
    283 
    284    def __init__(self, expected: bytes, got: bytes, host: str, port: int) -> None: 
    285        self.expected = expected 
    286        self.got = got 
    287        self.host = host 
    288        self.port = port 
    289        self.args = (expected, got, host, port) 
    290 
    291    def __repr__(self) -> str: 
    292        return "<{} expected={!r} got={!r} host={!r} port={!r}>".format( 
    293            self.__class__.__name__, self.expected, self.got, self.host, self.port 
    294        ) 
    295 
    296 
    297class ClientPayloadError(ClientError): 
    298    """Response payload error.""" 
    299 
    300 
    301class InvalidURL(ClientError, ValueError): 
    302    """Invalid URL. 
    303 
    304    URL used for fetching is malformed, e.g. it doesn't contains host 
    305    part. 
    306    """ 
    307 
    308    # Derive from ValueError for backward compatibility 
    309 
    310    def __init__(self, url: StrOrURL, description: Union[str, None] = None) -> None: 
    311        # The type of url is not yarl.URL because the exception can be raised 
    312        # on URL(url) call 
    313        self._url = url 
    314        self._description = description 
    315 
    316        if description: 
    317            super().__init__(url, description) 
    318        else: 
    319            super().__init__(url) 
    320 
    321    @property 
    322    def url(self) -> StrOrURL: 
    323        return self._url 
    324 
    325    @property 
    326    def description(self) -> "str | None": 
    327        return self._description 
    328 
    329    def __repr__(self) -> str: 
    330        return f"<{self.__class__.__name__} {self}>" 
    331 
    332    def __str__(self) -> str: 
    333        if self._description: 
    334            return f"{self._url} - {self._description}" 
    335        return str(self._url) 
    336 
    337 
    338class InvalidUrlClientError(InvalidURL): 
    339    """Invalid URL client error.""" 
    340 
    341 
    342class RedirectClientError(ClientError): 
    343    """Client redirect error.""" 
    344 
    345 
    346class NonHttpUrlClientError(ClientError): 
    347    """Non http URL client error.""" 
    348 
    349 
    350class InvalidUrlRedirectClientError(InvalidUrlClientError, RedirectClientError): 
    351    """Invalid URL redirect client error.""" 
    352 
    353 
    354class NonHttpUrlRedirectClientError(NonHttpUrlClientError, RedirectClientError): 
    355    """Non http URL redirect client error.""" 
    356 
    357 
    358class ClientSSLError(ClientConnectorError): 
    359    """Base error for ssl.*Errors.""" 
    360 
    361 
    362if ssl is not None: 
    363    cert_errors = (ssl.CertificateError,) 
    364    cert_errors_bases = ( 
    365        ClientSSLError, 
    366        ssl.CertificateError, 
    367    ) 
    368 
    369    ssl_errors = (ssl.SSLError,) 
    370    ssl_error_bases = (ClientSSLError, ssl.SSLError) 
    371else:  # pragma: no cover 
    372    cert_errors = tuple() 
    373    cert_errors_bases = ( 
    374        ClientSSLError, 
    375        ValueError, 
    376    ) 
    377 
    378    ssl_errors = tuple() 
    379    ssl_error_bases = (ClientSSLError,) 
    380 
    381 
    382class ClientConnectorSSLError(*ssl_error_bases):  # type: ignore[misc] 
    383    """Response ssl error.""" 
    384 
    385 
    386class ClientConnectorCertificateError(*cert_errors_bases):  # type: ignore[misc] 
    387    """Response certificate error.""" 
    388 
    389    def __init__( 
    390        self, connection_key: ConnectionKey, certificate_error: Exception 
    391    ) -> None: 
    392        self._conn_key = connection_key 
    393        self._certificate_error = certificate_error 
    394        self.args = (connection_key, certificate_error) 
    395 
    396    @property 
    397    def certificate_error(self) -> Exception: 
    398        return self._certificate_error 
    399 
    400    @property 
    401    def host(self) -> str: 
    402        return self._conn_key.host 
    403 
    404    @property 
    405    def port(self) -> Optional[int]: 
    406        return self._conn_key.port 
    407 
    408    @property 
    409    def ssl(self) -> bool: 
    410        return self._conn_key.is_ssl 
    411 
    412    def __str__(self) -> str: 
    413        return ( 
    414            "Cannot connect to host {0.host}:{0.port} ssl:{0.ssl} " 
    415            "[{0.certificate_error.__class__.__name__}: " 
    416            "{0.certificate_error.args}]".format(self) 
    417        ) 
    418 
    419 
    420class WSMessageTypeError(TypeError): 
    421    """WebSocket message type is not valid."""