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

control-frames.mddocs/

0

# Control Frames

1

2

HTTP/2 control frames for connection and stream management including SETTINGS, RST_STREAM, PRIORITY, PING, GOAWAY, and WINDOW_UPDATE frames. These frames manage the HTTP/2 connection lifecycle and provide flow control mechanisms.

3

4

## Capabilities

5

6

### SETTINGS Frames

7

8

SETTINGS frames convey configuration parameters that affect endpoint communication behavior. Settings are not negotiated and describe characteristics of the sending peer.

9

10

```python { .api }

11

class SettingsFrame(Frame):

12

def __init__(self, stream_id: int = 0, settings: dict[int, int] | None = None, **kwargs) -> None:

13

"""

14

Create a SETTINGS frame.

15

16

Parameters:

17

- stream_id (int): Must be 0 (connection-level frame)

18

- settings (dict[int, int] | None): Settings key-value pairs

19

- flags (Iterable[str]): Frame flags ("ACK")

20

- **kwargs: Additional arguments for parent classes

21

22

Raises:

23

- InvalidDataError: If stream_id is non-zero or ACK flag with non-empty settings

24

"""

25

26

@property

27

def settings(self) -> dict[int, int]:

28

"""Dictionary mapping setting identifiers to values."""

29

30

# Settings constants

31

HEADER_TABLE_SIZE: int # 0x01

32

ENABLE_PUSH: int # 0x02

33

MAX_CONCURRENT_STREAMS: int # 0x03

34

INITIAL_WINDOW_SIZE: int # 0x04

35

MAX_FRAME_SIZE: int # 0x05

36

MAX_HEADER_LIST_SIZE: int # 0x06

37

ENABLE_CONNECT_PROTOCOL: int # 0x08

38

39

# Inherited from Frame

40

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

41

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

42

```

43

44

**Defined Flags:**

45

- `ACK` (0x01): Acknowledges receipt of SETTINGS frame

46

47

### RST_STREAM Frames

48

49

RST_STREAM frames allow abnormal termination of streams, indicating cancellation or error conditions.

50

51

```python { .api }

52

class RstStreamFrame(Frame):

53

def __init__(self, stream_id: int, error_code: int = 0, **kwargs) -> None:

54

"""

55

Create a RST_STREAM frame.

56

57

Parameters:

58

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

59

- error_code (int): 32-bit error code for stream termination

60

- **kwargs: Additional arguments for parent classes

61

62

Raises:

63

- InvalidDataError: If stream_id is zero

64

"""

65

66

@property

67

def error_code(self) -> int:

68

"""32-bit error code for stream reset."""

69

70

# Inherited from Frame

71

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

72

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

73

```

74

75

**Defined Flags:** None

76

77

### PRIORITY Frames

78

79

PRIORITY frames specify sender-advised priority of streams and can be sent at any time to reprioritize existing streams.

80

81

```python { .api }

82

class PriorityFrame(Priority, Frame):

83

def __init__(self, stream_id: int, depends_on: int = 0, stream_weight: int = 0,

84

exclusive: bool = False, **kwargs) -> None:

85

"""

86

Create a PRIORITY frame.

87

88

Parameters:

89

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

90

- depends_on (int): Stream ID this stream depends on

91

- stream_weight (int): Stream weight (0-255)

92

- exclusive (bool): Whether this stream has exclusive dependency

93

- **kwargs: Additional arguments for parent classes

94

95

Raises:

96

- InvalidDataError: If stream_id is zero

97

"""

98

99

# Priority properties inherited from Priority mixin

100

@property

101

def depends_on(self) -> int: ...

102

103

@property

104

def stream_weight(self) -> int: ...

105

106

@property

107

def exclusive(self) -> bool: ...

108

109

# Inherited from Frame

110

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

111

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

112

```

113

114

**Defined Flags:** None

115

116

### PING Frames

117

118

PING frames measure round-trip time and determine connection liveness. They can be sent from any endpoint.

119

120

