diff --git a/src/browser/tests/net/xhr.html b/src/browser/tests/net/xhr.html
index b7132f6e..7dc89a23 100644
--- a/src/browser/tests/net/xhr.html
+++ b/src/browser/tests/net/xhr.html
@@ -222,3 +222,33 @@
testing.expectEqual(XMLHttpRequest.UNSENT, req.readyState);
});
+
+
diff --git a/src/http/Client.zig b/src/http/Client.zig
index 3cce2ddb..cda2adeb 100644
--- a/src/http/Client.zig
+++ b/src/http/Client.zig
@@ -111,6 +111,10 @@ config: *const Config,
cdp_client: ?CDPClient = null,
+// keep track of when curl_multi_perform is happening so that we can avoid
+// recursive calls into curl (which it will fail)
+performing: bool = false,
+
// libcurl can monitor arbitrary sockets, this lets us use libcurl to poll
// both HTTP data as well as messages from an CDP connection.
// Furthermore, we have some tension between blocking scripts and request
@@ -735,7 +739,12 @@ pub const PerformStatus = enum {
fn perform(self: *Client, timeout_ms: c_int) !PerformStatus {
const multi = self.multi;
var running: c_int = undefined;
- try errorMCheck(c.curl_multi_perform(multi, &running));
+
+ {
+ self.performing = true;
+ defer self.performing = false;
+ try errorMCheck(c.curl_multi_perform(multi, &running));
+ }
// We're potentially going to block for a while until we get data. Process
// whatever messages we have waiting ahead of time.
@@ -1092,6 +1101,8 @@ pub const Transfer = struct {
// the headers, and the [encoded] body.
bytes_received: usize = 0,
+ aborted: bool = false,
+
max_response_size: ?usize = null,
// We'll store the response header here
@@ -1224,10 +1235,27 @@ pub const Transfer = struct {
pub fn abort(self: *Transfer, err: anyerror) void {
requestFailed(self, err, true);
+ if (self._handle == null) {
+ self.deinit();
+ return;
+ }
+
+ const client = self.client;
+ if (client.performing) {
+ // We're currently in a curl_multi_perform. We cannot call endTransfer
+ // as that calls curl_multi_remove_handle, and you can't do that
+ // from a curl callback. Instead, we flag this transfer and all of
+ // our callbacks will check for this flag and abort the transfer for
+ // us
+ self.aborted = true;
+ return;
+ }
+
if (self._handle != null) {
- self.client.endTransfer(self);
+ client.endTransfer(self);
}
self.deinit();
+
}
pub fn terminate(self: *Transfer) void {
@@ -1359,7 +1387,7 @@ pub const Transfer = struct {
.transfer = transfer,
});
- return proceed;
+ return proceed and transfer.aborted == false;
}
// headerCallback is called by curl on each request's header line read.
@@ -1519,6 +1547,10 @@ pub const Transfer = struct {
.transfer = transfer,
});
+ if (transfer.aborted) {
+ return -1;
+ }
+
return @intCast(chunk_len);
}