Initial commit: 首次建仓,建立目录结构
This commit is contained in:
@ -0,0 +1,426 @@
|
||||
"""HTTP related errors."""
|
||||
|
||||
import asyncio
|
||||
import warnings
|
||||
from typing import TYPE_CHECKING, 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: # pragma: no cover
|
||||
ssl = SSLContext = None # type: ignore[assignment]
|
||||
|
||||
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: int | None = None,
|
||||
status: int | None = None,
|
||||
message: str = "",
|
||||
headers: MultiMapping[str] | None = 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 f"{self.status}, message={self.message!r}, url={str(self.request_info.real_url)!r}"
|
||||
|
||||
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) -> int | None:
|
||||
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
|
||||
)
|
||||
|
||||
# OSError.__reduce__ does too much black magick
|
||||
__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: 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 f"<{self.__class__.__name__} expected={self.expected!r} got={self.got!r} host={self.host!r} port={self.port!r}>"
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
# Derive from ValueError for backward compatibility
|
||||
|
||||
def __init__(self, url: StrOrURL, description: str | None = None) -> None:
|
||||
# The type of url is not yarl.URL because the exception can be raised
|
||||
# on URL(url) call
|
||||
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: # pragma: no cover
|
||||
cert_errors = tuple()
|
||||
cert_errors_bases = (
|
||||
ClientSSLError,
|
||||
ValueError,
|
||||
)
|
||||
|
||||
ssl_errors = tuple()
|
||||
ssl_error_bases = (ClientSSLError,)
|
||||
|
||||
|
||||
class ClientConnectorSSLError(*ssl_error_bases): # type: ignore[misc]
|
||||
"""Response ssl error."""
|
||||
|
||||
|
||||
class ClientConnectorCertificateError(*cert_errors_bases): # type: ignore[misc]
|
||||
"""Response certificate error."""
|
||||
|
||||
_conn_key: ConnectionKey
|
||||
|
||||
def __init__(
|
||||
# TODO: If we require ssl in future, this can become ssl.CertificateError
|
||||
self,
|
||||
connection_key: ConnectionKey,
|
||||
certificate_error: Exception,
|
||||
) -> None:
|
||||
if isinstance(certificate_error, cert_errors + (OSError,)):
|
||||
# ssl.CertificateError has errno and strerror, so we should be fine
|
||||
os_error = certificate_error
|
||||
else:
|
||||
os_error = OSError()
|
||||
|
||||
super().__init__(connection_key, os_error)
|
||||
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) -> int | None:
|
||||
return self._conn_key.port
|
||||
|
||||
@property
|
||||
def ssl(self) -> bool:
|
||||
return self._conn_key.is_ssl
|
||||
|
||||
def __str__(self) -> str:
|
||||
return (
|
||||
f"Cannot connect to host {self.host}:{self.port} ssl:{self.ssl} "
|
||||
f"[{self.certificate_error.__class__.__name__}: "
|
||||
f"{self.certificate_error.args}]"
|
||||
)
|
||||
|
||||
|
||||
class WSMessageTypeError(TypeError):
|
||||
"""WebSocket message type is not valid."""
|
||||
Reference in New Issue
Block a user