or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdcontrol-frames.mddata-headers.mdframe-operations.mdindex.md

advanced-features.mddocs/

0

# Advanced Features

1

2

Support for PUSH_PROMISE, CONTINUATION, ALTSVC frames and extension frames for handling unknown frame types, plus comprehensive flag management and error handling. These features provide complete HTTP/2 implementation support and extensibility.

3

4

## Capabilities

5

6

### PUSH_PROMISE Frames

7

8

PUSH_PROMISE frames notify peers of streams the sender intends to initiate, enabling server push functionality.

9

10

```python { .api }

11

class PushPromiseFrame(Padding, Frame):

12

def __init__(self, stream_id: int, promised_stream_id: int = 0,

13

data: bytes = b"", **kwargs) -> None:

14

"""

15

Create a PUSH_PROMISE frame.

16

17

Parameters:

18

- stream_id (int): Stream identifier (must be non-zero)

19

- promised_stream_id (int): Stream ID being promised (must be even and non-zero)

20

- data (bytes): HPACK-encoded header block for promised request

21

- pad_length (int): Padding length if PADDED flag set

22

- flags (Iterable[str]): Frame flags ("END_HEADERS", "PADDED")

23

- **kwargs: Additional arguments for parent classes

24

25

Raises:

26

- InvalidDataError: If stream_id is zero or promised_stream_id is invalid

27

"""

28

29

@property

30

def promised_stream_id(self) -> int:

31

"""Stream ID that will be used for the promised stream."""

32

33

@property

34

def data(self) -> bytes:

35

"""HPACK-encoded header block for the promised request."""

36

37

# Inherited from Frame

38

def serialize_body(self) -> bytes: ...

39

def parse_body(self, data: memoryview) -> None: ...

40

```

41

42

**Defined Flags:**

43

- `END_HEADERS` (0x04): Indicates end of header list

44

- `PADDED` (0x08): Indicates frame contains padding

45

46

### ALTSVC Frames

47

48

ALTSVC frames advertise alternate services that can handle requests, as defined in RFC 7838.

49

50

```python { .api }

51

class AltSvcFrame(Frame):

52

def __init__(self, stream_id: int, origin: bytes = b"", field: bytes = b"", **kwargs) -> None:

53

"""

54

Create an ALTSVC frame.

55

56

Parameters:

57

- stream_id (int): Stream identifier (0 for connection, non-zero for stream)

58

- origin (bytes): Origin for alternate service (empty if stream_id != 0)

59

- field (bytes): Alt-Svc field value

60

- **kwargs: Additional arguments for parent classes

61

62

Note: Either stream_id must be 0 XOR origin must be empty (not both)

63

64

Raises:

65

- InvalidDataError: If origin or field are not bytes

66

"""

67

68

@property

69

def origin(self) -> bytes:

70

"""Origin that the alternate service applies to."""

71

72

@property

73

def field(self) -> bytes:

74

"""Alt-Svc field value describing the alternate service."""

75

76

# Inherited from Frame

77

def serialize_body(self) -> bytes: ...

78

def parse_body(self, data: memoryview) -> None: ...

79

```

80

81

**Defined Flags:** None

82

83

### Extension Frames

84

85

Extension frames wrap unknown frame types to enable processing of non-standard frames.

86

87

```python { .api }

88

class ExtensionFrame(Frame):

89

def __init__(self, type: int, stream_id: int, flag_byte: int = 0x0,

90

body: bytes = b"", **kwargs) -> None:

91

"""

92

Create an extension frame for unknown frame types.

93

94

Parameters:

95

- type (int): Frame type identifier

96

- stream_id (int): Stream identifier

97

- flag_byte (int): Raw flag byte value

98

- body (bytes): Frame body data

99

- **kwargs: Additional arguments for parent classes

100

"""

101

102

@property

103

def type(self) -> int:

104

"""Frame type identifier."""

105

106

@property

107

def flag_byte(self) -> int:

108

"""Raw flag byte from frame header."""

109

110

@property

111

def body(self) -> bytes:

112

"""Raw frame body data."""

113

114

def parse_flags(self, flag_byte: int) -> None:

115

"""

116

Store raw flag byte instead of parsing individual flags.

117

118

Parameters:

119

- flag_byte (int): 8-bit flag value from frame header

120

"""

121

122

def serialize(self) -> bytes:

123

"""

124

Serialize extension frame exactly as received.

125

126

Returns:

127

bytes: Complete frame with original type and flag byte

128

"""

129

130

# Inherited from Frame

131

def parse_body(self, data: memoryview) -> None: ...

132

```

