From 244bfbdd275fb1e22232d148ac80a28b50663b02 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Wed, 3 Jun 2026 11:37:52 +0800 Subject: [PATCH] HttpClient dupes CDP-supplied proxy to ensure lifetime I'm hoping this is the cause of our flaky CI runs. The existing code kept a reference to the CDD command-owned proxy address. This commit has HttpClient dupe/own the proxy to ensure the correct lifetime. --- src/browser/HttpClient.zig | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/browser/HttpClient.zig b/src/browser/HttpClient.zig index 874ed98b..53860b81 100644 --- a/src/browser/HttpClient.zig +++ b/src/browser/HttpClient.zig @@ -111,10 +111,15 @@ network: *Network, arena_pool: *ArenaPool, -// The current proxy. CDP can change it, changeProxy(null) restores -// from config. +// The current proxy. Callers can change it, changeProxy(null) restores +// from config. May point either at `http_proxy_owned` (a caller-supplied +// dupe) or at the config string (which we must not free). http_proxy: ?[:0]const u8 = null, +// When a caller (e.g. CDP) supplies a proxy, we have to dupe it to take ownership +// which we'll be responsible for freeing. +http_proxy_owned: ?[:0]const u8 = null, + // track if the client use a proxy for connections. // We can't use http_proxy because we want also to track proxy configured via // CDP. @@ -260,6 +265,9 @@ pub fn deinit(self: *Client) void { self.handles.deinit(); self.clearUserAgentOverride(); + if (self.http_proxy_owned) |owned| { + self.allocator.free(owned); + } self.robots_layer.deinit(self.allocator); self.deferring_layer.deinit(); @@ -337,7 +345,22 @@ pub fn setTlsVerify(self: *Client, verify: bool) !void { // can be changed at any point in the easy's lifecycle. pub fn changeProxy(self: *Client, proxy: ?[:0]const u8) !void { try self.ensureNoActiveConnection(); - self.http_proxy = proxy orelse self.network.config.httpProxy(); + + // Free any previously-duped proxy before we overwrite http_proxy. + if (self.http_proxy_owned) |owned| { + self.allocator.free(owned); + self.http_proxy_owned = null; + } + + // Reset to the config default; if dupeZ below fails, http_proxy is + // left pointing at this rather than at the freed dup. + self.http_proxy = self.network.config.httpProxy(); + + if (proxy) |p| { + const owned = try self.allocator.dupeZ(u8, p); + self.http_proxy_owned = owned; + self.http_proxy = owned; + } self.use_proxy = self.http_proxy != null; }