mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-07-31 09:46:05 -04:00
Merge pull request #2867 from lightpanda-io/nikneym/curl-easy-setopt-refactor
`libcurl`: refactor `curl_easy_setopt`
This commit is contained in:
@@ -2098,7 +2098,7 @@ pub const Transfer = struct {
|
||||
return result;
|
||||
}
|
||||
|
||||
fn dataCallback(buffer: [*]const u8, chunk_count: usize, chunk_len: usize, data: *anyopaque) usize {
|
||||
fn dataCallback(buffer: [*]const u8, chunk_count: usize, chunk_len: usize, data: *anyopaque) callconv(.c) usize {
|
||||
// libcurl should only ever emit 1 chunk at a time
|
||||
if (comptime IS_DEBUG) {
|
||||
std.debug.assert(chunk_count == 1);
|
||||
|
||||
@@ -545,7 +545,7 @@ fn dispatchCloseEvent(self: *WebSocket, code: u16, reason: []const u8, was_clean
|
||||
}
|
||||
}
|
||||
|
||||
fn sendDataCallback(buffer: [*]u8, buf_count: usize, buf_len: usize, data: *anyopaque) usize {
|
||||
fn sendDataCallback(buffer: [*]u8, buf_count: usize, buf_len: usize, data: *anyopaque) callconv(.c) usize {
|
||||
if (comptime IS_DEBUG) {
|
||||
std.debug.assert(buf_count == 1);
|
||||
}
|
||||
@@ -624,7 +624,7 @@ fn writeContent(self: *WebSocket, conn: *http.Connection, buf: []u8, byte_msg: M
|
||||
return to_copy;
|
||||
}
|
||||
|
||||
fn receivedDataCallback(buffer: [*]const u8, buf_count: usize, buf_len: usize, data: *anyopaque) usize {
|
||||
fn receivedDataCallback(buffer: [*]const u8, buf_count: usize, buf_len: usize, data: *anyopaque) callconv(.c) usize {
|
||||
if (comptime IS_DEBUG) {
|
||||
std.debug.assert(buf_count == 1);
|
||||
}
|
||||
@@ -695,7 +695,7 @@ fn _receivedDataCallback(conn: *http.Connection, data: []const u8) !void {
|
||||
|
||||
// libcurl has no mechanism to signal that the connection is established. The
|
||||
// best option I could come up with was looking for an upgrade header response.
|
||||
fn receivedHeaderCallback(buffer: [*]const u8, header_count: usize, buf_len: usize, data: *anyopaque) usize {
|
||||
fn receivedHeaderCallback(buffer: [*]const u8, header_count: usize, buf_len: usize, data: *anyopaque) callconv(.c) usize {
|
||||
if (comptime IS_DEBUG) {
|
||||
std.debug.assert(header_count == 1);
|
||||
}
|
||||
|
||||
@@ -328,11 +328,13 @@ pub const ResponseHead = struct {
|
||||
/// Returns CURL_SOCKET_BAD to block; otherwise creates and returns a real socket fd.
|
||||
/// clientp is a *const IpFilter passed via CURLOPT_OPENSOCKETDATA.
|
||||
fn opensocketCallback(
|
||||
purpose: libcurl.CurlSockType,
|
||||
address: *libcurl.CurlSockAddr,
|
||||
clientp: ?*anyopaque,
|
||||
) libcurl.CurlSocket {
|
||||
_: c_uint,
|
||||
addr: [*c]libcurl.CurlSockAddr,
|
||||
) callconv(.c) libcurl.CurlSocket {
|
||||
const address: *libcurl.CurlSockAddr = @ptrCast(addr);
|
||||
const filter: *const IpFilter = @ptrCast(@alignCast(clientp orelse return libcurl.CURL_SOCKET_BAD));
|
||||
|
||||
if (filter.isBlockedSockaddr(address)) {
|
||||
if (address.family == posix.AF.INET or address.family == posix.AF.INET6) {
|
||||
const ip = std.net.Address.initPosix(@ptrCast(&address.addr));
|
||||
@@ -342,7 +344,7 @@ fn opensocketCallback(
|
||||
}
|
||||
return libcurl.CURL_SOCKET_BAD;
|
||||
}
|
||||
_ = purpose; // purpose is informational; we always open the same socket type
|
||||
|
||||
const fd = posix.socket(
|
||||
@intCast(address.family),
|
||||
@intCast(address.socktype),
|
||||
@@ -576,7 +578,7 @@ pub const Connection = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn discardBody(_: [*]const u8, count: usize, len: usize, _: ?*anyopaque) usize {
|
||||
fn discardBody(_: [*]const u8, count: usize, len: usize, _: ?*anyopaque) callconv(.c) usize {
|
||||
return count * len;
|
||||
}
|
||||
|
||||
@@ -760,7 +762,7 @@ pub const Handles = struct {
|
||||
}
|
||||
};
|
||||
|
||||
fn debugCallback(_: *libcurl.Curl, msg_type: libcurl.CurlInfoType, raw: [*c]u8, len: usize, _: *anyopaque) c_int {
|
||||
fn debugCallback(_: *libcurl.Curl, msg_type: libcurl.CurlInfoType, raw: [*c]u8, len: usize, _: ?*anyopaque) callconv(.c) c_int {
|
||||
const data = raw[0..len];
|
||||
switch (msg_type) {
|
||||
.text => std.debug.print("libcurl [text]: {s}\n", .{data}),
|
||||
@@ -864,7 +866,7 @@ test "opensocketCallback: private IPv4 returns CURL_SOCKET_BAD" {
|
||||
|
||||
const filter = IpFilter.init(true, null);
|
||||
var sa = makeSockAddrV4(.{ 127, 0, 0, 1 });
|
||||
const result = opensocketCallback(.ipcxn, &sa, @ptrCast(@constCast(&filter)));
|
||||
const result = opensocketCallback(@ptrCast(@constCast(&filter)), @intFromEnum(libcurl.CurlSockType.ipcxn), &sa);
|
||||
try testing.expectEqual(libcurl.CURL_SOCKET_BAD, result);
|
||||
}
|
||||
|
||||
@@ -873,7 +875,7 @@ test "opensocketCallback: public IPv4 opens a real socket" {
|
||||
const filter = IpFilter.init(true, null);
|
||||
var sa = makeSockAddrV4(.{ 8, 8, 8, 8 });
|
||||
|
||||
const fd = opensocketCallback(.ipcxn, &sa, @ptrCast(@constCast(&filter)));
|
||||
const fd = opensocketCallback(@ptrCast(@constCast(&filter)), @intFromEnum(libcurl.CurlSockType.ipcxn), &sa);
|
||||
defer posix.close(fd);
|
||||
|
||||
// A real fd is always >= 0
|
||||
@@ -882,7 +884,7 @@ test "opensocketCallback: public IPv4 opens a real socket" {
|
||||
|
||||
test "opensocketCallback: null clientp returns CURL_SOCKET_BAD (fail-closed)" {
|
||||
var sa = makeSockAddrV4(.{ 8, 8, 8, 8 });
|
||||
const result = opensocketCallback(.ipcxn, &sa, null);
|
||||
const result = opensocketCallback(null, @intFromEnum(libcurl.CurlSockType.ipcxn), &sa);
|
||||
try testing.expectEqual(libcurl.CURL_SOCKET_BAD, result);
|
||||
}
|
||||
|
||||
@@ -890,7 +892,7 @@ test "opensocketCallback: block_private=false allows private IP" {
|
||||
// When block_private is false the filter blocks nothing
|
||||
const filter = IpFilter.init(false, null);
|
||||
var sa = makeSockAddrV4(.{ 127, 0, 0, 1 });
|
||||
const fd = opensocketCallback(.ipcxn, &sa, @ptrCast(@constCast(&filter)));
|
||||
const fd = opensocketCallback(@ptrCast(@constCast(&filter)), @intFromEnum(libcurl.CurlSockType.ipcxn), &sa);
|
||||
defer posix.close(fd);
|
||||
|
||||
try testing.expect(fd >= 0);
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const crypto = @import("libcrypto.zig");
|
||||
|
||||
const c = @cImport({
|
||||
@cInclude("curl/curl.h");
|
||||
});
|
||||
@@ -31,9 +33,7 @@ pub const CurlCode = c.CURLcode;
|
||||
pub const CurlMCode = c.CURLMcode;
|
||||
pub const CurlSList = c.curl_slist;
|
||||
pub const CurlHeader = c.curl_header;
|
||||
pub const CurlHttpPost = c.curl_httppost;
|
||||
pub const CurlSocket = c.curl_socket_t;
|
||||
pub const CurlBlob = c.curl_blob;
|
||||
pub const CurlOffT = c.curl_off_t;
|
||||
|
||||
pub const CURLE = struct {
|
||||
@@ -41,12 +41,14 @@ pub const CURLE = struct {
|
||||
pub const ABORTED_BY_CALLBACK = c.CURLE_ABORTED_BY_CALLBACK;
|
||||
};
|
||||
|
||||
pub const CurlDebugFunction = fn (*Curl, CurlInfoType, [*c]u8, usize, *anyopaque) c_int;
|
||||
pub const CurlHeaderFunction = fn ([*]const u8, usize, usize, *anyopaque) usize;
|
||||
pub const CurlWriteFunction = fn ([*]const u8, usize, usize, *anyopaque) usize;
|
||||
pub const CurlHeaderFunction = *const fn ([*]const u8, usize, usize, *anyopaque) callconv(.c) usize;
|
||||
pub const CurlWriteFunction = *const fn ([*]const u8, usize, usize, *anyopaque) callconv(.c) usize;
|
||||
pub const curl_writefunc_error: usize = c.CURL_WRITEFUNC_ERROR;
|
||||
pub const curl_readfunc_pause: usize = c.CURL_READFUNC_PAUSE;
|
||||
pub const CurlReadFunction = fn ([*]u8, usize, usize, *anyopaque) usize;
|
||||
pub const CurlReadFunction = *const fn ([*]u8, usize, usize, *anyopaque) callconv(.c) usize;
|
||||
pub const CurlOpenSocketFunction = *const fn (?*anyopaque, c_uint, [*c]CurlSockAddr) callconv(.c) CurlSocket;
|
||||
pub const CurlSslCtxFunction = *const fn (*Curl, *anyopaque, *anyopaque) callconv(.c) CurlCode;
|
||||
pub const CurlDebugFunction = *const fn (*Curl, CurlInfoType, [*c]u8, usize, ?*anyopaque) callconv(.c) c_int;
|
||||
|
||||
pub const CurlSockType = enum(c.curlsocktype) {
|
||||
ipcxn = c.CURLSOCKTYPE_IPCXN,
|
||||
@@ -200,12 +202,8 @@ pub const CurlOption = enum(c.CURLoption) {
|
||||
url = c.CURLOPT_URL,
|
||||
timeout_ms = c.CURLOPT_TIMEOUT_MS,
|
||||
connect_timeout_ms = c.CURLOPT_CONNECTTIMEOUT_MS,
|
||||
max_redirs = c.CURLOPT_MAXREDIRS,
|
||||
follow_location = c.CURLOPT_FOLLOWLOCATION,
|
||||
redir_protocols_str = c.CURLOPT_REDIR_PROTOCOLS_STR,
|
||||
proxy = c.CURLOPT_PROXY,
|
||||
ca_info_blob = c.CURLOPT_CAINFO_BLOB,
|
||||
proxy_ca_info_blob = c.CURLOPT_PROXY_CAINFO_BLOB,
|
||||
ssl_verify_host = c.CURLOPT_SSL_VERIFYHOST,
|
||||
ssl_verify_peer = c.CURLOPT_SSL_VERIFYPEER,
|
||||
proxy_ssl_verify_host = c.CURLOPT_PROXY_SSL_VERIFYHOST,
|
||||
@@ -215,7 +213,6 @@ pub const CurlOption = enum(c.CURLoption) {
|
||||
debug_function = c.CURLOPT_DEBUGFUNCTION,
|
||||
custom_request = c.CURLOPT_CUSTOMREQUEST,
|
||||
post = c.CURLOPT_POST,
|
||||
http_post = c.CURLOPT_HTTPPOST,
|
||||
post_field_size = c.CURLOPT_POSTFIELDSIZE,
|
||||
copy_post_fields = c.CURLOPT_COPYPOSTFIELDS,
|
||||
http_get = c.CURLOPT_HTTPGET,
|
||||
@@ -595,43 +592,29 @@ pub fn curl_easy_perform(easy: *Curl) Error!void {
|
||||
}
|
||||
|
||||
pub fn curl_easy_setopt(easy: *Curl, comptime option: CurlOption, value: anytype) Error!void {
|
||||
const opt: c.CURLoption = @intFromEnum(option);
|
||||
const code = switch (option) {
|
||||
const v = switch (comptime option) {
|
||||
.verbose,
|
||||
.post,
|
||||
.upload,
|
||||
.http_get,
|
||||
.ssl_verify_host,
|
||||
.ssl_verify_peer,
|
||||
.proxy_ssl_verify_host,
|
||||
.proxy_ssl_verify_peer,
|
||||
=> blk: {
|
||||
const n: c_long = switch (@typeInfo(@TypeOf(value))) {
|
||||
.bool => switch (option) {
|
||||
.ssl_verify_host, .proxy_ssl_verify_host => if (value) 2 else 0,
|
||||
else => if (value) 1 else 0,
|
||||
},
|
||||
else => @compileError("expected bool|integer for " ++ @tagName(option) ++ ", got " ++ @typeName(@TypeOf(value))),
|
||||
};
|
||||
break :blk c.curl_easy_setopt(easy, opt, n);
|
||||
},
|
||||
=> @as(c_long, @intFromBool(value)),
|
||||
|
||||
// VERIFYHOST expects 0 or 2 (since 7.66.0, 1 is treated the same as 2).
|
||||
// https://curl.se/libcurl/c/CURLOPT_SSL_VERIFYHOST.html
|
||||
.ssl_verify_host,
|
||||
.proxy_ssl_verify_host,
|
||||
=> @as(c_long, if (value) 2 else 0),
|
||||
|
||||
.timeout_ms,
|
||||
.connect_timeout_ms,
|
||||
.max_redirs,
|
||||
.follow_location,
|
||||
.post_field_size,
|
||||
.connect_only,
|
||||
=> blk: {
|
||||
const n: c_long = switch (@typeInfo(@TypeOf(value))) {
|
||||
.comptime_int, .int => @intCast(value),
|
||||
else => @compileError("expected integer for " ++ @tagName(option) ++ ", got " ++ @typeName(@TypeOf(value))),
|
||||
};
|
||||
break :blk c.curl_easy_setopt(easy, opt, n);
|
||||
},
|
||||
=> @as(c_long, @intCast(value)),
|
||||
|
||||
.url,
|
||||
.redir_protocols_str,
|
||||
.proxy,
|
||||
.accept_encoding,
|
||||
.custom_request,
|
||||
@@ -639,128 +622,28 @@ pub fn curl_easy_setopt(easy: *Curl, comptime option: CurlOption, value: anytype
|
||||
.user_pwd,
|
||||
.proxy_user_pwd,
|
||||
.copy_post_fields,
|
||||
=> blk: {
|
||||
const s: ?[*]const u8 = value;
|
||||
break :blk c.curl_easy_setopt(easy, opt, s);
|
||||
},
|
||||
=> @as(?[*]const u8, value),
|
||||
|
||||
.ca_info_blob,
|
||||
.proxy_ca_info_blob,
|
||||
=> blk: {
|
||||
const blob: CurlBlob = value;
|
||||
break :blk c.curl_easy_setopt(easy, opt, blob);
|
||||
},
|
||||
|
||||
.http_post => blk: {
|
||||
// CURLOPT_HTTPPOST expects ?*curl_httppost (multipart formdata)
|
||||
const ptr: ?*CurlHttpPost = value;
|
||||
break :blk c.curl_easy_setopt(easy, opt, ptr);
|
||||
},
|
||||
|
||||
.http_header => blk: {
|
||||
const list: ?*CurlSList = value;
|
||||
break :blk c.curl_easy_setopt(easy, opt, list);
|
||||
},
|
||||
.http_header => @as(?*CurlSList, value),
|
||||
|
||||
.private,
|
||||
.header_data,
|
||||
.read_data,
|
||||
.write_data,
|
||||
.opensocket_data,
|
||||
=> blk: {
|
||||
const ptr: ?*anyopaque = switch (@typeInfo(@TypeOf(value))) {
|
||||
.null => null,
|
||||
else => @ptrCast(value),
|
||||
};
|
||||
break :blk c.curl_easy_setopt(easy, opt, ptr);
|
||||
},
|
||||
=> @as(?*anyopaque, @ptrCast(value)),
|
||||
|
||||
.debug_function => blk: {
|
||||
const cb: c.curl_debug_callback = switch (@typeInfo(@TypeOf(value))) {
|
||||
.null => null,
|
||||
.@"fn" => struct {
|
||||
fn cb(handle: ?*Curl, msg_type: c.curl_infotype, raw: [*c]u8, len: usize, user: ?*anyopaque) callconv(.c) c_int {
|
||||
const h = handle orelse unreachable;
|
||||
const u = user orelse unreachable;
|
||||
return value(h, @enumFromInt(@intFromEnum(msg_type)), raw, len, u);
|
||||
}
|
||||
}.cb,
|
||||
else => @compileError("expected Zig function or null for " ++ @tagName(option) ++ ", got " ++ @typeName(@TypeOf(value))),
|
||||
};
|
||||
break :blk c.curl_easy_setopt(easy, opt, cb);
|
||||
},
|
||||
.ssl_ctx_data => @as(*crypto.X509_STORE, value),
|
||||
|
||||
.opensocket_function => blk: {
|
||||
const cb: c.curl_opensocket_callback = switch (@typeInfo(@TypeOf(value))) {
|
||||
.null => null,
|
||||
.@"fn" => struct {
|
||||
fn cb(clientp: ?*anyopaque, purpose: c.curlsocktype, address: [*c]c.curl_sockaddr) callconv(.c) c.curl_socket_t {
|
||||
const addr: *CurlSockAddr = @ptrCast(address orelse return CURL_SOCKET_BAD);
|
||||
return value(@enumFromInt(purpose), addr, clientp);
|
||||
}
|
||||
}.cb,
|
||||
else => @compileError("expected Zig function or null for " ++ @tagName(option) ++ ", got " ++ @typeName(@TypeOf(value))),
|
||||
};
|
||||
break :blk c.curl_easy_setopt(easy, opt, cb);
|
||||
},
|
||||
|
||||
.header_function => blk: {
|
||||
const cb: c.curl_write_callback = switch (@typeInfo(@TypeOf(value))) {
|
||||
.null => null,
|
||||
.@"fn" => struct {
|
||||
fn cb(buffer: [*c]u8, count: usize, len: usize, user: ?*anyopaque) callconv(.c) usize {
|
||||
const u = user orelse unreachable;
|
||||
return value(@ptrCast(buffer), count, len, u);
|
||||
}
|
||||
}.cb,
|
||||
else => @compileError("expected Zig function or null for " ++ @tagName(option) ++ ", got " ++ @typeName(@TypeOf(value))),
|
||||
};
|
||||
break :blk c.curl_easy_setopt(easy, opt, cb);
|
||||
},
|
||||
|
||||
.read_function => blk: {
|
||||
const cb: c.curl_write_callback = switch (@typeInfo(@TypeOf(value))) {
|
||||
.null => null,
|
||||
.@"fn" => |info| struct {
|
||||
fn cb(buffer: [*c]u8, count: usize, len: usize, user: ?*anyopaque) callconv(.c) usize {
|
||||
const user_arg = if (@typeInfo(info.params[3].type.?) == .optional)
|
||||
user
|
||||
else
|
||||
user orelse unreachable;
|
||||
return value(@ptrCast(buffer), count, len, user_arg);
|
||||
}
|
||||
}.cb,
|
||||
else => @compileError("expected Zig function or null for " ++ @tagName(option) ++ ", got " ++ @typeName(@TypeOf(value))),
|
||||
};
|
||||
break :blk c.curl_easy_setopt(easy, opt, cb);
|
||||
},
|
||||
.write_function => blk: {
|
||||
const cb: c.curl_write_callback = switch (@typeInfo(@TypeOf(value))) {
|
||||
.null => null,
|
||||
.@"fn" => |info| struct {
|
||||
fn cb(buffer: [*c]u8, count: usize, len: usize, user: ?*anyopaque) callconv(.c) usize {
|
||||
const user_arg = if (@typeInfo(info.params[3].type.?) == .optional)
|
||||
user
|
||||
else
|
||||
user orelse unreachable;
|
||||
return value(@ptrCast(buffer), count, len, user_arg);
|
||||
}
|
||||
}.cb,
|
||||
else => @compileError("expected Zig function or null for " ++ @tagName(option) ++ ", got " ++ @typeName(@TypeOf(value))),
|
||||
};
|
||||
break :blk c.curl_easy_setopt(easy, opt, cb);
|
||||
},
|
||||
.ssl_ctx_function => blk: {
|
||||
const cb: c.curl_ssl_ctx_callback = @ptrCast(value);
|
||||
break :blk c.curl_easy_setopt(easy, opt, cb);
|
||||
},
|
||||
.ssl_ctx_data => blk: {
|
||||
// We can make sure that passed data is always X509_STORE since we
|
||||
// don't require anything else throughout project.
|
||||
break :blk c.curl_easy_setopt(easy, opt, value);
|
||||
},
|
||||
.debug_function => @as(CurlDebugFunction, value),
|
||||
.opensocket_function => @as(CurlOpenSocketFunction, value),
|
||||
.header_function => @as(CurlHeaderFunction, value),
|
||||
.read_function => @as(CurlReadFunction, value),
|
||||
.write_function => @as(CurlWriteFunction, value),
|
||||
.ssl_ctx_function => @as(CurlSslCtxFunction, value),
|
||||
};
|
||||
try errorCheck(code);
|
||||
const code = c.curl_easy_setopt(easy, @intFromEnum(option), v);
|
||||
return errorCheck(code);
|
||||
}
|
||||
|
||||
pub fn curl_easy_getinfo(easy: *Curl, comptime info: CurlInfo, out: anytype) Error!void {
|
||||
|
||||
Reference in New Issue
Block a user