hyperium / h2

HTTP 2.0 client & server implementation for Rust.
MIT License
1.36k stars 272 forks source link

Fix opening new streams over max_concurrent_streams #707

Closed seanmonstar closed 1 year ago

seanmonstar commented 1 year ago

From the original PR (#706):


There was a bug where opening streams over the max concurrent streams was possible if max_concurrent_streams were lowered beyond the current number of open streams and there were already new streams adding to the pending_send queue.

There was two mechanisms for streams to end up in that queue.

  1. send_headers would push directly onto pending_send when below max_concurrent_streams
  2. prioritize would pop from pending_open until max_concurrent_streams was reached.

For case 1, a settings frame could be received after pushing many streams onto pending_send and before the socket was ready to write again. For case 2, the pending_send queue could have Headers frames queued going into a Not Ready state with the socket, a settings frame could be received, and then the headers would be written anyway after the ack.

The fix is therefore also two fold. Fixing case 1 is as simple as letting Prioritize decide when to transition streams from pending_open to pending_send since only it knows the readiness of the socket and whether the headers can be written immediately. This is slightly complicated by the fact that previously SendRequest would block when streams would be added as "pending open". That was addressed by guessing when to block based on max concurrent streams rather than the stream state.

The fix for Prioritize was to conservatively pop streams from pending_open when the socket is immediately available for writing a headers frame. This required a change to queuing to support pushing on the front of pending_send to ensure headers frames don't linger in pending_send.

The alternative to this was adding a check to pending_send whether a new stream would exceed max concurrent. In that case, headers frames would need to carefully be reenqueued. This seemed to impose more complexity to ensure ordering of stream IDs would be maintained.


Fixes #704