Do you have the example server without flow control working? If not do so.
https://github.com/python-hyper/hyper-h2/blob/master/examples/asyncio/asyncio-server.py
You are mixing manual and automatic flow control. Reread the automatic flow control section here and use the automatic control.
https://python-hyper.org/projects/h2/en/stable/advanced-usage.html
This automatic strategy is built around a single method: acknowledge_received_data. This method flags to the connection object that your application has dealt with a certain number of flow controlled bytes, and that the window should be incremented in some way. Whenever your application has “processed” some received bytes, this method should be called to signal that they have been processed.
The key difference between this method and increment_flow_control_window is that the method acknowledge_received_data does not guarantee that it will emit a WINDOW_UPDATE frame, and if it does it will not necessarily emit them for only the stream or only the frame. Instead, the WINDOW_UPDATE frames will be coalesced: they will be emitted only when a certain number of bytes have been freed up.
Now look at the curio example which uses flow control. If you are receiving the window update events from the server likely you are not handling stream id 0 properly.
https://github.com/python-hyper/hyper-h2/blob/master/examples/curio/curio-server.py
Specifically the send data function:
while True:
while not self.conn.local_flow_control_window(stream_id):
await self.wait_for_flow_control(stream_id)
chunk_size = min(
self.conn.local_flow_control_window(stream_id),
READ_CHUNK_SIZE,
)
data = fileobj.read(chunk_size)
keep_reading = (len(data) == chunk_size)
self.conn.send_data(stream_id, data, not keep_reading)
await self.sock.sendall(self.conn.data_to_send())
If you want to send 4k bytes you wait on the flow control window, send your 2k bytes then wait again on the flow control window.
If you are receiving the window update you should have code like this
async def window_updated(self, event):
"""
Unblock streams waiting on flow control, if needed.
"""
stream_id = event.stream_id
if stream_id and stream_id in self.flow_control_events:
evt = self.flow_control_events.pop(stream_id)
await evt.set()
elif not stream_id:
# Need to keep a real list here to use only the events present at
# this time.
blocked_streams = list(self.flow_control_events.keys())
for stream_id in blocked_streams:
event = self.flow_control_events.pop(stream_id)
await event.set()
return