Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/websockets/exceptions.py: 53%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

152 statements  

1""" 

2:mod:`websockets.exceptions` defines the following hierarchy of exceptions. 

3 

4* :exc:`WebSocketException` 

5 * :exc:`ConnectionClosed` 

6 * :exc:`ConnectionClosedOK` 

7 * :exc:`ConnectionClosedError` 

8 * :exc:`InvalidURI` 

9 * :exc:`InvalidProxy` 

10 * :exc:`InvalidHandshake` 

11 * :exc:`SecurityError` 

12 * :exc:`RequestLineTooLong` 

13 * :exc:`StatusLineTooLong` 

14 * :exc:`HeaderLineTooLong` 

15 * :exc:`TooManyHeaders` 

16 * :exc:`ProxyError` 

17 * :exc:`InvalidProxyMessage` 

18 * :exc:`InvalidProxyStatus` 

19 * :exc:`InvalidMessage` 

20 * :exc:`InvalidMethod` 

21 * :exc:`InvalidProtocol` 

22 * :exc:`InvalidStatus` 

23 * :exc:`InvalidStatusCode` (legacy) 

24 * :exc:`InvalidHeader` 

25 * :exc:`InvalidHeaderFormat` 

26 * :exc:`InvalidHeaderValue` 

27 * :exc:`InvalidOrigin` 

28 * :exc:`InvalidUpgrade` 

29 * :exc:`NegotiationError` 

30 * :exc:`DuplicateParameter` 

31 * :exc:`InvalidParameterName` 

32 * :exc:`InvalidParameterValue` 

33 * :exc:`AbortHandshake` (legacy) 

34 * :exc:`RedirectHandshake` (legacy) 

35 * :exc:`ProtocolError` (Sans-I/O) 

36 * :exc:`PayloadTooBig` (Sans-I/O) 

37 * :exc:`TooManyFragments` 

38 * :exc:`InvalidState` (Sans-I/O) 

39 * :exc:`ConcurrencyError` 

40 

41""" 

42 

43from __future__ import annotations 

44 

45import warnings 

46 

47from .imports import lazy_import 

48 

49 

50__all__ = [ 

51 "WebSocketException", 

52 "ConnectionClosed", 

53 "ConnectionClosedOK", 

54 "ConnectionClosedError", 

55 "InvalidURI", 

56 "InvalidProxy", 

57 "InvalidHandshake", 

58 "SecurityError", 

59 "RequestLineTooLong", 

60 "StatusLineTooLong", 

61 "HeaderLineTooLong", 

62 "TooManyHeaders", 

63 "TooManyFragments", 

64 "ProxyError", 

65 "InvalidProxyMessage", 

66 "InvalidProxyStatus", 

67 "InvalidMessage", 

68 "InvalidMethod", 

69 "InvalidProtocol", 

70 "InvalidStatus", 

71 "InvalidHeader", 

72 "InvalidHeaderFormat", 

73 "InvalidHeaderValue", 

74 "InvalidOrigin", 

75 "InvalidUpgrade", 

76 "NegotiationError", 

77 "DuplicateParameter", 

78 "InvalidParameterName", 

79 "InvalidParameterValue", 

80 "ProtocolError", 

81 "PayloadTooBig", 

82 "InvalidState", 

83 "ConcurrencyError", 

84] 

85 

86 

87class WebSocketException(Exception): 

88 """ 

89 Base class for all exceptions defined by websockets. 

90 

91 """ 

92 

93 

94class ConnectionClosed(WebSocketException): 

95 """ 

96 Raised when trying to interact with a closed connection. 

97 

98 Attributes: 

99 rcvd: If a close frame was received, its code and reason are available 

100 in ``rcvd.code`` and ``rcvd.reason``. 

101 sent: If a close frame was sent, its code and reason are available 

102 in ``sent.code`` and ``sent.reason``. 

103 rcvd_then_sent: If close frames were received and sent, this attribute 

104 tells in which order this happened, from the perspective of this 

105 side of the connection. 

106 

107 """ 

108 

109 def __init__( 

110 self, 

111 rcvd: frames.Close | None, 

112 sent: frames.Close | None, 

113 rcvd_then_sent: bool | None = None, 

114 ) -> None: 

115 self.rcvd = rcvd 

116 self.sent = sent 

117 self.rcvd_then_sent = rcvd_then_sent 

