or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

abnf-protocol.mdexceptions.mdindex.mdlogging.mdsocket-config.mdwebsocket-app.mdwebsocket-core.md

abnf-protocol.mddocs/

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

```