```python { .api }

121

class PingFrame(Frame):

122

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

123

"""

124

Create a PING frame.

125

126

Parameters:

127

- stream_id (int): Must be 0 (connection-level frame)

128

- opaque_data (bytes): Up to 8 bytes of opaque data

129

- flags (Iterable[str]): Frame flags ("ACK")

130

- **kwargs: Additional arguments for parent classes

131

132

Raises:

133

- InvalidDataError: If stream_id is non-zero

134

- InvalidFrameError: If opaque_data exceeds 8 bytes

135

"""

136

137

@property

138

def opaque_data(self) -> bytes:

139

"""Opaque data payload (exactly 8 bytes when serialized)."""

140

141

# Inherited from Frame

142

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

143

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

144

```

145

146

**Defined Flags:**

147

- `ACK` (0x01): Acknowledges receipt of PING frame

148

149

### GOAWAY Frames

150

151

GOAWAY frames inform the remote peer to stop creating streams, initiating graceful connection shutdown.

152

153

```python { .api }

154

class GoAwayFrame(Frame):

155

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

156

error_code: int = 0, additional_data: bytes = b"", **kwargs) -> None:

157

"""

158

Create a GOAWAY frame.

159

160

Parameters:

161

- stream_id (int): Must be 0 (connection-level frame)

162

- last_stream_id (int): Highest stream ID processed by sender

163

- error_code (int): 32-bit error code for connection termination

164

- additional_data (bytes): Additional diagnostic information

165

- **kwargs: Additional arguments for parent classes

166

167

Raises:

168

- InvalidDataError: If stream_id is non-zero

169

"""

170

171

@property

172

def last_stream_id(self) -> int:

173

"""Highest stream ID that was processed by the sender."""

174

175

@property

176

def error_code(self) -> int:

177

"""32-bit error code for connection termination."""

178

179

@property

180

def additional_data(self) -> bytes:

181

"""Additional diagnostic data."""

182

183

# Inherited from Frame

184

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

185

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

186

```

187

188

**Defined Flags:** None

189

190

### WINDOW_UPDATE Frames

191

192

WINDOW_UPDATE frames implement flow control at both stream and connection levels.

193

194

```python { .api }

195

class WindowUpdateFrame(Frame):

196

def __init__(self, stream_id: int, window_increment: int = 0, **kwargs) -> None:

197

"""

198

Create a WINDOW_UPDATE frame.

199

200

Parameters:

201

- stream_id (int): Stream identifier (0 for connection-level)

202

- window_increment (int): Flow control window increment (1 to 2^31-1)

203

- **kwargs: Additional arguments for parent classes

204

205

Raises:

206

- InvalidDataError: If window_increment is not in valid range

207

"""

208

209

@property

210

def window_increment(self) -> int:

211

"""Amount to increment the flow control window."""

212

213

# Inherited from Frame

214

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

215

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

216

```

217

218

**Defined Flags:** None

219

220

## Usage Examples

221

222

### SETTINGS Frame Operations

223

224

```python

225

from hyperframe.frame import SettingsFrame

226

227

# Create SETTINGS frame with configuration

228

settings_frame = SettingsFrame(settings={

229

SettingsFrame.HEADER_TABLE_SIZE: 4096,

230

SettingsFrame.ENABLE_PUSH: 1,

231

SettingsFrame.MAX_CONCURRENT_STREAMS: 100,

232

SettingsFrame.INITIAL_WINDOW_SIZE: 65536,

233

SettingsFrame.MAX_FRAME_SIZE: 16384,

234

SettingsFrame.MAX_HEADER_LIST_SIZE: 8192

235

})

236

237

# Create SETTINGS ACK frame

238

settings_ack = SettingsFrame(flags=["ACK"])

239

240

print(f"Settings: {settings_frame.settings}")

241

print(f"ACK frame has no settings: {len(settings_ack.settings) == 0}")

242

243

# Access setting constants

244

print(f"Header table size setting ID: {SettingsFrame.HEADER_TABLE_SIZE}")

245

```

