From 1bdf464ef2c228bc53cc2eb52b07a4f56a9f750e Mon Sep 17 00:00:00 2001 From: Navid EMAD Date: Sun, 15 Mar 2026 23:07:23 +0100 Subject: [PATCH] Fix CDP WebSocket connection dying during complex page navigation The CDP timeout handler in httpLoop had two compounding bugs: 1. Unit mismatch: timestamp(.monotonic) returns seconds, but ms_remaining is in milliseconds. The comparison and subtraction mixed units. 2. Double-counting: In the .done branch, elapsed was computed as absolute time since last_message, but last_message was never updated in this branch. Each iteration subtracted the growing total elapsed seconds from an already-decremented ms_remaining. During complex page loads, Session._wait() returns .done rapidly (due to JS macrotask execution, background tasks, or errors). Each rapid .done return subtracted the growing elapsed (seconds) from ms_remaining (milliseconds), draining it to zero in ~2 seconds instead of the configured 10-second timeout. Fix: use milliTimestamp() for consistent units, update last_message in the .done branch for incremental elapsed tracking, and use >= for correct boundary comparison. Fixes #1849 Co-Authored-By: Claude Opus 4.6 --- src/Server.zig | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Server.zig b/src/Server.zig index 23ddefb5..f899d43c 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -295,7 +295,7 @@ pub const Client = struct { } var cdp = &self.mode.cdp; - var last_message = timestamp(.monotonic); + var last_message = milliTimestamp(.monotonic); var ms_remaining = self.ws.timeout_ms; while (true) { @@ -304,7 +304,7 @@ pub const Client = struct { if (self.readSocket() == false) { return; } - last_message = timestamp(.monotonic); + last_message = milliTimestamp(.monotonic); ms_remaining = self.ws.timeout_ms; }, .no_page => { @@ -319,16 +319,18 @@ pub const Client = struct { if (self.readSocket() == false) { return; } - last_message = timestamp(.monotonic); + last_message = milliTimestamp(.monotonic); ms_remaining = self.ws.timeout_ms; }, .done => { - const elapsed = timestamp(.monotonic) - last_message; - if (elapsed > ms_remaining) { + const now = milliTimestamp(.monotonic); + const elapsed = now - last_message; + if (elapsed >= ms_remaining) { log.info(.app, "CDP timeout", .{}); return; } ms_remaining -= @intCast(elapsed); + last_message = now; }, } } @@ -501,6 +503,7 @@ fn buildJSONVersionResponse( } pub const timestamp = @import("datetime.zig").timestamp; +pub const milliTimestamp = @import("datetime.zig").milliTimestamp; const testing = std.testing; test "server: buildJSONVersionResponse" {