118 assert (self.rcvd_then_sent is None) == (self.rcvd is None or self.sent is None) 

119 

120 def __str__(self) -> str: 

121 if self.rcvd is None: 

122 if self.sent is None: 

123 return "no close frame received or sent" 

124 else: 

125 return f"sent {self.sent}; no close frame received" 

126 else: 

127 if self.sent is None: 

128 return f"received {self.rcvd}; no close frame sent" 

129 else: 

130 if self.rcvd_then_sent: 

131 return f"received {self.rcvd}; then sent {self.sent}" 

132 else: 

133 return f"sent {self.sent}; then received {self.rcvd}" 

134 

135 # code and reason attributes are provided for backwards-compatibility 

136 

137 @property 

138 def code(self) -> int: 

139 warnings.warn( # deprecated in 13.1 - 2024-09-21 

140 "ConnectionClosed.code is deprecated; " 

141 "use Protocol.close_code or ConnectionClosed.rcvd.code", 

142 DeprecationWarning, 

143 ) 

144 if self.rcvd is None: 

145 return frames.CloseCode.ABNORMAL_CLOSURE 

146 return self.rcvd.code 

147 

148 @property 

149 def reason(self) -> str: 

150 warnings.warn( # deprecated in 13.1 - 2024-09-21 

151 "ConnectionClosed.reason is deprecated; " 

152 "use Protocol.close_reason or ConnectionClosed.rcvd.reason", 

153 DeprecationWarning, 

154 ) 

155 if self.rcvd is None: 

156 return "" 

157 return self.rcvd.reason 

158 

159 

160class ConnectionClosedOK(ConnectionClosed): 

161 """ 

162 Like :exc:`ConnectionClosed`, when the connection terminated properly. 

163 

164 A close code with code 1000 (OK) or 1001 (going away) or without a code was 

165 received and sent. 

166 

167 """ 

168 

169 

170class ConnectionClosedError(ConnectionClosed): 

171 """ 

172 Like :exc:`ConnectionClosed`, when the connection terminated with an error. 

173 

174 A close frame with a code other than 1000 (OK) or 1001 (going away) was 

175 received or sent, or the closing handshake didn't complete properly. 

176 

177 """ 

178 

179 

180class InvalidURI(WebSocketException): 

181 """ 

182 Raised when connecting to a URI that isn't a valid WebSocket URI. 

183 

184 """ 

185 

186 def __init__(self, uri: str, msg: str) -> None: 

187 self.uri = uri 

188 self.msg = msg 

189 

190 def __str__(self) -> str: 

191 return f"{self.uri} isn't a valid URI: {self.msg}" 

192 

193 

194class InvalidProxy(WebSocketException): 

195 """ 

196 Raised when connecting via a proxy that isn't valid. 

197 

198 """ 

199 

200 def __init__(self, proxy: str, msg: str) -> None: 

201 self.proxy = proxy 

202 self.msg = msg 

203 

204 def __str__(self) -> str: 

205 return f"{self.proxy} isn't a valid proxy: {self.msg}" 

206 

207 

208class InvalidHandshake(WebSocketException): 

209 """ 

210 Base class for exceptions raised when the opening handshake fails. 

211 

212 """ 

213 

214 

215class SecurityError(InvalidHandshake): 

216 """ 

217 Raised when a handshake request or response breaks a security rule. 

218 

219 Security limits can be configured with :doc:`environment variables 

220 <../reference/variables>`. 

221 

222 """ 

223 

224 

225class RequestLineTooLong(SecurityError): 

226 """ 

227 Raised when the request line of a handshake request is too long. 

228 

229 """ 

230 

231 

232class StatusLineTooLong(SecurityError): 

233 """ 

234 Raised when the status line of a handshake response is too long. 

235 

236 """ 

237 

238 

239class HeaderLineTooLong(SecurityError): 

240 """ 

241 Raised when a header line of a handshake request or response is too long. 

242 

243 """ 

244 

245 

246class TooManyHeaders(SecurityError): 

247 """ 

248 Raised when a handshake request or response has too many headers. 

249 

250 """ 

251 

252 

253class ProxyError(InvalidHandshake): 

254 """ 

255 Raised when failing to connect to a proxy. 

256 

257 """ 

258 

259 

260class InvalidProxyMessage(ProxyError): 

