mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 14:33:47 +00:00
Centralizes configuration, eliminates unnecessary copying of config
This commit is contained in:
@@ -21,6 +21,7 @@ const log = @import("../log.zig");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const Http = @import("Http.zig");
|
||||
const Config = @import("../Config.zig");
|
||||
const URL = @import("../browser/URL.zig");
|
||||
const Notification = @import("../Notification.zig");
|
||||
const CookieJar = @import("../browser/webapi/storage/Cookie.zig").Jar;
|
||||
@@ -124,7 +125,7 @@ pub const CDPClient = struct {
|
||||
|
||||
const TransferQueue = std.DoublyLinkedList;
|
||||
|
||||
pub fn init(allocator: Allocator, ca_blob: ?c.curl_blob, opts: Http.Opts) !*Client {
|
||||
pub fn init(allocator: Allocator, ca_blob: ?c.curl_blob, config: *const Config) !*Client {
|
||||
var transfer_pool = std.heap.MemoryPool(Transfer).init(allocator);
|
||||
errdefer transfer_pool.deinit();
|
||||
|
||||
@@ -134,11 +135,19 @@ pub fn init(allocator: Allocator, ca_blob: ?c.curl_blob, opts: Http.Opts) !*Clie
|
||||
const multi = c.curl_multi_init() orelse return error.FailedToInitializeMulti;
|
||||
errdefer _ = c.curl_multi_cleanup(multi);
|
||||
|
||||
try errorMCheck(c.curl_multi_setopt(multi, c.CURLMOPT_MAX_HOST_CONNECTIONS, @as(c_long, opts.max_host_open)));
|
||||
try errorMCheck(c.curl_multi_setopt(multi, c.CURLMOPT_MAX_HOST_CONNECTIONS, @as(c_long, config.httpMaxHostOpen())));
|
||||
|
||||
var handles = try Handles.init(allocator, client, ca_blob, &opts);
|
||||
const user_agent = try config.userAgent(allocator);
|
||||
var proxy_bearer_header: ?[:0]const u8 = null;
|
||||
if (config.proxyBearerToken()) |bt| {
|
||||
proxy_bearer_header = try std.fmt.allocPrintSentinel(allocator, "Proxy-Authorization: Bearer {s}", .{bt}, 0);
|
||||
}
|
||||
|
||||
var handles = try Handles.init(allocator, client, ca_blob, config, user_agent, proxy_bearer_header);
|
||||
errdefer handles.deinit(allocator);
|
||||
|
||||
const http_proxy = config.httpProxy();
|
||||
|
||||
client.* = .{
|
||||
.queue = .{},
|
||||
.active = 0,
|
||||
@@ -146,9 +155,9 @@ pub fn init(allocator: Allocator, ca_blob: ?c.curl_blob, opts: Http.Opts) !*Clie
|
||||
.multi = multi,
|
||||
.handles = handles,
|
||||
.allocator = allocator,
|
||||
.http_proxy = opts.http_proxy,
|
||||
.use_proxy = opts.http_proxy != null,
|
||||
.user_agent = opts.user_agent,
|
||||
.http_proxy = http_proxy,
|
||||
.use_proxy = http_proxy != null,
|
||||
.user_agent = user_agent,
|
||||
.transfer_pool = transfer_pool,
|
||||
};
|
||||
|
||||
@@ -657,16 +666,23 @@ const Handles = struct {
|
||||
|
||||
const HandleList = std.DoublyLinkedList;
|
||||
|
||||
// pointer to opts is not stable, don't hold a reference to it!
|
||||
fn init(allocator: Allocator, client: *Client, ca_blob: ?c.curl_blob, opts: *const Http.Opts) !Handles {
|
||||
const count = if (opts.max_concurrent == 0) 1 else opts.max_concurrent;
|
||||
fn init(
|
||||
allocator: Allocator,
|
||||
client: *Client,
|
||||
ca_blob: ?c.curl_blob,
|
||||
config: *const Config,
|
||||
user_agent: [:0]const u8,
|
||||
proxy_bearer_header: ?[:0]const u8,
|
||||
) !Handles {
|
||||
const count: usize = config.httpMaxConcurrent();
|
||||
if (count == 0) return error.InvalidMaxConcurrent;
|
||||
|
||||
const handles = try allocator.alloc(Handle, count);
|
||||
errdefer allocator.free(handles);
|
||||
|
||||
var available: HandleList = .{};
|
||||
for (0..count) |i| {
|
||||
handles[i] = try Handle.init(client, ca_blob, opts);
|
||||
handles[i] = try Handle.init(client, ca_blob, config, user_agent, proxy_bearer_header);
|
||||
available.append(&handles[i].node);
|
||||
}
|
||||
|
||||
@@ -713,9 +729,14 @@ pub const Handle = struct {
|
||||
conn: Http.Connection,
|
||||
node: Handles.HandleList.Node,
|
||||
|
||||
// pointer to opts is not stable, don't hold a reference to it!
|
||||
fn init(client: *Client, ca_blob: ?c.curl_blob, opts: *const Http.Opts) !Handle {
|
||||
const conn = try Http.Connection.init(ca_blob, opts);
|
||||
fn init(
|
||||
client: *Client,
|
||||
ca_blob: ?c.curl_blob,
|
||||
config: *const Config,
|
||||
user_agent: [:0]const u8,
|
||||
proxy_bearer_header: ?[:0]const u8,
|
||||
) !Handle {
|
||||
const conn = try Http.Connection.init(ca_blob, config, user_agent, proxy_bearer_header);
|
||||
errdefer conn.deinit();
|
||||
|
||||
const easy = conn.easy;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
const std = @import("std");
|
||||
const lp = @import("lightpanda");
|
||||
const Config = @import("../Config.zig");
|
||||
|
||||
pub const c = @cImport({
|
||||
@cInclude("curl/curl.h");
|
||||
@@ -40,12 +41,14 @@ const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
// once for all http connections is a win.
|
||||
const Http = @This();
|
||||
|
||||
opts: Opts,
|
||||
config: *const Config,
|
||||
client: *Client,
|
||||
ca_blob: ?c.curl_blob,
|
||||
arena: ArenaAllocator,
|
||||
user_agent: [:0]const u8,
|
||||
proxy_bearer_header: ?[:0]const u8,
|
||||
|
||||
pub fn init(allocator: Allocator, opts: Opts) !Http {
|
||||
pub fn init(allocator: Allocator, config: *const Config) !Http {
|
||||
try errorCheck(c.curl_global_init(c.CURL_GLOBAL_SSL));
|
||||
errdefer c.curl_global_cleanup();
|
||||
|
||||
@@ -56,24 +59,28 @@ pub fn init(allocator: Allocator, opts: Opts) !Http {
|
||||
var arena = ArenaAllocator.init(allocator);
|
||||
errdefer arena.deinit();
|
||||
|
||||
var adjusted_opts = opts;
|
||||
if (opts.proxy_bearer_token) |bt| {
|
||||
adjusted_opts.proxy_bearer_token = try std.fmt.allocPrintSentinel(arena.allocator(), "Proxy-Authorization: Bearer {s}", .{bt}, 0);
|
||||
const user_agent = try config.userAgent(arena.allocator());
|
||||
|
||||
var proxy_bearer_header: ?[:0]const u8 = null;
|
||||
if (config.proxyBearerToken()) |bt| {
|
||||
proxy_bearer_header = try std.fmt.allocPrintSentinel(arena.allocator(), "Proxy-Authorization: Bearer {s}", .{bt}, 0);
|
||||
}
|
||||
|
||||
var ca_blob: ?c.curl_blob = null;
|
||||
if (opts.tls_verify_host) {
|
||||
if (config.tlsVerifyHost()) {
|
||||
ca_blob = try loadCerts(allocator, arena.allocator());
|
||||
}
|
||||
|
||||
var client = try Client.init(allocator, ca_blob, adjusted_opts);
|
||||
var client = try Client.init(allocator, ca_blob, config);
|
||||
errdefer client.deinit();
|
||||
|
||||
return .{
|
||||
.arena = arena,
|
||||
.client = client,
|
||||
.ca_blob = ca_blob,
|
||||
.opts = adjusted_opts,
|
||||
.config = config,
|
||||
.user_agent = user_agent,
|
||||
.proxy_bearer_header = proxy_bearer_header,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -100,59 +107,60 @@ pub fn removeCDPClient(self: *Http) void {
|
||||
}
|
||||
|
||||
pub fn newConnection(self: *Http) !Connection {
|
||||
return Connection.init(self.ca_blob, &self.opts);
|
||||
return Connection.init(self.ca_blob, self.config, self.user_agent, self.proxy_bearer_header);
|
||||
}
|
||||
|
||||
pub fn newHeaders(self: *const Http) Headers {
|
||||
return Headers.init(self.opts.user_agent);
|
||||
return Headers.init(self.user_agent);
|
||||
}
|
||||
|
||||
pub const Connection = struct {
|
||||
easy: *c.CURL,
|
||||
opts: Connection.Opts,
|
||||
user_agent: [:0]const u8,
|
||||
proxy_bearer_header: ?[:0]const u8,
|
||||
|
||||
const Opts = struct {
|
||||
proxy_bearer_token: ?[:0]const u8,
|
||||
pub fn init(
|
||||
ca_blob_: ?c.curl_blob,
|
||||
config: *const Config,
|
||||
user_agent: [:0]const u8,
|
||||
};
|
||||
|
||||
// pointer to opts is not stable, don't hold a reference to it!
|
||||
pub fn init(ca_blob_: ?c.curl_blob, opts: *const Http.Opts) !Connection {
|
||||
proxy_bearer_header: ?[:0]const u8,
|
||||
) !Connection {
|
||||
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))));
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_CONNECTTIMEOUT_MS, @as(c_long, @intCast(opts.connect_timeout_ms))));
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_TIMEOUT_MS, @as(c_long, @intCast(config.httpTimeout()))));
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_CONNECTTIMEOUT_MS, @as(c_long, @intCast(config.httpConnectTimeout()))));
|
||||
|
||||
// redirect behavior
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_MAXREDIRS, @as(c_long, @intCast(opts.max_redirects))));
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_MAXREDIRS, @as(c_long, @intCast(config.httpMaxRedirects()))));
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_FOLLOWLOCATION, @as(c_long, 2)));
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_REDIR_PROTOCOLS_STR, "HTTP,HTTPS")); // remove FTP and FTPS from the default
|
||||
|
||||
// proxy
|
||||
if (opts.http_proxy) |proxy| {
|
||||
const http_proxy = config.httpProxy();
|
||||
if (http_proxy) |proxy| {
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_PROXY, proxy.ptr));
|
||||
}
|
||||
|
||||
// tls
|
||||
if (ca_blob_) |ca_blob| {
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_CAINFO_BLOB, ca_blob));
|
||||
if (opts.http_proxy != null) {
|
||||
if (http_proxy != null) {
|
||||
// Note, this can be difference for the proxy and for the main
|
||||
// request. Might be something worth exposting as command
|
||||
// line arguments at some point.
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_PROXY_CAINFO_BLOB, ca_blob));
|
||||
}
|
||||
} else {
|
||||
lp.assert(opts.tls_verify_host == false, "Http.init tls_verify_host", .{});
|
||||
lp.assert(config.tlsVerifyHost() == false, "Http.init tls_verify_host", .{});
|
||||
|
||||
// Verify peer checks that the cert is signed by a CA, verify host makes sure the
|
||||
// cert contains the server name.
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_SSL_VERIFYHOST, @as(c_long, 0)));
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_SSL_VERIFYPEER, @as(c_long, 0)));
|
||||
|
||||
if (opts.http_proxy != null) {
|
||||
if (http_proxy != null) {
|
||||
// Note, this can be difference for the proxy and for the main
|
||||
// request. Might be something worth exposting as command
|
||||
// line arguments at some point.
|
||||
@@ -180,10 +188,8 @@ pub const Connection = struct {
|
||||
|
||||
return .{
|
||||
.easy = easy,
|
||||
.opts = .{
|
||||
.user_agent = opts.user_agent,
|
||||
.proxy_bearer_token = opts.proxy_bearer_token,
|
||||
},
|
||||
.user_agent = user_agent,
|
||||
.proxy_bearer_header = proxy_bearer_header,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -237,7 +243,7 @@ pub const Connection = struct {
|
||||
|
||||
// These are headers that may not be send to the users for inteception.
|
||||
pub fn secretHeaders(self: *const Connection, headers: *Headers) !void {
|
||||
if (self.opts.proxy_bearer_token) |hdr| {
|
||||
if (self.proxy_bearer_header) |hdr| {
|
||||
try headers.add(hdr);
|
||||
}
|
||||
}
|
||||
@@ -245,7 +251,7 @@ pub const Connection = struct {
|
||||
pub fn request(self: *const Connection) !u16 {
|
||||
const easy = self.easy;
|
||||
|
||||
var header_list = try Headers.init(self.opts.user_agent);
|
||||
var header_list = try Headers.init(self.user_agent);
|
||||
defer header_list.deinit();
|
||||
try self.secretHeaders(&header_list);
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_HTTPHEADER, header_list.headers));
|
||||
@@ -347,18 +353,6 @@ pub fn errorMCheck(code: c.CURLMcode) errors.Multi!void {
|
||||
return errors.fromMCode(code);
|
||||
}
|
||||
|
||||
pub const Opts = struct {
|
||||
timeout_ms: u31,
|
||||
max_host_open: u8,
|
||||
max_concurrent: u8,
|
||||
connect_timeout_ms: u31,
|
||||
max_redirects: u8 = 10,
|
||||
tls_verify_host: bool = true,
|
||||
http_proxy: ?[:0]const u8 = null,
|
||||
proxy_bearer_token: ?[:0]const u8 = null,
|
||||
user_agent: [:0]const u8,
|
||||
};
|
||||
|
||||
pub const Method = enum(u8) {
|
||||
GET = 0,
|
||||
PUT = 1,
|
||||
|
||||
Reference in New Issue
Block a user