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

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

135 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:`ProxyError` 

13 * :exc:`InvalidProxyMessage` 

14 * :exc:`InvalidProxyStatus` 

15 * :exc:`InvalidMessage` 

16 * :exc:`InvalidStatus` 

17 * :exc:`InvalidStatusCode` (legacy) 

18 * :exc:`InvalidHeader` 

19 * :exc:`InvalidHeaderFormat` 

20 * :exc:`InvalidHeaderValue` 

21 * :exc:`InvalidOrigin` 

22 * :exc:`InvalidUpgrade` 

23 * :exc:`NegotiationError` 

24 * :exc:`DuplicateParameter` 

25 * :exc:`InvalidParameterName` 

26 * :exc:`InvalidParameterValue` 

27 * :exc:`AbortHandshake` (legacy) 

28 * :exc:`RedirectHandshake` (legacy) 

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

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

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

32 * :exc:`ConcurrencyError` 

33 

34""" 

35 

36from __future__ import annotations 

37 

38import warnings 

39 

40from .imports import lazy_import 

41 

42 

43__all__ = [ 

44 "WebSocketException", 

45 "ConnectionClosed", 

46 "ConnectionClosedOK", 

47 "ConnectionClosedError", 

48 "InvalidURI", 

49 "InvalidProxy", 

50 "InvalidHandshake", 

51 "SecurityError", 

52 "ProxyError", 

53 "InvalidProxyMessage", 

54 "InvalidProxyStatus", 

55 "InvalidMessage", 

56 "InvalidStatus", 

57 "InvalidHeader", 

58 "InvalidHeaderFormat", 

59 "InvalidHeaderValue", 

60 "InvalidOrigin", 

61 "InvalidUpgrade", 

62 "NegotiationError", 

63 "DuplicateParameter", 

64 "InvalidParameterName", 

65 "InvalidParameterValue", 

66 "ProtocolError", 

67 "PayloadTooBig", 

68 "InvalidState", 

69 "ConcurrencyError", 

70] 

71 

72 

73class WebSocketException(Exception): 

74 """ 

75 Base class for all exceptions defined by websockets. 

76 

77 """ 

78 

79 

80class ConnectionClosed(WebSocketException): 

81 """ 

82 Raised when trying to interact with a closed connection. 

83 

84 Attributes: 

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

86 in ``rcvd.code`` and ``rcvd.reason``. 

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

88 in ``sent.code`` and ``sent.reason``. 

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

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

91 side of the connection. 

92 

93 """ 

94 

95 def __init__( 

96 self, 

97 rcvd: frames.Close | None, 

98 sent: frames.Close | None, 

99 rcvd_then_sent: bool | None = None, 

100 ) -> None: 

101 self.rcvd = rcvd 

102 self.sent = sent 

103 self.rcvd_then_sent = rcvd_then_sent 

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

105 

106 def __str__(self) -> str: 

107 if self.rcvd is None: 

108 if self.sent is None: 

109 return "no close frame received or sent" 

110 else: 

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

112 else: 

113 if self.sent is None: 

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

115 else: 

116 if self.rcvd_then_sent: 

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

118 else: 

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

120 

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

122 

123 @property 

124 def code(self) -> int: 

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

126 "ConnectionClosed.code is deprecated; " 

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

128 DeprecationWarning, 

129 ) 

130 if self.rcvd is None: 

131 return frames.CloseCode.ABNORMAL_CLOSURE 

132 return self.rcvd.code 

133 

134 @property 

135 def reason(self) -> str: 

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

137 "ConnectionClosed.reason is deprecated; " 

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

139 DeprecationWarning, 

140 ) 

141 if self.rcvd is None: 

142 return "" 

143 return self.rcvd.reason 

144 

145 

146class ConnectionClosedOK(ConnectionClosed): 

147 """ 

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

149 

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

151 received and sent. 

152 

153 """ 

154 

155 

156class ConnectionClosedError(ConnectionClosed): 

157 """ 

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

159 

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

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

162 

163 """ 

164 

165 

166class InvalidURI(WebSocketException): 

167 """ 

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

169 

170 """ 

171 

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

173 self.uri = uri 

174 self.msg = msg 

175 

176 def __str__(self) -> str: 

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

178 

179 

180class InvalidProxy(WebSocketException): 

181 """ 

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

183 

184 """ 

185 

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

187 self.proxy = proxy 

188 self.msg = msg 

189 

190 def __str__(self) -> str: 

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

192 

193 

194class InvalidHandshake(WebSocketException): 

195 """ 

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

197 

198 """ 

199 

200 

201class SecurityError(InvalidHandshake): 

202 """ 

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

204 

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

