exceptions.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. """
  2. :mod:`websockets.exceptions` defines the following exception hierarchy:
  3. * :exc:`WebSocketException`
  4. * :exc:`ConnectionClosed`
  5. * :exc:`ConnectionClosedError`
  6. * :exc:`ConnectionClosedOK`
  7. * :exc:`InvalidHandshake`
  8. * :exc:`SecurityError`
  9. * :exc:`InvalidMessage`
  10. * :exc:`InvalidHeader`
  11. * :exc:`InvalidHeaderFormat`
  12. * :exc:`InvalidHeaderValue`
  13. * :exc:`InvalidOrigin`
  14. * :exc:`InvalidUpgrade`
  15. * :exc:`InvalidStatusCode`
  16. * :exc:`NegotiationError`
  17. * :exc:`DuplicateParameter`
  18. * :exc:`InvalidParameterName`
  19. * :exc:`InvalidParameterValue`
  20. * :exc:`AbortHandshake`
  21. * :exc:`RedirectHandshake`
  22. * :exc:`InvalidState`
  23. * :exc:`InvalidURI`
  24. * :exc:`PayloadTooBig`
  25. * :exc:`ProtocolError`
  26. """
  27. import http
  28. from typing import Optional
  29. from .datastructures import Headers, HeadersLike
  30. __all__ = [
  31. "WebSocketException",
  32. "ConnectionClosed",
  33. "ConnectionClosedError",
  34. "ConnectionClosedOK",
  35. "InvalidHandshake",
  36. "SecurityError",
  37. "InvalidMessage",
  38. "InvalidHeader",
  39. "InvalidHeaderFormat",
  40. "InvalidHeaderValue",
  41. "InvalidOrigin",
  42. "InvalidUpgrade",
  43. "InvalidStatusCode",
  44. "NegotiationError",
  45. "DuplicateParameter",
  46. "InvalidParameterName",
  47. "InvalidParameterValue",
  48. "AbortHandshake",
  49. "RedirectHandshake",
  50. "InvalidState",
  51. "InvalidURI",
  52. "PayloadTooBig",
  53. "ProtocolError",
  54. "WebSocketProtocolError",
  55. ]
  56. class WebSocketException(Exception):
  57. """
  58. Base class for all exceptions defined by :mod:`websockets`.
  59. """
  60. # See https://www.iana.org/assignments/websocket/websocket.xhtml
  61. CLOSE_CODES = {
  62. 1000: "OK",
  63. 1001: "going away",
  64. 1002: "protocol error",
  65. 1003: "unsupported type",
  66. # 1004 is reserved
  67. 1005: "no status code [internal]",
  68. 1006: "connection closed abnormally [internal]",
  69. 1007: "invalid data",
  70. 1008: "policy violation",
  71. 1009: "message too big",
  72. 1010: "extension required",
  73. 1011: "unexpected error",
  74. 1012: "service restart",
  75. 1013: "try again later",
  76. 1014: "bad gateway",
  77. 1015: "TLS failure [internal]",
  78. }
  79. def format_close(code: int, reason: str) -> str:
  80. """
  81. Display a human-readable version of the close code and reason.
  82. """
  83. if 3000 <= code < 4000:
  84. explanation = "registered"
  85. elif 4000 <= code < 5000:
  86. explanation = "private use"
  87. else:
  88. explanation = CLOSE_CODES.get(code, "unknown")
  89. result = f"code = {code} ({explanation}), "
  90. if reason:
  91. result += f"reason = {reason}"
  92. else:
  93. result += "no reason"
  94. return result
  95. class ConnectionClosed(WebSocketException):
  96. """
  97. Raised when trying to interact with a closed connection.
  98. Provides the connection close code and reason in its ``code`` and
  99. ``reason`` attributes respectively.
  100. """
  101. def __init__(self, code: int, reason: str) -> None:
  102. self.code = code
  103. self.reason = reason
  104. super().__init__(format_close(code, reason))
  105. class ConnectionClosedError(ConnectionClosed):
  106. """
  107. Like :exc:`ConnectionClosed`, when the connection terminated with an error.
  108. This means the close code is different from 1000 (OK) and 1001 (going away).
  109. """
  110. def __init__(self, code: int, reason: str) -> None:
  111. assert code != 1000 and code != 1001
  112. super().__init__(code, reason)
  113. class ConnectionClosedOK(ConnectionClosed):
  114. """
  115. Like :exc:`ConnectionClosed`, when the connection terminated properly.
  116. This means the close code is 1000 (OK) or 1001 (going away).
  117. """
  118. def __init__(self, code: int, reason: str) -> None:
  119. assert code == 1000 or code == 1001
  120. super().__init__(code, reason)
  121. class InvalidHandshake(WebSocketException):
  122. """
  123. Raised during the handshake when the WebSocket connection fails.
  124. """
  125. class SecurityError(InvalidHandshake):
  126. """
  127. Raised when a handshake request or response breaks a security rule.
  128. Security limits are hard coded.
  129. """
  130. class InvalidMessage(InvalidHandshake):
  131. """
  132. Raised when a handshake request or response is malformed.
  133. """
  134. class InvalidHeader(InvalidHandshake):
  135. """
  136. Raised when a HTTP header doesn't have a valid format or value.
  137. """
  138. def __init__(self, name: str, value: Optional[str] = None) -> None:
  139. self.name = name
  140. self.value = value
  141. if value is None:
  142. message = f"missing {name} header"
  143. elif value == "":
  144. message = f"empty {name} header"
  145. else:
  146. message = f"invalid {name} header: {value}"
  147. super().__init__(message)
  148. class InvalidHeaderFormat(InvalidHeader):
  149. """
  150. Raised when a HTTP header cannot be parsed.
  151. The format of the header doesn't match the grammar for that header.
  152. """
  153. def __init__(self, name: str, error: str, header: str, pos: int) -> None:
  154. self.name = name
  155. error = f"{error} at {pos} in {header}"
  156. super().__init__(name, error)
  157. class InvalidHeaderValue(InvalidHeader):
  158. """
  159. Raised when a HTTP header has a wrong value.
  160. The format of the header is correct but a value isn't acceptable.
  161. """
  162. class InvalidOrigin(InvalidHeader):
  163. """
  164. Raised when the Origin header in a request isn't allowed.
  165. """
  166. def __init__(self, origin: Optional[str]) -> None:
  167. super().__init__("Origin", origin)
  168. class InvalidUpgrade(InvalidHeader):
  169. """
  170. Raised when the Upgrade or Connection header isn't correct.
  171. """
  172. class InvalidStatusCode(InvalidHandshake):
  173. """
  174. Raised when a handshake response status code is invalid.
  175. The integer status code is available in the ``status_code`` attribute.
  176. """
  177. def __init__(self, status_code: int) -> None:
  178. self.status_code = status_code
  179. message = f"server rejected WebSocket connection: HTTP {status_code}"
  180. super().__init__(message)
  181. class NegotiationError(InvalidHandshake):
  182. """
  183. Raised when negotiating an extension fails.
  184. """
  185. class DuplicateParameter(NegotiationError):
  186. """
  187. Raised when a parameter name is repeated in an extension header.
  188. """
  189. def __init__(self, name: str) -> None:
  190. self.name = name
  191. message = f"duplicate parameter: {name}"
  192. super().__init__(message)
  193. class InvalidParameterName(NegotiationError):
  194. """
  195. Raised when a parameter name in an extension header is invalid.
  196. """
  197. def __init__(self, name: str) -> None:
  198. self.name = name
  199. message = f"invalid parameter name: {name}"
  200. super().__init__(message)
  201. class InvalidParameterValue(NegotiationError):
  202. """
  203. Raised when a parameter value in an extension header is invalid.
  204. """
  205. def __init__(self, name: str, value: Optional[str]) -> None:
  206. self.name = name
  207. self.value = value
  208. if value is None:
  209. message = f"missing value for parameter {name}"
  210. elif value == "":
  211. message = f"empty value for parameter {name}"
  212. else:
  213. message = f"invalid value for parameter {name}: {value}"
  214. super().__init__(message)
  215. class AbortHandshake(InvalidHandshake):
  216. """
  217. Raised to abort the handshake on purpose and return a HTTP response.
  218. This exception is an implementation detail.
  219. The public API is :meth:`~legacy.server.WebSocketServerProtocol.process_request`.
  220. """
  221. def __init__(
  222. self,
  223. status: http.HTTPStatus,
  224. headers: HeadersLike,
  225. body: bytes = b"",
  226. ) -> None:
  227. self.status = status
  228. self.headers = Headers(headers)
  229. self.body = body
  230. message = f"HTTP {status}, {len(self.headers)} headers, {len(body)} bytes"
  231. super().__init__(message)
  232. class RedirectHandshake(InvalidHandshake):
  233. """
  234. Raised when a handshake gets redirected.
  235. This exception is an implementation detail.
  236. """
  237. def __init__(self, uri: str) -> None:
  238. self.uri = uri
  239. def __str__(self) -> str:
  240. return f"redirect to {self.uri}"
  241. class InvalidState(WebSocketException, AssertionError):
  242. """
  243. Raised when an operation is forbidden in the current state.
  244. This exception is an implementation detail.
  245. It should never be raised in normal circumstances.
  246. """
  247. class InvalidURI(WebSocketException):
  248. """
  249. Raised when connecting to an URI that isn't a valid WebSocket URI.
  250. """
  251. def __init__(self, uri: str) -> None:
  252. self.uri = uri
  253. message = "{} isn't a valid URI".format(uri)
  254. super().__init__(message)
  255. class PayloadTooBig(WebSocketException):
  256. """
  257. Raised when receiving a frame with a payload exceeding the maximum size.
  258. """
  259. class ProtocolError(WebSocketException):
  260. """
  261. Raised when a frame breaks the protocol.
  262. """
  263. WebSocketProtocolError = ProtocolError # for backwards compatibility