0
# Frame Protocol and Status Codes
1
2
WebSocket frame handling, message types, and protocol status codes for low-level protocol control and custom frame processing. The ABNF class implements the WebSocket frame format according to RFC 6455.
3
4
## Capabilities
5
6
### ABNF Frame Class
7
8
WebSocket frame implementation handling frame construction, validation, and binary serialization according to the ABNF specification.
9
10
```python { .api }
11
class ABNF:
12
# Operation code constants
13
OPCODE_CONT = 0x0 # Continuation frame
14
OPCODE_TEXT = 0x1 # Text frame
15
OPCODE_BINARY = 0x2 # Binary frame
16
OPCODE_CLOSE = 0x8 # Close frame
17
OPCODE_PING = 0x9 # Ping frame
18
OPCODE_PONG = 0xa # Pong frame
19
20
# Valid opcodes tuple
21
OPCODES = (OPCODE_CONT, OPCODE_TEXT, OPCODE_BINARY, OPCODE_CLOSE, OPCODE_PING, OPCODE_PONG)
22
23
# Human readable opcode mapping
24
OPCODE_MAP = {
25
OPCODE_CONT: "cont",
26
OPCODE_TEXT: "text",
27
OPCODE_BINARY: "binary",
28
OPCODE_CLOSE: "close",
29
OPCODE_PING: "ping",
30
OPCODE_PONG: "pong",
31
}
32
33
# Frame length thresholds
34
LENGTH_7 = 0x7E # 126 - use 2-byte length
35
LENGTH_16 = 1 << 16 # 65536 - use 8-byte length
36
LENGTH_63 = 1 << 63 # Maximum frame size
37
38
def __init__(
39
self,
40
fin: int = 0,
41
rsv1: int = 0,
42
rsv2: int = 0,
43
rsv3: int = 0,
44
opcode: int = OPCODE_TEXT,
45
mask_value: int = 1,
46
data: Union[str, bytes, None] = "",
47
) -> None:
48
"""
49
Construct ABNF frame.
50
51
Parameters:
52
- fin: Final fragment flag (0 or 1)
53
- rsv1: Reserved bit 1 (must be 0)
54
- rsv2: Reserved bit 2 (must be 0)
55
- rsv3: Reserved bit 3 (must be 0)
56
- opcode: Frame opcode (see OPCODE_* constants)
57
- mask_value: Mask flag (1 for client frames, 0 for server)
58
- data: Frame payload data
59
"""
60
```
61
62
### Frame Operations
63
64
Create, validate, and serialize WebSocket frames with proper protocol compliance.
65
66
```python { .api }
67
def validate(self, skip_utf8_validation: bool = False) -> None:
68
"""
69
Validate frame according to WebSocket protocol rules.
70
71
Parameters:
72
- skip_utf8_validation: Skip UTF-8 validation for text frames
73
74
Raises:
75
WebSocketProtocolException: If frame violates protocol rules
76
"""
77
78
@staticmethod
79
def create_frame(data: Union[bytes, str], opcode: int, fin: int = 1) -> "ABNF":
80
"""
81
Create frame for sending data.
82
83
Parameters:
84
- data: Payload data (auto-encoded to UTF-8 if string and opcode is TEXT)
85
- opcode: Frame opcode (OPCODE_TEXT, OPCODE_BINARY, etc.)
86
- fin: Final fragment flag (1 for complete message, 0 for fragmented)
87
88
Returns:
89
ABNF: Configured frame ready for transmission
90
"""
91
92
def format(self) -> bytes:
93
"""
94
Serialize frame to wire format bytes.
95
96
Returns:
97
bytes: Complete frame in WebSocket wire format
98
99
Raises:
100
ValueError: If frame configuration is invalid
101
"""
102
103
@staticmethod
104
def mask(mask_key: Union[str, bytes], data: Union[str, bytes]) -> bytes:
105
"""
106
Apply XOR mask to data (mask or unmask).
107
108
Parameters:
109
- mask_key: 4-byte mask key
110
- data: Data to mask/unmask
111
112
Returns:
113
bytes: Masked/unmasked data
114
"""
115
```
116
117
### WebSocket Status Codes
118
119
Standard close status codes for WebSocket connection termination.
120
121
```python { .api }
122
# Normal closure codes
123
STATUS_NORMAL = 1000 # Normal closure
124
STATUS_GOING_AWAY = 1001 # Endpoint going away
125
STATUS_PROTOCOL_ERROR = 1002 # Protocol error
126
STATUS_UNSUPPORTED_DATA_TYPE = 1003 # Unsupported data type
127
128
# Special status codes
129
STATUS_STATUS_NOT_AVAILABLE = 1005 # No status code available
130
STATUS_ABNORMAL_CLOSED = 1006 # Abnormal closure (no close frame)
131
132
# Error status codes
133
STATUS_INVALID_PAYLOAD = 1007 # Invalid UTF-8 or other payload error
134
STATUS_POLICY_VIOLATION = 1008 # Policy violation
135
STATUS_MESSAGE_TOO_BIG = 1009 # Message too big
136
STATUS_INVALID_EXTENSION = 1010 # Extension negotiation failure
137
STATUS_UNEXPECTED_CONDITION = 1011 # Unexpected server condition
138
139
# Infrastructure status codes
140
STATUS_SERVICE_RESTART = 1012 # Service restart
141
STATUS_TRY_AGAIN_LATER = 1013 # Try again later
142
STATUS_BAD_GATEWAY = 1014 # Bad gateway
143
STATUS_TLS_HANDSHAKE_ERROR = 1015 # TLS handshake error
144
145
# Valid close status code range
146
VALID_CLOSE_STATUS = (
147
STATUS_NORMAL,
148
STATUS_GOING_AWAY,
149
STATUS_PROTOCOL_ERROR,
150
STATUS_UNSUPPORTED_DATA_TYPE,
151
STATUS_INVALID_PAYLOAD,
152
STATUS_POLICY_VIOLATION,
153
STATUS_MESSAGE_TOO_BIG,
154
STATUS_INVALID_EXTENSION,
155
STATUS_UNEXPECTED_CONDITION,
156
STATUS_SERVICE_RESTART,
157
STATUS_TRY_AGAIN_LATER,
158
STATUS_BAD_GATEWAY,
159
)
160
```
161
162
### Frame Buffer Classes
163
164
Advanced frame handling classes for buffering and managing continuation frames.
165
166
```python { .api }
167
class frame_buffer:
168
"""
169
Buffer incoming frame data from socket until complete frames are received.
170
171
Handles partial frame reception and frame reassembly from network packets.
172
"""
173
def __init__(self, recv_fn: Callable[[int], int], skip_utf8_validation: bool) -> None: ...
174
def recv_frame(self) -> ABNF: ...
175
def clear(self) -> None: ...
176
177
class continuous_frame:
178
"""
179
Handle WebSocket message fragmentation across multiple frames.
180
181
Manages continuation frames and message reassembly for large messages.
182
"""
183
def __init__(self, fire_cont_frame: bool, skip_utf8_validation: bool) -> None: ...
184
def validate(self, frame: ABNF) -> None: ...
185
def add(self, frame: ABNF) -> None: ...
186
def is_fire(self, frame: ABNF) -> Union[bool, int]: ...
187
def extract(self, frame: ABNF) -> tuple: ...
188
```
189
190
## Usage Examples
191
192
### Creating and Sending Custom Frames
193
194
```python
195
from websocket import WebSocket, ABNF
196
197
ws = WebSocket()
198
ws.connect("ws://echo.websocket.events/")
199
200
# Create text frame
201
text_frame = ABNF.create_frame("Hello, World!", ABNF.OPCODE_TEXT)
202
ws.send_frame(text_frame)
203
204
# Create binary frame
205
binary_data = b"\x01\x02\x03\x04"
206
binary_frame = ABNF.create_frame(binary_data, ABNF.OPCODE_BINARY)
207
ws.send_frame(binary_frame)
208
209
# Create ping frame
210
ping_frame = ABNF.create_frame("ping-payload", ABNF.OPCODE_PING)
211
ws.send_frame(ping_frame)
212
213
ws.close()
214
```
215
216
### Fragmented Message Sending
217
218
```python
219
from websocket import WebSocket, ABNF
220
221
ws = WebSocket()
222
ws.connect("ws://echo.websocket.events/")
223
224
message = "This is a long message that will be sent in fragments"
225
chunks = [message[i:i+10] for i in range(0, len(message), 10)]
226
227
# Send first fragment (not final)
228
first_frame = ABNF.create_frame(chunks[0], ABNF.OPCODE_TEXT, fin=0)
229
ws.send_frame(first_frame)
230
231
# Send middle fragments (continuation, not final)
232
for chunk in chunks[1:-1]:
233
cont_frame = ABNF.create_frame(chunk, ABNF.OPCODE_CONT, fin=0)
234
ws.send_frame(cont_frame)
235
236
# Send final fragment
237
final_frame = ABNF.create_frame(chunks[-1], ABNF.OPCODE_CONT, fin=1)
238
ws.send_frame(final_frame)
239
240
ws.close()
241
```
242
243
### Frame Analysis and Inspection
244
245
```python
246
from websocket import WebSocket, ABNF
247
248
ws = WebSocket()
249
ws.connect("ws://echo.websocket.events/")
250
251
# Send a message to get echo back
252
ws.send("Test message")
253
254
# Receive and analyze frame
255
opcode, frame = ws.recv_data_frame()
256
257
print(f"Frame details:")
258
print(f" Opcode: {opcode} ({ABNF.OPCODE_MAP.get(opcode, 'unknown')})")
259
print(f" Final frame: {bool(frame.fin)}")
260
print(f" Reserved bits: RSV1={frame.rsv1}, RSV2={frame.rsv2}, RSV3={frame.rsv3}")
261
print(f" Masked: {bool(frame.mask_value)}")
262
print(f" Data length: {len(frame.data)}")
263
print(f" Data: {frame.data}")
264
265
# Validate frame
266
try:
267
frame.validate()
268
print("Frame is valid")
269
except Exception as e:
270
print(f"Frame validation error: {e}")
271
272
ws.close()
273
```
274
275
### Custom Close Handling
276
277
```python
278
from websocket import WebSocket, ABNF, STATUS_NORMAL, STATUS_GOING_AWAY
279
import struct
280
281
ws = WebSocket()
282
ws.connect("ws://echo.websocket.events/")
283
284
# Send custom close frame with reason
285
close_reason = "Custom shutdown"
286
close_data = struct.pack("!H", STATUS_GOING_AWAY) + close_reason.encode('utf-8')
287
close_frame = ABNF.create_frame(close_data, ABNF.OPCODE_CLOSE)
288
289
ws.send_frame(close_frame)
290
291
# Wait for close response
292
try:
293
opcode, response_frame = ws.recv_data_frame()
294
if opcode == ABNF.OPCODE_CLOSE:
295
if len(response_frame.data) >= 2:
296
status_code = struct.unpack("!H", response_frame.data[:2])[0]
297
reason = response_frame.data[2:].decode('utf-8')
298
print(f"Server closed with status {status_code}: {reason}")
299
else:
300
print("Server closed without status code")
301
except Exception as e:
302
print(f"Close handshake error: {e}")
303
304
ws.shutdown()
305
```
306
307
### Frame Masking and Unmasking
308
309
```python
310
from websocket import ABNF
311
import os
312
313
# Example of manual frame masking
314
data = b"Secret payload data"
315
mask_key = os.urandom(4) # Generate random 4-byte mask
316
317
# Mask the data
318
masked_data = ABNF.mask(mask_key, data)
319
print(f"Original: {data}")
320
print(f"Mask key: {mask_key.hex()}")
321
print(f"Masked: {masked_data.hex()}")
322
323
# Unmask the data (XOR is reversible)
324
unmasked_data = ABNF.mask(mask_key, masked_data)
325
print(f"Unmasked: {unmasked_data}")
326
print(f"Match original: {unmasked_data == data}")
327
```
328
329
### Status Code Usage
330
331
```python
332
from websocket import WebSocket, STATUS_NORMAL, STATUS_GOING_AWAY, STATUS_PROTOCOL_ERROR
333
334
ws = WebSocket()
335
ws.connect("ws://echo.websocket.events/")
336
337
# Close with different status codes based on condition
338
try:
339
# Normal operation
340
ws.send("Hello")
341
response = ws.recv()
342
print(f"Got response: {response}")
343
344
# Normal close
345
ws.close(status=STATUS_NORMAL, reason=b"Completed successfully")
346
347
except Exception as e:
348
# Error close
349
print(f"Error occurred: {e}")
350
ws.close(status=STATUS_PROTOCOL_ERROR, reason=b"Protocol error occurred")
351
```
352
353
## Types
354
355
```python { .api }
356
# Frame operation types
357
OpcodeType = int # 0x0-0xf, see OPCODE_* constants
358
StatusCodeType = int # 1000-4999, see STATUS_* constants
359
MaskKeyType = Union[str, bytes] # 4-byte mask key
360
FrameDataType = Union[str, bytes, None] # Frame payload data
361
362
# Frame validation function signature
363
ValidationCallable = Callable[[bytes], bool]
364
365
# Frame masking function signature
366
MaskingCallable = Callable[[int], bytes]
367
```