From 036ebd465305430859fc6ef7adcc628c4207412d Mon Sep 17 00:00:00 2001 From: LucaCappelletti94 Date: Sat, 17 Oct 2026 21:06:43 +0200 Subject: [PATCH] Bound the inspector page list so a proof whose browser never opens fails in time --- crates/connetto-test-harness/src/inspector.rs | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/crates/connetto-test-harness/src/inspector.rs b/crates/connetto-test-harness/src/inspector.rs index c5435232..00ceb78f 100644 --- a/crates/connetto-test-harness/src/inspector.rs +++ b/crates/connetto-test-harness/src/inspector.rs @@ -366,18 +366,28 @@ async fn next_json( } } -/// The pages an inspector endpoint on `port` lists. +/// The pages an inspector endpoint on `port` lists, within [`REPLY_BOUND`]. /// /// # Errors /// -/// When the endpoint does not answer with a JSON list. +/// When the endpoint does not answer with a JSON list within [`REPLY_BOUND`]. pub async fn list_pages(port: u16) -> Result> { - openidconnect::reqwest::get(format!("http://127.0.0.1:{port}/json")) - .await - .context("listing inspector pages")? - .json() + list_pages_within(port, REPLY_BOUND).await +} + +/// An `adb forward` with no browser behind it accepts the connection and never answers. +async fn list_pages_within(port: u16, bound: Duration) -> Result> { + let listed = async { + openidconnect::reqwest::get(format!("http://127.0.0.1:{port}/json")) + .await + .context("listing inspector pages")? + .json() + .await + .context("reading inspector pages") + }; + timeout(bound, listed) .await - .context("reading inspector pages") + .map_err(|_| anyhow!("the inspector page list got no reply within {bound:?}"))? } #[cfg(test)] @@ -648,4 +658,24 @@ mod tests { .await .unwrap(); } + + #[tokio::test] + async fn a_page_list_that_never_answers_fails_within_its_bound() { + // Like an `adb forward` with no browser behind it, the connection is accepted and never answered. + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let port = listener.local_addr().unwrap().port(); + tokio::spawn(async move { + let _held = listener.accept().await.unwrap(); + std::future::pending::<()>().await; + }); + let bound = Duration::from_millis(200); + let listed = timeout(Duration::from_secs(5), list_pages_within(port, bound)) + .await + .expect("the page list returned within its bound"); + let error = listed.expect_err("an endpoint that never answers lists no pages"); + assert!( + format!("{error:#}").contains("no reply within 200ms"), + "{error:#}" + ); + } }