Handle scripts that don't return a 200 status code

This was already being handled for async scripts, but for sync scripts, we'd
log the error then proceed to try and execute the body (which would be some
error message).

This allows the header_callback to return a boolean to indicate whether or not
the http client should continue to process the request or abort it.
This commit is contained in:
Karl Seguin
2026-01-22 14:15:00 +08:00
parent c095950ef9
commit f6397e2731
5 changed files with 45 additions and 20 deletions

View File

@@ -590,7 +590,7 @@ fn _documentIsComplete(self: *Page) !void {
); );
} }
fn pageHeaderDoneCallback(transfer: *Http.Transfer) !void { fn pageHeaderDoneCallback(transfer: *Http.Transfer) !bool {
var self: *Page = @ptrCast(@alignCast(transfer.ctx)); var self: *Page = @ptrCast(@alignCast(transfer.ctx));
// would be different than self.url in the case of a redirect // would be different than self.url in the case of a redirect
@@ -607,6 +607,8 @@ fn pageHeaderDoneCallback(transfer: *Http.Transfer) !void {
.content_type = header.contentType(), .content_type = header.contentType(),
}); });
} }
return true;
} }
fn pageDataCallback(transfer: *Http.Transfer, data: []const u8) !void { fn pageDataCallback(transfer: *Http.Transfer, data: []const u8) !void {

View File

@@ -663,7 +663,7 @@ pub const Script = struct {
log.debug(.http, "script fetch start", .{ .req = transfer }); log.debug(.http, "script fetch start", .{ .req = transfer });
} }
fn headerCallback(transfer: *Http.Transfer) !void { fn headerCallback(transfer: *Http.Transfer) !bool {
const self: *Script = @ptrCast(@alignCast(transfer.ctx)); const self: *Script = @ptrCast(@alignCast(transfer.ctx));
const header = &transfer.response_header.?; const header = &transfer.response_header.?;
self.status = header.status; self.status = header.status;
@@ -673,7 +673,7 @@ pub const Script = struct {
.status = header.status, .status = header.status,
.content_type = header.contentType(), .content_type = header.contentType(),
}); });
return; return false;
} }
if (comptime IS_DEBUG) { if (comptime IS_DEBUG) {
@@ -694,6 +694,7 @@ pub const Script = struct {
try buffer.ensureTotalCapacity(self.manager.allocator, cl); try buffer.ensureTotalCapacity(self.manager.allocator, cl);
} }
self.source = .{ .remote = buffer }; self.source = .{ .remote = buffer };
return true;
} }
fn dataCallback(transfer: *Http.Transfer, data: []const u8) !void { fn dataCallback(transfer: *Http.Transfer, data: []const u8) !void {
@@ -733,7 +734,7 @@ pub const Script = struct {
log.warn(.http, "script fetch error", .{ log.warn(.http, "script fetch error", .{
.err = err, .err = err,
.req = self.url, .req = self.url,
.mode = self.mode, .mode = std.meta.activeTag(self.mode),
.kind = self.kind, .kind = self.kind,
.status = self.status, .status = self.status,
}); });
@@ -753,9 +754,13 @@ pub const Script = struct {
return; return;
} }
if (self.mode == .import) { switch (self.mode) {
const entry = self.manager.imported_modules.getPtr(self.url).?; .import_async => |ia| ia.callback(ia.data, error.FailedToLoad),
.import => {
const entry = manager.imported_modules.getPtr(self.url).?;
entry.state = .err; entry.state = .err;
},
else => {},
} }
self.deinit(true); self.deinit(true);
manager.evaluate(); manager.evaluate();

View File

@@ -89,7 +89,7 @@ pub fn deinit(self: *Fetch) void {
} }
} }
fn httpHeaderDoneCallback(transfer: *Http.Transfer) !void { fn httpHeaderDoneCallback(transfer: *Http.Transfer) !bool {
const self: *Fetch = @ptrCast(@alignCast(transfer.ctx)); const self: *Fetch = @ptrCast(@alignCast(transfer.ctx));
if (transfer.getContentLength()) |cl| { if (transfer.getContentLength()) |cl| {
@@ -133,6 +133,8 @@ fn httpHeaderDoneCallback(transfer: *Http.Transfer) !void {
while (it.next()) |hdr| { while (it.next()) |hdr| {
try res._headers.append(hdr.name, hdr.value, self._page); try res._headers.append(hdr.name, hdr.value, self._page);
} }
return true;
} }
fn httpDataCallback(transfer: *Http.Transfer, data: []const u8) !void { fn httpDataCallback(transfer: *Http.Transfer, data: []const u8) !void {

View File

@@ -290,7 +290,7 @@ fn httpHeaderCallback(transfer: *Http.Transfer, header: Http.Header) !void {
try self._response_headers.append(self._arena, joined); try self._response_headers.append(self._arena, joined);
} }
fn httpHeaderDoneCallback(transfer: *Http.Transfer) !void { fn httpHeaderDoneCallback(transfer: *Http.Transfer) !bool {
const self: *XMLHttpRequest = @ptrCast(@alignCast(transfer.ctx)); const self: *XMLHttpRequest = @ptrCast(@alignCast(transfer.ctx));
const header = &transfer.response_header.?; const header = &transfer.response_header.?;
@@ -305,7 +305,8 @@ fn httpHeaderDoneCallback(transfer: *Http.Transfer) !void {
if (header.contentType()) |ct| { if (header.contentType()) |ct| {
self._response_mime = Mime.parse(ct) catch |e| { self._response_mime = Mime.parse(ct) catch |e| {
return self.handleError(e); log.info(.http, "invalid content type", .{.content_Type = ct, .err = e, .url = self._url,});
return false;
}; };
} }
@@ -332,6 +333,8 @@ fn httpHeaderDoneCallback(transfer: *Http.Transfer) !void {
try self.stateChanged(.headers_received, local, page); try self.stateChanged(.headers_received, local, page);
try self._proto.dispatch(.load_start, .{ .loaded = 0, .total = self._response_len orelse 0 }, local, page); try self._proto.dispatch(.load_start, .{ .loaded = 0, .total = self._response_len orelse 0 }, local, page);
try self.stateChanged(.loading, local, page); try self.stateChanged(.loading, local, page);
return true;
} }
fn httpDataCallback(transfer: *Http.Transfer, data: []const u8) !void { fn httpDataCallback(transfer: *Http.Transfer, data: []const u8) !void {

View File

@@ -594,15 +594,19 @@ fn processMessages(self: *Client) !bool {
defer transfer.deinit(); defer transfer.deinit();
if (errorCheck(msg.data.result)) { if (errorCheck(msg.data.result)) blk: {
// In case of request w/o data, we need to call the header done // In case of request w/o data, we need to call the header done
// callback now. // callback now.
if (!transfer._header_done_called) { if (!transfer._header_done_called) {
transfer.headerDoneCallback(easy) catch |err| { const proceed = transfer.headerDoneCallback(easy) catch |err| {
log.err(.http, "header_done_callback", .{ .err = err }); log.err(.http, "header_done_callback", .{ .err = err });
self.requestFailed(transfer, err); self.requestFailed(transfer, err);
continue; continue;
}; };
if (!proceed) {
self.requestFailed(transfer, error.Abort);
break :blk;
}
} }
transfer.req.done_callback(transfer.ctx) catch |err| { transfer.req.done_callback(transfer.ctx) catch |err| {
// transfer isn't valid at this point, don't use it. // transfer isn't valid at this point, don't use it.
@@ -771,7 +775,7 @@ pub const Request = struct {
ctx: *anyopaque = undefined, ctx: *anyopaque = undefined,
start_callback: ?*const fn (transfer: *Transfer) anyerror!void = null, start_callback: ?*const fn (transfer: *Transfer) anyerror!void = null,
header_callback: *const fn (transfer: *Transfer) anyerror!void, header_callback: *const fn (transfer: *Transfer) anyerror!bool,
data_callback: *const fn (transfer: *Transfer, data: []const u8) anyerror!void, data_callback: *const fn (transfer: *Transfer, data: []const u8) anyerror!void,
done_callback: *const fn (ctx: *anyopaque) anyerror!void, done_callback: *const fn (ctx: *anyopaque) anyerror!void,
error_callback: *const fn (ctx: *anyopaque, err: anyerror) void, error_callback: *const fn (ctx: *anyopaque, err: anyerror) void,
@@ -1042,7 +1046,7 @@ pub const Transfer = struct {
// headerDoneCallback is called once the headers have been read. // headerDoneCallback is called once the headers have been read.
// It can be called either on dataCallback or once the request for those // It can be called either on dataCallback or once the request for those
// w/o body. // w/o body.
fn headerDoneCallback(transfer: *Transfer, easy: *c.CURL) !void { fn headerDoneCallback(transfer: *Transfer, easy: *c.CURL) !bool {
if (comptime IS_DEBUG) { if (comptime IS_DEBUG) {
std.debug.assert(transfer._header_done_called == false); std.debug.assert(transfer._header_done_called == false);
} }
@@ -1070,7 +1074,7 @@ pub const Transfer = struct {
if (i >= ct.?.amount) break; if (i >= ct.?.amount) break;
} }
transfer.req.header_callback(transfer) catch |err| { const proceed = transfer.req.header_callback(transfer) catch |err| {
log.err(.http, "header_callback", .{ .err = err, .req = transfer }); log.err(.http, "header_callback", .{ .err = err, .req = transfer });
return err; return err;
}; };
@@ -1080,6 +1084,8 @@ pub const Transfer = struct {
.transfer = transfer, .transfer = transfer,
}); });
} }
return proceed;
} }
// headerCallback is called by curl on each request's header line read. // headerCallback is called by curl on each request's header line read.
@@ -1193,7 +1199,7 @@ pub const Transfer = struct {
return buf_len; return buf_len;
} }
fn dataCallback(buffer: [*]const u8, chunk_count: usize, chunk_len: usize, data: *anyopaque) callconv(.c) usize { fn dataCallback(buffer: [*]const u8, chunk_count: usize, chunk_len: usize, data: *anyopaque) callconv(.c) isize {
// libcurl should only ever emit 1 chunk at a time // libcurl should only ever emit 1 chunk at a time
if (comptime IS_DEBUG) { if (comptime IS_DEBUG) {
std.debug.assert(chunk_count == 1); std.debug.assert(chunk_count == 1);
@@ -1206,14 +1212,18 @@ pub const Transfer = struct {
}; };
if (transfer._redirecting or transfer._auth_challenge != null) { if (transfer._redirecting or transfer._auth_challenge != null) {
return chunk_len; return @intCast(chunk_len);
} }
if (!transfer._header_done_called) { if (!transfer._header_done_called) {
transfer.headerDoneCallback(easy) catch |err| { const proceed = transfer.headerDoneCallback(easy) catch |err| {
log.err(.http, "header_done_callback", .{ .err = err, .req = transfer }); log.err(.http, "header_done_callback", .{ .err = err, .req = transfer });
return c.CURL_WRITEFUNC_ERROR; return c.CURL_WRITEFUNC_ERROR;
}; };
if (!proceed) {
// signal abort to libcurl
return -1;
}
} }
transfer.bytes_received += chunk_len; transfer.bytes_received += chunk_len;
@@ -1230,7 +1240,7 @@ pub const Transfer = struct {
}); });
} }
return chunk_len; return @intCast(chunk_len);
} }
pub fn responseHeaderIterator(self: *Transfer) HeaderIterator { pub fn responseHeaderIterator(self: *Transfer) HeaderIterator {
@@ -1288,7 +1298,10 @@ pub const Transfer = struct {
} }
} }
try req.header_callback(transfer); if (try req.header_callback(transfer) == false) {
transfer.abort(error.Abort);
return;
}
if (body) |b| { if (body) |b| {
try req.data_callback(transfer, b); try req.data_callback(transfer, b);