or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

asyncio-client.mdasyncio-server.mddata-structures.mdexceptions.mdextensions.mdindex.mdprotocol.mdrouting.mdsync-client.mdsync-server.md

data-structures.mddocs/

0

# Data Structures

1

2

Core data structures for HTTP headers, WebSocket frames, close codes, opcodes, and type definitions used throughout the WebSocket implementation. These provide the foundation for protocol handling and message processing.

3

4

## Capabilities

5

6

### HTTP Headers Management

7

8

Case-insensitive HTTP headers container with dict-like interface for WebSocket handshake processing.

9

10

```python { .api }

11

class Headers:

12

"""

13

Case-insensitive HTTP headers container.

14

15

Provides dict-like interface for managing HTTP headers used in

16

WebSocket handshake with proper case-insensitive handling.

17

"""

18

19

def __init__(self, *args, **kwargs):

20

"""

21

Initialize headers container.

22

23

Parameters:

24

- *args: Dict, list of tuples, or Headers instance

25

- **kwargs: Header key-value pairs

26

27

Examples:

28

Headers({"Content-Type": "text/html"})

29

Headers([("Host", "example.com"), ("Origin", "https://app.com")])

30

Headers(host="example.com", origin="https://app.com")

31

"""

32

33

def __getitem__(self, key: str) -> str:

34

"""

35

Get header value by name (case-insensitive).

36

37

Parameters:

38

- key: Header name

39

40

Returns:

41

str: Header value

42

43

Raises:

44

- KeyError: If header doesn't exist

45

"""

46

47

def __setitem__(self, key: str, value: str) -> None:

48

"""

49

Set header value by name (case-insensitive).

50

51

Parameters:

52

- key: Header name

53

- value: Header value

54

"""

55

56

def __delitem__(self, key: str) -> None:

57

"""

58

Delete header by name (case-insensitive).

59

60

Parameters:

61

- key: Header name

62

63

Raises:

64

- KeyError: If header doesn't exist

65

"""

66

67

def __contains__(self, key: str) -> bool:

68

"""

69

Check if header exists (case-insensitive).

70

71

Parameters:

72

- key: Header name

73

74

Returns:

75

bool: True if header exists

76

"""

77

78

def __iter__(self):

79

"""Iterate over header names."""

80

81

def __len__(self) -> int:

82

"""Get number of headers."""

83

84

def get(self, key: str, default: str = None) -> str | None:

85

"""

86

Get header value with default.

87

88

Parameters:

89

- key: Header name (case-insensitive)

90

- default: Default value if header doesn't exist

91

92

Returns:

93

str | None: Header value or default

94

"""

95

96

def get_all(self, key: str) -> List[str]:

97

"""

98

Get all values for a header name.

99

100

Parameters:

101

- key: Header name (case-insensitive)

102

103

Returns:

104

List[str]: List of all values for the header

105

"""

106

107

def add(self, key: str, value: str) -> None:

108

"""

109

Add header value (allows multiple values for same name).

110

111

Parameters:

112

- key: Header name

113

- value: Header value to add

114

"""

115

116

def items(self):

117

"""Iterate over (name, value) pairs."""

118

119

def keys(self):

120

"""Get header names."""

121

122

def values(self):

123

"""Get header values."""

124

125

def copy(self) -> Headers:

126

"""Create a copy of the headers."""

127

128

def update(self, *args, **kwargs) -> None:

129

"""

130

Update headers with new values.

131

132

Parameters same as __init__()

133

"""

134

135

# Type alias for header input formats

136

HeadersLike = Union[Headers, Dict[str, str], List[Tuple[str, str]]]

137

138

class MultipleValuesError(Exception):

139

"""

140

Raised when single-value header has multiple values.

141

142

Some HTTP headers should only have one value, but the headers

143

container received multiple values for the same header name.

144

"""

145

146

def __init__(self, key: str, values: List[str]):

147

"""

148

Initialize multiple values error.

149

150

Parameters:

151

- key: Header name that has multiple values

152

- values: List of values found for the header

153

"""

154

self.key = key

155

self.values = values

156

super().__init__(f"Multiple values for header {key}: {values}")

157

```

158

159

### WebSocket Frame Structures

160

161

Low-level WebSocket frame representation and frame opcodes for protocol implementation.

162

163

