You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

367 line
8.6 KiB

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