206 <../reference/variables>`. 

207 

208 """ 

209 

210 

211class ProxyError(InvalidHandshake): 

212 """ 

213 Raised when failing to connect to a proxy. 

214 

215 """ 

216 

217 

218class InvalidProxyMessage(ProxyError): 

219 """ 

220 Raised when an HTTP proxy response is malformed. 

221 

222 """ 

223 

224 

225class InvalidProxyStatus(ProxyError): 

226 """ 

227 Raised when an HTTP proxy rejects the connection. 

228 

229 """ 

230 

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

232 self.response = response 

233 

234 def __str__(self) -> str: 

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

236 

237 

238class InvalidMessage(InvalidHandshake): 

239 """ 

240 Raised when a handshake request or response is malformed. 

241 

242 """ 

243 

244 

245class InvalidStatus(InvalidHandshake): 

246 """ 

247 Raised when a handshake response rejects the WebSocket upgrade. 

248 

249 """ 

250 

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

252 self.response = response 

253 

254 def __str__(self) -> str: 

255 return ( 

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

257 ) 

258 

259 

260class InvalidHeader(InvalidHandshake): 

261 """ 

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

263 

264 """ 

265 

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

267 self.name = name 

268 self.value = value 

269 

270 def __str__(self) -> str: 

271 if self.value is None: 

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

273 elif self.value == "": 

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

275 else: 

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

277 

278 

279class InvalidHeaderFormat(InvalidHeader): 

280 """ 

281 Raised when an HTTP header cannot be parsed. 

282 

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

284 

285 """ 

286 

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

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

289 

290 

291class InvalidHeaderValue(InvalidHeader): 

292 """ 

293 Raised when an HTTP header has a wrong value. 

294 

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

296 

297 """ 

298 

299 

300class InvalidOrigin(InvalidHeader): 

301 """ 

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

303 

304 """ 

305 

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

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

308 

309 

310class InvalidUpgrade(InvalidHeader): 

311 """ 

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

313 

314 """ 

315 

316 

317class NegotiationError(InvalidHandshake): 

318 """ 

319 Raised when negotiating an extension or a subprotocol fails. 

320 

321 """ 

322 

323 

324class DuplicateParameter(NegotiationError): 

325 """ 

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

327 

328 """ 

329 

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

331 self.name = name 

332 

333 def __str__(self) -> str: 

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

335 

336 

337class InvalidParameterName(NegotiationError): 

338 """ 

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

340 

341 """ 

342 

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

344 self.name = name 

345 

346 def __str__(self) -> str: 

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

348 

349 

350class InvalidParameterValue(NegotiationError): 

351 """ 

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

353 

354 """ 

355 

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

357 self.name = name 

358 self.value = value 

359 

360 def __str__(self) -> str: 

361 if self.value is None: 

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

363 elif self.value == "": 

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

365 else: 

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

367 

368 

369class ProtocolError(WebSocketException): 

370 """ 

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

372 

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

374 

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

376 * receiving or sending an invalid sequence of frames. 

377 

378 """ 

379 

380 

381class PayloadTooBig(WebSocketException): 

382 """ 

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

384 

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

386 the I/O layer. 

387 

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

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

390 

391 """ 

392 

393 def __init__( 

394 self, 

395 size_or_message: int | None | str, 

396 max_size: int | None = None, 

397 current_size: int | None = None, 

398 ) -> None: 

399 if isinstance(size_or_message, str): 

400 assert max_size is None 

401 assert current_size is None 

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

403 "PayloadTooBig(message) is deprecated; " 

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

405 DeprecationWarning, 

406 ) 

407 self.message: str | None = size_or_message 

408 else: 

409 self.message = None 

410 self.size: int | None = size_or_message 

411 assert max_size is not None 

412 self.max_size: int = max_size 

413 self.current_size: int | None = None 

414 self.set_current_size(current_size) 

415 

416 def __str__(self) -> str: 

417 if self.message is not None: 

418 return self.message 

419 else: 

420 message = "frame " 

421 if self.size is not None: 

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

423 if self.current_size is not None: 

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

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

426 return message 

427 

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

429 assert self.current_size is None 

430 if current_size is not None: 

431 self.max_size += current_size 

432 self.current_size = current_size 

433 

434 

435class InvalidState(WebSocketException, AssertionError): 

436 """ 

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

438 

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

440 

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

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

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

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

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

446 

447 """ 

448 

449 

450class ConcurrencyError(WebSocketException, RuntimeError): 

451 """ 

452 Raised when receiving or sending messages concurrently. 

453 

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

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

456 

457 """ 

458 

459 

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

461from . import frames, http11 # noqa: E402 

462 

463 

464lazy_import( 

465 globals(), 

466 deprecated_aliases={ 

467 # deprecated in 14.0 - 2024-11-09 

468 "AbortHandshake": ".legacy.exceptions", 

469 "InvalidStatusCode": ".legacy.exceptions", 

470 "RedirectHandshake": ".legacy.exceptions", 

471 "WebSocketProtocolError": ".legacy.exceptions", 

472 }, 

473)