```python { .api }

164

class Frame:

165

"""

166

WebSocket frame representation.

167

168

Represents a single WebSocket frame with opcode, payload data,

169

and control flags according to RFC 6455.

170

"""

171

172

def __init__(

173

self,

174

opcode: Opcode,

175

data: bytes,

176

fin: bool = True,

177

rsv1: bool = False,

178

rsv2: bool = False,

179

rsv3: bool = False

180

):

181

"""

182

Initialize WebSocket frame.

183

184

Parameters:

185

- opcode: Frame opcode (text, binary, control, etc.)

186

- data: Frame payload data

187

- fin: Final fragment flag (True for complete messages)

188

- rsv1: Reserved bit 1 (used by extensions)

189

- rsv2: Reserved bit 2 (used by extensions)

190

- rsv3: Reserved bit 3 (used by extensions)

191

"""

192

self.opcode = opcode

193

self.data = data

194

self.fin = fin

195

self.rsv1 = rsv1

196

self.rsv2 = rsv2

197

self.rsv3 = rsv3

198

199

def __repr__(self) -> str:

200

"""String representation of frame."""

201

202

@property

203

def is_data_frame(self) -> bool:

204

"""Check if frame carries data (text/binary)."""

205

206

@property

207

def is_control_frame(self) -> bool:

208

"""Check if frame is control frame (close/ping/pong)."""

209

210

class Opcode(Enum):

211

"""

212

WebSocket frame opcodes as defined in RFC 6455.

213

214

Specifies the type of WebSocket frame and how the

215

payload should be interpreted.

216

"""

217

CONT = 0 # Continuation frame

218

TEXT = 1 # Text frame (UTF-8 data)

219

BINARY = 2 # Binary frame (arbitrary data)

220

# 3-7 reserved for future data frames

221

CLOSE = 8 # Close connection frame

222

PING = 9 # Ping frame

223

PONG = 10 # Pong frame

224

# 11-15 reserved for future control frames

225

226

# Opcode constants for convenience

227

OP_CONT = Opcode.CONT

228

OP_TEXT = Opcode.TEXT

229

OP_BINARY = Opcode.BINARY

230

OP_CLOSE = Opcode.CLOSE

231

OP_PING = Opcode.PING

232

OP_PONG = Opcode.PONG

233

234

# Opcode groups

235

DATA_OPCODES = frozenset([Opcode.CONT, Opcode.TEXT, Opcode.BINARY])

236

CTRL_OPCODES = frozenset([Opcode.CLOSE, Opcode.PING, Opcode.PONG])

237

```

238

239

### Close Frame and Close Codes

240

241

WebSocket connection close frame structure and standardized close codes.

242

243

```python { .api }

244

class Close:

245

"""

246

WebSocket close frame representation.

247

248

Represents close frame with close code and optional reason,

249

used for graceful connection termination.

250

"""

251

252

def __init__(self, code: int, reason: str = ""):

253

"""

254

Initialize close frame.

255

256

Parameters:

257

- code: Close code (see CloseCode enum)

258

- reason: Optional human-readable close reason (max 123 bytes UTF-8)

259

260

Raises:

261

- ValueError: If code is invalid or reason is too long

262

"""

263

self.code = code

264

self.reason = reason

265

266

def __repr__(self) -> str:

267

"""String representation of close frame."""

268

269

@classmethod

270

def parse(cls, data: bytes) -> Close:

271

"""

272

Parse close frame payload.

273

274

Parameters:

275

- data: Close frame payload bytes

276

277

Returns:

278

Close: Parsed close frame

279

280

Raises:

281

- ValueError: If payload format is invalid

282

"""

283

284

def serialize(self) -> bytes:

285

"""

286

Serialize close frame to bytes.

287

288

Returns:

289

bytes: Close frame payload for transmission

290

"""

291

292

class CloseCode(Enum):

293

"""

294

Standard WebSocket close codes as defined in RFC 6455.

295

296

Indicates the reason for closing the WebSocket connection.

297

"""

298

NORMAL_CLOSURE = 1000 # Normal closure

299

GOING_AWAY = 1001 # Endpoint going away

300

PROTOCOL_ERROR = 1002 # Protocol error

301

UNSUPPORTED_DATA = 1003 # Unsupported data type

302

# 1004 reserved

303

NO_STATUS_RCVD = 1005 # No status received (reserved)

304

ABNORMAL_CLOSURE = 1006 # Abnormal closure (reserved)

305

INVALID_FRAME_PAYLOAD_DATA = 1007 # Invalid UTF-8 or extension data

306

POLICY_VIOLATION = 1008 # Policy violation

307

MESSAGE_TOO_BIG = 1009 # Message too big

308

MANDATORY_EXTENSION = 1010 # Missing mandatory extension

309

INTERNAL_ERROR = 1011 # Internal server error

310

SERVICE_RESTART = 1012 # Service restart

311

TRY_AGAIN_LATER = 1013 # Try again later

312

BAD_GATEWAY = 1014 # Bad gateway

313

TLS_HANDSHAKE = 1015 # TLS handshake failure (reserved)

314

315

# Close code constants for convenience

316

CLOSE_CODES = {

317

1000: "Normal Closure",

318

1001: "Going Away",

319

1002: "Protocol Error",

320

1003: "Unsupported Data",

321

1005: "No Status Received",

322

1006: "Abnormal Closure",

323

1007: "Invalid Frame Payload Data",

324

1008: "Policy Violation",

325

1009: "Message Too Big",

326

1010: "Mandatory Extension",

327

1011: "Internal Error",

328

1012: "Service Restart",

329

1013: "Try Again Later",

330

1014: "Bad Gateway",

331

1015: "TLS Handshake"

332

}

333

```

