0
# Frame Operations
1
2
Core functionality for parsing HTTP/2 frames from binary data and creating new frames with proper validation and serialization support. This forms the foundation of all HTTP/2 frame handling in hyperframe.
3
4
## Capabilities
5
6
### Frame Base Class
7
8
The base Frame class provides common functionality for all HTTP/2 frame types including stream ID management, flag handling, and serialization/parsing infrastructure.
9
10
```python { .api }
11
class Frame:
12
def __init__(self, stream_id: int, flags: Iterable[str] = ()) -> None:
13
"""
14
Initialize a frame with stream ID and optional flags.
15
16
Parameters:
17
- stream_id (int): Stream identifier (0 for connection-level frames)
18
- flags (Iterable[str]): Initial flags to set on the frame
19
20
Raises:
21
- InvalidDataError: If stream_id validation fails based on stream_association
22
"""
23
24
@property
25
def stream_id(self) -> int: ...
26
27
@property
28
def flags(self) -> Flags: ...
29
30
@property
31
def body_len(self) -> int: ...
32
33
def serialize(self) -> bytes:
34
"""
35
Convert frame into binary representation.
36
37
Returns:
38
bytes: Complete frame including 9-byte header and body
39
"""
40
41
def serialize_body(self) -> bytes:
42
"""
43
Serialize frame body data. Must be implemented by subclasses.
44
45
Returns:
46
bytes: Frame body data
47
48
Raises:
49
NotImplementedError: If called on base Frame class
50
"""
51
52
def parse_body(self, data: memoryview) -> None:
53
"""
54
Parse frame body from binary data. Must be implemented by subclasses.
55
56
Parameters:
57
- data (memoryview): Body data to parse
58
59
Raises:
60
NotImplementedError: If called on base Frame class
61
"""
62
63
def parse_flags(self, flag_byte: int) -> Flags:
64
"""
65
Parse flag byte and set appropriate flags.
66
67
Parameters:
68
- flag_byte (int): 8-bit flag value from frame header
69
70
Returns:
71
Flags: Updated flags object
72
"""
73
```
74
75
### Frame Header Parsing
76
77
Static methods for parsing frame headers and handling frame discovery from binary streams.
78
79
```python { .api }
80
@staticmethod
81
def parse_frame_header(header: memoryview, strict: bool = False) -> tuple[Frame, int]:
82
"""
83
Parse 9-byte frame header and return appropriate Frame object.
84
85
Parameters:
86
- header (memoryview): Exactly 9 bytes of frame header data
87
- strict (bool): If True, raise exception for unknown frame types
88
89
Returns:
90
tuple[Frame, int]: Frame object and body length to read
91
92
Raises:
93
- InvalidFrameError: If header data is malformed
94
- UnknownFrameError: If frame type unknown and strict=True
95
"""
96
97
@staticmethod
98
def explain(data: memoryview) -> tuple[Frame, int]:
99
"""
100
Debug helper that parses and prints a complete frame.
101
102
Parameters:
103
- data (memoryview): Complete frame data (header + body)
104
105
Returns:
106
tuple[Frame, int]: Parsed frame and body length
107
"""
108
```
109
110
### Mixin Classes
111
112
Base functionality that can be inherited by frame types supporting padding or priority information.
113
114
```python { .api }
115
class Padding:
116
def __init__(self, stream_id: int, pad_length: int = 0, **kwargs) -> None:
117
"""
118
Initialize padding support.
119
120
Parameters:
121
- stream_id (int): Stream identifier
122
- pad_length (int): Number of padding bytes to add
123
- **kwargs: Additional arguments passed to parent class
124
"""
125
126
@property
127
def pad_length(self) -> int: ...
128
129
def serialize_padding_data(self) -> bytes:
130
"""
131
Serialize padding length field if PADDED flag is set.
132
133
Returns:
134
bytes: Padding length byte or empty if not padded
135
"""
136
137
def parse_padding_data(self, data: memoryview) -> int:
138
"""
139
Parse padding length from frame data if PADDED flag is set.
140
141
Parameters:
142
- data (memoryview): Frame body data starting with padding info
143
144
Returns:
145
int: Number of bytes consumed (1 if padded, 0 if not)
146
147
Raises:
148
- InvalidFrameError: If padding data is malformed
149
"""
150
151
class Priority:
152
def __init__(self, stream_id: int, depends_on: int = 0, stream_weight: int = 0,
153
exclusive: bool = False, **kwargs) -> None:
154
"""
155
Initialize priority support.
156
157
Parameters:
158
- stream_id (int): Stream identifier
159
- depends_on (int): Stream ID this stream depends on
160
- stream_weight (int): Stream weight (0-255)
161
- exclusive (bool): Whether this stream has exclusive dependency
162
- **kwargs: Additional arguments passed to parent class
163
"""
164
165
@property
166
def depends_on(self) -> int: ...
167
168
@property
169
def stream_weight(self) -> int: ...
170
171
@property
172
def exclusive(self) -> bool: ...
173
174
def serialize_priority_data(self) -> bytes:
175
"""
176
Serialize 5-byte priority information.
177
178
Returns:
179
bytes: Priority data (4 bytes stream dependency + 1 byte weight)
180
"""
181
182
def parse_priority_data(self, data: memoryview) -> int:
183
"""
184
Parse priority information from frame data.
185
186
Parameters:
187
- data (memoryview): Frame body data starting with priority info
188
189
Returns:
190
int: Number of bytes consumed (always 5)
191
192
Raises:
193
- InvalidFrameError: If priority data is malformed
194
"""
195
```
196
197
## Usage Examples
198
199
### Basic Frame Parsing
200
201
```python
202
from hyperframe.frame import Frame
203
204
# Parse frame from binary data
205
frame_data = b'\\x00\\x00\\x05\\x00\\x00\\x00\\x00\\x00\\x01Hello'
206
frame, body_length = Frame.parse_frame_header(frame_data[:9])
207
frame.parse_body(memoryview(frame_data[9:9 + body_length]))
208
209
print(f"Frame type: {frame.type}")
210
print(f"Stream ID: {frame.stream_id}")
211
print(f"Body length: {body_length}")
212
```
213
214
### Creating Custom Frames
215
216
```python
217
from hyperframe.frame import DataFrame
218
219
# Create DATA frame with padding
220
frame = DataFrame(
221
stream_id=1,
222
data=b"Hello, HTTP/2!",
223
pad_length=8,
224
flags=["END_STREAM", "PADDED"]
225
)
226
227
# Serialize to binary
228
binary_data = frame.serialize()
229
print(f"Serialized frame: {len(binary_data)} bytes")
230
```
231
232
### Handling Unknown Frame Types
233
234
```python
235
from hyperframe.frame import Frame, ExtensionFrame
236
from hyperframe.exceptions import UnknownFrameError
237
238
# Parse with strict=False to handle unknown frames
239
frame_data = b'\\x00\\x00\\x05\\xFF\\x00\\x00\\x00\\x00\\x01Hello' # Type 0xFF
240
frame, length = Frame.parse_frame_header(frame_data[:9], strict=False)
241
242
if isinstance(frame, ExtensionFrame):
243
print(f"Unknown frame type: 0x{frame.type:02X}")
244
frame.parse_body(memoryview(frame_data[9:9 + length]))
245
print(f"Frame body: {frame.body}")
246
247
# Parse with strict=True raises exception
248
try:
249
Frame.parse_frame_header(frame_data[:9], strict=True)
250
except UnknownFrameError as e:
251
print(f"Unknown frame: type=0x{e.frame_type:02X}, length={e.length}")
252
```