mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
801 lines
26 KiB
Zig
801 lines
26 KiB
Zig
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
|
//
|
|
// Francis Bouvier <francis@lightpanda.io>
|
|
// Pierre Tachoire <pierre@lightpanda.io>
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
|
|
const c = @cImport({
|
|
@cInclude("curl/curl.h");
|
|
});
|
|
|
|
const IS_DEBUG = builtin.mode == .Debug;
|
|
|
|
pub const Curl = c.CURL;
|
|
pub const CurlM = c.CURLM;
|
|
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 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 curl_writefunc_error: usize = c.CURL_WRITEFUNC_ERROR;
|
|
|
|
pub const FreeCallback = fn (ptr: ?*anyopaque) void;
|
|
pub const StrdupCallback = fn (str: [*:0]const u8) ?[*:0]u8;
|
|
pub const MallocCallback = fn (size: usize) ?*anyopaque;
|
|
pub const CallocCallback = fn (nmemb: usize, size: usize) ?*anyopaque;
|
|
pub const ReallocCallback = fn (ptr: ?*anyopaque, size: usize) ?*anyopaque;
|
|
|
|
pub const CurlAllocator = struct {
|
|
free: FreeCallback,
|
|
strdup: StrdupCallback,
|
|
malloc: MallocCallback,
|
|
calloc: CallocCallback,
|
|
realloc: ReallocCallback,
|
|
};
|
|
|
|
pub const CurlGlobalFlags = packed struct(u8) {
|
|
ssl: bool = false,
|
|
_reserved: u7 = 0,
|
|
|
|
pub fn to_c(self: @This()) c_long {
|
|
var flags: c_long = 0;
|
|
if (self.ssl) flags |= c.CURL_GLOBAL_SSL;
|
|
return flags;
|
|
}
|
|
};
|
|
|
|
pub const CurlHeaderOrigin = enum(c_uint) {
|
|
header = c.CURLH_HEADER,
|
|
trailer = c.CURLH_TRAILER,
|
|
connect = c.CURLH_CONNECT,
|
|
@"1xx" = c.CURLH_1XX,
|
|
pseudo = c.CURLH_PSEUDO,
|
|
};
|
|
|
|
pub const CurlWaitEvents = packed struct(c_short) {
|
|
pollin: bool = false,
|
|
pollpri: bool = false,
|
|
pollout: bool = false,
|
|
_reserved: u13 = 0,
|
|
};
|
|
|
|
pub const CurlInfoType = enum(c.curl_infotype) {
|
|
text = c.CURLINFO_TEXT,
|
|
header_in = c.CURLINFO_HEADER_IN,
|
|
header_out = c.CURLINFO_HEADER_OUT,
|
|
data_in = c.CURLINFO_DATA_IN,
|
|
data_out = c.CURLINFO_DATA_OUT,
|
|
ssl_data_in = c.CURLINFO_SSL_DATA_IN,
|
|
ssl_data_out = c.CURLINFO_SSL_DATA_OUT,
|
|
end = c.CURLINFO_END,
|
|
};
|
|
|
|
pub const CurlWaitFd = extern struct {
|
|
fd: CurlSocket,
|
|
events: CurlWaitEvents,
|
|
revents: CurlWaitEvents,
|
|
};
|
|
|
|
comptime {
|
|
const debug_cb_check: c.curl_debug_callback = struct {
|
|
fn cb(handle: ?*Curl, msg_type: c.curl_infotype, raw: [*c]u8, len: usize, user: ?*anyopaque) callconv(.c) c_int {
|
|
_ = handle;
|
|
_ = msg_type;
|
|
_ = raw;
|
|
_ = len;
|
|
_ = user;
|
|
return 0;
|
|
}
|
|
}.cb;
|
|
const write_cb_check: c.curl_write_callback = struct {
|
|
fn cb(buffer: [*c]u8, count: usize, len: usize, user: ?*anyopaque) callconv(.c) usize {
|
|
_ = buffer;
|
|
_ = count;
|
|
_ = len;
|
|
_ = user;
|
|
return 0;
|
|
}
|
|
}.cb;
|
|
_ = debug_cb_check;
|
|
_ = write_cb_check;
|
|
|
|
if (@sizeOf(CurlWaitFd) != @sizeOf(c.curl_waitfd)) {
|
|
@compileError("CurlWaitFd size mismatch");
|
|
}
|
|
if (@offsetOf(CurlWaitFd, "fd") != @offsetOf(c.curl_waitfd, "fd") or
|
|
@offsetOf(CurlWaitFd, "events") != @offsetOf(c.curl_waitfd, "events") or
|
|
@offsetOf(CurlWaitFd, "revents") != @offsetOf(c.curl_waitfd, "revents"))
|
|
{
|
|
@compileError("CurlWaitFd layout mismatch");
|
|
}
|
|
if (c.CURL_WAIT_POLLIN != 1 or c.CURL_WAIT_POLLPRI != 2 or c.CURL_WAIT_POLLOUT != 4) {
|
|
@compileError("CURL_WAIT_* flag values don't match CurlWaitEvents packed struct bit layout");
|
|
}
|
|
}
|
|
|
|
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,
|
|
proxy_ssl_verify_peer = c.CURLOPT_PROXY_SSL_VERIFYPEER,
|
|
accept_encoding = c.CURLOPT_ACCEPT_ENCODING,
|
|
verbose = c.CURLOPT_VERBOSE,
|
|
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,
|
|
http_header = c.CURLOPT_HTTPHEADER,
|
|
cookie = c.CURLOPT_COOKIE,
|
|
private = c.CURLOPT_PRIVATE,
|
|
proxy_user_pwd = c.CURLOPT_PROXYUSERPWD,
|
|
user_pwd = c.CURLOPT_USERPWD,
|
|
header_data = c.CURLOPT_HEADERDATA,
|
|
header_function = c.CURLOPT_HEADERFUNCTION,
|
|
write_data = c.CURLOPT_WRITEDATA,
|
|
write_function = c.CURLOPT_WRITEFUNCTION,
|
|
};
|
|
|
|
pub const CurlMOption = enum(c.CURLMoption) {
|
|
max_host_connections = c.CURLMOPT_MAX_HOST_CONNECTIONS,
|
|
};
|
|
|
|
pub const CurlInfo = enum(c.CURLINFO) {
|
|
effective_url = c.CURLINFO_EFFECTIVE_URL,
|
|
private = c.CURLINFO_PRIVATE,
|
|
redirect_count = c.CURLINFO_REDIRECT_COUNT,
|
|
response_code = c.CURLINFO_RESPONSE_CODE,
|
|
};
|
|
|
|
pub const Error = error{
|
|
UnsupportedProtocol,
|
|
FailedInit,
|
|
UrlMalformat,
|
|
NotBuiltIn,
|
|
CouldntResolveProxy,
|
|
CouldntResolveHost,
|
|
CouldntConnect,
|
|
WeirdServerReply,
|
|
RemoteAccessDenied,
|
|
FtpAcceptFailed,
|
|
FtpWeirdPassReply,
|
|
FtpAcceptTimeout,
|
|
FtpWeirdPasvReply,
|
|
FtpWeird227Format,
|
|
FtpCantGetHost,
|
|
Http2,
|
|
FtpCouldntSetType,
|
|
PartialFile,
|
|
FtpCouldntRetrFile,
|
|
QuoteError,
|
|
HttpReturnedError,
|
|
WriteError,
|
|
UploadFailed,
|
|
ReadError,
|
|
OutOfMemory,
|
|
OperationTimedout,
|
|
FtpPortFailed,
|
|
FtpCouldntUseRest,
|
|
RangeError,
|
|
SslConnectError,
|
|
BadDownloadResume,
|
|
FileCouldntReadFile,
|
|
LdapCannotBind,
|
|
LdapSearchFailed,
|
|
AbortedByCallback,
|
|
BadFunctionArgument,
|
|
InterfaceFailed,
|
|
TooManyRedirects,
|
|
UnknownOption,
|
|
SetoptOptionSyntax,
|
|
GotNothing,
|
|
SslEngineNotfound,
|
|
SslEngineSetfailed,
|
|
SendError,
|
|
RecvError,
|
|
SslCertproblem,
|
|
SslCipher,
|
|
PeerFailedVerification,
|
|
BadContentEncoding,
|
|
FilesizeExceeded,
|
|
UseSslFailed,
|
|
SendFailRewind,
|
|
SslEngineInitfailed,
|
|
LoginDenied,
|
|
TftpNotfound,
|
|
TftpPerm,
|
|
RemoteDiskFull,
|
|
TftpIllegal,
|
|
TftpUnknownid,
|
|
RemoteFileExists,
|
|
TftpNosuchuser,
|
|
SslCacertBadfile,
|
|
RemoteFileNotFound,
|
|
Ssh,
|
|
SslShutdownFailed,
|
|
Again,
|
|
SslCrlBadfile,
|
|
SslIssuerError,
|
|
FtpPretFailed,
|
|
RtspCseqError,
|
|
RtspSessionError,
|
|
FtpBadFileList,
|
|
ChunkFailed,
|
|
NoConnectionAvailable,
|
|
SslPinnedpubkeynotmatch,
|
|
SslInvalidcertstatus,
|
|
Http2Stream,
|
|
RecursiveApiCall,
|
|
AuthError,
|
|
Http3,
|
|
QuicConnectError,
|
|
Proxy,
|
|
SslClientcert,
|
|
UnrecoverablePoll,
|
|
TooLarge,
|
|
Unknown,
|
|
};
|
|
|
|
pub fn errorFromCode(code: c.CURLcode) Error {
|
|
if (comptime IS_DEBUG) {
|
|
std.debug.assert(code != c.CURLE_OK);
|
|
}
|
|
|
|
return switch (code) {
|
|
c.CURLE_UNSUPPORTED_PROTOCOL => Error.UnsupportedProtocol,
|
|
c.CURLE_FAILED_INIT => Error.FailedInit,
|
|
c.CURLE_URL_MALFORMAT => Error.UrlMalformat,
|
|
c.CURLE_NOT_BUILT_IN => Error.NotBuiltIn,
|
|
c.CURLE_COULDNT_RESOLVE_PROXY => Error.CouldntResolveProxy,
|
|
c.CURLE_COULDNT_RESOLVE_HOST => Error.CouldntResolveHost,
|
|
c.CURLE_COULDNT_CONNECT => Error.CouldntConnect,
|
|
c.CURLE_WEIRD_SERVER_REPLY => Error.WeirdServerReply,
|
|
c.CURLE_REMOTE_ACCESS_DENIED => Error.RemoteAccessDenied,
|
|
c.CURLE_FTP_ACCEPT_FAILED => Error.FtpAcceptFailed,
|
|
c.CURLE_FTP_WEIRD_PASS_REPLY => Error.FtpWeirdPassReply,
|
|
c.CURLE_FTP_ACCEPT_TIMEOUT => Error.FtpAcceptTimeout,
|
|
c.CURLE_FTP_WEIRD_PASV_REPLY => Error.FtpWeirdPasvReply,
|
|
c.CURLE_FTP_WEIRD_227_FORMAT => Error.FtpWeird227Format,
|
|
c.CURLE_FTP_CANT_GET_HOST => Error.FtpCantGetHost,
|
|
c.CURLE_HTTP2 => Error.Http2,
|
|
c.CURLE_FTP_COULDNT_SET_TYPE => Error.FtpCouldntSetType,
|
|
c.CURLE_PARTIAL_FILE => Error.PartialFile,
|
|
c.CURLE_FTP_COULDNT_RETR_FILE => Error.FtpCouldntRetrFile,
|
|
c.CURLE_QUOTE_ERROR => Error.QuoteError,
|
|
c.CURLE_HTTP_RETURNED_ERROR => Error.HttpReturnedError,
|
|
c.CURLE_WRITE_ERROR => Error.WriteError,
|
|
c.CURLE_UPLOAD_FAILED => Error.UploadFailed,
|
|
c.CURLE_READ_ERROR => Error.ReadError,
|
|
c.CURLE_OUT_OF_MEMORY => Error.OutOfMemory,
|
|
c.CURLE_OPERATION_TIMEDOUT => Error.OperationTimedout,
|
|
c.CURLE_FTP_PORT_FAILED => Error.FtpPortFailed,
|
|
c.CURLE_FTP_COULDNT_USE_REST => Error.FtpCouldntUseRest,
|
|
c.CURLE_RANGE_ERROR => Error.RangeError,
|
|
c.CURLE_SSL_CONNECT_ERROR => Error.SslConnectError,
|
|
c.CURLE_BAD_DOWNLOAD_RESUME => Error.BadDownloadResume,
|
|
c.CURLE_FILE_COULDNT_READ_FILE => Error.FileCouldntReadFile,
|
|
c.CURLE_LDAP_CANNOT_BIND => Error.LdapCannotBind,
|
|
c.CURLE_LDAP_SEARCH_FAILED => Error.LdapSearchFailed,
|
|
c.CURLE_ABORTED_BY_CALLBACK => Error.AbortedByCallback,
|
|
c.CURLE_BAD_FUNCTION_ARGUMENT => Error.BadFunctionArgument,
|
|
c.CURLE_INTERFACE_FAILED => Error.InterfaceFailed,
|
|
c.CURLE_TOO_MANY_REDIRECTS => Error.TooManyRedirects,
|
|
c.CURLE_UNKNOWN_OPTION => Error.UnknownOption,
|
|
c.CURLE_SETOPT_OPTION_SYNTAX => Error.SetoptOptionSyntax,
|
|
c.CURLE_GOT_NOTHING => Error.GotNothing,
|
|
c.CURLE_SSL_ENGINE_NOTFOUND => Error.SslEngineNotfound,
|
|
c.CURLE_SSL_ENGINE_SETFAILED => Error.SslEngineSetfailed,
|
|
c.CURLE_SEND_ERROR => Error.SendError,
|
|
c.CURLE_RECV_ERROR => Error.RecvError,
|
|
c.CURLE_SSL_CERTPROBLEM => Error.SslCertproblem,
|
|
c.CURLE_SSL_CIPHER => Error.SslCipher,
|
|
c.CURLE_PEER_FAILED_VERIFICATION => Error.PeerFailedVerification,
|
|
c.CURLE_BAD_CONTENT_ENCODING => Error.BadContentEncoding,
|
|
c.CURLE_FILESIZE_EXCEEDED => Error.FilesizeExceeded,
|
|
c.CURLE_USE_SSL_FAILED => Error.UseSslFailed,
|
|
c.CURLE_SEND_FAIL_REWIND => Error.SendFailRewind,
|
|
c.CURLE_SSL_ENGINE_INITFAILED => Error.SslEngineInitfailed,
|
|
c.CURLE_LOGIN_DENIED => Error.LoginDenied,
|
|
c.CURLE_TFTP_NOTFOUND => Error.TftpNotfound,
|
|
c.CURLE_TFTP_PERM => Error.TftpPerm,
|
|
c.CURLE_REMOTE_DISK_FULL => Error.RemoteDiskFull,
|
|
c.CURLE_TFTP_ILLEGAL => Error.TftpIllegal,
|
|
c.CURLE_TFTP_UNKNOWNID => Error.TftpUnknownid,
|
|
c.CURLE_REMOTE_FILE_EXISTS => Error.RemoteFileExists,
|
|
c.CURLE_TFTP_NOSUCHUSER => Error.TftpNosuchuser,
|
|
c.CURLE_SSL_CACERT_BADFILE => Error.SslCacertBadfile,
|
|
c.CURLE_REMOTE_FILE_NOT_FOUND => Error.RemoteFileNotFound,
|
|
c.CURLE_SSH => Error.Ssh,
|
|
c.CURLE_SSL_SHUTDOWN_FAILED => Error.SslShutdownFailed,
|
|
c.CURLE_AGAIN => Error.Again,
|
|
c.CURLE_SSL_CRL_BADFILE => Error.SslCrlBadfile,
|
|
c.CURLE_SSL_ISSUER_ERROR => Error.SslIssuerError,
|
|
c.CURLE_FTP_PRET_FAILED => Error.FtpPretFailed,
|
|
c.CURLE_RTSP_CSEQ_ERROR => Error.RtspCseqError,
|
|
c.CURLE_RTSP_SESSION_ERROR => Error.RtspSessionError,
|
|
c.CURLE_FTP_BAD_FILE_LIST => Error.FtpBadFileList,
|
|
c.CURLE_CHUNK_FAILED => Error.ChunkFailed,
|
|
c.CURLE_NO_CONNECTION_AVAILABLE => Error.NoConnectionAvailable,
|
|
c.CURLE_SSL_PINNEDPUBKEYNOTMATCH => Error.SslPinnedpubkeynotmatch,
|
|
c.CURLE_SSL_INVALIDCERTSTATUS => Error.SslInvalidcertstatus,
|
|
c.CURLE_HTTP2_STREAM => Error.Http2Stream,
|
|
c.CURLE_RECURSIVE_API_CALL => Error.RecursiveApiCall,
|
|
c.CURLE_AUTH_ERROR => Error.AuthError,
|
|
c.CURLE_HTTP3 => Error.Http3,
|
|
c.CURLE_QUIC_CONNECT_ERROR => Error.QuicConnectError,
|
|
c.CURLE_PROXY => Error.Proxy,
|
|
c.CURLE_SSL_CLIENTCERT => Error.SslClientcert,
|
|
c.CURLE_UNRECOVERABLE_POLL => Error.UnrecoverablePoll,
|
|
c.CURLE_TOO_LARGE => Error.TooLarge,
|
|
else => Error.Unknown,
|
|
};
|
|
}
|
|
|
|
pub const ErrorMulti = error{
|
|
BadHandle,
|
|
BadEasyHandle,
|
|
OutOfMemory,
|
|
InternalError,
|
|
BadSocket,
|
|
UnknownOption,
|
|
AddedAlready,
|
|
RecursiveApiCall,
|
|
WakeupFailure,
|
|
BadFunctionArgument,
|
|
AbortedByCallback,
|
|
UnrecoverablePoll,
|
|
Unknown,
|
|
};
|
|
|
|
pub const ErrorHeader = error{
|
|
OutOfMemory,
|
|
BadArgument,
|
|
NotBuiltIn,
|
|
Unknown,
|
|
};
|
|
|
|
pub fn errorMFromCode(code: c.CURLMcode) ErrorMulti {
|
|
if (comptime IS_DEBUG) {
|
|
std.debug.assert(code != c.CURLM_OK);
|
|
}
|
|
|
|
return switch (code) {
|
|
c.CURLM_BAD_HANDLE => ErrorMulti.BadHandle,
|
|
c.CURLM_BAD_EASY_HANDLE => ErrorMulti.BadEasyHandle,
|
|
c.CURLM_OUT_OF_MEMORY => ErrorMulti.OutOfMemory,
|
|
c.CURLM_INTERNAL_ERROR => ErrorMulti.InternalError,
|
|
c.CURLM_BAD_SOCKET => ErrorMulti.BadSocket,
|
|
c.CURLM_UNKNOWN_OPTION => ErrorMulti.UnknownOption,
|
|
c.CURLM_ADDED_ALREADY => ErrorMulti.AddedAlready,
|
|
c.CURLM_RECURSIVE_API_CALL => ErrorMulti.RecursiveApiCall,
|
|
c.CURLM_WAKEUP_FAILURE => ErrorMulti.WakeupFailure,
|
|
c.CURLM_BAD_FUNCTION_ARGUMENT => ErrorMulti.BadFunctionArgument,
|
|
c.CURLM_ABORTED_BY_CALLBACK => ErrorMulti.AbortedByCallback,
|
|
c.CURLM_UNRECOVERABLE_POLL => ErrorMulti.UnrecoverablePoll,
|
|
else => ErrorMulti.Unknown,
|
|
};
|
|
}
|
|
|
|
pub fn errorHFromCode(code: c.CURLHcode) ErrorHeader {
|
|
if (comptime IS_DEBUG) {
|
|
std.debug.assert(code != c.CURLHE_OK);
|
|
}
|
|
|
|
return switch (code) {
|
|
c.CURLHE_OUT_OF_MEMORY => ErrorHeader.OutOfMemory,
|
|
c.CURLHE_BAD_ARGUMENT => ErrorHeader.BadArgument,
|
|
c.CURLHE_NOT_BUILT_IN => ErrorHeader.NotBuiltIn,
|
|
else => ErrorHeader.Unknown,
|
|
};
|
|
}
|
|
|
|
pub fn errorCheck(code: c.CURLcode) Error!void {
|
|
if (code == c.CURLE_OK) {
|
|
return;
|
|
}
|
|
return errorFromCode(code);
|
|
}
|
|
|
|
pub fn errorMCheck(code: c.CURLMcode) ErrorMulti!void {
|
|
if (code == c.CURLM_OK) {
|
|
return;
|
|
}
|
|
if (code == c.CURLM_CALL_MULTI_PERFORM) {
|
|
return;
|
|
}
|
|
return errorMFromCode(code);
|
|
}
|
|
|
|
pub fn errorHCheck(code: c.CURLHcode) ErrorHeader!void {
|
|
if (code == c.CURLHE_OK) {
|
|
return;
|
|
}
|
|
return errorHFromCode(code);
|
|
}
|
|
|
|
pub const CurlMsgType = enum(c.CURLMSG) {
|
|
none = c.CURLMSG_NONE,
|
|
done = c.CURLMSG_DONE,
|
|
last = c.CURLMSG_LAST,
|
|
};
|
|
|
|
pub const CurlMsgData = union(CurlMsgType) {
|
|
none: ?*anyopaque,
|
|
done: ?Error,
|
|
last: ?*anyopaque,
|
|
};
|
|
|
|
pub const CurlMsg = struct {
|
|
easy_handle: *Curl,
|
|
data: CurlMsgData,
|
|
};
|
|
|
|
pub fn curl_global_init(flags: CurlGlobalFlags, comptime curl_allocator: ?CurlAllocator) Error!void {
|
|
const alloc = curl_allocator orelse {
|
|
return errorCheck(c.curl_global_init(flags.to_c()));
|
|
};
|
|
|
|
// The purpose of these wrappers is to hide callconv
|
|
// and provide an easy place to add logging when debugging.
|
|
const free = struct {
|
|
fn cb(ptr: ?*anyopaque) callconv(.c) void {
|
|
alloc.free(ptr);
|
|
}
|
|
}.cb;
|
|
const strdup = struct {
|
|
fn cb(str: [*c]const u8) callconv(.c) [*c]u8 {
|
|
const s: [*:0]const u8 = @ptrCast(str orelse return null);
|
|
return @ptrCast(alloc.strdup(s));
|
|
}
|
|
}.cb;
|
|
const malloc = struct {
|
|
fn cb(size: usize) callconv(.c) ?*anyopaque {
|
|
return alloc.malloc(size);
|
|
}
|
|
}.cb;
|
|
const calloc = struct {
|
|
fn cb(nmemb: usize, size: usize) callconv(.c) ?*anyopaque {
|
|
return alloc.calloc(nmemb, size);
|
|
}
|
|
}.cb;
|
|
const realloc = struct {
|
|
fn cb(ptr: ?*anyopaque, size: usize) callconv(.c) ?*anyopaque {
|
|
return alloc.realloc(ptr, size);
|
|
}
|
|
}.cb;
|
|
|
|
try errorCheck(c.curl_global_init_mem(flags.to_c(), malloc, free, realloc, strdup, calloc));
|
|
}
|
|
|
|
pub fn curl_global_cleanup() void {
|
|
c.curl_global_cleanup();
|
|
}
|
|
|
|
pub fn curl_version() [*c]const u8 {
|
|
return c.curl_version();
|
|
}
|
|
|
|
pub fn curl_easy_init() ?*Curl {
|
|
return c.curl_easy_init();
|
|
}
|
|
|
|
pub fn curl_easy_cleanup(easy: *Curl) void {
|
|
c.curl_easy_cleanup(easy);
|
|
}
|
|
|
|
pub fn curl_easy_perform(easy: *Curl) Error!void {
|
|
try errorCheck(c.curl_easy_perform(easy));
|
|
}
|
|
|
|
pub fn curl_easy_setopt(easy: *Curl, comptime option: CurlOption, value: anytype) Error!void {
|
|
const opt: c.CURLoption = @intFromEnum(option);
|
|
const code = switch (option) {
|
|
.verbose,
|
|
.post,
|
|
.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);
|
|
},
|
|
|
|
.timeout_ms,
|
|
.connect_timeout_ms,
|
|
.max_redirs,
|
|
.follow_location,
|
|
.post_field_size,
|
|
=> 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);
|
|
},
|
|
|
|
.url,
|
|
.redir_protocols_str,
|
|
.proxy,
|
|
.accept_encoding,
|
|
.custom_request,
|
|
.cookie,
|
|
.user_pwd,
|
|
.proxy_user_pwd,
|
|
.copy_post_fields,
|
|
=> blk: {
|
|
const s: ?[*]const u8 = value;
|
|
break :blk c.curl_easy_setopt(easy, opt, s);
|
|
},
|
|
|
|
.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);
|
|
},
|
|
|
|
.private,
|
|
.header_data,
|
|
.write_data,
|
|
=> blk: {
|
|
const ptr: ?*anyopaque = switch (@typeInfo(@TypeOf(value))) {
|
|
.null => null,
|
|
else => @ptrCast(value),
|
|
};
|
|
break :blk c.curl_easy_setopt(easy, opt, ptr);
|
|
},
|
|
|
|
.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);
|
|
},
|
|
|
|
.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);
|
|
},
|
|
|
|
.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);
|
|
},
|
|
};
|
|
try errorCheck(code);
|
|
}
|
|
|
|
pub fn curl_easy_getinfo(easy: *Curl, comptime info: CurlInfo, out: anytype) Error!void {
|
|
if (@typeInfo(@TypeOf(out)) != .pointer) {
|
|
@compileError("curl_easy_getinfo out must be a pointer, got " ++ @typeName(@TypeOf(out)));
|
|
}
|
|
|
|
const inf: c.CURLINFO = @intFromEnum(info);
|
|
const code = switch (info) {
|
|
.effective_url => blk: {
|
|
const p: *[*c]u8 = out;
|
|
break :blk c.curl_easy_getinfo(easy, inf, p);
|
|
},
|
|
.response_code,
|
|
.redirect_count,
|
|
=> blk: {
|
|
const p: *c_long = out;
|
|
break :blk c.curl_easy_getinfo(easy, inf, p);
|
|
},
|
|
.private => blk: {
|
|
const p: **anyopaque = out;
|
|
break :blk c.curl_easy_getinfo(easy, inf, p);
|
|
},
|
|
};
|
|
try errorCheck(code);
|
|
}
|
|
|
|
pub fn curl_easy_header(
|
|
easy: *Curl,
|
|
name: [*:0]const u8,
|
|
index: usize,
|
|
comptime origin: CurlHeaderOrigin,
|
|
request: c_int,
|
|
hout: *?*CurlHeader,
|
|
) ErrorHeader!void {
|
|
var c_hout: [*c]CurlHeader = null;
|
|
const code = c.curl_easy_header(easy, name, index, @intFromEnum(origin), request, &c_hout);
|
|
switch (code) {
|
|
c.CURLHE_OK => {
|
|
hout.* = @ptrCast(c_hout);
|
|
return;
|
|
},
|
|
c.CURLHE_BADINDEX,
|
|
c.CURLHE_MISSING,
|
|
c.CURLHE_NOHEADERS,
|
|
c.CURLHE_NOREQUEST,
|
|
=> {
|
|
hout.* = null;
|
|
return;
|
|
},
|
|
else => {
|
|
hout.* = null;
|
|
return errorHFromCode(code);
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn curl_easy_nextheader(
|
|
easy: *Curl,
|
|
comptime origin: CurlHeaderOrigin,
|
|
request: c_int,
|
|
prev: ?*CurlHeader,
|
|
) ?*CurlHeader {
|
|
const ptr = c.curl_easy_nextheader(easy, @intFromEnum(origin), request, prev);
|
|
if (ptr == null) return null;
|
|
return @ptrCast(ptr);
|
|
}
|
|
|
|
pub fn curl_multi_init() ?*CurlM {
|
|
return c.curl_multi_init();
|
|
}
|
|
|
|
pub fn curl_multi_cleanup(multi: *CurlM) ErrorMulti!void {
|
|
try errorMCheck(c.curl_multi_cleanup(multi));
|
|
}
|
|
|
|
pub fn curl_multi_setopt(multi: *CurlM, comptime option: CurlMOption, value: anytype) ErrorMulti!void {
|
|
const opt: c.CURLMoption = @intFromEnum(option);
|
|
const code = switch (option) {
|
|
.max_host_connections => 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_multi_setopt(multi, opt, n);
|
|
},
|
|
};
|
|
try errorMCheck(code);
|
|
}
|
|
|
|
pub fn curl_multi_add_handle(multi: *CurlM, easy: *Curl) ErrorMulti!void {
|
|
try errorMCheck(c.curl_multi_add_handle(multi, easy));
|
|
}
|
|
|
|
pub fn curl_multi_remove_handle(multi: *CurlM, easy: *Curl) ErrorMulti!void {
|
|
try errorMCheck(c.curl_multi_remove_handle(multi, easy));
|
|
}
|
|
|
|
pub fn curl_multi_perform(multi: *CurlM, running_handles: *c_int) ErrorMulti!void {
|
|
try errorMCheck(c.curl_multi_perform(multi, running_handles));
|
|
}
|
|
|
|
pub fn curl_multi_poll(
|
|
multi: *CurlM,
|
|
extra_fds: []CurlWaitFd,
|
|
timeout_ms: c_int,
|
|
numfds: ?*c_int,
|
|
) ErrorMulti!void {
|
|
const raw_fds: [*c]c.curl_waitfd = if (extra_fds.len == 0) null else @ptrCast(extra_fds.ptr);
|
|
try errorMCheck(c.curl_multi_poll(multi, raw_fds, @intCast(extra_fds.len), timeout_ms, numfds));
|
|
}
|
|
|
|
pub fn curl_multi_waitfds(multi: *CurlM, ufds: []CurlWaitFd, fd_count: *c_uint) ErrorMulti!void {
|
|
const raw_fds: [*c]c.curl_waitfd = if (ufds.len == 0) null else @ptrCast(ufds.ptr);
|
|
try errorMCheck(c.curl_multi_waitfds(multi, raw_fds, @intCast(ufds.len), fd_count));
|
|
}
|
|
|
|
pub fn curl_multi_timeout(multi: *CurlM, timeout_ms: *c_long) ErrorMulti!void {
|
|
try errorMCheck(c.curl_multi_timeout(multi, timeout_ms));
|
|
}
|
|
|
|
pub fn curl_multi_info_read(multi: *CurlM, msgs_in_queue: *c_int) ?CurlMsg {
|
|
const ptr = c.curl_multi_info_read(multi, msgs_in_queue);
|
|
if (ptr == null) return null;
|
|
|
|
const msg: *const c.CURLMsg = @ptrCast(ptr);
|
|
const easy_handle = msg.easy_handle orelse unreachable;
|
|
|
|
return switch (msg.msg) {
|
|
c.CURLMSG_NONE => .{
|
|
.easy_handle = easy_handle,
|
|
.data = .{ .none = msg.data.whatever },
|
|
},
|
|
c.CURLMSG_DONE => .{
|
|
.easy_handle = easy_handle,
|
|
.data = .{ .done = if (errorCheck(msg.data.result)) |_| null else |err| err },
|
|
},
|
|
c.CURLMSG_LAST => .{
|
|
.easy_handle = easy_handle,
|
|
.data = .{ .last = msg.data.whatever },
|
|
},
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn curl_slist_append(list: ?*CurlSList, header: [*:0]const u8) ?*CurlSList {
|
|
return c.curl_slist_append(list, header);
|
|
}
|
|
|
|
pub fn curl_slist_free_all(list: ?*CurlSList) void {
|
|
if (list) |ptr| {
|
|
c.curl_slist_free_all(ptr);
|
|
}
|
|
}
|