From 2414a67d28a4a56b49e46be279b33ded6993687c Mon Sep 17 00:00:00 2001 From: Muki Kiboigo Date: Mon, 8 Jun 2026 07:58:42 -0700 Subject: [PATCH] header callback returns a HeaderResult --- src/browser/Frame.zig | 4 +-- src/browser/HttpClient.zig | 44 +++++++++++++++-------- src/browser/ScriptManagerBase.zig | 7 ++-- src/browser/webapi/Worker.zig | 8 ++--- src/browser/webapi/net/Fetch.zig | 6 ++-- src/browser/webapi/net/XMLHttpRequest.zig | 6 ++-- src/network/layer/CacheLayer.zig | 14 ++++---- src/network/layer/DeferringLayer.zig | 14 ++++---- src/network/layer/Forward.zig | 3 +- src/network/layer/InterceptionLayer.zig | 7 ++-- src/network/layer/RobotsLayer.zig | 6 ++-- 11 files changed, 70 insertions(+), 49 deletions(-) diff --git a/src/browser/Frame.zig b/src/browser/Frame.zig index abe0fb1fe..10de50f2c 100644 --- a/src/browser/Frame.zig +++ b/src/browser/Frame.zig @@ -1070,7 +1070,7 @@ fn notifyParentLoadComplete(self: *Frame) void { parent.iframeCompletedLoading(self.iframe.?); } -fn frameHeaderDoneCallback(response: HttpClient.Response) !bool { +fn frameHeaderDoneCallback(response: HttpClient.Response) !HttpClient.HeaderResult { var self: *Frame = @ptrCast(@alignCast(response.ctx)); // Commit point for a pending root navigation. The session has been @@ -1141,7 +1141,7 @@ fn frameHeaderDoneCallback(response: HttpClient.Response) !bool { }); } - return true; + return .proceed; } fn frameDataCallback(response: HttpClient.Response, data: []const u8) !void { diff --git a/src/browser/HttpClient.zig b/src/browser/HttpClient.zig index 026974bcb..5334bc263 100644 --- a/src/browser/HttpClient.zig +++ b/src/browser/HttpClient.zig @@ -711,10 +711,12 @@ const Synthetic = struct { if (req.start_callback) |cb| { try cb(response); } - const proceed = try req.header_callback(response); - if (!proceed) { + + const result = try req.header_callback(response); + if (result == .abort) { return error.Abort; } + if (fulfilled.body) |b| { if (b.len > 0) { try req.data_callback(response, b); @@ -736,14 +738,14 @@ const SyncContext = struct { status: u16 = 0, body: std.ArrayList(u8), - fn headerCallback(response: Response) anyerror!bool { + fn headerCallback(response: Response) anyerror!HeaderResult { const self: *SyncContext = @ptrCast(@alignCast(response.ctx)); lp.assert(response.status() != null, "HttpClient.SyncRequest.headerCallback", .{ .value = response.status() }); self.status = response.status().?; if (response.contentLength()) |cl| { try self.body.ensureTotalCapacity(self.allocator, cl); } - return true; + return .proceed; } fn dataCallback(response: Response, data: []const u8) anyerror!void { @@ -1143,10 +1145,14 @@ fn processOneMessage(self: *Client, msg: http.Handles.MultiMessage, transfer: *T if (!transfer.res.header_done_called) { // In case of request w/o data, we need to call the header done // callback now. - const proceed = try transfer.headerDoneCallback(msg.conn); - if (!proceed) { - transfer.requestFailed(error.Abort, true); - return true; + const result = try transfer.headerDoneCallback(msg.conn); + switch (result) { + .proceed => {}, + .handled => return true, + .abort => { + transfer.requestFailed(error.Abort, true); + return true; + }, } } @@ -1282,9 +1288,18 @@ fn ensureNoActiveConnection(self: *const Client) !void { } } +pub const HeaderResult = enum { + /// Continue processing normally. + proceed, + /// Caller took ownership of the response; stop w/o error or abort. + handled, + /// Abort the Transfer, + abort, +}; + pub const Request = struct { pub const StartCallback = *const fn (response: Response) anyerror!void; - pub const HeaderCallback = *const fn (response: Response) anyerror!bool; + pub const HeaderCallback = *const fn (response: Response) anyerror!HeaderResult; pub const DataCallback = *const fn (response: Response, data: []const u8) anyerror!void; pub const DoneCallback = *const fn (ctx: *anyopaque) anyerror!void; pub const ErrorCallback = *const fn (ctx: *anyopaque, err: anyerror) void; @@ -1989,7 +2004,7 @@ pub const Transfer = struct { // headerDoneCallback is called once the headers have been read. // It can be called either on dataCallback or once the request for those // w/o body. - fn headerDoneCallback(transfer: *Transfer, conn: *const http.Connection) !bool { + fn headerDoneCallback(transfer: *Transfer, conn: *const http.Connection) !HeaderResult { lp.assert(transfer.res.header_done_called == false, "Transfer.headerDoneCallback", .{}); defer transfer.res.header_done_called = true; @@ -2015,12 +2030,13 @@ pub const Transfer = struct { } } - const proceed = transfer.req.header_callback(Client.Response.fromTransfer(transfer)) catch |err| { + const result = transfer.req.header_callback(Client.Response.fromTransfer(transfer)) catch |err| { log.err(.http, "header_callback", .{ .err = err, .req = transfer }); return err; }; - return proceed and transfer.state != .aborted; + if (result == .proceed and transfer.state == .aborted) return .abort; + return result; } fn dataCallback(buffer: [*]const u8, chunk_count: usize, chunk_len: usize, data: *anyopaque) usize { @@ -2160,8 +2176,8 @@ pub fn continueTransfer(self: *Client, transfer: *Transfer) !void { } const Noop = struct { - fn headerCallback(_: Response) !bool { - return true; + fn headerCallback(_: Response) !HeaderResult { + return .proceed; } fn dataCallback(_: Response, _: []const u8) !void {} fn doneCallback(_: *anyopaque) !void {} diff --git a/src/browser/ScriptManagerBase.zig b/src/browser/ScriptManagerBase.zig index f917ced60..62eaa2737 100644 --- a/src/browser/ScriptManagerBase.zig +++ b/src/browser/ScriptManagerBase.zig @@ -585,7 +585,7 @@ pub const Script = struct { log.debug(.http, "script fetch start", .{ .req = response }); } - pub fn headerCallback(response: HttpClient.Response) !bool { + pub fn headerCallback(response: HttpClient.Response) !HttpClient.HeaderResult { const self: *Script = @ptrCast(@alignCast(response.ctx)); self.status = response.status().?; @@ -595,7 +595,8 @@ pub const Script = struct { .status = response.status(), .content_type = response.contentType(), }); - return false; + + return .abort; } if (comptime IS_DEBUG) { @@ -646,7 +647,7 @@ pub const Script = struct { try buffer.ensureTotalCapacity(self.arena, cl); } self.source = .{ .remote = buffer }; - return true; + return .proceed; } pub fn dataCallback(response: HttpClient.Response, data: []const u8) !void { diff --git a/src/browser/webapi/Worker.zig b/src/browser/webapi/Worker.zig index 5216fe85f..552597fc8 100644 --- a/src/browser/webapi/Worker.zig +++ b/src/browser/webapi/Worker.zig @@ -139,16 +139,16 @@ pub fn asEventTarget(self: *Worker) *EventTarget { return self._proto; } -fn httpHeaderCallback(response: HttpClient.Response) !bool { +fn httpHeaderCallback(response: HttpClient.Response) !HttpClient.HeaderResult { const self: *Worker = @ptrCast(@alignCast(response.ctx)); - const status = response.status() orelse return false; + const status = response.status() orelse return .abort; if (status < 200 or status >= 300) { log.warn(.browser, "Worker status", .{ .url = self._url, .status = status, }); - return false; + return .abort; } self._http_response = response; @@ -156,7 +156,7 @@ fn httpHeaderCallback(response: HttpClient.Response) !bool { try self._script_buffer.ensureTotalCapacity(self._arena, cl); } - return true; + return .proceed; } fn httpDataCallback(response: HttpClient.Response, data: []const u8) !void { diff --git a/src/browser/webapi/net/Fetch.zig b/src/browser/webapi/net/Fetch.zig index 2eac9ef7a..d20d61204 100644 --- a/src/browser/webapi/net/Fetch.zig +++ b/src/browser/webapi/net/Fetch.zig @@ -129,12 +129,12 @@ fn httpStartCallback(response: HttpClient.Response) !void { self._response._http_response = response; } -fn httpHeaderDoneCallback(response: HttpClient.Response) !bool { +fn httpHeaderDoneCallback(response: HttpClient.Response) !HttpClient.HeaderResult { const self: *Fetch = @ptrCast(@alignCast(response.ctx)); if (self._signal) |signal| { if (signal._aborted) { - return false; + return .abort; } } @@ -182,7 +182,7 @@ fn httpHeaderDoneCallback(response: HttpClient.Response) !bool { try res._headers.append(hdr.name, hdr.value, exec); } - return true; + return .proceed; } fn httpDataCallback(response: HttpClient.Response, data: []const u8) !void { diff --git a/src/browser/webapi/net/XMLHttpRequest.zig b/src/browser/webapi/net/XMLHttpRequest.zig index 4e2d5f463..7135c627d 100644 --- a/src/browser/webapi/net/XMLHttpRequest.zig +++ b/src/browser/webapi/net/XMLHttpRequest.zig @@ -463,7 +463,7 @@ fn httpHeaderCallback(response: HttpClient.Response, header: http.Header) !void try self._response_headers.append(self._arena, joined); } -fn httpHeaderDoneCallback(response: HttpClient.Response) !bool { +fn httpHeaderDoneCallback(response: HttpClient.Response) !HttpClient.HeaderResult { const self: *XMLHttpRequest = @ptrCast(@alignCast(response.ctx)); if (comptime IS_DEBUG) { @@ -481,7 +481,7 @@ fn httpHeaderDoneCallback(response: HttpClient.Response) !bool { .err = e, .url = self._url, }); - return false; + return .abort; }; } @@ -508,7 +508,7 @@ fn httpHeaderDoneCallback(response: HttpClient.Response) !bool { try self._proto.dispatch(.load_start, .{ .loaded = 0, .total = self._response_len orelse 0 }, exec); try self.stateChanged(.loading, exec); - return true; + return .proceed; } fn httpDataCallback(response: HttpClient.Response, data: []const u8) !void { diff --git a/src/network/layer/CacheLayer.zig b/src/network/layer/CacheLayer.zig index bf3d357b5..93eedabd7 100644 --- a/src/network/layer/CacheLayer.zig +++ b/src/network/layer/CacheLayer.zig @@ -28,6 +28,7 @@ const Cache = @import("../cache/Cache.zig"); const CachedMetadata = @import("../cache/Cache.zig").CachedMetadata; const CachedResponse = @import("../cache/Cache.zig").CachedResponse; +const HeaderResult = @import("../../browser/HttpClient.zig").HeaderResult; const Forward = @import("Forward.zig"); const log = lp.log; @@ -161,8 +162,8 @@ fn forwardFromCache( defer cached.data.deinit(); try forward.forwardStart(response); - const proceed = try forward.forwardHeader(response); - if (!proceed) return error.Abort; + const result = try forward.forwardHeader(response); + if (result == .abort) return error.Abort; switch (cached.data) { .buffer => |data| { @@ -203,8 +204,8 @@ fn serveFromCache(transfer: *Transfer, cached: *const CachedResponse) !void { try cb(response); } - const proceed = try req.header_callback(response); - if (!proceed) { + const result = try req.header_callback(response); + if (result == .abort) { return error.Abort; } @@ -254,7 +255,7 @@ const CacheContext = struct { return self.forward.forwardData(response, chunk); } - fn headerCallback(response: Response) anyerror!bool { + fn headerCallback(response: Response) anyerror!HeaderResult { const self: *CacheContext = @ptrCast(@alignCast(response.ctx)); // For non-transfer responses (fulfilled by interception, or future @@ -282,8 +283,7 @@ const CacheContext = struct { }; try forwardFromCache(transfer, &self.forward, &stale); - self.forward = Forward.noop(); - return true; + return .handled; } if (self.stale_entry) |stale| { diff --git a/src/network/layer/DeferringLayer.zig b/src/network/layer/DeferringLayer.zig index fd69b5c65..d6f708224 100644 --- a/src/network/layer/DeferringLayer.zig +++ b/src/network/layer/DeferringLayer.zig @@ -28,6 +28,7 @@ const Response = @import("../../browser/HttpClient.zig").Response; const Layer = @import("../../browser/HttpClient.zig").Layer; const StableResponse = @import("../../browser/HttpClient.zig").StableResponse; const Forward = @import("Forward.zig"); +const HeaderResult = @import("../../browser/HttpClient.zig").HeaderResult; const DeferringLayer = @This(); @@ -191,7 +192,7 @@ const DeferredContext = struct { try self.buffered.append(self.arena, .start); } - fn headerCallback(response: Response) anyerror!bool { + fn headerCallback(response: Response) anyerror!HeaderResult { const self: *DeferredContext = @ptrCast(@alignCast(response.ctx)); if (!self.deferring and !self.shouldDefer()) { @@ -202,7 +203,7 @@ const DeferredContext = struct { try self.setStableResponse(response); self.deferring = true; try self.buffered.append(self.arena, .header); - return true; + return .proceed; } fn dataCallback(response: Response, chunk: []const u8) anyerror!void { @@ -282,13 +283,12 @@ const DeferredContext = struct { const stable_response = self.stable_resp orelse @panic("stable_resp must be set for header events"); const response = Response.fromStable(&stable_response); - const proceed = self.forward.forwardHeader(response) catch |err| { + const result = self.forward.forwardHeader(response) catch |err| { log.err(.http, "deferred header callback", .{ .err = err, .url = self.url }); self.forward.forwardErr(err); return; }; - - if (!proceed) { + if (result == .abort) { self.forward.forwardErr(error.Abort); return; } @@ -333,12 +333,12 @@ const DeferredContext = struct { }; }, .header => { - const proceed = self.forward.forwardHeader(response) catch |err| { + const result = self.forward.forwardHeader(response) catch |err| { log.err(.http, "defer part header callback", .{ .err = err, .url = self.url }); self.forward.forwardErr(err); return; }; - if (!proceed) { + if (result == .abort) { self.forward.forwardErr(error.Abort); return; } diff --git a/src/network/layer/Forward.zig b/src/network/layer/Forward.zig index f167cd413..0aaee5121 100644 --- a/src/network/layer/Forward.zig +++ b/src/network/layer/Forward.zig @@ -21,6 +21,7 @@ // call forwardX(...) to invoke the captured originals with the original ctx. const Request = @import("../../browser/HttpClient.zig").Request; const Response = @import("../../browser/HttpClient.zig").Response; +const HeaderResult = @import("../../browser/HttpClient.zig").HeaderResult; const Forward = @This(); @@ -50,7 +51,7 @@ pub fn forwardStart(self: Forward, response: Response) anyerror!void { if (self.start) |cb| try cb(fwd); } -pub fn forwardHeader(self: Forward, response: Response) anyerror!bool { +pub fn forwardHeader(self: Forward, response: Response) anyerror!HeaderResult { var fwd = response; fwd.ctx = self.ctx; return self.header(fwd); diff --git a/src/network/layer/InterceptionLayer.zig b/src/network/layer/InterceptionLayer.zig index 9c3915725..17d1a077e 100644 --- a/src/network/layer/InterceptionLayer.zig +++ b/src/network/layer/InterceptionLayer.zig @@ -29,6 +29,7 @@ const Response = @import("../../browser/HttpClient.zig").Response; const FulfilledResponse = @import("../../browser/HttpClient.zig").FulfilledResponse; const Layer = @import("../../browser/HttpClient.zig").Layer; const Forward = @import("Forward.zig"); +const HeaderResult = @import("../../browser/HttpClient.zig").HeaderResult; const InterceptionLayer = @This(); @@ -111,7 +112,7 @@ pub const InterceptContext = struct { return self.forward.forwardStart(response); } - fn headerCallback(response: Response) anyerror!bool { + fn headerCallback(response: Response) anyerror!HeaderResult { const self: *InterceptContext = @ptrCast(@alignCast(response.ctx)); log.debug(.http, "intercept header", .{ .url = self.transfer.req.url, @@ -263,8 +264,8 @@ fn fulfillInner( try cb(response); } - const proceed = try req.header_callback(response); - if (!proceed) { + const result = try req.header_callback(response); + if (result == .abort) { return error.Abort; } diff --git a/src/network/layer/RobotsLayer.zig b/src/network/layer/RobotsLayer.zig index 6d979bfcf..a6f3165eb 100644 --- a/src/network/layer/RobotsLayer.zig +++ b/src/network/layer/RobotsLayer.zig @@ -23,6 +23,7 @@ const URL = @import("../../browser/URL.zig"); const Layer = @import("../../browser/HttpClient.zig").Layer; const Transfer = @import("../../browser/HttpClient.zig").Transfer; const Response = @import("../../browser/HttpClient.zig").Response; +const HeaderResult = @import("../../browser/HttpClient.zig").HeaderResult; const Robots = @import("../Robots.zig"); const Network = @import("../Network.zig"); @@ -205,7 +206,7 @@ const RobotsContext = struct { self.layer.allocator.destroy(self); } - fn headerCallback(response: Response) anyerror!bool { + fn headerCallback(response: Response) anyerror!HeaderResult { const self: *RobotsContext = @ptrCast(@alignCast(response.ctx)); switch (response.inner) { .transfer => |t| { @@ -219,7 +220,8 @@ const RobotsContext = struct { }, else => {}, } - return true; + + return .proceed; } fn dataCallback(response: Response, data: []const u8) anyerror!void {