Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

HTTP/2 Data padding is not counted toward the connection flow-control window. This deadlocks connections with peers that pad.

オープン
#9,772 コメント 6 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@Amir-Golmoradi がすでに取り組んでいます。

2026年10月8日 から。

  • #9786 @Amir-Golmoradi による — マージされずにクローズ

評価

難易度
3/5
見積もり時間
1〜2日
初心者へのやさしさ
67/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
kotlin
領域
networking

調査の方向性

Start with Http2Reader.readData, Http2Connection.updateConnectionFlowControl, and Http2Stream.FramingSource.receive to trace how DATA frame bytes reach connection-level accounting. Add a regression test in Http2ConnectionTest that sends padded DATA frames and reads the stream-0 WINDOW_UPDATE; done when its increment accounts for the full padded payload, as RFC 9113 requires.

索引モデルが issue の本文から書いたものです。

説明

bug
Summary

Http2Reader does not count DATA frame padding toward the connection-level flow-control window. When the peer pads its DATA frames, the server's view of the connection window drains faster than the client's counter grows, the server's window reaches zero before the client reaches its next WINDOW_UPDATE threshold, and the connection deadlocks. PING/PONG keeps working, so the connection stays in the pool and every subsequent call on it times out.

Observed on 5.5.0; the code is unchanged on main.

Where

Http2Reader.readData reads the pad-length byte, computes the body length without padding, calls handler.data(..., dataLength) with the body length only, and then skips the padding:
https://github.com/lysine-dev/okhttp/blob/e9eae50d36e938603811a41f9e2ab04182bcb515/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/Http2Reader.kt#L198-L202

Http2Connection.updateConnectionFlowControl is therefore only ever fed the body length, both through the stream path (Http2Stream.FramingSource.receive) and the unknown-stream path:
https://github.com/lysine-dev/okhttp/blob/e9eae50d36e938603811a41f9e2ab04182bcb515/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/Http2Connection.kt#L189
https://github.com/lysine-dev/okhttp/blob/e9eae50d36e938603811a41f9e2ab04182bcb515/okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/http2/Http2Stream.kt#L517

The pad-length byte and the padding bytes are consumed from the socket but never credited back to the peer.

Spec

RFC 9113 §6.1: "The entire DATA frame payload is included in flow control, including the Pad Length and Padding fields if present."
https://www.rfc-editor.org/rfc/rfc9113.html#section-6.1

Go's net/http2 fixed the same bug in 2016 and refunds padding immediately on receipt (golang/go#16556, https://go.googlesource.com/net/+/35028a49ca5a73b486af60cd20ac21cd6b67bfdb). Google's quiche fixed a padding flow-control leak that stalled Envoy connections (envoyproxy/envoy#32401, https://quiche.googlesource.com/quiche/+/9e875938052443c7f51b25012093be59e20598ac). Netty charges data.readableBytes() + padding.

How it shows up in practice

Google Front End pads every response DATA frame with a random 0–255 bytes. A production client of ours talking to an API behind GFE deadlocked every long-lived connection at the same point. Measured with a FlowControlListener on the pool:

response body ~250 B
DATA frame on the wire ~372 B average, uniform 247–503 B (`DATA END_STREAM
fraction the client counts ~0.665
client window 16 MiB advertised, WINDOW_UPDATE of 8 MiB once 8 MiB has been counted
server bytes sent at the freeze 24.0 MiB (16 MiB initial + one 8 MiB grant)
WindowCounter at the freeze total 15.7–16.7 MiB, acknowledged 8,388,6xx: below the threshold for the second grant
responses per connection before the freeze ~67,000

After the freeze the only inbound frames were PINGs and the server's own WINDOW_UPDATEs for our request bytes. No HEADERS or DATA, no GOAWAY, no RST_STREAM. Http2Connection.isHealthy() kept returning true, so the connection was reused for every call until the server eventually closed it.

Failing test

The Http2Writer used by the test peer cannot write padded frames, so the test writes DATA frames by hand: a 9-byte frame header with FLAG_PADDED, one pad-length byte, the body, then the padding. Send enough frames for the padded total to exceed half the client window, then read the WINDOW_UPDATE the client sends on stream 0. Expected: the increment equals the padded bytes received. Actual on 5.5.0: the increment equals the body bytes only. (I can turn this into a test in Http2ConnectionTest if that helps.)

Workaround

Restricting the affected client to HTTP/1.1 removes the problem entirely.

主要言語
Kotlin
スター
47.1k
フォーク
9.3k
平均マージ
18時間 18分
マージ済み PR(30日)
15

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

lysine-dev/okhttp のほかの issue

lysine-dev/okhttp の issue をすべて見る

似ている issue

Kotlin の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。