header callback returns a HeaderResult

This commit is contained in:
Muki Kiboigo
2026-06-08 07:58:42 -07:00
parent 38c2fcdf7b
commit 2414a67d28
11 changed files with 70 additions and 49 deletions

View File

@@ -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 {

View File

@@ -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 {}

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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| {

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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 {