261 """ 

262 Raised when an HTTP proxy response is malformed. 

263 

264 """ 

265 

266 

267class InvalidProxyStatus(ProxyError): 

268 """ 

269 Raised when an HTTP proxy rejects the connection. 

270 

271 """ 

272 

273 def __init__(self, response: http11.Response) -> None: 

274 self.response = response 

275 

276 def __str__(self) -> str: 

277 return f"proxy rejected connection: HTTP {self.response.status_code:d}" 

278 

279 

280class InvalidMessage(InvalidHandshake): 

281 """ 

282 Raised when a handshake request or response is malformed. 

283 

284 """ 

285 

286 

287class InvalidMethod(InvalidHandshake): 

288 """ 

289 Raised when a handshake request doesn't use HTTP GET. 

290 

291 """ 

292 

293 def __init__(self, method: str) -> None: 

294 self.method = method 

295 

296 def __str__(self) -> str: 

297 return f"unsupported HTTP method: {self.method}" 

298 

299 

300class InvalidProtocol(InvalidHandshake): 

301 """ 

302 Raised when a handshake request doesn't use HTTP/1.1. 

303 

304 """ 

305 

306 def __init__(self, protocol: str) -> None: 

307 self.protocol = protocol 

308 

309 def __str__(self) -> str: 

310 return f"unsupported HTTP version: {self.protocol}" 

311 

312 

313class InvalidStatus(InvalidHandshake): 

314 """ 

315 Raised when a handshake response rejects the WebSocket upgrade. 

316 

317 """ 

318 

319 def __init__(self, response: http11.Response) -> None: 

320 self.response = response 

321 

322 def __str__(self) -> str: 

323 return ( 

324 f"server rejected WebSocket connection: HTTP {self.response.status_code:d}" 

325 ) 

326 

327 

328class InvalidHeader(InvalidHandshake): 

329 """ 

330 Raised when an HTTP header doesn't have a valid format or value. 

331 

332 """ 

333 

334 def __init__(self, name: str, value: str | None = None) -> None: 

335 self.name = name 

336 self.value = value 

337 

338 def __str__(self) -> str: 

339 if self.value is None: 

340 return f"missing {self.name} header" 

341 elif self.value == "": 

342 return f"empty {self.name} header" 

343 else: 

344 return f"invalid {self.name} header: {self.value}" 

345 

346 

347class InvalidHeaderFormat(InvalidHeader): 

348 """ 

349 Raised when an HTTP header cannot be parsed. 

350 

351 The format of the header doesn't match the grammar for that header. 

352 

353 """ 

354 

355 def __init__(self, name: str, error: str, header: str, pos: int) -> None: 

356 super().__init__(name, f"{error} at {pos} in {header}") 

357 

358 

359class InvalidHeaderValue(InvalidHeader): 

360 """ 

361 Raised when an HTTP header has a wrong value. 

362 

363 The format of the header is correct but the value isn't acceptable. 

364 

365 """ 

366 

367 

368class InvalidOrigin(InvalidHeader): 

369 """ 

370 Raised when the Origin header in a request isn't allowed. 

371 

372 """ 

373 

374 def __init__(self, origin: str | None) -> None: 

375 super().__init__("Origin", origin) 

376 

377 

378class InvalidUpgrade(InvalidHeader): 

379 """ 

380 Raised when the Upgrade or Connection header isn't correct. 

381 

382 """ 

383 

384 

385class NegotiationError(InvalidHandshake): 

386 """ 

387 Raised when negotiating an extension or a subprotocol fails. 

388 

389 """ 

390 

391 

392class DuplicateParameter(NegotiationError): 

393 """ 

394 Raised when a parameter name is repeated in an extension header. 

395 

396 """ 

397 

398 def __init__(self, name: str) -> None: 

399 self.name = name 

400 

401 def __str__(self) -> str: 

402 return f"duplicate parameter: {self.name}" 

403 

404 

405class InvalidParameterName(NegotiationError): 

406 """ 

407 Raised when a parameter name in an extension header is invalid. 

408 

409 """ 

410 

411 def __init__(self, name: str) -> None: 

412 self.name = name 

413 

414 def __str__(self) -> str: 

415 return f"invalid parameter name: {self.name}" 

416 

417 

418class InvalidParameterValue(NegotiationError): 

