mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-16 08:18:59 +00:00
ScriptManager & HttpClient support for JS modules
Improve cleanup/shutdown (*cough* memory leaks *cough*)
This commit is contained in:
@@ -52,6 +52,8 @@ transfer_pool: std.heap.MemoryPool(Transfer),
|
||||
queue_node_pool: std.heap.MemoryPool(RequestQueue.Node),
|
||||
//@newhttp
|
||||
http_proxy: ?std.Uri = null,
|
||||
blocking: Handle,
|
||||
blocking_active: if (builtin.mode == .Debug) bool else void = if (builtin.mode == .Debug) false else {},
|
||||
|
||||
const RequestQueue = std.DoublyLinkedList(Request);
|
||||
|
||||
@@ -69,13 +71,17 @@ pub fn init(allocator: Allocator, ca_blob: c.curl_blob, opts: Http.Opts) !*Clien
|
||||
errdefer _ = c.curl_multi_cleanup(multi);
|
||||
|
||||
var handles = try Handles.init(allocator, client, ca_blob, opts);
|
||||
errdefer handles.deinit(allocator, multi);
|
||||
errdefer handles.deinit(allocator);
|
||||
|
||||
var blocking = try Handle.init(client, ca_blob, opts);
|
||||
errdefer blocking.deinit();
|
||||
|
||||
client.* = .{
|
||||
.queue = .{},
|
||||
.active = 0,
|
||||
.multi = multi,
|
||||
.handles = handles,
|
||||
.blocking = blocking,
|
||||
.allocator = allocator,
|
||||
.transfer_pool = transfer_pool,
|
||||
.queue_node_pool = queue_node_pool,
|
||||
@@ -85,7 +91,10 @@ pub fn init(allocator: Allocator, ca_blob: c.curl_blob, opts: Http.Opts) !*Clien
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Client) void {
|
||||
self.handles.deinit(self.allocator, self.multi);
|
||||
self.abort();
|
||||
self.blocking.deinit();
|
||||
self.handles.deinit(self.allocator);
|
||||
|
||||
_ = c.curl_multi_cleanup(self.multi);
|
||||
|
||||
self.transfer_pool.deinit();
|
||||
@@ -94,7 +103,15 @@ pub fn deinit(self: *Client) void {
|
||||
}
|
||||
|
||||
pub fn abort(self: *Client) void {
|
||||
self.handles.abort(self.multi);
|
||||
while (self.handles.in_use.first) |node| {
|
||||
var transfer = Transfer.fromEasy(node.data.easy) catch |err| {
|
||||
log.err(.http, "get private info", .{ .err = err, .source = "abort" });
|
||||
continue;
|
||||
};
|
||||
transfer.req.error_callback(transfer.ctx, error.Abort);
|
||||
self.endTransfer(transfer);
|
||||
}
|
||||
std.debug.assert(self.active == 0);
|
||||
|
||||
var n = self.queue.first;
|
||||
while (n) |node| {
|
||||
@@ -102,7 +119,6 @@ pub fn abort(self: *Client) void {
|
||||
self.queue_node_pool.destroy(node);
|
||||
}
|
||||
self.queue = .{};
|
||||
self.active = 0;
|
||||
|
||||
// Maybe a bit of overkill
|
||||
// We can remove some (all?) of these once we're confident its right.
|
||||
@@ -116,17 +132,18 @@ pub fn abort(self: *Client) void {
|
||||
}
|
||||
|
||||
pub fn tick(self: *Client, timeout_ms: usize) !void {
|
||||
var handles = &self.handles.available;
|
||||
var handles = &self.handles;
|
||||
while (true) {
|
||||
if (handles.first == null) {
|
||||
if (handles.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
const queue_node = self.queue.popFirst() orelse break;
|
||||
const req = queue_node.data;
|
||||
self.queue_node_pool.destroy(queue_node);
|
||||
|
||||
defer self.queue_node_pool.destroy(queue_node);
|
||||
|
||||
const handle = handles.popFirst().?.data;
|
||||
try self.makeRequest(handle, queue_node.data);
|
||||
// we know this exists, because we checked isEmpty() above
|
||||
const handle = handles.getFreeHandle().?;
|
||||
try self.makeRequest(handle, req);
|
||||
}
|
||||
|
||||
try self.perform(@intCast(timeout_ms));
|
||||
@@ -142,6 +159,19 @@ pub fn request(self: *Client, req: Request) !void {
|
||||
self.queue.append(node);
|
||||
}
|
||||
|
||||
// See ScriptManager.blockingGet
|
||||
pub fn blockingRequest(self: *Client, req: Request) !void {
|
||||
if (comptime builtin.mode == .Debug) {
|
||||
std.debug.assert(self.blocking_active == false);
|
||||
self.blocking_active = true;
|
||||
}
|
||||
defer if (comptime builtin.mode == .Debug) {
|
||||
self.blocking_active = false;
|
||||
};
|
||||
|
||||
return self.makeRequest(&self.blocking, req);
|
||||
}
|
||||
|
||||
fn makeRequest(self: *Client, handle: *Handle, req: Request) !void {
|
||||
const easy = handle.easy;
|
||||
|
||||
@@ -215,8 +245,9 @@ fn perform(self: *Client, timeout_ms: c_int) !void {
|
||||
const ctx = transfer.ctx;
|
||||
const done_callback = transfer.req.done_callback;
|
||||
const error_callback = transfer.req.error_callback;
|
||||
// release it ASAP so that it's avaiable (since some done_callbacks
|
||||
// will load more resources).
|
||||
|
||||
// release it ASAP so that it's available; some done_callbacks
|
||||
// will load more resources.
|
||||
self.endTransfer(transfer);
|
||||
|
||||
if (errorCheck(msg.data.result)) {
|
||||
@@ -266,18 +297,9 @@ const Handles = struct {
|
||||
|
||||
var available: HandleList = .{};
|
||||
for (0..count) |i| {
|
||||
const easy = c.curl_easy_init() orelse return error.FailedToInitializeEasy;
|
||||
errdefer _ = c.curl_easy_cleanup(easy);
|
||||
|
||||
handles[i] = .{
|
||||
.easy = easy,
|
||||
.client = client,
|
||||
.node = undefined,
|
||||
};
|
||||
try handles[i].configure(ca_blob, opts);
|
||||
|
||||
handles[i].node.data = &handles[i];
|
||||
available.append(&handles[i].node);
|
||||
handles[i] = try Handle.init(client, ca_blob, opts);
|
||||
handles[i].node = .{ .data = &handles[i] };
|
||||
available.append(&handles[i].node.?);
|
||||
}
|
||||
|
||||
return .{
|
||||
@@ -287,22 +309,15 @@ const Handles = struct {
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(self: *Handles, allocator: Allocator, multi: *c.CURLM) void {
|
||||
self.abort(multi);
|
||||
fn deinit(self: *Handles, allocator: Allocator) void {
|
||||
for (self.handles) |*h| {
|
||||
_ = c.curl_easy_cleanup(h.easy);
|
||||
h.deinit();
|
||||
}
|
||||
allocator.free(self.handles);
|
||||
}
|
||||
|
||||
fn abort(self: *Handles, multi: *c.CURLM) void {
|
||||
while (self.in_use.first) |node| {
|
||||
const handle = node.data;
|
||||
errorMCheck(c.curl_multi_remove_handle(multi, handle.easy)) catch |err| {
|
||||
log.err(.http, "remove handle", .{ .err = err });
|
||||
};
|
||||
self.release(handle);
|
||||
}
|
||||
fn isEmpty(self: *const Handles) bool {
|
||||
return self.available.first == null;
|
||||
}
|
||||
|
||||
fn getFreeHandle(self: *Handles) ?*Handle {
|
||||
@@ -316,7 +331,10 @@ const Handles = struct {
|
||||
}
|
||||
|
||||
fn release(self: *Handles, handle: *Handle) void {
|
||||
const node = &handle.node;
|
||||
// client.blocking is a handle without a node, it doesn't exist in the
|
||||
// eitehr the in_use or available lists.
|
||||
const node = &(handle.node orelse return);
|
||||
|
||||
self.in_use.remove(node);
|
||||
node.prev = null;
|
||||
node.next = null;
|
||||
@@ -324,19 +342,15 @@ const Handles = struct {
|
||||
}
|
||||
};
|
||||
|
||||
// wraps a c.CURL (an easy handle), mostly to make it easier to keep a
|
||||
// handle_pool and error_buffer associated with each easy handle
|
||||
// wraps a c.CURL (an easy handle)
|
||||
const Handle = struct {
|
||||
easy: *c.CURL,
|
||||
client: *Client,
|
||||
node: Handles.HandleList.Node,
|
||||
error_buffer: [c.CURL_ERROR_SIZE:0]u8 = undefined,
|
||||
node: ?Handles.HandleList.Node,
|
||||
|
||||
// Is called by Handles when already partially initialized. Done like this
|
||||
// so that we have a stable pointer to error_buffer.
|
||||
fn configure(self: *Handle, ca_blob: c.curl_blob, opts: Http.Opts) !void {
|
||||
const easy = self.easy;
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_ERRORBUFFER, &self.error_buffer));
|
||||
fn init(client: *Client, ca_blob: c.curl_blob, opts: Http.Opts) !Handle {
|
||||
const easy = c.curl_easy_init() orelse return error.FailedToInitializeEasy;
|
||||
errdefer _ = c.curl_easy_cleanup(easy);
|
||||
|
||||
// timeouts
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_TIMEOUT_MS, @as(c_long, @intCast(opts.timeout_ms))));
|
||||
@@ -348,9 +362,9 @@ const Handle = struct {
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_REDIR_PROTOCOLS_STR, "HTTP,HTTPS")); // remove FTP and FTPS from the default
|
||||
|
||||
// callbacks
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_HEADERDATA, self));
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_HEADERDATA, easy));
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_HEADERFUNCTION, Transfer.headerCallback));
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_WRITEDATA, self));
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_WRITEDATA, easy));
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_WRITEFUNCTION, Transfer.bodyCallback));
|
||||
|
||||
// tls
|
||||
@@ -367,6 +381,16 @@ const Handle = struct {
|
||||
if (comptime Http.ENABLE_DEBUG) {
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_VERBOSE, @as(c_long, 1)));
|
||||
}
|
||||
|
||||
return .{
|
||||
.easy = easy,
|
||||
.node = null,
|
||||
.client = client,
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(self: *const Handle) void {
|
||||
_ = c.curl_easy_cleanup(self.easy);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -430,9 +454,9 @@ pub const Transfer = struct {
|
||||
// libcurl should only ever emit 1 header at a time
|
||||
std.debug.assert(header_count == 1);
|
||||
|
||||
const handle: *Handle = @alignCast(@ptrCast(data));
|
||||
var transfer = fromEasy(handle.easy) catch |err| {
|
||||
log.err(.http, "get private info", .{ .err = err });
|
||||
const easy: *c.CURL = @alignCast(@ptrCast(data));
|
||||
var transfer = fromEasy(easy) catch |err| {
|
||||
log.err(.http, "get private info", .{ .err = err, .source = "header callback" });
|
||||
return 0;
|
||||
};
|
||||
|
||||
@@ -467,7 +491,7 @@ pub const Transfer = struct {
|
||||
transfer._redirecting = false;
|
||||
|
||||
var url: [*c]u8 = undefined;
|
||||
errorCheck(c.curl_easy_getinfo(handle.easy, c.CURLINFO_EFFECTIVE_URL, &url)) catch |err| {
|
||||
errorCheck(c.curl_easy_getinfo(easy, c.CURLINFO_EFFECTIVE_URL, &url)) catch |err| {
|
||||
log.err(.http, "failed to get URL", .{ .err = err });
|
||||
return 0;
|
||||
};
|
||||
@@ -514,9 +538,9 @@ pub const Transfer = struct {
|
||||
// libcurl should only ever emit 1 chunk at a time
|
||||
std.debug.assert(chunk_count == 1);
|
||||
|
||||
const handle: *Handle = @alignCast(@ptrCast(data));
|
||||
var transfer = fromEasy(handle.easy) catch |err| {
|
||||
log.err(.http, "get private info", .{ .err = err });
|
||||
const easy: *c.CURL = @alignCast(@ptrCast(data));
|
||||
var transfer = fromEasy(easy) catch |err| {
|
||||
log.err(.http, "get private info", .{ .err = err, .source = "body callback" });
|
||||
return c.CURL_WRITEFUNC_ERROR;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user