Source code for websockets.exceptions

"""
:mod:`websockets.exceptions` defines the following exception hierarchy:

* :exc:`WebSocketException`
    * :exc:`ConnectionClosed`
        * :exc:`ConnectionClosedError`
        * :exc:`ConnectionClosedOK`
    * :exc:`InvalidHandshake`
        * :exc:`SecurityError`
        * :exc:`InvalidMessage`
        * :exc:`InvalidHeader`
            * :exc:`InvalidHeaderFormat`
            * :exc:`InvalidHeaderValue`
            * :exc:`InvalidOrigin`
            * :exc:`InvalidUpgrade`
        * :exc:`InvalidStatusCode`
        * :exc:`NegotiationError`
            * :exc:`DuplicateParameter`
            * :exc:`InvalidParameterName`
            * :exc:`InvalidParameterValue`
        * :exc:`AbortHandshake`
        * :exc:`RedirectHandshake`
    * :exc:`InvalidState`
    * :exc:`InvalidURI`
    * :exc:`PayloadTooBig`
    * :exc:`ProtocolError`

"""

import http
from typing import Optional

from .datastructures import Headers, HeadersLike


__all__ = [
    "WebSocketException",
    "ConnectionClosed",
    "ConnectionClosedError",
    "ConnectionClosedOK",
    "InvalidHandshake",
    "SecurityError",
    "InvalidMessage",
    "InvalidHeader",
    "InvalidHeaderFormat",
    "InvalidHeaderValue",
    "InvalidOrigin",
    "InvalidUpgrade",
    "InvalidStatusCode",
    "NegotiationError",
    "DuplicateParameter",
    "InvalidParameterName",
    "InvalidParameterValue",
    "AbortHandshake",
    "RedirectHandshake",
    "InvalidState",
    "InvalidURI",
    "PayloadTooBig",
    "ProtocolError",
    "WebSocketProtocolError",
]


