Skip to content

Spurious GoAway(b"too_many_data_frames", ENHANCE_YOUR_CALM, Library) #939

Description

@dmitryvk

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions