Protect against transfer.abort() being called during callback

This was already handled in most cases, but not for a body-less response. It's
safe to call transfer.abort() during a callback, so long as the performing flag
is set to true. This was set during the normal libcurl callbacks, but for a
body-less response, we manually invoke the header_done_callback and were not
setting the performing flag.
This commit is contained in:
Karl Seguin
2026-03-02 11:44:42 +08:00
parent 516335e0ed
commit 82e3f126ff
3 changed files with 59 additions and 19 deletions

View File

@@ -789,25 +789,30 @@ fn processMessages(self: *Client) !bool {
if (msg.err) |err| {
requestFailed(transfer, err, true);
} else blk: {
// In case of request w/o data, we need to call the header done
// callback now.
if (!transfer._header_done_called) {
const proceed = transfer.headerDoneCallback(&msg.conn) catch |err| {
log.err(.http, "header_done_callback", .{ .err = err });
{
self.handles.performing = true;
defer self.handles.performing = false;
// In case of request w/o data, we need to call the header done
// callback now.
if (!transfer._header_done_called) {
const proceed = transfer.headerDoneCallback(&msg.conn) catch |err| {
log.err(.http, "header_done_callback", .{ .err = err });
requestFailed(transfer, err, true);
continue;
};
if (!proceed) {
requestFailed(transfer, error.Abort, true);
break :blk;
}
}
transfer.req.done_callback(transfer.ctx) catch |err| {
// transfer isn't valid at this point, don't use it.
log.err(.http, "done_callback", .{ .err = err });
requestFailed(transfer, err, true);
continue;
};
if (!proceed) {
requestFailed(transfer, error.Abort, true);
break :blk;
}
}
transfer.req.done_callback(transfer.ctx) catch |err| {
// transfer isn't valid at this point, don't use it.
log.err(.http, "done_callback", .{ .err = err });
requestFailed(transfer, err, true);
continue;
};
transfer.req.notification.dispatch(.http_request_done, &.{
.transfer = transfer,
@@ -1041,10 +1046,6 @@ pub const Transfer = struct {
pub fn abort(self: *Transfer, err: anyerror) void {
requestFailed(self, err, true);
if (self._conn == null) {
self.deinit();
return;
}
const client = self.client;
if (client.handles.performing) {