From fc45c0980fce01dcd4cbb97b620980586b2b70c3 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Thu, 16 Jul 2026 09:09:49 +0200 Subject: [PATCH] cdp: send a going-away close frame (1001) on pending terminate Previously tick() tore the connection down without a WebSocket close frame, so clients saw an abnormal closure (1006). Safe to send from tick(): the worker thread is the sole writer of the socket. --- src/cdp/CDP.zig | 24 ++++++++++++++++++++++++ src/network/WS.zig | 1 + 2 files changed, 25 insertions(+) diff --git a/src/cdp/CDP.zig b/src/cdp/CDP.zig index 1b2884b70..c3a051fa4 100644 --- a/src/cdp/CDP.zig +++ b/src/cdp/CDP.zig @@ -246,6 +246,11 @@ pub fn tick(self: *CDP) !bool { // so the flag can't be a stale leftover here. Exit. if (self.browser.env.terminatePending()) { log.warn(.cdp, "closing connection", .{ .reason = "pending terminate" }); + // The worker thread is the sole writer of this socket, so sending the + // close frame here can't interleave with another write. + self.conn.send(&WS.CLOSE_GOING_AWAY) catch |err| { + log.warn(.app, "CDP terminate close", .{ .err = err }); + }; return false; } @@ -1435,6 +1440,25 @@ test "cdp: disconnect latches so the worker keeps exiting" { try testing.expectError(error.ClientDisconnected, client.tick(0)); } +test "cdp: tick sends a close frame on pending terminate" { + var ctx = try testing.context(); + defer ctx.deinit(); + + const cdp = ctx.cdp(); + cdp.browser.env.requestTerminate(); + // Clear the pending terminate so deinit's V8 calls don't trip over the + // terminating-state asserts. + defer cdp.browser.env.cancelTerminate(); + + try testing.expectEqual(false, try cdp.tick()); + + // The client should receive a close frame (code 1001, going away), not + // just an abrupt socket close. + var buf: [WS.CLOSE_GOING_AWAY.len]u8 = undefined; + const n = try posix.read(ctx.socket, &buf); + try testing.expectEqualSlices(u8, &WS.CLOSE_GOING_AWAY, buf[0..n]); +} + test "cdp: syncRequest short-circuits after disconnect" { var ctx = try testing.context(); defer ctx.deinit(); diff --git a/src/network/WS.zig b/src/network/WS.zig index d458601de..25f9f55ef 100644 --- a/src/network/WS.zig +++ b/src/network/WS.zig @@ -26,6 +26,7 @@ pub const EMPTY_PONG = [_]u8{ 138, 0 }; // CLOSE, 2 length, code pub const CLOSE_NORMAL = [_]u8{ 136, 2, 3, 232 }; // code: 1000 +pub const CLOSE_GOING_AWAY = [_]u8{ 136, 2, 3, 233 }; // code: 1001 pub const CLOSE_TOO_BIG = [_]u8{ 136, 2, 3, 241 }; // 1009 pub const CLOSE_PROTOCOL_ERROR = [_]u8{ 136, 2, 3, 234 }; //code: 1002