334

335

### Type Definitions

336

337

Common type aliases and definitions used throughout the WebSocket implementation.

338

339

```python { .api }

340

# Core data types

341

Data = Union[str, bytes] # WebSocket message content (text or binary)

342

343

# Header-related types

344

HeadersLike = Union[Headers, Dict[str, str], List[Tuple[str, str]]]

345

346

# WebSocket protocol types

347

Origin = str # Origin header value

348

Subprotocol = str # WebSocket subprotocol name

349

ExtensionName = str # WebSocket extension name

350

ExtensionParameter = Tuple[str, Optional[str]] # Extension parameter (name, value)

351

352

# HTTP-related types

353

StatusLike = Union[int, HTTPStatus] # HTTP status code types

354

355

# Logging types

356

LoggerLike = Union[logging.Logger, logging.LoggerAdapter] # Logger compatibility

357

358

# Async types (for type hints)

359

if TYPE_CHECKING:

360

from typing import AsyncIterator, Awaitable, Callable, Coroutine

361

362

# Handler function types

363

AsyncHandler = Callable[[Any], Awaitable[None]]

364

SyncHandler = Callable[[Any], None]

365

366

# Connection types

367

ConnectionType = Union['ClientConnection', 'ServerConnection']

368

```

369

370

## Usage Examples

371

372

### Headers Management

373

374

```python

375

from websockets.datastructures import Headers, MultipleValuesError

376

377

def headers_examples():

378

"""Demonstrate Headers usage."""

379

380

# Create headers in different ways

381

headers1 = Headers({"Host": "example.com", "Origin": "https://app.com"})

382

headers2 = Headers([("Content-Type", "text/html"), ("Accept", "text/html")])

383

headers3 = Headers(host="example.com", origin="https://app.com")

384

385

print("=== Basic Headers Operations ===")

386

387

# Case-insensitive access

388

headers = Headers({"Content-Type": "application/json"})

389

print(f"Content-Type: {headers['content-type']}") # Works!

390

print(f"CONTENT-TYPE: {headers['CONTENT-TYPE']}") # Also works!

391

392

# Check existence

393

print(f"Has Content-Type: {'content-type' in headers}")

394

395

# Get with default

396

print(f"Authorization: {headers.get('authorization', 'None')}")

397

398

# Add multiple values

399

headers.add("Accept", "text/html")

400

headers.add("Accept", "application/json")

401

print(f"All Accept values: {headers.get_all('accept')}")

402

403

# Iterate over headers

404

print("\nAll headers:")

405

for name, value in headers.items():

406

print(f" {name}: {value}")

407

408

# Copy and update

409

new_headers = headers.copy()

410

new_headers.update({"User-Agent": "MyApp/1.0", "Accept-Language": "en-US"})

411

412

print(f"\nUpdated headers count: {len(new_headers)}")

413

414

# Handle multiple values error

415

try:

416

# This would raise MultipleValuesError if header should be single-value

417

single_value = headers["accept"] # But Accept can have multiple values

418

print(f"Single Accept value: {single_value}")

419

except MultipleValuesError as e:

420

print(f"Multiple values error: {e}")

421

422

headers_examples()

423

```

