0
# Exception Handling
1
2
Simple WebSocket provides specific exception classes for handling WebSocket-related errors. These exceptions provide detailed information about connection failures and closures, enabling proper error handling and recovery strategies.
3
4
## Capabilities
5
6
### Base Exception Class
7
8
Base exception class for all simple-websocket related errors, serving as the parent class for WebSocket-specific exceptions.
9
10
```python { .api }
11
class SimpleWebsocketError(RuntimeError):
12
"""
13
Base exception class for all simple-websocket related errors.
14
15
Inherits from RuntimeError and serves as the parent class for all
16
WebSocket-specific exceptions in the simple-websocket library.
17
"""
18
```
19
20
### Connection Error Exception
21
22
Exception raised when WebSocket connection cannot be established or encounters an error during operation.
23
24
```python { .api }
25
class ConnectionError(SimpleWebsocketError):
26
"""
27
Connection error exception class.
28
29
Raised when:
30
- WebSocket handshake fails
31
- Invalid server response during connection
32
- Network connectivity issues
33
- SSL/TLS certificate errors
34
- Server rejection of connection
35
"""
36
37
def __init__(self, status_code=None):
38
"""
39
Initialize connection error.
40
41
Parameters:
42
- status_code: HTTP status code associated with the error (optional)
43
"""
44
```
45
46
### Connection Closed Exception
47
48
Exception raised when WebSocket connection is closed, either by the remote peer or due to protocol violations.
49
50
```python { .api }
51
class ConnectionClosed(SimpleWebsocketError):
52
"""
53
Connection closed exception class.
54
55
Raised when:
56
- Remote peer closes the connection
57
- Connection is closed due to protocol violation
58
- Network connection is lost
59
- Maximum message size exceeded
60
- Ping/pong timeout occurs
61
"""
62
63
def __init__(self, reason=None, message=None):
64
"""
65
Initialize connection closed exception.
66
67
Parameters:
68
- reason: CloseReason enum value indicating why connection was closed
69
- message: Optional text message describing the closure
70
"""
71
```
72
73
### Exception Attributes
74
75
```python { .api }
76
class ConnectionError:
77
status_code: int | None # HTTP status code if available
78
79
class ConnectionClosed:
80
reason: CloseReason # Close reason code from WebSocket protocol
81
message: str | None # Optional close message text
82
```
83
84
## Close Reason Codes
85
86
WebSocket connections can be closed for various reasons. The `ConnectionClosed` exception includes a reason code that indicates why the connection was terminated:
87
88
### Standard Close Codes
89
90
- **1000 (Normal Closure)**: Connection completed successfully
91
- **1001 (Going Away)**: Server going down or browser navigating away
92
- **1002 (Protocol Error)**: WebSocket protocol error
93
- **1003 (Unsupported Data)**: Unsupported data type received
94
- **1005 (No Status Received)**: No close status code was provided
95
- **1006 (Abnormal Closure)**: Connection closed abnormally
96
- **1007 (Invalid Data)**: Invalid UTF-8 data received
97
- **1008 (Policy Violation)**: Message violates endpoint policy
98
- **1009 (Message Too Big)**: Message too large to process
99
- **1010 (Missing Extension)**: Required extension not negotiated
100
- **1011 (Internal Error)**: Server encountered unexpected condition
101
- **1015 (TLS Handshake)**: TLS handshake failure
102
103
## Usage Examples
104
105
### Basic Error Handling
106
107
```python
108
import simple_websocket
109
110
def handle_websocket_connection():
111
try:
112
# Attempt to connect
113
ws = simple_websocket.Client.connect("ws://localhost:8000/ws")
114
115
# Send and receive messages
116
ws.send("Hello, Server!")
117
response = ws.receive(timeout=10)
118
print(f"Response: {response}")
119
120
except simple_websocket.ConnectionError as e:
121
print(f"Failed to connect: {e}")
122
if e.status_code:
123
print(f"HTTP Status: {e.status_code}")
124
125
except simple_websocket.ConnectionClosed as e:
126
print(f"Connection was closed: {e}")
127
print(f"Close reason: {e.reason}")
128
if e.message:
129
print(f"Close message: {e.message}")
130
131
except Exception as e:
132
print(f"Unexpected error: {e}")
133
134
finally:
135
# Always clean up
136
try:
137
ws.close()
138
except:
139
pass # Connection may already be closed
140
```
141
142
### Async Error Handling
143
144
```python
145
import asyncio
146
import simple_websocket
147
148
async def async_websocket_handler():
149
ws = None
150
try:
151
# Attempt async connection
152
ws = await simple_websocket.AioClient.connect("wss://secure-server.com/ws")
153
154
# Send message and wait for response
155
await ws.send("Hello, Async Server!")
156
response = await ws.receive(timeout=15)
157
print(f"Server response: {response}")
158
159
except simple_websocket.ConnectionError as e:
160
print(f"Connection failed: {e}")
161
if e.status_code == 403:
162
print("Access forbidden - check authentication")
163
elif e.status_code == 404:
164
print("WebSocket endpoint not found")
165
elif e.status_code >= 500:
166
print("Server error - try again later")
167
168
except simple_websocket.ConnectionClosed as e:
169
print(f"Connection closed: {e}")
170
171
# Handle different close reasons
172
if e.reason == 1000: # Normal closure
173
print("Connection closed normally")
174
elif e.reason == 1001: # Going away
175
print("Server is shutting down")
176
elif e.reason == 1008: # Policy violation
177
print("Message violated server policy")
178
elif e.reason == 1009: # Message too big
179
print("Message was too large")
180
else:
181
print(f"Connection closed with code: {e.reason}")
182
183
except asyncio.TimeoutError:
184
print("Timeout waiting for server response")
185
186
finally:
187
if ws:
188
await ws.close()
189
190
# Run the async handler
191
asyncio.run(async_websocket_handler())
192
```
193
194
### Server Error Handling
195
196
```python
197
import simple_websocket
198
199
def websocket_server_handler(environ, start_response):
200
ws = None
201
try:
202
# Accept WebSocket connection
203
ws = simple_websocket.Server.accept(
204
environ,
205
subprotocols=['chat', 'echo'],
206
max_message_size=1024 * 1024 # 1MB limit
207
)
208
209
print(f"Client connected with subprotocol: {ws.subprotocol}")
210
211
while True:
212
try:
213
# Receive message with timeout
214
message = ws.receive(timeout=30)
215
216
if message is None:
217
# Timeout occurred
218
print("Client timeout - closing connection")
219
ws.close(reason=1000, message="Timeout")
220
break
221
222
# Echo the message
223
ws.send(f"Echo: {message}")
224
225
except simple_websocket.ConnectionClosed as e:
226
print(f"Client disconnected: {e}")
227
if e.reason == 1001:
228
print("Client navigated away")
229
elif e.reason == 1006:
230
print("Connection lost unexpectedly")
231
break
232
233
except simple_websocket.ConnectionError as e:
234
print(f"Failed to establish WebSocket connection: {e}")
235
if e.status_code == 400:
236
print("Bad WebSocket request")
237
elif e.status_code == 426:
238
print("Upgrade required")
239
240
except Exception as e:
241
print(f"Unexpected server error: {e}")
242
243
finally:
244
if ws:
245
try:
246
ws.close(reason=1011, message="Server error")
247
except:
248
pass # Connection may already be closed
249
```
250
251
### Custom Exception Handling
252
253
```python
254
import simple_websocket
255
from wsproto.frame_protocol import CloseReason
256
257
class WebSocketManager:
258
def __init__(self):
259
self.ws = None
260
self.reconnect_attempts = 0
261
self.max_reconnect_attempts = 3
262
263
async def connect_with_retry(self, url):
264
"""Connect with automatic retry logic."""
265
while self.reconnect_attempts < self.max_reconnect_attempts:
266
try:
267
self.ws = await simple_websocket.AioClient.connect(url)
268
self.reconnect_attempts = 0 # Reset on successful connection
269
return True
270
271
except simple_websocket.ConnectionError as e:
272
self.reconnect_attempts += 1
273
print(f"Connection attempt {self.reconnect_attempts} failed: {e}")
274
275
if e.status_code == 401:
276
print("Authentication required - cannot retry")
277
return False
278
elif e.status_code == 404:
279
print("Endpoint not found - cannot retry")
280
return False
281
elif self.reconnect_attempts < self.max_reconnect_attempts:
282
await asyncio.sleep(2 ** self.reconnect_attempts) # Exponential backoff
283
284
print("Max reconnection attempts reached")
285
return False
286
287
async def handle_message_loop(self):
288
"""Handle messages with comprehensive error handling."""
289
try:
290
while True:
291
message = await self.ws.receive(timeout=60)
292
293
if message is None:
294
# Send ping to keep connection alive
295
await self.ws.send("ping")
296
continue
297
298
# Process message
299
await self.process_message(message)
300
301
except simple_websocket.ConnectionClosed as e:
302
print(f"Connection closed: {e}")
303
304
# Decide whether to reconnect based on close reason
305
should_reconnect = e.reason in [
306
CloseReason.GOING_AWAY, # 1001
307
CloseReason.ABNORMAL_CLOSURE, # 1006
308
CloseReason.INTERNAL_ERROR # 1011
309
]
310
311
if should_reconnect:
312
print("Attempting to reconnect...")
313
return await self.connect_with_retry(self.url)
314
else:
315
print("Connection closed permanently")
316
return False
317
318
async def process_message(self, message):
319
"""Process received message."""
320
try:
321
# Your message processing logic here
322
print(f"Processing: {message}")
323
await self.ws.send(f"Processed: {message}")
324
325
except Exception as e:
326
print(f"Error processing message: {e}")
327
# Send error response
328
await self.ws.send(f"Error: {str(e)}")
329
```
330
331
## Best Practices
332
333
### 1. Always Handle Both Exception Types
334
335
```python
336
try:
337
# WebSocket operations
338
pass
339
except simple_websocket.ConnectionError as e:
340
# Handle connection establishment errors
341
pass
342
except simple_websocket.ConnectionClosed as e:
343
# Handle connection closure
344
pass
345
```
346
347
### 2. Check Close Reasons for Appropriate Response
348
349
```python
350
except simple_websocket.ConnectionClosed as e:
351
if e.reason in [1000, 1001]: # Normal closures
352
# Clean shutdown
353
pass
354
elif e.reason in [1002, 1003, 1007]: # Protocol errors
355
# Log error, don't retry
356
pass
357
elif e.reason in [1006, 1011]: # Abnormal/server errors
358
# Consider reconnection
359
pass
360
```
361
362
### 3. Use Finally Blocks for Cleanup
363
364
```python
365
try:
366
# WebSocket operations
367
pass
368
except simple_websocket.ConnectionError:
369
# Handle errors
370
pass
371
finally:
372
# Always cleanup resources
373
if ws:
374
try:
375
ws.close()
376
except:
377
pass # Ignore cleanup errors
378
```