Skip to content

Commit aa4d1c2

Browse files
committed
Omit padding when the PADDED flag is unset
1 parent 632e309 commit aa4d1c2

3 files changed

Lines changed: 39 additions & 4 deletions

File tree

‎CHANGELOG.rst‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ dev
1919

2020
**Bugfixes**
2121

22-
-
22+
- Omit configured padding from DATA, HEADERS, and PUSH_PROMISE frames when the
23+
PADDED flag is unset.
2324

2425
6.1.0 (2025-01-22)
2526
------------------

‎src/hyperframe/frame.py‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,8 @@ def __init__(self, stream_id: int, data: bytes = b"", **kwargs: Any) -> None:
301301

302302
def serialize_body(self) -> bytes:
303303
padding_data = self.serialize_padding_data()
304-
padding = b"\0" * self.pad_length
304+
# 2026-10-11: Padding is payload unless the PADDED flag describes it.
305+
padding = b"\0" * self.pad_length if "PADDED" in self.flags else b""
305306
if isinstance(self.data, memoryview):
306307
self.data = self.data.tobytes()
307308
return b"".join([padding_data, self.data, padding])
@@ -514,7 +515,8 @@ def _body_repr(self) -> str:
514515

515516
def serialize_body(self) -> bytes:
516517
padding_data = self.serialize_padding_data()
517-
padding = b"\0" * self.pad_length
518+
# 2026-10-11: Padding is payload unless the PADDED flag describes it.
519+
padding = b"\0" * self.pad_length if "PADDED" in self.flags else b""
518520
data = _STRUCT_L.pack(self.promised_stream_id)
519521
return b"".join([padding_data, data, self.data, padding])
520522

@@ -741,7 +743,8 @@ def _body_repr(self) -> str:
741743

742744
def serialize_body(self) -> bytes:
743745
padding_data = self.serialize_padding_data()
744-
padding = b"\0" * self.pad_length
746+
# 2026-10-11: Padding is payload unless the PADDED flag describes it.
747+
padding = b"\0" * self.pad_length if "PADDED" in self.flags else b""
745748

746749
if "PRIORITY" in self.flags:
747750
priority_data = self.serialize_priority_data()

‎tests/test_frames.py‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,37 @@ def test_cannot_parse_invalid_frame_header(self):
112112
with pytest.raises(InvalidFrameError):
113113
Frame.parse_frame_header(b'\x00\x00\x08\x00\x01\x00\x00\x00')
114114

115+
@pytest.mark.parametrize('frame_type', [DataFrame, HeadersFrame, PushPromiseFrame])
116+
@pytest.mark.parametrize('pad_length', [0, 1, 255])
117+
@pytest.mark.parametrize('remove_flag', [False, True])
118+
def test_unpadded_frames_omit_configured_padding(self, frame_type, pad_length, remove_flag):
119+
# 2026-10-11: A cleared PADDED flag must not turn padding into payload.
120+
f = frame_type(1, data=b'hello', pad_length=pad_length)
121+
fixed_length = 0
122+
if frame_type is HeadersFrame:
123+
f.flags.add('PRIORITY')
124+
fixed_length = 5
125+
elif frame_type is PushPromiseFrame:
126+
f.promised_stream_id = 2
127+
fixed_length = 4
128+
if remove_flag:
129+
f.flags.add('PADDED')
130+
assert decode_frame(f.serialize()).data == b'hello'
131+
f.flags.discard('PADDED')
132+
133+
wire = f.serialize()
134+
decoded = decode_frame(wire)
135+
assert decoded.data == b'hello'
136+
assert len(wire) - 9 == fixed_length + len(b'hello')
137+
assert decoded.pad_length == 0
138+
if frame_type is DataFrame:
139+
assert f.flow_controlled_length == len(wire) - 9
140+
141+
f.flags.add('PADDED')
142+
decoded = decode_frame(f.serialize())
143+
assert decoded.data == b'hello'
144+
assert decoded.pad_length == pad_length
145+
115146

116147
class TestDataFrame:
117148
payload = b'\x00\x00\x08\x00\x01\x00\x00\x00\x01testdata'

0 commit comments

Comments
 (0)