[docs]class WebSocketException(Exception): """ Base class for all exceptions defined by :mod:`websockets`. """
# See https://www.iana.org/assignments/websocket/websocket.xhtml CLOSE_CODES = { 1000: "OK", 1001: "going away", 1002: "protocol error", 1003: "unsupported type", # 1004 is reserved 1005: "no status code [internal]", 1006: "connection closed abnormally [internal]", 1007: "invalid data", 1008: "policy violation", 1009: "message too big", 1010: "extension required", 1011: "unexpected error", 1012: "service restart", 1013: "try again later", 1014: "bad gateway", 1015: "TLS failure [internal]", } def format_close(code: int, reason: str) -> str: """ Display a human-readable version of the close code and reason. """ if 3000 <= code < 4000: explanation = "registered" elif 4000 <= code < 5000: explanation = "private use" else: explanation = CLOSE_CODES.get(code, "unknown") result = f"code = {code} ({explanation}), " if reason: result += f"reason = {reason}" else: result += "no reason" return result
[docs]class ConnectionClosed(WebSocketException): """ Raised when trying to interact with a closed connection. Provides the connection close code and reason in its ``code`` and ``reason`` attributes respectively. """ def __init__(self, code: int, reason: str) -> None: self.code = code self.reason = reason super().__init__(format_close(code, reason))
[docs]class ConnectionClosedError(ConnectionClosed): """ Like :exc:`ConnectionClosed`, when the connection terminated with an error. This means the close code is different from 1000 (OK) and 1001 (going away). """ def __init__(self, code: int, reason: str) -> None: assert code != 1000 and code != 1001 super().__init__(code, reason)
[docs]class ConnectionClosedOK(ConnectionClosed): """ Like :exc:`ConnectionClosed`, when the connection terminated properly. This means the close code is 1000 (OK) or 1001 (going away). """ def __init__(self, code: int, reason: str) -> None: assert code == 1000 or code == 1001 super().__init__(code, reason)
[docs]class InvalidHandshake(WebSocketException): """ Raised during the handshake when the WebSocket connection fails. """
[docs]class SecurityError(InvalidHandshake): """ Raised when a handshake request or response breaks a security rule. Security limits are hard coded. """
[docs]class InvalidMessage(InvalidHandshake): """ Raised when a handshake request or response is malformed. """
[docs]class InvalidHeader(InvalidHandshake): """ Raised when a HTTP header doesn't have a valid format or value. """ def __init__(self, name: str, value: Optional[str] = None) -> None: self.name = name self.value = value if value is None: message = f"missing {name} header" elif value == "": message = f"empty {name} header" else: message = f"invalid {name} header: {value}" super().__init__(message)
[docs]class InvalidHeaderFormat(InvalidHeader): """ Raised when a HTTP header cannot be parsed. The format of the header doesn't match the grammar for that header. """ def __init__(self, name: str, error: str, header: str, pos: int) -> None: self.name = name error = f"{error} at {pos} in {header}" super().__init__(name, error)
[docs]class InvalidHeaderValue(InvalidHeader): """ Raised when a HTTP header has a wrong value. The format of the header is correct but a value isn't acceptable. """
[docs]class InvalidOrigin(InvalidHeader): """ Raised when the Origin header in a request isn't allowed. """ def __init__(self, origin: Optional[str]) -> None: super().__init__("Origin", origin)
[docs]class InvalidUpgrade(InvalidHeader): """ Raised when the Upgrade or Connection header isn't correct. """
[docs]class InvalidStatusCode(InvalidHandshake): """ Raised when a handshake response status code is invalid. The integer status code is available in the ``status_code`` attribute. """ def __init__(self, status_code: int) -> None: self.status_code = status_code message = f"server rejected WebSocket connection: HTTP {status_code}" super().__init__(message)
[docs]class NegotiationError(InvalidHandshake): """ Raised when negotiating an extension fails. """
[docs]class DuplicateParameter(NegotiationError): """ Raised when a parameter name is repeated in an extension header. """ def __init__(self, name: str) -> None: self.name = name message = f"duplicate parameter: {name}" super().__init__(message)
[docs]class InvalidParameterName(NegotiationError): """ Raised when a parameter name in an extension header is invalid. """ def __init__(self, name: str) -> None: self.name = name message = f"invalid parameter name: {name}" super().__init__(message)
[docs]class InvalidParameterValue(NegotiationError): """ Raised when a parameter value in an extension header is invalid. """ def __init__(self, name: str, value: Optional[str]) -> None: self.name = name self.value = value if value is None: message = f"missing value for parameter {name}" elif value == "": message = f"empty value for parameter {name}" else: message = f"invalid value for parameter {name}: {value}" super().__init__(message)
[docs]class AbortHandshake(InvalidHandshake): """ Raised to abort the handshake on purpose and return a HTTP response. This exception is an implementation detail. The public API is :meth:`~legacy.server.WebSocketServerProtocol.process_request`. """ def __init__( self, status: http.HTTPStatus, headers: HeadersLike, body: bytes = b"", ) -> None: self.status = status self.headers = Headers(headers) self.body = body message = f"HTTP {status}, {len(self.headers)} headers, {len(body)} bytes" super().__init__(message)
[docs]class RedirectHandshake(InvalidHandshake): """ Raised when a handshake gets redirected. This exception is an implementation detail. """ def __init__(self, uri: str) -> None: self.uri = uri def __str__(self) -> str: return f"redirect to {self.uri}"
[docs]class InvalidState(WebSocketException, AssertionError): """ Raised when an operation is forbidden in the current state. This exception is an implementation detail. It should never be raised in normal circumstances. """
[docs]class InvalidURI(WebSocketException): """ Raised when connecting to an URI that isn't a valid WebSocket URI. """ def __init__(self, uri: str) -> None: self.uri = uri message = "{} isn't a valid URI".format(uri) super().__init__(message)
[docs]class PayloadTooBig(WebSocketException): """ Raised when receiving a frame with a payload exceeding the maximum size. """
[docs]class ProtocolError(WebSocketException): """ Raised when a frame breaks the protocol. """
WebSocketProtocolError = ProtocolError # for backwards compatibility