207 lines
5.2 KiB
Python
207 lines
5.2 KiB
Python
__all__ = [
|
|
'AbortHandshake', 'ConnectionClosed', 'DuplicateParameter',
|
|
'InvalidHandshake', 'InvalidHeader', 'InvalidHeaderFormat',
|
|
'InvalidHeaderValue', 'InvalidMessage', 'InvalidOrigin',
|
|
'InvalidParameterName', 'InvalidParameterValue', 'InvalidState',
|
|
'InvalidStatusCode', 'InvalidUpgrade', 'InvalidURI', 'NegotiationError',
|
|
'PayloadTooBig', 'WebSocketProtocolError',
|
|
]
|
|
|
|
|
|
class InvalidHandshake(Exception):
|
|
"""
|
|
Exception raised when a handshake request or response is invalid.
|
|
|
|
"""
|
|
|
|
|
|
class AbortHandshake(InvalidHandshake):
|
|
"""
|
|
Exception raised to abort a handshake and return a HTTP response.
|
|
|
|
"""
|
|
def __init__(self, status, headers, body=b''):
|
|
self.status = status
|
|
self.headers = headers
|
|
self.body = body
|
|
message = "HTTP {}, {} headers, {} bytes".format(
|
|
status, len(headers), len(body))
|
|
super().__init__(message)
|
|
|
|
|
|
class InvalidMessage(InvalidHandshake):
|
|
"""
|
|
Exception raised when the HTTP message in a handshake request is malformed.
|
|
|
|
"""
|
|
|
|
|
|
class InvalidHeader(InvalidHandshake):
|
|
"""
|
|
Exception raised when a HTTP header doesn't have a valid format or value.
|
|
|
|
"""
|
|
def __init__(self, name, value):
|
|
if value:
|
|
message = "Invalid {} header: {}".format(name, value)
|
|
else:
|
|
message = "Missing or empty {} header".format(name)
|
|
super().__init__(message)
|
|
|
|
|
|
class InvalidHeaderFormat(InvalidHeader):
|
|
"""
|
|
Exception raised when a Sec-WebSocket-* HTTP header cannot be parsed.
|
|
|
|
"""
|
|
def __init__(self, name, error, string, pos):
|
|
error = "{} at {} in {}".format(error, pos, string)
|
|
super().__init__(name, error)
|
|
|
|
|
|
class InvalidHeaderValue(InvalidHeader):
|
|
"""
|
|
Exception raised when a Sec-WebSocket-* HTTP header has a wrong value.
|
|
|
|
"""
|
|
|
|
|
|
class InvalidUpgrade(InvalidHeader):
|
|
"""
|
|
Exception raised when a Upgrade or Connection header isn't correct.
|
|
|
|
"""
|
|
|
|
|
|
class InvalidOrigin(InvalidHeader):
|
|
"""
|
|
Exception raised when the Origin header in a request isn't allowed.
|
|
|
|
"""
|
|
def __init__(self, origin):
|
|
super().__init__('Origin', origin)
|
|
|
|
|
|
class InvalidStatusCode(InvalidHandshake):
|
|
"""
|
|
Exception raised when a handshake response status code is invalid.
|
|
|
|
Provides the integer status code in its ``status_code`` attribute.
|
|
|
|
"""
|
|
def __init__(self, status_code):
|
|
self.status_code = status_code
|
|
message = "Status code not 101: {}".format(status_code)
|
|
super().__init__(message)
|
|
|
|
|
|
class NegotiationError(InvalidHandshake):
|
|
"""
|
|
Exception raised when negociating an extension fails.
|
|
|
|
"""
|
|
|
|
|
|
class InvalidParameterName(NegotiationError):
|
|
"""
|
|
Exception raised when a parameter name in an extension header is invalid.
|
|
|
|
"""
|
|
def __init__(self, name):
|
|
self.name = name
|
|
message = "Invalid parameter name: {}".format(name)
|
|
super().__init__(message)
|
|
|
|
|
|
class InvalidParameterValue(NegotiationError):
|
|
"""
|
|
Exception raised when a parameter value in an extension header is invalid.
|
|
|
|
"""
|
|
def __init__(self, name, value):
|
|
self.name = name
|
|
self.value = value
|
|
message = "Invalid value for parameter {}: {}".format(name, value)
|
|
super().__init__(message)
|
|
|
|
|
|
class DuplicateParameter(NegotiationError):
|
|
"""
|
|
Exception raised when a parameter name is repeated in an extension header.
|
|
|
|
"""
|
|
def __init__(self, name):
|
|
self.name = name
|
|
message = "Duplicate parameter: {}".format(name)
|
|
super().__init__(message)
|
|
|
|
|
|
class InvalidState(Exception):
|
|
"""
|
|
Exception raised when an operation is forbidden in the current state.
|
|
|
|
"""
|
|
|
|
|
|
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",
|
|
1015: "TLS failure [internal]",
|
|
}
|
|
|
|
|
|
class ConnectionClosed(InvalidState):
|
|
"""
|
|
Exception raised when trying to read or write on a closed connection.
|
|
|
|
Provides the connection close code and reason in its ``code`` and
|
|
``reason`` attributes respectively.
|
|
|
|
"""
|
|
def __init__(self, code, reason):
|
|
self.code = code
|
|
self.reason = reason
|
|
message = "WebSocket connection is closed: "
|
|
if 3000 <= code < 4000:
|
|
explanation = "registered"
|
|
elif 4000 <= code < 5000:
|
|
explanation = "private use"
|
|
else:
|
|
explanation = CLOSE_CODES.get(code, "unknown")
|
|
message += "code = {} ({}), ".format(code, explanation)
|
|
if reason:
|
|
message += "reason = {}".format(reason)
|
|
else:
|
|
message += "no reason"
|
|
super().__init__(message)
|
|
|
|
|
|
class InvalidURI(Exception):
|
|
"""
|
|
Exception raised when an URI isn't a valid websocket URI.
|
|
|
|
"""
|
|
|
|
|
|
class PayloadTooBig(Exception):
|
|
"""
|
|
Exception raised when a frame's payload exceeds the maximum size.
|
|
|
|
"""
|
|
|
|
|
|
class WebSocketProtocolError(Exception):
|
|
"""
|
|
Internal exception raised when the remote side breaks the protocol.
|
|
|
|
"""
|