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
```