Skip to content

Commit b08b9d7

Browse files
vladyslavKriechi
authored andcommitted
Allow sending 0-byte DATA frames when flow-control window is negative
1 parent 73a232d commit b08b9d7

4 files changed

Lines changed: 15 additions & 4 deletions

File tree

CHANGELOG.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ dev
2323

2424
**Bugfixes**
2525

26-
-
26+
- Fix to allow sending 0 bytes on a stream even if the flow control window is negative.
2727

2828
4.3.0 (2025-08-23)
2929
------------------

src/h2/connection.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -888,7 +888,7 @@ def send_data(self,
888888
"Frame size on stream ID %d is %d", stream_id, frame_size,
889889
)
890890

891-
if frame_size > self.local_flow_control_window(stream_id):
891+
if frame_size > 0 and frame_size > self.local_flow_control_window(stream_id):
892892
msg = f"Cannot send {frame_size} bytes, flow control window is {self.local_flow_control_window(stream_id)}"
893893
raise FlowControlError(msg)
894894
if frame_size > self.max_outbound_frame_size:
@@ -907,7 +907,7 @@ def send_data(self,
907907
"Outbound flow control window size is %d",
908908
self.outbound_flow_control_window,
909909
)
910-
assert self.outbound_flow_control_window >= 0
910+
assert self.outbound_flow_control_window >= 0 or frame_size == 0
911911

912912
def end_stream(self, stream_id: int) -> None:
913913
"""

src/h2/stream.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -981,7 +981,7 @@ def send_data(self,
981981

982982
# Subtract flow_controlled_length to account for possible padding
983983
self.outbound_flow_control_window -= df.flow_controlled_length
984-
assert self.outbound_flow_control_window >= 0
984+
assert self.outbound_flow_control_window >= 0 or df.flow_controlled_length == 0
985985

986986
return [df]
987987

tests/test_flow_control_window.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ def test_flow_control_decreases_with_padded_data(self, frame_factory) -> None:
103103
self.DEFAULT_FLOW_WINDOW - len(b"some data") - 10 - 1
104104
)
105105
assert (c.remote_flow_control_window(1) == remaining_length)
106+
107+
def test_can_send_zero_bytes_when_window_negative(self) -> None:
108+
"""
109+
Sending 0 bytes when the flow control window is negative should not
110+
raise a FlowControlError, allowing empty END_STREAM frames to be sent.
111+
"""
112+
c = h2.connection.H2Connection()
113+
c.send_headers(1, self.example_request_headers)
114+
c.outbound_flow_control_window = -100
115+
c._get_stream_by_id(1).outbound_flow_control_window = -100
116+
c.send_data(1, b"", end_stream=True)
106117

107118
def test_flow_control_is_limited_by_connection(self) -> None:
108119
"""

0 commit comments

Comments
 (0)