419 """ 

420 Raised when a parameter value in an extension header is invalid. 

421 

422 """ 

423 

424 def __init__(self, name: str, value: str | None) -> None: 

425 self.name = name 

426 self.value = value 

427 

428 def __str__(self) -> str: 

429 if self.value is None: 

430 return f"missing value for parameter {self.name}" 

431 elif self.value == "": 

432 return f"empty value for parameter {self.name}" 

433 else: 

434 return f"invalid value for parameter {self.name}: {self.value}" 

435 

436 

437class ProtocolError(WebSocketException): 

438 """ 

439 Raised when receiving or sending a frame that breaks the protocol. 

440 

441 The Sans-I/O implementation raises this exception when: 

442 

443 * receiving or sending a frame that contains invalid data; 

444 * receiving or sending an invalid sequence of frames. 

445 

446 """ 

447 

448 

449class PayloadTooBig(WebSocketException): 

450 """ 

451 Raised when parsing a frame with a payload that exceeds the maximum size. 

452 

453 The Sans-I/O layer uses this exception internally. It doesn't bubble up to 

454 the I/O layer. 

455 

456 The :meth:`~websockets.extensions.Extension.decode` method of extensions 

457 must raise :exc:`PayloadTooBig` if decoding a frame would exceed the limit. 

458 

459 """ 

460 

461 def __init__( 

462 self, 

463 size_or_message: int | None | str, 

464 max_size: int | None = None, 

465 current_size: int | None = None, 

466 ) -> None: 

467 if isinstance(size_or_message, str): 

468 assert max_size is None 

469 assert current_size is None 

470 warnings.warn( # deprecated in 14.0 - 2024-11-09 

471 "PayloadTooBig(message) is deprecated; " 

472 "change to PayloadTooBig(size, max_size)", 

473 DeprecationWarning, 

474 ) 

475 self.message: str | None = size_or_message 

476 else: 

477 self.message = None 

478 self.size: int | None = size_or_message 

479 assert max_size is not None 

480 self.max_size: int = max_size 

481 self.current_size: int | None = None 

482 self.set_current_size(current_size) 

483 

484 def __str__(self) -> str: 

485 if self.message is not None: 

486 return self.message 

487 else: 

488 message = "frame " 

489 if self.size is not None: 

490 message += f"with {self.size} bytes " 

491 if self.current_size is not None: 

492 message += f"after reading {self.current_size} bytes " 

493 message += f"exceeds limit of {self.max_size} bytes" 

494 return message 

495 

496 def set_current_size(self, current_size: int | None) -> None: 

497 assert self.current_size is None 

498 if current_size is not None: 

499 self.max_size += current_size 

500 self.current_size = current_size 

501 

502 

503class TooManyFragments(WebSocketException): 

504 """Raised when reassembling message that contains too many frames.""" 

505 

506 def __init__(self, max_fragments: int) -> None: 

507 super().__init__(f"fragmented message exceeds limit of {max_fragments} frames") 

508 

509 

510class InvalidState(WebSocketException, AssertionError): 

511 """ 

512 Raised when sending a frame is forbidden in the current state. 

513 

514 Specifically, the Sans-I/O layer raises this exception when: 

515 

516 * sending a data frame to a connection in a state other 

517 :attr:`~websockets.protocol.State.OPEN`; 

518 * sending a control frame to a connection in a state other than 

519 :attr:`~websockets.protocol.State.OPEN` or 

520 :attr:`~websockets.protocol.State.CLOSING`. 

521 

522 """ 

523 

524 

525class ConcurrencyError(WebSocketException, RuntimeError): 

526 """ 

527 Raised when receiving or sending messages concurrently. 

528 

529 WebSocket is a connection-oriented protocol. Reads must be serialized; so 

530 must be writes. However, reading and writing concurrently is possible. 

531 

532 """ 

533 

534 

535# At the bottom to break import cycles created by type annotations. 

536from . import frames, http11 # noqa: E402 

537 

538 

539lazy_import( 

540 globals(), 

541 deprecated_aliases={ 

542 # deprecated in 14.0 - 2024-11-09 

543 "AbortHandshake": ".legacy.exceptions", 

544 "InvalidStatusCode": ".legacy.exceptions", 

545 "RedirectHandshake": ".legacy.exceptions", 

546 "WebSocketProtocolError": ".legacy.exceptions", 

547 }, 

548)