From bb9e238f6c223bbc33528be3713cebb1624ed8dd Mon Sep 17 00:00:00 2001 From: Muki Kiboigo Date: Sun, 26 Apr 2026 22:59:11 -0700 Subject: [PATCH] Requests now use arenas from the arena pool --- src/browser/HttpClient.zig | 23 +++++++++++++++-------- src/cdp/domains/fetch.zig | 8 ++++---- src/network/layer/InterceptionLayer.zig | 19 +++---------------- src/network/layer/RobotsLayer.zig | 17 ++++++++++------- 4 files changed, 32 insertions(+), 35 deletions(-) diff --git a/src/browser/HttpClient.zig b/src/browser/HttpClient.zig index c61c457a..90d6b8ab 100644 --- a/src/browser/HttpClient.zig +++ b/src/browser/HttpClient.zig @@ -400,7 +400,10 @@ pub fn request(self: *Client, req: Request) !void { // Assign Request Id. var our_req = req; our_req.params.request_id = self.incrReqId(); - our_req.params.arena = ArenaAllocator.init(self.allocator); + + const arena = try self.network.app.arena_pool.acquire(.small, "Request.arena"); + errdefer self.network.app.arena_pool.release(arena); + our_req.params.arena = arena; return self.entry_layer.request(self, our_req); } @@ -854,7 +857,7 @@ fn ensureNoActiveConnection(self: *const Client) !void { pub const RequestParams = struct { /// This is unsafe to access until you pass it to `Client.request()` where it gets assigned. - arena: ArenaAllocator = undefined, + arena: Allocator = undefined, /// This is unsafe to access until you pass it to `Client.request()` where it gets assigned. request_id: u32 = undefined, @@ -893,7 +896,6 @@ pub const RequestParams = struct { pub fn deinit(self: *const RequestParams) void { self.headers.deinit(); - self.arena.deinit(); } }; @@ -918,7 +920,7 @@ pub const Request = struct { pub fn getCookieString(self: *Request) !?[:0]const u8 { const jar = self.params.cookie_jar orelse return null; - var aw: std.Io.Writer.Allocating = .init(self.params.arena.allocator()); + var aw: std.Io.Writer.Allocating = .init(self.params.arena); try jar.forRequest(self.params.url, &aw.writer, .{ .is_http = true, .origin_url = self.params.cookie_origin, @@ -1102,7 +1104,7 @@ pub const Transfer = struct { self._conn = null; } - self.req.deinit(); + self.client.deinitRequest(self.req); self.client.transfer_pool.destroy(self); } @@ -1266,7 +1268,7 @@ pub const Transfer = struct { fn handleRedirect(transfer: *Transfer) !void { const req = &transfer.req; const conn = transfer._conn.?; - const arena = transfer.req.params.arena.allocator(); + const arena = transfer.req.params.arena; transfer._redirect_count += 1; if (transfer._redirect_count > transfer.client.network.config.httpMaxRedirects()) { @@ -1443,7 +1445,7 @@ pub const Transfer = struct { transfer._callback_error = error.ResponseTooLarge; return http.writefunc_error; } - transfer._stream_buffer.ensureTotalCapacity(transfer.req.params.arena.allocator(), cl) catch {}; + transfer._stream_buffer.ensureTotalCapacity(transfer.req.params.arena, cl) catch {}; } } @@ -1456,7 +1458,7 @@ pub const Transfer = struct { } const chunk = buffer[0..chunk_len]; - transfer._stream_buffer.appendSlice(transfer.req.params.arena.allocator(), chunk) catch |err| { + transfer._stream_buffer.appendSlice(transfer.req.params.arena, chunk) catch |err| { transfer._callback_error = err; return http.writefunc_error; }; @@ -1568,6 +1570,11 @@ pub fn continueTransfer(self: *Client, transfer: *Transfer) !void { return self.process(transfer); } +pub fn deinitRequest(self: *Client, req: Request) void { + req.deinit(); + self.network.app.arena_pool.release(req.params.arena); +} + const Noop = struct { fn headerCallback(_: Response) !bool { return true; diff --git a/src/cdp/domains/fetch.zig b/src/cdp/domains/fetch.zig index 7eff321e..672e4b92 100644 --- a/src/cdp/domains/fetch.zig +++ b/src/cdp/domains/fetch.zig @@ -255,7 +255,7 @@ fn continueRequest(cmd: *CDP.Command) !void { .new_url = params.url, }); - const arena = request.params.arena.allocator(); + const arena = request.params.arena; // Update the request with the new parameters if (params.url) |url| { request.params.url = try arena.dupeZ(u8, url); @@ -313,7 +313,7 @@ fn continueWithAuth(cmd: *CDP.Command) !void { const request_id = try idFromRequestId(params.requestId); const pending = intercept_state.remove(request_id) orelse return error.RequestNotFound; const transfer = pending.transfer; - var request = transfer.req; + const request = transfer.req; log.debug(.cdp, "request intercept", .{ .state = "continue with auth", @@ -331,7 +331,7 @@ fn continueWithAuth(cmd: *CDP.Command) !void { // cancel the request, deinit the transfer on error. errdefer transfer.abortAuthChallenge(); - const arena = request.params.arena.allocator(); + const arena = request.params.arena; transfer.updateCredentials(try std.fmt.allocPrintSentinel( arena, "{s}:{s}", @@ -380,7 +380,7 @@ fn fulfillRequest(cmd: *CDP.Command) !void { var body: ?[]const u8 = null; if (params.body) |b| { const decoder = std.base64.standard.Decoder; - const buf = try request.params.arena.allocator().alloc(u8, try decoder.calcSizeForSlice(b)); + const buf = try request.params.arena.alloc(u8, try decoder.calcSizeForSlice(b)); try decoder.decode(buf, b); body = buf; } diff --git a/src/network/layer/InterceptionLayer.zig b/src/network/layer/InterceptionLayer.zig index 041af5f1..696ad5de 100644 --- a/src/network/layer/InterceptionLayer.zig +++ b/src/network/layer/InterceptionLayer.zig @@ -209,14 +209,12 @@ pub fn continueRequest(self: *InterceptionLayer, client: *Client, req: Request) } pub fn abortRequest(self: *InterceptionLayer, client: *Client, req: Request) void { - _ = client; - if (comptime IS_DEBUG) { log.debug(.http, "abort transfer", .{ .intercepted = self.intercepted }); } self.intercepted -= 1; - defer req.deinit(); + defer client.deinitRequest(req); req.error_callback(req.ctx, error.Abort); } @@ -253,7 +251,7 @@ fn fulfillInner( pub fn fulfillRequest( self: *InterceptionLayer, - _: *Client, + client: *Client, req: Request, status: u16, headers: []const http.Header, @@ -264,21 +262,10 @@ pub fn fulfillRequest( } self.intercepted -= 1; - defer req.deinit(); + defer client.deinitRequest(req); fulfillInner(req, status, headers, body) catch |err| { req.error_callback(req.ctx, err); return err; }; } - -pub fn abortAuthChallenge(self: *InterceptionLayer, req: Request) void { - if (comptime IS_DEBUG) { - log.debug(.http, "abort auth transfer", .{ .intercepted = self.intercepted }); - } - - self.intercepted -= 1; - defer req.deinit(); - req.error_callback(req.ctx, error.AbortAuthChallenge); - return; -} diff --git a/src/network/layer/RobotsLayer.zig b/src/network/layer/RobotsLayer.zig index 11b7c8f3..92303a68 100644 --- a/src/network/layer/RobotsLayer.zig +++ b/src/network/layer/RobotsLayer.zig @@ -66,7 +66,7 @@ fn request(ptr: *anyopaque, client: *Client, req: Request) anyerror!void { const path = URL.getPathname(req.params.url); if (!robots.isAllowed(path)) { - defer req.deinit(); + defer client.deinitRequest(req); log.warn(.http, "blocked by robots", .{ .url = req.params.url }); req.error_callback(req.ctx, error.RobotsBlocked); @@ -108,11 +108,14 @@ fn fetchRobotsThenRequest( const headers = try client.newHeaders(); log.debug(.browser, "fetching robots.txt", .{ .robots_url = robots_url }); + const new_arena = try client.network.app.arena_pool.acquire(.small, "RobotsLayer.fetchRobots"); + errdefer client.network.app.arena_pool.release(new_arena); + try self.next.request(client, .{ .ctx = robots_ctx, .params = .{ // We have to do this ourselves because we are not going through the top level `request`. - .arena = std.heap.ArenaAllocator.init(client.allocator), + .arena = new_arena, .request_id = client.incrReqId(), .url = robots_url, .method = .GET, @@ -145,24 +148,24 @@ fn flushPending(self: *RobotsLayer, client: *Client, robots_url: [:0]const u8, a for (queued.value.items) |queued_req| { if (!allowed) { log.warn(.http, "blocked by robots", .{ .url = queued_req.params.url }); - defer queued_req.deinit(); + defer client.deinitRequest(queued_req); queued_req.error_callback(queued_req.ctx, error.RobotsBlocked); } else { self.next.request(client, queued_req) catch |e| { - defer queued_req.deinit(); + defer client.deinitRequest(queued_req); queued_req.error_callback(queued_req.ctx, e); }; } } } -fn flushPendingShutdown(self: *RobotsLayer, robots_url: [:0]const u8) void { +fn flushPendingShutdown(self: *RobotsLayer, robots_url: [:0]const u8, client: *Client) void { var queued = self.pending.fetchRemove(robots_url) orelse @panic("RobotsLayer.flushPendingShutdown: missing queue"); defer queued.value.deinit(self.allocator); for (queued.value.items) |queued_req| { - defer queued_req.deinit(); + defer client.deinitRequest(queued_req); if (queued_req.shutdown_callback) |cb| cb(queued_req.ctx); } } @@ -265,6 +268,6 @@ const RobotsContext = struct { defer client.network.app.arena_pool.release(self.arena); log.debug(.http, "robots fetch shutdown", .{}); - l.flushPendingShutdown(robots_url); + l.flushPendingShutdown(robots_url, client); } };