0
# Data and Header Frames
1
2
Implementation of HTTP/2 DATA and HEADERS frames for carrying application data and HTTP headers. These frames support padding, priority information, and proper stream association for efficient HTTP/2 communication.
3
4
## Capabilities
5
6
### DATA Frames
7
8
DATA frames carry arbitrary application data associated with a stream, such as HTTP request or response payloads.
9
10
```python { .api }
11
class DataFrame(Padding, Frame):
12
def __init__(self, stream_id: int, data: bytes = b"", **kwargs) -> None:
13
"""
14
Create a DATA frame.
15
16
Parameters:
17
- stream_id (int): Stream identifier (must be non-zero)
18
- data (bytes): Application data to carry
19
- pad_length (int): Padding length if PADDED flag set
20
- flags (Iterable[str]): Frame flags ("END_STREAM", "PADDED")
21
- **kwargs: Additional arguments for parent classes
22
23
Raises:
24
- InvalidDataError: If stream_id is zero
25
"""
26
27
@property
28
def data(self) -> bytes:
29
"""Application data carried by this frame."""
30
31
@property
32
def flow_controlled_length(self) -> int:
33
"""
34
Length of frame that counts toward flow control.
35
36
Returns:
37
int: Data length plus padding overhead
38
"""
39
40
# Inherited from Frame
41
def serialize_body(self) -> bytes: ...
42
def parse_body(self, data: memoryview) -> None: ...
43
```
44
45
**Defined Flags:**
46
- `END_STREAM` (0x01): Indicates this is the last frame for the stream
47
- `PADDED` (0x08): Indicates frame contains padding
48
49
### HEADERS Frames
50
51
HEADERS frames carry HTTP header information and can open new streams. They support priority information and padding.
52
53
```python { .api }
54
class HeadersFrame(Padding, Priority, Frame):
55
def __init__(self, stream_id: int, data: bytes = b"", **kwargs) -> None:
56
"""
57
Create a HEADERS frame.
58
59
Parameters:
60
- stream_id (int): Stream identifier (must be non-zero)
61
- data (bytes): HPACK-encoded header block
62
- pad_length (int): Padding length if PADDED flag set
63
- depends_on (int): Stream dependency if PRIORITY flag set
64
- stream_weight (int): Stream weight if PRIORITY flag set
65
- exclusive (bool): Exclusive dependency if PRIORITY flag set
66
- flags (Iterable[str]): Frame flags
67
- **kwargs: Additional arguments for parent classes
68
69
Raises:
70
- InvalidDataError: If stream_id is zero
71
"""
72
73
@property
74
def data(self) -> bytes:
75
"""HPACK-encoded header block data."""
76
77
# Inherited from Frame
78
def serialize_body(self) -> bytes: ...
79
def parse_body(self, data: memoryview) -> None: ...
80
```
81
82
**Defined Flags:**
83
- `END_STREAM` (0x01): Indicates this is the last frame for the stream
84
- `END_HEADERS` (0x04): Indicates end of header list (no CONTINUATION frames follow)
85
- `PADDED` (0x08): Indicates frame contains padding
86
- `PRIORITY` (0x20): Indicates frame contains priority information
87
88
### CONTINUATION Frames
89
90
CONTINUATION frames continue header block fragments when headers don't fit in a single HEADERS or PUSH_PROMISE frame.
91
92
```python { .api }
93
class ContinuationFrame(Frame):
94
def __init__(self, stream_id: int, data: bytes = b"", **kwargs) -> None:
95
"""
96
Create a CONTINUATION frame.
97
98
Parameters:
99
- stream_id (int): Stream identifier (must be non-zero)
100
- data (bytes): HPACK-encoded header block fragment
101
- flags (Iterable[str]): Frame flags ("END_HEADERS")
102
- **kwargs: Additional arguments for parent classes
103
104
Raises:
105
- InvalidDataError: If stream_id is zero
106
"""
107
108
@property
109
def data(self) -> bytes:
110
"""HPACK-encoded header block fragment."""
111
112
# Inherited from Frame
113
def serialize_body(self) -> bytes: ...
114
def parse_body(self, data: memoryview) -> None: ...
115
```
116
117
**Defined Flags:**
118
- `END_HEADERS` (0x04): Indicates end of header list
119
120
## Usage Examples
121
122
### Creating DATA Frames
123
124
```python
125
from hyperframe.frame import DataFrame
126
127
# Simple DATA frame
128
data_frame = DataFrame(stream_id=1, data=b"Hello, World!")
129
130
# DATA frame with END_STREAM flag
131
final_frame = DataFrame(
132
stream_id=1,
133
data=b"Final chunk",
134
flags=["END_STREAM"]
135
)
136
137
# DATA frame with padding
138
padded_frame = DataFrame(
139
stream_id=1,
140
data=b"Padded data",
141
pad_length=10,
142
flags=["PADDED"]
143
)
144
145
# Check flow control length
146
print(f"Flow control length: {padded_frame.flow_controlled_length}")
147
```
148
149
### Creating HEADERS Frames
150
151
```python
152
from hyperframe.frame import HeadersFrame
153
154
# Simple HEADERS frame
155
headers_frame = HeadersFrame(
156
stream_id=1,
157
data=b"\\x00\\x07:method\\x03GET", # HPACK-encoded headers
158
flags=["END_HEADERS"]
159
)
160
161
# HEADERS frame with priority
162
priority_headers = HeadersFrame(
163
stream_id=1,
164
data=b"\\x00\\x07:method\\x04POST",
165
depends_on=0,
166
stream_weight=16,
167
exclusive=False,
168
flags=["END_HEADERS", "PRIORITY"]
169
)
170
171
# HEADERS frame opening stream
172
stream_opener = HeadersFrame(
173
stream_id=1,
174
data=b"\\x00\\x07:method\\x03GET\\x00\\x05:path\\x01/",
175
flags=["END_HEADERS", "END_STREAM"] # GET request with no body
176
)
177
```
178
179
### Working with CONTINUATION Frames
180
181
```python
182
from hyperframe.frame import HeadersFrame, ContinuationFrame
183
184
# Large header block split across frames
185
header_data = b"\\x00\\x07:method\\x03GET" + b"\\x00" * 1000 # Large headers
186
187
# Initial HEADERS frame (without END_HEADERS)
188
headers_frame = HeadersFrame(
189
stream_id=1,
190
data=header_data[:500]
191
# Note: no END_HEADERS flag
192
)
193
194
# CONTINUATION frame with remaining data
195
continuation_frame = ContinuationFrame(
196
stream_id=1,
197
data=header_data[500:],
198
flags=["END_HEADERS"] # End of header block
199
)
200
201
print(f"Headers frame: {len(headers_frame.data)} bytes")
202
print(f"Continuation frame: {len(continuation_frame.data)} bytes")
203
```
204
205
### Parsing Data and Header Frames
206
207
```python
208
from hyperframe.frame import Frame
209
210
# Parse DATA frame
211
data_bytes = b'\\x00\\x00\\x0D\\x00\\x01\\x00\\x00\\x00\\x01Hello, World!'
212
frame, length = Frame.parse_frame_header(data_bytes[:9])
213
frame.parse_body(memoryview(data_bytes[9:9 + length]))
214
215
print(f"Frame type: DATA")
216
print(f"Stream ID: {frame.stream_id}")
217
print(f"Data: {frame.data}")
218
print(f"END_STREAM: {'END_STREAM' in frame.flags}")
219
220
# Parse HEADERS frame with priority
221
headers_bytes = (
222
b'\\x00\\x00\\x09\\x01\\x24\\x00\\x00\\x00\\x01' # Header
223
b'\\x00\\x00\\x00\\x00\\x10' # Priority (depends_on=0, weight=16)
224
b'\\x00\\x07:method\\x03GET' # HPACK data
225
)
226
frame, length = Frame.parse_frame_header(headers_bytes[:9])
227
frame.parse_body(memoryview(headers_bytes[9:9 + length]))
228
229
print(f"Frame type: HEADERS")
230
print(f"Priority info: depends_on={frame.depends_on}, weight={frame.stream_weight}")
231
print(f"Exclusive: {frame.exclusive}")
232
```
233
234
### Error Handling
235
236
```python
237
from hyperframe.frame import DataFrame
238
from hyperframe.exceptions import InvalidDataError, InvalidPaddingError
239
240
# Invalid stream ID
241
try:
242
DataFrame(stream_id=0, data=b"Invalid") # DATA frames must have non-zero stream ID
243
except InvalidDataError as e:
244
print(f"Error: {e}")
245
246
# Invalid padding
247
try:
248
frame_data = b'\\x00\\x00\\x02\\x00\\x08\\x00\\x00\\x00\\x01\\xFF\\xFF' # Padding > body length
249
frame, length = Frame.parse_frame_header(frame_data[:9])
250
frame.parse_body(memoryview(frame_data[9:9 + length]))
251
except InvalidPaddingError as e:
252
print(f"Padding error: {e}")
253
```