From 5de2d056e7a84f2d8fba03efde8bafcd16ec58ef Mon Sep 17 00:00:00 2001 From: Teakowa <27560638+Teakowa@users.noreply.github.com> Date: Tue, 20 Oct 2026 21:25:48 +0800 Subject: [PATCH] fix(ui): keep connections alive in `wright ui` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every response carried `Connection: close`, so Chrome closed a socket it may already have dispatched the next POST onto. The dropped request never reached the dispatch loop and the fetch hung, leaving the rule pane on "Loading rule…". tiny_http parks each connection's reader on its own socket inside an unbounded worker pool, so keep-alive is safe with the serial dispatch loop. Also retry the E2E profile cleanup so Chrome's lingering writes after SIGKILL do not fail a local run. Refs #862 --- crates/wright-cli/src/ui.rs | 13 +++++-------- ui/scripts/e2e.mjs | 4 +++- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/wright-cli/src/ui.rs b/crates/wright-cli/src/ui.rs index 882bc3e1..f6dfd071 100644 --- a/crates/wright-cli/src/ui.rs +++ b/crates/wright-cli/src/ui.rs @@ -349,20 +349,17 @@ mod server { content_type: &str, body: Vec, ) -> tiny_http::Response>> { - // `Connection: close` on every answer: the server loop handles one - // request at a time, and a browser keep-alive or preconnect socket - // would otherwise stall every later request waiting on its first - // request line. + // Keep-alive stays enabled on purpose: each connection's reader task + // parks on its own socket inside tiny_http's unbounded worker pool, so + // an idle or preconnected socket never stalls the serial dispatch + // loop. Answering `Connection: close` instead would drop requests the + // browser had already dispatched onto a socket it believed reusable. tiny_http::Response::empty(status) .with_data(Cursor::new(body.clone()), Some(body.len())) .with_header( tiny_http::Header::from_bytes("Content-Type".as_bytes(), content_type.as_bytes()) .expect("content types are ASCII"), ) - .with_header( - tiny_http::Header::from_bytes("Connection".as_bytes(), "close".as_bytes()) - .expect("Connection: close is ASCII"), - ) } /// Open the session URL in the system browser with the platform's diff --git a/ui/scripts/e2e.mjs b/ui/scripts/e2e.mjs index 0b384e1e..a099d3f1 100644 --- a/ui/scripts/e2e.mjs +++ b/ui/scripts/e2e.mjs @@ -488,7 +488,9 @@ async function main() { cdp?.close() chrome?.kill('SIGKILL') ui.kill() - rmSync(profile, { recursive: true, force: true }) + // Chrome keeps writing profile files briefly after SIGKILL; retry the + // removal instead of flaking the run on a teardown race. + rmSync(profile, { recursive: true, force: true, maxRetries: 20, retryDelay: 100 }) } if (failures > 0) {