| """HTTP related errors.""" |
|
|
| import asyncio |
| import warnings |
| from typing import TYPE_CHECKING, Optional, Tuple, Union |
|
|
| from multidict import MultiMapping |
|
|
| from .typedefs import StrOrURL |
|
|
| if TYPE_CHECKING: |
| import ssl |
|
|
| SSLContext = ssl.SSLContext |
| else: |
| try: |
| import ssl |
|
|
| SSLContext = ssl.SSLContext |
| except ImportError: |
| ssl = SSLContext = None |
|
|
| if TYPE_CHECKING: |
| from .client_reqrep import ClientResponse, ConnectionKey, Fingerprint, RequestInfo |
| from .http_parser import RawResponseMessage |
| else: |
| RequestInfo = ClientResponse = ConnectionKey = RawResponseMessage = None |
|
|
| __all__ = ( |
| "ClientError", |
| "ClientConnectionError", |
| "ClientConnectionResetError", |
| "ClientOSError", |
| "ClientConnectorError", |
| "ClientProxyConnectionError", |
| "ClientSSLError", |
| "ClientConnectorDNSError", |
| "ClientConnectorSSLError", |
| "ClientConnectorCertificateError", |
| "ConnectionTimeoutError", |
| "SocketTimeoutError", |
| "ServerConnectionError", |
| "ServerTimeoutError", |
| "ServerDisconnectedError", |
| "ServerFingerprintMismatch", |
| "ClientResponseError", |
| "ClientHttpProxyError", |
| "WSServerHandshakeError", |
| "ContentTypeError", |
| "ClientPayloadError", |
| "InvalidURL", |
| "InvalidUrlClientError", |
| "RedirectClientError", |
| "NonHttpUrlClientError", |
| "InvalidUrlRedirectClientError", |
| "NonHttpUrlRedirectClientError", |
| "WSMessageTypeError", |
| ) |
|
|
|
|
| class ClientError(Exception): |
| """Base class for client connection errors.""" |
|
|
|
|
| class ClientResponseError(ClientError): |
| """Base class for exceptions that occur after getting a response. |
| |
| request_info: An instance of RequestInfo. |
| history: A sequence of responses, if redirects occurred. |
| status: HTTP status code. |
| message: Error message. |
| headers: Response headers. |
| """ |
|
|
| def __init__( |
| self, |
| request_info: RequestInfo, |
| history: Tuple[ClientResponse, ...], |
| *, |
| code: Optional[int] = None, |
| status: Optional[int] = None, |
| message: str = "", |
| headers: Optional[MultiMapping[str]] = None, |
| ) -> None: |
| self.request_info = request_info |
| if code is not None: |
| if status is not None: |
| raise ValueError( |
| "Both code and status arguments are provided; " |
| "code is deprecated, use status instead" |
| ) |
| warnings.warn( |
| "code argument is deprecated, use status instead", |
| DeprecationWarning, |
| stacklevel=2, |
| ) |
| if status is not None: |
| self.status = status |
| elif code is not None: |
| self.status = code |
| else: |
| self.status = 0 |
| self.message = message |
| self.headers = headers |
| self.history = history |
| self.args = (request_info, history) |
|
|
| def __str__(self) -> str: |
| return "{}, message={!r}, url={!r}".format( |
| self.status, |
| self.message, |
| str(self.request_info.real_url), |
| ) |
|
|
| def __repr__(self) -> str: |
| args = f"{self.request_info!r}, {self.history!r}" |
| if self.status != 0: |
| args += f", status={self.status!r}" |
| if self.message != "": |
| args += f", message={self.message!r}" |
| if self.headers is not None: |
| args += f", headers={self.headers!r}" |
| return f"{type(self).__name__}({args})" |
|
|
| @property |
| def code(self) -> int: |
| warnings.warn( |
| "code property is deprecated, use status instead", |
| DeprecationWarning, |
| stacklevel=2, |
| ) |
| return self.status |
|
|
| @code.setter |
| def code(self, value: int) -> None: |
| warnings.warn( |
| "code property is deprecated, use status instead", |
| DeprecationWarning, |
| stacklevel=2, |
| ) |
| self.status = value |
|
|
|
|
| class ContentTypeError(ClientResponseError): |
| """ContentType found is not valid.""" |
|
|
|
|
| class WSServerHandshakeError(ClientResponseError): |
| """websocket server handshake error.""" |
|
|
|
|
| class ClientHttpProxyError(ClientResponseError): |
| """HTTP proxy error. |
| |
| Raised in :class:`aiohttp.connector.TCPConnector` if |
| proxy responds with status other than ``200 OK`` |
| on ``CONNECT`` request. |
| """ |
|
|
|
|
| class TooManyRedirects(ClientResponseError): |
| """Client was redirected too many times.""" |
|
|
|
|
| class ClientConnectionError(ClientError): |
| """Base class for client socket errors.""" |
|
|
|
|
| class ClientConnectionResetError(ClientConnectionError, ConnectionResetError): |
| """ConnectionResetError""" |
|
|
|
|
| class ClientOSError(ClientConnectionError, OSError): |
| """OSError error.""" |
|
|
|
|
| class ClientConnectorError(ClientOSError): |
| """Client connector error. |
| |
| Raised in :class:`aiohttp.connector.TCPConnector` if |
| a connection can not be established. |
| """ |
|
|
| def __init__(self, connection_key: ConnectionKey, os_error: OSError) -> None: |
| self._conn_key = connection_key |
| self._os_error = os_error |
| super().__init__(os_error.errno, os_error.strerror) |
| self.args = (connection_key, os_error) |
|
|
| @property |
| def os_error(self) -> OSError: |
| return self._os_error |
|
|
| @property |
| def host(self) -> str: |
| return self._conn_key.host |
|
|
| @property |
| def port(self) -> Optional[int]: |
| return self._conn_key.port |
|
|
| @property |
| def ssl(self) -> Union[SSLContext, bool, "Fingerprint"]: |
| return self._conn_key.ssl |
|
|
| def __str__(self) -> str: |
| return "Cannot connect to host {0.host}:{0.port} ssl:{1} [{2}]".format( |
| self, "default" if self.ssl is True else self.ssl, self.strerror |
| ) |
|
|
| |
| __reduce__ = BaseException.__reduce__ |
|
|
|
|
| class ClientConnectorDNSError(ClientConnectorError): |
| """DNS resolution failed during client connection. |
| |
| Raised in :class:`aiohttp.connector.TCPConnector` if |
| DNS resolution fails. |
| """ |
|
|
|
|
| class ClientProxyConnectionError(ClientConnectorError): |
| """Proxy connection error. |
| |
| Raised in :class:`aiohttp.connector.TCPConnector` if |
| connection to proxy can not be established. |
| """ |
|
|
|
|
| class UnixClientConnectorError(ClientConnectorError): |
| """Unix connector error. |
| |
| Raised in :py:class:`aiohttp.connector.UnixConnector` |
| if connection to unix socket can not be established. |
| """ |
|
|
| def __init__( |
| self, path: str, connection_key: ConnectionKey, os_error: OSError |
| ) -> None: |
| self._path = path |
| super().__init__(connection_key, os_error) |
|
|
| @property |
| def path(self) -> str: |
| return self._path |
|
|
| def __str__(self) -> str: |
| return "Cannot connect to unix socket {0.path} ssl:{1} [{2}]".format( |
| self, "default" if self.ssl is True else self.ssl, self.strerror |
| ) |
|
|
|
|
| class ServerConnectionError(ClientConnectionError): |
| """Server connection errors.""" |
|
|
|
|
| class ServerDisconnectedError(ServerConnectionError): |
| """Server disconnected.""" |
|
|
| def __init__(self, message: Union[RawResponseMessage, str, None] = None) -> None: |
| if message is None: |
| message = "Server disconnected" |
|
|
| self.args = (message,) |
| self.message = message |
|
|
|
|
| class ServerTimeoutError(ServerConnectionError, asyncio.TimeoutError): |
| """Server timeout error.""" |
|
|
|
|
| class ConnectionTimeoutError(ServerTimeoutError): |
| """Connection timeout error.""" |
|
|
|
|
| class SocketTimeoutError(ServerTimeoutError): |
| """Socket timeout error.""" |
|
|
|
|
| class ServerFingerprintMismatch(ServerConnectionError): |
| """SSL certificate does not match expected fingerprint.""" |
|
|
| def __init__(self, expected: bytes, got: bytes, host: str, port: int) -> None: |
| self.expected = expected |
| self.got = got |
| self.host = host |
| self.port = port |
| self.args = (expected, got, host, port) |
|
|
| def __repr__(self) -> str: |
| return "<{} expected={!r} got={!r} host={!r} port={!r}>".format( |
| self.__class__.__name__, self.expected, self.got, self.host, self.port |
| ) |
|
|
|
|
| class ClientPayloadError(ClientError): |
| """Response payload error.""" |
|
|
|
|
| class InvalidURL(ClientError, ValueError): |
| """Invalid URL. |
| |
| URL used for fetching is malformed, e.g. it doesn't contains host |
| part. |
| """ |
|
|
| |
|
|
| def __init__(self, url: StrOrURL, description: Union[str, None] = None) -> None: |
| |
| |
| self._url = url |
| self._description = description |
|
|
| if description: |
| super().__init__(url, description) |
| else: |
| super().__init__(url) |
|
|
| @property |
| def url(self) -> StrOrURL: |
| return self._url |
|
|
| @property |
| def description(self) -> "str | None": |
| return self._description |
|
|
| def __repr__(self) -> str: |
| return f"<{self.__class__.__name__} {self}>" |
|
|
| def __str__(self) -> str: |
| if self._description: |
| return f"{self._url} - {self._description}" |
| return str(self._url) |
|
|
|
|
| class InvalidUrlClientError(InvalidURL): |
| """Invalid URL client error.""" |
|
|
|
|
| class RedirectClientError(ClientError): |
| """Client redirect error.""" |
|
|
|
|
| class NonHttpUrlClientError(ClientError): |
| """Non http URL client error.""" |
|
|
|
|
| class InvalidUrlRedirectClientError(InvalidUrlClientError, RedirectClientError): |
| """Invalid URL redirect client error.""" |
|
|
|
|
| class NonHttpUrlRedirectClientError(NonHttpUrlClientError, RedirectClientError): |
| """Non http URL redirect client error.""" |
|
|
|
|
| class ClientSSLError(ClientConnectorError): |
| """Base error for ssl.*Errors.""" |
|
|
|
|
| if ssl is not None: |
| cert_errors = (ssl.CertificateError,) |
| cert_errors_bases = ( |
| ClientSSLError, |
| ssl.CertificateError, |
| ) |
|
|
| ssl_errors = (ssl.SSLError,) |
| ssl_error_bases = (ClientSSLError, ssl.SSLError) |
| else: |
| cert_errors = tuple() |
| cert_errors_bases = ( |
| ClientSSLError, |
| ValueError, |
| ) |
|
|
| ssl_errors = tuple() |
| ssl_error_bases = (ClientSSLError,) |
|
|
|
|
| class ClientConnectorSSLError(*ssl_error_bases): |
| """Response ssl error.""" |
|
|
|
|
| class ClientConnectorCertificateError(*cert_errors_bases): |
| """Response certificate error.""" |
|
|
| def __init__( |
| self, connection_key: ConnectionKey, certificate_error: Exception |
| ) -> None: |
| self._conn_key = connection_key |
| self._certificate_error = certificate_error |
| self.args = (connection_key, certificate_error) |
|
|
| @property |
| def certificate_error(self) -> Exception: |
| return self._certificate_error |
|
|
| @property |
| def host(self) -> str: |
| return self._conn_key.host |
|
|
| @property |
| def port(self) -> Optional[int]: |
| return self._conn_key.port |
|
|
| @property |
| def ssl(self) -> bool: |
| return self._conn_key.is_ssl |
|
|
| def __str__(self) -> str: |
| return ( |
| "Cannot connect to host {0.host}:{0.port} ssl:{0.ssl} " |
| "[{0.certificate_error.__class__.__name__}: " |
| "{0.certificate_error.args}]".format(self) |
| ) |
|
|
|
|
| class WSMessageTypeError(TypeError): |
| """WebSocket message type is not valid.""" |
|
|