Version
h2 0.4.16
Platform
Linux 7.0.0-29-generic #29-Ubuntu SMP PREEMPT_DYNAMIC Fri Jul 17 20:52:35 UTC 2026 GNU/Linux
Summary
In h2-0.4.16, a new protection against malicious peers was added (193833e), but it's breaking legitimate uses of reqwest (i.e., where h2 is used on client side).
My case is as follows:
- I have a server and a client (that I own) that communicate over TLS + HTTP/2
- Server is written with
poem
- Client is written with
reqwest, using a single reqwest::Client instance (which by default uses just 1 TCP connection, I believe)
- The client is making concurrent requests to the server: ~200-300 concurrent requests, at times even more
- The server is returning small responses (json with 2 fixed-width fields)
- If the server is returning HTTP 201 status code, the client reads the body completely
- If the server is returning HTTP 4xx, the client skips reading the body (and we're expecting some amount of HTTP 409 responses)
With previous versions of h2, it was working fine.
After updating h2 to 0.4.16, the client started failing with the following error:
error decoding response body for url (REDACTED): request or response body error: error reading a body from connection: connection error detected: detected excessive load generating behavior (b\"too_many_data_frames\")
I've prepared a reproduction here: https://github.com/dmitryvk/h2-too_many_data_frames-repro
Note: If the server is returning large responses (I've tried 512 bytes), I'm not seeing any failures.
There seems to be two cases that we're hitting:
- if we're not reading response bodies, we're hitting
too_many_data_frames almost immediately
- if we're reading response bodies, we're hitting
too_many_data_frames pretty quickly
Here's my general concerns regarding the DATA frame budget:
- It is the client that decides how many requests to make and how quickly to read the bodies. I.e., the client is not protecting itself against a misbehaving server but rather implicitly limits itself without a good reason.
- (That's my assumption and speculation) The server may return multiple responses in a single TCP packet, and the caller of
h2 would not even have a chance to start reading response body.
Some of the solutions I can propose (just to start a discussion):
- Make DATA frame budget configurable and/or optional
- Don't track final DATA frames of response body against the budget
Side note: It would be also very useful to have metrics exposed from h2.
I am planning to try and implement some of my proposed fixes and open a PR.
Code Sample
https://github.com/dmitryvk/h2-too_many_data_frames-repro
Expected Behavior
reqwest returns responses successfully
Actual Behavior
reqwest returns reqwest::Error { kind: Request, url: "http://localhost:3000/", source: hyper_util::client::legacy::Error(SendRequest, hyper::Error(Http2, Error { kind: GoAway(b"too_many_data_frames", ENHANCE_YOUR_CALM, Library) })) }
Additional Context
No response
Version
h2 0.4.16
Platform
Linux 7.0.0-29-generic #29-Ubuntu SMP PREEMPT_DYNAMIC Fri Jul 17 20:52:35 UTC 2026 GNU/Linux
Summary
In h2-0.4.16, a new protection against malicious peers was added (193833e), but it's breaking legitimate uses of
reqwest(i.e., whereh2is used on client side).My case is as follows:
poemreqwest, using a singlereqwest::Clientinstance (which by default uses just 1 TCP connection, I believe)With previous versions of h2, it was working fine.
After updating h2 to 0.4.16, the client started failing with the following error:
I've prepared a reproduction here: https://github.com/dmitryvk/h2-too_many_data_frames-repro
Note: If the server is returning large responses (I've tried 512 bytes), I'm not seeing any failures.
There seems to be two cases that we're hitting:
too_many_data_framesalmost immediatelytoo_many_data_framespretty quicklyHere's my general concerns regarding the DATA frame budget:
h2would not even have a chance to start reading response body.Some of the solutions I can propose (just to start a discussion):
Side note: It would be also very useful to have metrics exposed from h2.
I am planning to try and implement some of my proposed fixes and open a PR.
Code Sample
https://github.com/dmitryvk/h2-too_many_data_frames-repro
Expected Behavior
reqwestreturns responses successfullyActual Behavior
reqwestreturnsreqwest::Error { kind: Request, url: "http://localhost:3000/", source: hyper_util::client::legacy::Error(SendRequest, hyper::Error(Http2, Error { kind: GoAway(b"too_many_data_frames", ENHANCE_YOUR_CALM, Library) })) }Additional Context
No response