424

425

### Frame Processing

426

427

```python

428

from websockets.datastructures import Frame, Opcode, Close, CloseCode

429

430

def frame_examples():

431

"""Demonstrate Frame and Close usage."""

432

433

print("=== WebSocket Frame Examples ===")

434

435

# Create different frame types

436

text_frame = Frame(Opcode.TEXT, b"Hello, WebSocket!", fin=True)

437

binary_frame = Frame(Opcode.BINARY, b"\x00\x01\x02\x03", fin=True)

438

ping_frame = Frame(Opcode.PING, b"ping-data")

439

pong_frame = Frame(Opcode.PONG, b"pong-data")

440

441

frames = [text_frame, binary_frame, ping_frame, pong_frame]

442

443

for frame in frames:

444

print(f"Frame: {frame}")

445

print(f" Opcode: {frame.opcode}")

446

print(f" Data length: {len(frame.data)}")

447

print(f" Is data frame: {frame.is_data_frame}")

448

print(f" Is control frame: {frame.is_control_frame}")

449

print()

450

451

# Close frame examples

452

print("=== Close Frame Examples ===")

453

454

# Create close frames

455

normal_close = Close(CloseCode.NORMAL_CLOSURE, "Normal shutdown")

456

error_close = Close(CloseCode.PROTOCOL_ERROR, "Invalid frame received")

457

458

close_frames = [normal_close, error_close]

459

460

for close_frame in close_frames:

461

print(f"Close frame: {close_frame}")

462

print(f" Code: {close_frame.code}")

463

print(f" Reason: {close_frame.reason}")

464

465

# Serialize and parse

466

serialized = close_frame.serialize()

467

parsed = Close.parse(serialized)

468

print(f" Serialized: {serialized.hex()}")

469

print(f" Parsed back: {parsed}")

470

print()

471

472

# Close code information

473

print("=== Close Codes ===")

474

for code, description in CLOSE_CODES.items():

475

print(f" {code}: {description}")

476

477

frame_examples()

478

```

479

480

### Type Usage Examples

481

482

```python

483

from websockets.datastructures import Data, HeadersLike, LoggerLike

484

import logging

485

from typing import Union, List, Tuple, Dict

486

487

def type_examples():

488

"""Demonstrate type usage."""

489

490

print("=== Type Usage Examples ===")

491

492

# Data type examples

493

def process_message(data: Data) -> str:

494

"""Process WebSocket message data."""

495

if isinstance(data, str):

496

return f"Text message: {data}"

497

elif isinstance(data, bytes):

498

return f"Binary message: {len(data)} bytes"

499

500

# Test with different data types

501

text_msg = "Hello, World!"

502

binary_msg = b"\x00\x01\x02\x03"

503

504

print(process_message(text_msg))

505

print(process_message(binary_msg))

506

507

# HeadersLike examples

508

def process_headers(headers: HeadersLike) -> Headers:

509

"""Process headers in various input formats."""

510

if isinstance(headers, Headers):

511

return headers

512

elif isinstance(headers, dict):

513

return Headers(headers)

514

elif isinstance(headers, list):

515

return Headers(headers)

516

else:

517

raise ValueError("Invalid headers format")

518

519

# Test different header formats

520

header_formats = [

521

{"Host": "example.com"},

522

[("Host", "example.com"), ("Origin", "https://app.com")],

523

Headers(host="example.com")

524

]

525

526

for headers in header_formats:

527

processed = process_headers(headers)

528

print(f"Processed headers: {dict(processed.items())}")

529

530

# Logger type examples

531

def setup_logging(logger: LoggerLike) -> None:

532

"""Setup logging with different logger types."""

533

if hasattr(logger, 'info'):

534

logger.info("Logger setup complete")

535

536

# Test with different logger types

537

standard_logger = logging.getLogger("test")

538

adapter_logger = logging.LoggerAdapter(standard_logger, {"context": "websocket"})

539

540

setup_logging(standard_logger)

541

setup_logging(adapter_logger)

542

543

type_examples()

544

```

545

546