133

134

### Flag Management

135

136

Type-safe flag management system ensuring only valid flags are set per frame type.

137

138

```python { .api }

139

class Flag:

140

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

141

"""

142

Create a flag definition.

143

144

Parameters:

145

- name (str): Flag name (e.g., "END_STREAM")

146

- bit (int): Bit value for the flag (e.g., 0x01)

147

"""

148

149

@property

150

def name(self) -> str:

151

"""Flag name."""

152

153

@property

154

def bit(self) -> int:

155

"""Flag bit value."""

156

157

class Flags:

158

def __init__(self, defined_flags: Iterable[Flag]) -> None:

159

"""

160

Create a flags container for a specific frame type.

161

162

Parameters:

163

- defined_flags (Iterable[Flag]): Valid flags for this frame type

164

"""

165

166

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

167

"""

168

Add a flag to the set.

169

170

Parameters:

171

- value (str): Flag name to add

172

173

Raises:

174

- ValueError: If flag is not defined for this frame type

175

"""

176

177

def discard(self, value: str) -> None:

178

"""

179

Remove a flag from the set if present.

180

181

Parameters:

182

- value (str): Flag name to remove

183

"""

184

185

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

186

"""

187

Check if flag is set.

188

189

Parameters:

190

- flag (str): Flag name to check

191

192

Returns:

193

bool: True if flag is set

194

"""

195

196

def __iter__(self) -> Iterator[str]:

197

"""

198

Iterate over set flags.

199

200

Returns:

201

Iterator[str]: Iterator over flag names

202

"""

203

204

def __len__(self) -> int:

205

"""

206

Get number of set flags.

207

208

Returns:

209

int: Number of flags currently set

210

"""

211

```

212

213

## Usage Examples

214

215

### PUSH_PROMISE Operations

216

217

```python

218

from hyperframe.frame import PushPromiseFrame

219

220

# Server promising to push a resource

221

push_promise = PushPromiseFrame(

222

stream_id=1, # Original request stream

223

promised_stream_id=2, # Stream for pushed response

224

data=b"\\x00\\x05:path\\x0A/style.css", # HPACK headers for promised request

225

flags=["END_HEADERS"]

226

)

227

228

print(f"Promising stream {push_promise.promised_stream_id} from stream {push_promise.stream_id}")

229

230

# PUSH_PROMISE with padding

231

padded_promise = PushPromiseFrame(

232

stream_id=1,

233

promised_stream_id=4,

234

data=b"\\x00\\x05:path\\x0B/script.js",

235

pad_length=8,

236

flags=["END_HEADERS", "PADDED"]

237

)

238

```

239

240

### ALTSVC Frame Usage

241

242

```python

243

from hyperframe.frame import AltSvcFrame

244

245

# Connection-level alternate service

246

connection_altsvc = AltSvcFrame(

247

stream_id=0, # Connection level

248

origin=b"example.com",

249

field=b'h2="alt.example.com:443"; ma=3600'

250

)

251

252

# Stream-specific alternate service

253

stream_altsvc = AltSvcFrame(

254

stream_id=1, # Stream specific

255

origin=b"", # Empty for stream-specific

256

field=b'h2="alt2.example.com:443"'

257

)

258

259

print(f"Connection Alt-Svc: {connection_altsvc.field}")

260

print(f"Stream Alt-Svc: {stream_altsvc.field}")

261

```

262

263

### Extension Frame Handling

264

265

