fix: Fix double-release and other (edge-case) memory issues

Our `Arena` from the `ArenaPool` now tracks if it's already been released. On
a subsequent release, it panics then and there. Without this, the code will
almost certainly panic anyways, but it will panic in a seemingly unrelated
place. Hopefully this makes identifying future cases of this easier (since we'll
get the stack trace of the re-releaser).

Fix 3 separate memory issues, all edge cases.

1 - an XHR error handler that re-open/sends would incorrectly null the new
    transfer (maybe this isn't that odd, maybe it's a common retry-on-error).

2 - On a curl_easy_pause error (from WebSocket.zig) we now unqueue the just-
    queued message, because the error will errdefer the message arena to be
    cleaned up.

3 - ScriptManager now cleans up after itself on a failure prior to submit()
    being called.
This commit is contained in:
Karl Seguin committed 2026-08-26 18:24:12 +08:00
1 parent 5f8eb43867
commit cfd921ecab
8 files changed
+200 -36

No files matched your search

+12
View File
@@ -140,6 +140,11 @@ use_proxy: bool,
// Current TLS verification state, applied per-connection in makeRequest.
tls_verify: bool = true,
// Test-only fault injection: makes the next submit() fail synchronously from
// inside the pipeline, the shape where error_callback fires AND the error is
// returned to the caller (see Transfer.submit).
test_fail_submit: if (lp.IS_TEST) ?anyerror else void = if (lp.IS_TEST) null else {},
// User agent override set via CDP Emulation.setUserAgentOverride.
// When set, takes precedence over the config's http_headers value.
// Allocated from self.allocator when set, null otherwise.
@@ -914,6 +919,12 @@ const SubmitFrom = enum { start, after_intercept, network };
fn pipeline(self: *Client, transfer: *Transfer, from: SubmitFrom) !void {
sw: switch (from) {
.start => {
if (comptime lp.IS_TEST) {
if (self.test_fail_submit) |err| {
return err;
}
}
if (self.network.web_bot_auth) |wba| {
const authority = URL.getHost(transfer.req.url);
try wba.signRequest(transfer, authority);
@@ -3672,6 +3683,7 @@ fn initTestClient(client: *Client, pool: *ArenaPool) void {
.single_flight = .init(testing.allocator),
};
client.url_blocklist = null;
client.test_fail_submit = null;
// isUrlBlocked reaches through here for the adblocker; tests that want
// one assign it to `client.network` after this returns.
test_network.adblocker = null;