### Custom Data Structures

547

548

```python

549

from websockets.datastructures import Headers, Frame, Opcode

550

from typing import NamedTuple, Optional

551

import json

552

553

class WebSocketMessage(NamedTuple):

554

"""Custom message structure for application-level messages."""

555

type: str

556

data: Union[str, bytes, dict]

557

timestamp: float

558

headers: Optional[Headers] = None

559

560

class MessageFrame:

561

"""Custom frame wrapper with application metadata."""

562

563

def __init__(self, frame: Frame, message_id: str = None):

564

self.frame = frame

565

self.message_id = message_id

566

self.processed_at = time.time()

567

568

@property

569

def is_text(self) -> bool:

570

return self.frame.opcode == Opcode.TEXT

571

572

@property

573

def is_binary(self) -> bool:

574

return self.frame.opcode == Opcode.BINARY

575

576

def to_json(self) -> str:

577

"""Convert frame to JSON representation."""

578

return json.dumps({

579

"message_id": self.message_id,

580

"opcode": self.frame.opcode.name,

581

"data_length": len(self.frame.data),

582

"processed_at": self.processed_at,

583

"fin": self.frame.fin

584

})

585

586

def custom_structures_example():

587

"""Demonstrate custom data structures."""

588

import time

589

590

print("=== Custom Data Structures ===")

591

592

# Create custom message

593

message = WebSocketMessage(

594

type="chat_message",

595

data={"user": "Alice", "text": "Hello everyone!"},

596

timestamp=time.time(),

597

headers=Headers({"Room": "general", "Priority": "normal"})

598

)

599

600

print(f"Message: {message}")

601

print(f"Message type: {message.type}")

602

print(f"Message headers: {dict(message.headers.items())}")

603

604

# Create custom frame wrapper

605

frame = Frame(Opcode.TEXT, json.dumps(message.data).encode())

606

wrapped_frame = MessageFrame(frame, message_id="msg_001")

607

608

print(f"\nWrapped frame: {wrapped_frame.to_json()}")

609

print(f"Is text frame: {wrapped_frame.is_text}")

610

print(f"Is binary frame: {wrapped_frame.is_binary}")

611

612

custom_structures_example()

613

```

614

615

### WebSocket Protocol Constants

616

617

```python

618

from websockets.datastructures import Opcode, CloseCode, DATA_OPCODES, CTRL_OPCODES

619

620

def protocol_constants_example():

621

"""Demonstrate protocol constants usage."""

622

623

print("=== WebSocket Protocol Constants ===")

624

625

# Opcode information

626

print("Data Opcodes:")

627

for opcode in DATA_OPCODES:

628

print(f" {opcode.name} ({opcode.value})")

629

630

print("\nControl Opcodes:")

631

for opcode in CTRL_OPCODES:

632

print(f" {opcode.name} ({opcode.value})")

633

634

# Frame type checking

635

def classify_frame(opcode: Opcode) -> str:

636

if opcode in DATA_OPCODES:

637

return "data"

638

elif opcode in CTRL_OPCODES:

639

return "control"

640

else:

641

return "unknown"

642

643

test_opcodes = [Opcode.TEXT, Opcode.BINARY, Opcode.CLOSE, Opcode.PING]

644

645

print(f"\nFrame Classification:")

646

for opcode in test_opcodes:

647

classification = classify_frame(opcode)

648

print(f" {opcode.name}: {classification}")

649

650

# Close code categories

651

def categorize_close_code(code: int) -> str:

652

if 1000 <= code <= 1003:

653

return "normal"

654

elif 1004 <= code <= 1006:

655

return "reserved"

656

elif 1007 <= code <= 1011:

657

return "error"

658

elif 1012 <= code <= 1014:

659

return "service"

660

elif code == 1015:

661

return "tls"

662

elif 3000 <= code <= 3999:

663

return "library"

664

elif 4000 <= code <= 4999:

665

return "application"

666

else:

667

return "invalid"

668

669

test_codes = [1000, 1002, 1006, 1011, 3000, 4000, 9999]

670

671

print(f"\nClose Code Categories:")

672

for code in test_codes:

673

category = categorize_close_code(code)

674

description = CLOSE_CODES.get(code, "Unknown")

675

print(f" {code} ({description}): {category}")

676

677

protocol_constants_example()

678

```