False `SendRequest` readiness for HTTP/1 connections
メンテナーはふだん 1 日以内に返信
評価
- 難易度
- 4/5
- 見積もり時間
- 1〜2日
- 初心者へのやさしさ
- 55/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- rust
調査の方向性
Start with the reproduction in the issue and compare the readiness behavior against hyper versions before and after 1.12. Then trace how http1::SendRequest readiness interacts with the connection dispatch queue, using #4207 and #4208 for context. Done when readiness stays pending while a request is in flight and the reproduction no longer observes immediate cancellation.
索引モデルが issue の本文から書いたものです。
説明
Version
hyper 1.12
Platform
Darwin 25.6.0
Summary
I believe to have found a readiness race in hyper 1.12 (and at the time of writing master), possibly as a result of the fix for #4207.
http1::SendRequest can signal readiness while a request is still in flight. Using it to send a request results in the request being cancelled immediately.
I have tried to conjure a minimal reproduction example in this gist. With it, you can also set the hyper dependency to <1.12 and observe that the repro test no longer detects the issue.
Code Sample
use std::future::Future;
use std::future::poll_fn;
use std::io;
use std::pin::Pin;
use std::pin::pin;
use std::task::Context;
use std::task::Poll;
use hyper::Request;
use hyper::client::conn::http1;
use hyper::rt::Read;
use hyper::rt::ReadBufCursor;
use hyper::rt::Write;
// Accept writes, but never respond or close: the first request stays in flight.
struct SilentPeer;
impl Read for SilentPeer {
fn poll_read(
self: Pin<&mut Self>,
_: &mut Context<'_>,
_: ReadBufCursor<'_>,
) -> Poll<io::Result<()>> {
Poll::Pending
}
}
impl Write for SilentPeer {
fn poll_write(
self: Pin<&mut Self>,
_: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Poll::Ready(Ok(buf.len()))
}
fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_shutdown(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
}
#[tokio::test]
async fn successful_readiness_must_not_be_withdrawn_before_send() {
let (mut sender, connection) = http1::handshake::<_, String>(SilentPeer).await.unwrap();
let mut connection = pin!(connection);
poll_fn(|cx| {
assert!(connection.as_mut().poll(cx).is_pending());
Poll::Ready(())
})
.await;
sender.ready().await.unwrap();
// Retain this future: dropping it would cancel the request.
let _first = sender.send_request(Request::new(String::new()));
poll_fn(|cx| {
// Force the dispatch queue to report Pending despite the queued request.
while let Poll::Ready(budget) = tokio::task::coop::poll_proceed(cx) {
budget.made_progress();
}
assert!(connection.as_mut().poll(cx).is_pending());
match sender.poll_ready(cx) {
Poll::Pending => return Poll::Ready(()), // No false readiness: correct.
Poll::Ready(result) => result.unwrap(), // False readiness!
}
// Model the connection task running on another worker before our send.
let mut driver = pin!(tokio::task::unconstrained(connection.as_mut()));
assert!(driver.as_mut().poll(cx).is_pending());
assert!(
!sender.is_closed(),
"the peer has not closed the connection"
);
let mut second = pin!(sender.send_request(Request::new(String::new())));
let result = second.as_mut().poll(cx);
assert!(
!matches!(result, Poll::Ready(Err(_))),
"send_request failed after poll_ready returned Ready(Ok(())): {result:?}"
);
Poll::Ready(())
})
.await;
}
Expected Behavior
The http1::SendRequest indicates Poll::Pending until it is ready.
Actual Behavior
The http1::SendRequest indicates Poll::Ready and the http1::SendRequest::send_request future fails immediately with an error indicating request cancellation.
Additional Context
Possibly related issues:
- #4207
Possibly related pull requests:
- #4208
- 主要言語
- Rust
- スター
- 16.4k
- フォーク
- 1.8k
- 平均マージ
- 1日 13時間
- マージ済み PR(30日)
- 12
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートなし
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
hyperium/hyper のほかの issue
-
C-feature
難易度 1/5 1時間未満 初心者へのやさしさ 65/100
hyperium/hyper#2652 · リアクション 4 件 ·
メンテナーはふだん 1 日以内に返信
-
C-feature
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 48/100
メンテナーはふだん 1 日以内に返信
-
can't upgrade request that came in with `Connection: close, Upgrade`対応中かも @akasakariko が 2 日前に担当しました。 オープンC-bug
難易度 3/5 1〜2日 初心者へのやさしさ 62/100
hyperium/hyper#4244 · コメント 4 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 52/100
メンテナーはふだん 1 日以内に返信
似ている issue
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
swc-project/swc-next#1165 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 73/100
Alive24/shea-symphony#574 ·
-
bug tests Windows
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信
-
documentation good first issue help wanted
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
dev-five-git/devup-ui#879 ·
メンテナーはふだん 11 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 77/100
farion1231/cc-switch#9038 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信