246

247

### Stream Management Frames

248

249

```python

250

from hyperframe.frame import RstStreamFrame, PriorityFrame

251

252

# Reset a stream due to error

253

rst_frame = RstStreamFrame(stream_id=1, error_code=8) # CANCEL error

254

255

# Set stream priority

256

priority_frame = PriorityFrame(

257

stream_id=3,

258

depends_on=1, # Depends on stream 1

259

stream_weight=16, # Weight of 16

260

exclusive=False # Non-exclusive dependency

261

)

262

263

print(f"RST_STREAM error code: {rst_frame.error_code}")

264

print(f"Priority: depends_on={priority_frame.depends_on}, weight={priority_frame.stream_weight}")

265

```

266

267

### Connection Management

268

269

```python

270

from hyperframe.frame import PingFrame, GoAwayFrame

271

import time

272

273

# Send PING to measure RTT

274

ping_data = int(time.time()).to_bytes(8, 'big') # Timestamp as opaque data

275

ping_frame = PingFrame(opaque_data=ping_data)

276

277

# Respond to PING

278

ping_ack = PingFrame(opaque_data=ping_data, flags=["ACK"])

279

280

# Graceful connection shutdown

281

goaway_frame = GoAwayFrame(

282

last_stream_id=5,

283

error_code=0, # NO_ERROR

284

additional_data=b"Server shutdown"

285

)

286

287

print(f"PING data: {ping_frame.opaque_data}")

288

print(f"GOAWAY last stream: {goaway_frame.last_stream_id}")

289

```

290

291

### Flow Control

292

293

```python

294

from hyperframe.frame import WindowUpdateFrame

295

296

# Increase connection-level flow control window

297

connection_update = WindowUpdateFrame(

298

stream_id=0, # Connection level

299

window_increment=32768 # Increment by 32KB

300

)

301

302

# Increase stream-level flow control window

303

stream_update = WindowUpdateFrame(

304

stream_id=1, # Specific stream

305

window_increment=16384 # Increment by 16KB

306

)

307

308

print(f"Connection window increment: {connection_update.window_increment}")

309

print(f"Stream window increment: {stream_update.window_increment}")

310

```

311

312

### Parsing Control Frames

313

314

```python

315

from hyperframe.frame import Frame

316

317

# Parse SETTINGS frame

318

settings_bytes = (

319

b'\\x00\\x00\\x0C\\x04\\x00\\x00\\x00\\x00\\x00' # Header: 12 bytes, SETTINGS, no flags

320

b'\\x00\\x01\\x00\\x00\\x10\\x00' # HEADER_TABLE_SIZE = 4096

321

b'\\x00\\x02\\x00\\x00\\x00\\x01' # ENABLE_PUSH = 1

322

)

323

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

324

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

325

326

print(f"Settings frame: {frame.settings}")

327

328

# Parse WINDOW_UPDATE frame

329

window_bytes = b'\\x00\\x00\\x04\\x08\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x40\\x00'

330

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

331

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

332

333

print(f"Window update: stream={frame.stream_id}, increment={frame.window_increment}")

334

```

335

336

### Error Handling

337

338

```python

339

from hyperframe.frame import WindowUpdateFrame, SettingsFrame

340

from hyperframe.exceptions import InvalidDataError, InvalidFrameError

341

342

# Invalid window increment

343

try:

344

WindowUpdateFrame(stream_id=1, window_increment=0) # Must be >= 1

345

except InvalidDataError as e:

346

print(f"Window update error: {e}")

347

348

# Invalid SETTINGS ACK with data

349

try:

350

SettingsFrame(settings={1: 4096}, flags=["ACK"]) # ACK must have empty settings

351

except InvalidDataError as e:

352

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

353

354

# Invalid PING data length

355

try:

356

from hyperframe.frame import PingFrame

357

PingFrame(opaque_data=b"too much data for ping frame") # > 8 bytes

358

except InvalidFrameError as e:

359

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

360

```