diff --git a/src/browser/ScriptManager.zig b/src/browser/ScriptManager.zig index facd86a6..269d1de7 100644 --- a/src/browser/ScriptManager.zig +++ b/src/browser/ScriptManager.zig @@ -480,7 +480,11 @@ const PendingScript = struct { // will fail. This assertion exists to catch incorrect assumptions about // how libcurl works, or about how we've configured it. std.debug.assert(self.script.source.remote.capacity == 0); - self.script.source = .{ .remote = self.manager.buffer_pool.get() }; + var buffer = self.manager.buffer_pool.get(); + if (transfer.getContentLength()) |cl| { + try buffer.ensureTotalCapacity(self.manager.allocator, cl); + } + self.script.source = .{ .remote = buffer }; } fn dataCallback(self: *PendingScript, transfer: *Http.Transfer, data: []const u8) !void { diff --git a/src/browser/xhr/xhr.zig b/src/browser/xhr/xhr.zig index 76ca98e9..09891c20 100644 --- a/src/browser/xhr/xhr.zig +++ b/src/browser/xhr/xhr.zig @@ -438,6 +438,10 @@ pub const XMLHttpRequest = struct { self.state = .loading; self.dispatchEvt("readystatechange"); + + if (transfer.getContentLength()) |cl| { + try self.response_bytes.ensureTotalCapacity(self.arena, cl); + } } fn httpDataCallback(transfer: *Http.Transfer, data: []const u8) !void { diff --git a/src/http/Client.zig b/src/http/Client.zig index 02e45207..20ce82ee 100644 --- a/src/http/Client.zig +++ b/src/http/Client.zig @@ -1040,6 +1040,35 @@ pub const Transfer = struct { try req.done_callback(req.ctx); } + + // This function should be called during the dataCallback. Calling it after + // such as in the doneCallback is guaranteed to return null. + pub fn getContentLength(self: *const Transfer) ?u32 { + const cl = self.getContentLengthRawValue() orelse return null; + return std.fmt.parseInt(u32, cl, 10) catch null; + } + + fn getContentLengthRawValue(self: *const Transfer) ?[]const u8 { + if (self._handle) |handle| { + // If we have a handle, than this is a normal request. We can get the + // header value from the easy handle. + const cl = getResponseHeader(handle.conn.easy, "content-length", 0) orelse return null; + return cl.value; + } + + // If we have no handle, then maybe this is being called after the + // doneCallback. OR, maybe this is a "fulfilled" request. Let's check + // the injected headers (if we have any). + + const rh = self.response_header orelse return null; + for (rh._injected_headers) |hdr| { + if (std.ascii.eqlIgnoreCase(hdr.name, "content-length")) { + return hdr.value; + } + } + + return null; + } }; pub const ResponseHeader = struct {