1""" 2:mod:`websockets.exceptions` defines the following exception hierarchy: 3 4* :exc:`WebSocketException` 5 * :exc:`ConnectionClosed` 6 * :exc:`ConnectionClosedError` 7 * :exc:`ConnectionClosedOK` 8 * :exc:`InvalidHandshake` 9 * :exc:`SecurityError` 10 * :exc:`InvalidMessage` 11 * :exc:`InvalidHeader` 12 * :exc:`InvalidHeaderFormat` 13 * :exc:`InvalidHeaderValue` 14 * :exc:`InvalidOrigin` 15 * :exc:`InvalidUpgrade` 16 * :exc:`InvalidStatus` 17 * :exc:`InvalidStatusCode` (legacy) 18 * :exc:`NegotiationError` 19 * :exc:`DuplicateParameter` 20 * :exc:`InvalidParameterName` 21 * :exc:`InvalidParameterValue` 22 * :exc:`AbortHandshake` 23 * :exc:`RedirectHandshake` 24 * :exc:`InvalidState` 25 * :exc:`InvalidURI` 26 * :exc:`PayloadTooBig` 27 * :exc:`ProtocolError` 28 29""" 30 31from __future__ import annotations 32 33import http 34from typing import Optional 35 36from . import datastructures, frames, http11 37 38 39__all__ = [ 40 "WebSocketException", 41 "ConnectionClosed", 42 "ConnectionClosedError", 43 "ConnectionClosedOK", 44 "InvalidHandshake", 45 "SecurityError", 46 "InvalidMessage", 47 "InvalidHeader", 48 "InvalidHeaderFormat", 49 "InvalidHeaderValue", 50 "InvalidOrigin", 51 "InvalidUpgrade", 52 "InvalidStatus", 53 "InvalidStatusCode", 54 "NegotiationError", 55 "DuplicateParameter", 56 "InvalidParameterName", 57 "InvalidParameterValue", 58 "AbortHandshake", 59 "RedirectHandshake", 60 "InvalidState", 61 "InvalidURI", 62 "PayloadTooBig", 63 "ProtocolError", 64 "WebSocketProtocolError", 65] 66 67 68class WebSocketException(Exception): 69 """ 70 Base class for all exceptions defined by websockets. 71 72 """ 73 74 75class ConnectionClosed(WebSocketException): 76 """ 77 Raised when trying to interact with a closed connection. 78 79 Attributes: 80 rcvd (Optional[Close]): if a close frame was received, its code and 81 reason are available in ``rcvd.code`` and ``rcvd.reason``. 82 sent (Optional[Close]): if a close frame was sent, its code and reason 83 are available in ``sent.code`` and ``sent.reason``. 84 rcvd_then_sent (Optional[bool]): if close frames were received and 85 sent, this attribute tells in which order this happened, from the 86 perspective of this side of the connection. 87 88 """ 89 90 def __init__( 91 self, 92 rcvd: Optional[frames.Close], 93 sent: Optional[frames.Close], 94 rcvd_then_sent: Optional[bool] = None, 95 ) -> None: 96 self.rcvd = rcvd 97 self.sent = sent 98 self.rcvd_then_sent = rcvd_then_sent 99 100 def __str__(self) -> str: 101 if self.rcvd is None: 102 if self.sent is None: 103 assert self.rcvd_then_sent is None 104 return "no close frame received or sent" 105 else: 106 assert self.rcvd_then_sent is None 107 return f"sent {self.sent}; no close frame received" 108 else: 109 if self.sent is None: 110 assert self.rcvd_then_sent is None 111 return f"received {self.rcvd}; no close frame sent" 112 else: 113 assert self.rcvd_then_sent is not None 114 if self.rcvd_then_sent: 115 return f"received {self.rcvd}; then sent {self.sent}" 116 else: 117 return f"sent {self.sent}; then received {self.rcvd}" 118 119 # code and reason attributes are provided for backwards-compatibility 120 121 @property 122 def code(self) -> int: 123 return 1006 if self.rcvd is None else self.rcvd.code 124 125 @property 126 def reason(self) -> str: 127 return "" if self.rcvd is None else self.rcvd.reason 128 129 130class ConnectionClosedError(ConnectionClosed): 131 """ 132 Like :exc:`ConnectionClosed`, when the connection terminated with an error. 133 134 A close code other than 1000 (OK) or 1001 (going away) was received or 135 sent, or the closing handshake didn't complete properly. 136 137 """ 138 139 140class ConnectionClosedOK(ConnectionClosed): 141 """ 142 Like :exc:`ConnectionClosed`, when the connection terminated properly. 143 144 A close code 1000 (OK) or 1001 (going away) was received and sent. 145 146 """ 147 148 149class InvalidHandshake(WebSocketException): 150 """ 151 Raised during the handshake when the WebSocket connection fails. 152 153 """ 154 155 156class SecurityError(InvalidHandshake): 157 """ 158 Raised when a handshake request or response breaks a security rule. 159 160 Security limits are hard coded. 161 162 """ 163 164 165class InvalidMessage(InvalidHandshake): 166 """ 167 Raised when a handshake request or response is malformed. 168 169 """ 170 171 172class InvalidHeader(InvalidHandshake): 173 """ 174 Raised when a HTTP header doesn't have a valid format or value. 175 176 """ 177 178 def __init__(self, name: str, value: Optional[str] = None) -> None: 179 self.name = name 180 self.value = value 181 182 def __str__(self) -> str: 183 if self.value is None: 184 return f"missing {self.name} header" 185 elif self.value == "": 186 return f"empty {self.name} header" 187 else: 188 return f"invalid {self.name} header: {self.value}" 189 190 191class InvalidHeaderFormat(InvalidHeader): 192 """ 193 Raised when a HTTP header cannot be parsed. 194 195 The format of the header doesn't match the grammar for that header. 196 197 """ 198 199 def __init__(self, name: str, error: str, header: str, pos: int) -> None: 200 super().__init__(name, f"{error} at {pos} in {header}") 201 202 203class InvalidHeaderValue(InvalidHeader): 204 """ 205 Raised when a HTTP header has a wrong value. 206 207 The format of the header is correct but a value isn't acceptable. 208 209 """ 210 211 212class InvalidOrigin(InvalidHeader): 213 """ 214 Raised when the Origin header in a request isn't allowed. 215 216 """ 217 218 def __init__(self, origin: Optional[str]) -> None: 219 super().__init__("Origin", origin) 220 221 222class InvalidUpgrade(InvalidHeader): 223 """ 224 Raised when the Upgrade or Connection header isn't correct. 225 226 """ 227 228 229class InvalidStatus(InvalidHandshake): 230 """ 231 Raised when a handshake response rejects the WebSocket upgrade. 232 233 """ 234 235 def __init__(self, response: http11.Response) -> None: 236 self.response = response 237 238 def __str__(self) -> str: 239 return ( 240 "server rejected WebSocket connection: " 241 f"HTTP {self.response.status_code:d}" 242 ) 243 244 245class InvalidStatusCode(InvalidHandshake): 246 """ 247 Raised when a handshake response status code is invalid. 248 249 """ 250 251 def __init__(self, status_code: int, headers: datastructures.Headers) -> None: 252 self.status_code = status_code 253 self.headers = headers 254 255 def __str__(self) -> str: 256 return f"server rejected WebSocket connection: HTTP {self.status_code}" 257 258 259class NegotiationError(InvalidHandshake): 260 """ 261 Raised when negotiating an extension fails. 262 263 """ 264 265 266class DuplicateParameter(NegotiationError): 267 """ 268 Raised when a parameter name is repeated in an extension header. 269 270 """ 271 272 def __init__(self, name: str) -> None: 273 self.name = name 274 275 def __str__(self) -> str: 276 return f"duplicate parameter: {self.name}" 277 278 279class InvalidParameterName(NegotiationError): 280 """ 281 Raised when a parameter name in an extension header is invalid. 282 283 """ 284 285 def __init__(self, name: str) -> None: 286 self.name = name 287 288 def __str__(self) -> str: 289 return f"invalid parameter name: {self.name}" 290 291 292class InvalidParameterValue(NegotiationError): 293 """ 294 Raised when a parameter value in an extension header is invalid. 295 296 """ 297 298 def __init__(self, name: str, value: Optional[str]) -> None: 299 self.name = name 300 self.value = value 301 302 def __str__(self) -> str: 303 if self.value is None: 304 return f"missing value for parameter {self.name}" 305 elif self.value == "": 306 return f"empty value for parameter {self.name}" 307 else: 308 return f"invalid value for parameter {self.name}: {self.value}" 309 310 311class AbortHandshake(InvalidHandshake): 312 """ 313 Raised to abort the handshake on purpose and return a HTTP response. 314 315 This exception is an implementation detail. 316 317 The public API 318 is :meth:`~websockets.server.WebSocketServerProtocol.process_request`. 319 320 Attributes: 321 status (~http.HTTPStatus): HTTP status code. 322 headers (Headers): HTTP response headers. 323 body (bytes): HTTP response body. 324 """ 325 326 def __init__( 327 self, 328 status: http.HTTPStatus, 329 headers: datastructures.HeadersLike, 330 body: bytes = b"", 331 ) -> None: 332 self.status = status 333 self.headers = datastructures.Headers(headers) 334 self.body = body 335 336 def __str__(self) -> str: 337 return ( 338 f"HTTP {self.status:d}, " 339 f"{len(self.headers)} headers, " 340 f"{len(self.body)} bytes" 341 ) 342 343 344class RedirectHandshake(InvalidHandshake): 345 """ 346 Raised when a handshake gets redirected. 347 348 This exception is an implementation detail. 349 350 """ 351 352 def __init__(self, uri: str) -> None: 353 self.uri = uri 354 355 def __str__(self) -> str: 356 return f"redirect to {self.uri}" 357 358 359class InvalidState(WebSocketException, AssertionError): 360 """ 361 Raised when an operation is forbidden in the current state. 362 363 This exception is an implementation detail. 364 365 It should never be raised in normal circumstances. 366 367 """ 368 369 370class InvalidURI(WebSocketException): 371 """ 372 Raised when connecting to an URI that isn't a valid WebSocket URI. 373 374 """ 375 376 def __init__(self, uri: str, msg: str) -> None: 377 self.uri = uri 378 self.msg = msg 379 380 def __str__(self) -> str: 381 return f"{self.uri} isn't a valid URI: {self.msg}" 382 383 384class PayloadTooBig(WebSocketException): 385 """ 386 Raised when receiving a frame with a payload exceeding the maximum size. 387 388 """ 389 390 391class ProtocolError(WebSocketException): 392 """ 393 Raised when a frame breaks the protocol. 394 395 """ 396 397 398WebSocketProtocolError = ProtocolError # for backwards compatibility 399