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 <noreply@anthropic.com>
This commit is contained in:
Navid EMAD
2026-03-15 23:07:23 +01:00
parent a9b9cf14c3
commit 1bdf464ef2

View File

@@ -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" {