```python

266

from hyperframe.frame import Frame, ExtensionFrame

267

268

# Parse unknown frame type

269

unknown_frame_data = b'\\x00\\x00\\x05\\xEE\\x0F\\x00\\x00\\x00\\x01Hello' # Type 0xEE

270

frame, length = Frame.parse_frame_header(unknown_frame_data[:9], strict=False)

271

272

if isinstance(frame, ExtensionFrame):

273

frame.parse_body(memoryview(unknown_frame_data[9:9 + length]))

274

print(f"Unknown frame type: 0x{frame.type:02X}")

275

print(f"Flag byte: 0x{frame.flag_byte:02X}")

276

print(f"Body: {frame.body}")

277

278

# Round-trip serialization

279

serialized = frame.serialize()

280

assert serialized == unknown_frame_data

281

282

# Create extension frame manually

283

custom_frame = ExtensionFrame(

284

type=0xCC,

285

stream_id=3,

286

flag_byte=0x05,

287

body=b"Custom frame data"

288

)

289

```

290

291

### Flag Management

292

293

```python

294

from hyperframe.flags import Flag, Flags

295

from hyperframe.frame import DataFrame

296

297

# Working with frame flags

298

data_frame = DataFrame(stream_id=1, data=b"Test data")

299

300

# Add flags

301

data_frame.flags.add("END_STREAM")

302

print(f"END_STREAM set: {'END_STREAM' in data_frame.flags}")

303

304

# Try to add invalid flag

305

try:

306

data_frame.flags.add("INVALID_FLAG")

307

except ValueError as e:

308

print(f"Flag error: {e}")

309

310

# Check all set flags

311

print(f"Set flags: {list(data_frame.flags)}")

312

313

# Remove flag

314

data_frame.flags.discard("END_STREAM")

315

print(f"Flags after discard: {list(data_frame.flags)}")

316

317

# Create custom flag set

318

custom_flags = [

319

Flag("CUSTOM_FLAG_1", 0x01),

320

Flag("CUSTOM_FLAG_2", 0x02)

321

]

322

flag_set = Flags(custom_flags)

323

flag_set.add("CUSTOM_FLAG_1")

324

print(f"Custom flags: {list(flag_set)}")

325

```

326

327

### Complex Frame Scenarios

328

329

```python

330

from hyperframe.frame import HeadersFrame, ContinuationFrame, PushPromiseFrame

331

332

# Server push scenario with large headers

333

large_headers = b"\\x00\\x07:method\\x03GET" + b"\\x00" * 2000

334

335

# Initial PUSH_PROMISE without END_HEADERS

336

push_start = PushPromiseFrame(

337

stream_id=1,

338

promised_stream_id=2,

339

data=large_headers[:1000]

340

# No END_HEADERS flag

341

)

342

343

# CONTINUATION to complete the promise

344

push_continuation = ContinuationFrame(

345

stream_id=1,

346

data=large_headers[1000:],

347

flags=["END_HEADERS"]

348

)

349

350

print(f"Push promise starts: {len(push_start.data)} bytes")

351

print(f"Continuation completes: {len(push_continuation.data)} bytes")

352

353

# Later, server sends response on promised stream

354

response_headers = HeadersFrame(

355

stream_id=2, # Promised stream

356

data=b"\\x00\\x07:status\\x03200",

357

flags=["END_HEADERS"]

358

)

359

```

360

361

### Error Handling and Validation

362

363

```python

364

from hyperframe.frame import PushPromiseFrame, AltSvcFrame

365

from hyperframe.exceptions import InvalidDataError, InvalidFrameError

366

367

# Invalid PUSH_PROMISE stream ID

368

try:

369

PushPromiseFrame(stream_id=1, promised_stream_id=1) # Must be even

370

except InvalidDataError as e:

371

print(f"Push promise error: {e}")

372

373

# Invalid ALTSVC parameters

374

try:

375

AltSvcFrame(stream_id=1, origin="not bytes", field=b"valid") # Origin must be bytes

376

except InvalidDataError as e:

377

print(f"AltSvc error: {e}")

378

379

# Parse extension frame with malformed data

380

try:

381

bad_data = b'\\x00\\x00\\x05\\xFF\\x00\\x00\\x00\\x00\\x01ABC' # Only 3 bytes, claims 5

382

frame, length = Frame.parse_frame_header(bad_data[:9])

383

frame.parse_body(memoryview(bad_data[9:])) # Will be truncated

384

print(f"Extension frame body: {frame.body}") # Only gets available data

385

except Exception as e:

386

print(f"Parse error: {e}")

387

```