mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-09-14 23:15:14 -04:00
cdp: remove race between terminate/disconnect
A client that disconnects might get treated as a harsher terminate failure (e.g. watchdog). This doesn't have a huge impact, but it makes the CI flaky and it produces more logs than is necessary. In a terminate state, the driver will now check its inbox to see if this is a client disconnection.
This commit is contained in:
2 files changed
+25
-4
No files matched your search
@@ -566,7 +566,10 @@ pub fn activity(self: *const Client) Activity {
|
||||
// refs to page / session / V8 state; dispatching a
|
||||
// command that frees that state would UAF on unwind.
|
||||
// Cherry-pick only Fetch interception responses
|
||||
const DrainMode = enum { all, sync_wait };
|
||||
// .terminal - pops only close/disconnect: the connection is going away and
|
||||
// nothing else may be dispatched, but the peer still gets its
|
||||
// close reason.
|
||||
const DrainMode = enum { all, sync_wait, terminal };
|
||||
|
||||
// One-shot convenience: create and submit in a single call.
|
||||
pub fn request(self: *Client, req: Request, owner: ?*Owner) anyerror!void {
|
||||
@@ -1310,6 +1313,10 @@ fn makeRequest(self: *Client, conn: *http.Connection, transfer: *Transfer) anyer
|
||||
_ = try self.handles.perform();
|
||||
}
|
||||
|
||||
pub fn drainTerminal(self: *Client) !void {
|
||||
return self.drainInbox(.terminal);
|
||||
}
|
||||
|
||||
// Drain any client messages the Network thread pushed into our inbox
|
||||
// and dispatch them via the driver callbacks. Returns
|
||||
// error.ClientDisconnected if the inbox surfaced a disconnect message,
|
||||
@@ -1322,6 +1329,7 @@ fn drainInbox(self: *Client, mode: DrainMode) !void {
|
||||
const msg = switch (mode) {
|
||||
.all => self.inbox.pop(),
|
||||
.sync_wait => self.inbox.popIf(allowDuringSyncWait),
|
||||
.terminal => self.inbox.popIf(isTerminal),
|
||||
} orelse return;
|
||||
|
||||
defer msg.deinit();
|
||||
@@ -1369,6 +1377,13 @@ fn allowDuringSyncWait(msg: *Inbox.Message) bool {
|
||||
};
|
||||
}
|
||||
|
||||
fn isTerminal(msg: *Inbox.Message) bool {
|
||||
return switch (msg.payload) {
|
||||
.close, .disconnect => true,
|
||||
.ping, .cdp, .bidi => false,
|
||||
};
|
||||
}
|
||||
|
||||
fn isFetchInterceptionMethod(method: []const u8) bool {
|
||||
return std.mem.eql(u8, method, "Fetch.continueRequest") or
|
||||
std.mem.eql(u8, method, "Fetch.failRequest") or
|
||||
|
||||
@@ -77,11 +77,13 @@ pub fn onData(self: *const Driver, data: []const u8) anyerror!bool {
|
||||
|
||||
// Server run loop. Called when it drops the link unsolicited (peer EOF, ...)
|
||||
pub fn onLinkDisconnect(self: *const Driver, err: ?anyerror) void {
|
||||
self.browser.env.requestTerminate();
|
||||
const arena = self.browser.arena_pool.acquire(.tiny, "driver disconnect") catch |e| switch (e) {
|
||||
error.OutOfMemory => @panic("OOM"),
|
||||
};
|
||||
// order matters, this ensures that the disconnect message is in the inbox
|
||||
// when tick() discovers the terminatePending flag is set.
|
||||
self.browser.http_client.inbox.push(arena, .{ .disconnect = err });
|
||||
self.browser.env.requestTerminate();
|
||||
}
|
||||
|
||||
// Worker thread. We're processing messages from the inbox.
|
||||
@@ -134,8 +136,12 @@ pub fn run(self: *const Driver) void {
|
||||
// One iteration of the worker loop. Returns false to disconnect.
|
||||
fn tick(self: *const Driver) !bool {
|
||||
if (self.browser.env.terminatePending()) {
|
||||
// terminatePending means someone decided this browser must die
|
||||
// (e.g. the heap limit was reached).
|
||||
// Maybe something bad happened (e.g. watchdog) or maybe the client
|
||||
// just disconnected. Check the inbox to see if there's a disconnect
|
||||
// message and, if so, it'll handle it directly.
|
||||
self.browser.http_client.drainTerminal() catch |err| switch (err) {
|
||||
error.ClientDisconnected => return false,
|
||||
};
|
||||
log.warn(self.scope, "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.
|
||||
|
||||
Reference in new issue
Block a user