From 634868deaaea022e60b61fdd0f0200b1e7222829 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 30 Mar 2016 00:34:21 -0700 Subject: [PATCH 01/71] Add ctest verification, fill out sys bindings --- curl-sys/Cargo.toml | 34 +- curl-sys/lib.rs | 1372 +++++++++++++++++++++++++++++-------------- systest/Cargo.toml | 12 + systest/build.rs | 41 ++ systest/src/main.rs | 9 + 5 files changed, 1001 insertions(+), 467 deletions(-) create mode 100644 systest/Cargo.toml create mode 100644 systest/build.rs create mode 100644 systest/src/main.rs diff --git a/curl-sys/Cargo.toml b/curl-sys/Cargo.toml index 24bc4fa742..f08f9f03ce 100644 --- a/curl-sys/Cargo.toml +++ b/curl-sys/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "curl-sys" version = "0.1.34" -authors = ["Carl Lerche ", + "Alex Crichton "] links = "curl" build = "build.rs" license = "MIT" @@ -23,38 +24,7 @@ libc = "0.2" [target."cfg(all(unix, not(target_os = \"macos\")))".dependencies] openssl-sys = ">= 0" -# Unix platforms use OpenSSL for now to provide SSL functionality [target.i686-unknown-linux-gnu.dependencies] openssl-sys = ">= 0" -[target.i586-unknown-linux-gnu.dependencies] -openssl-sys = ">= 0" -[target.i686-linux-android.dependencies] -openssl-sys = ">= 0" [target.x86_64-unknown-linux-gnu.dependencies] openssl-sys = ">= 0" -[target.x86_64-unknown-linux-musl.dependencies] -openssl-sys = ">= 0" -[target.arm-unknown-linux-gnueabihf.dependencies] -openssl-sys = ">= 0" -[target.armv7-unknown-linux-gnueabihf.dependencies] -openssl-sys = "0.7.0" -[target.arm-linux-androideabi.dependencies] -openssl-sys = ">= 0" -[target.aarch64-unknown-linux-gnu.dependencies] -openssl-sys = ">= 0" -[target.powerpc-unknown-linux-gnu.dependencies] -openssl-sys = ">= 0" -[target.powerpc64-unknown-linux-gnu.dependencies] -openssl-sys = ">= 0" -[target.powerpc64le-unknown-linux-gnu.dependencies] -openssl-sys = ">= 0" -[target.i686-unknown-freebsd.dependencies] -openssl-sys = ">= 0" -[target.x86_64-unknown-freebsd.dependencies] -openssl-sys = ">= 0" -[target.x86_64-unknown-bitrig.dependencies] -openssl-sys = ">= 0" -[target.x86_64-unknown-openbsd.dependencies] -openssl-sys = ">= 0" -[target.x86_64-unknown-dragonfly.dependencies] -openssl-sys = ">= 0" diff --git a/curl-sys/lib.rs b/curl-sys/lib.rs index 7990bd66ea..4d70728c64 100644 --- a/curl-sys/lib.rs +++ b/curl-sys/lib.rs @@ -6,480 +6,982 @@ extern crate libz_sys; #[cfg(all(unix, not(target_os = "macos")))] extern crate openssl_sys; -use libc::{c_void, c_int, c_char, c_uint, c_long}; +use libc::{c_int, c_char, c_uint, c_long, c_double, c_void, size_t, time_t}; +use libc::{c_ulong, c_short}; -pub type CURLINFO = c_int; -pub type CURL = c_void; -pub type curl_slist = c_void; -pub type CURLoption = c_int; +#[cfg(target_env = "msvc")] +#[doc(hidden)] +pub type __enum_ty = libc::c_int; +#[cfg(not(target_env = "msvc"))] +#[doc(hidden)] +pub type __enum_ty = libc::c_uint; + +pub type CURLINFO = __enum_ty; +pub type CURLoption = __enum_ty; +pub type CURLcode = __enum_ty; +pub type CURLversion = __enum_ty; +pub type curl_off_t = c_long; + +pub enum CURL {} + +#[cfg(unix)] +pub type curl_socket_t = libc::c_int; +pub const CURL_SOCKET_BAD: curl_socket_t = -1; +#[cfg(all(windows, target_pointer_width = "32"))] +pub type curl_socket_t = libc::c_uint; +#[cfg(all(windows, target_pointer_width = "64"))] +pub type curl_socket_t = u64; +#[cfg(windows)] +pub const CURL_SOCKET_BAD: curl_socket_t = !0; + +#[repr(C)] +pub struct curl_httppost { + pub next: *mut curl_httppost, + pub name: *mut c_char, + pub namelength: c_long, + pub contents: *mut c_char, + pub contentslength: c_long, + pub buffer: *mut c_char, + pub bufferlength: c_long, + pub contenttype: *mut c_char, + pub contentheader: *mut curl_slist, + pub more: *mut curl_httppost, + pub flags: c_long, + pub showfilename: *mut c_char, + pub userp: *mut c_void, +} + +pub const HTTPPOST_FILENAME: c_long = 1 << 0; +pub const HTTPPOST_READFILE: c_long = 1 << 1; +pub const HTTPPOST_PTRNAME: c_long = 1 << 2; +pub const HTTPPOST_PTRCONTENTS: c_long = 1 << 3; +pub const HTTPPOST_BUFFER: c_long = 1 << 4; +pub const HTTPPOST_PTRBUFFER: c_long = 1 << 5; +pub const HTTPPOST_CALLBACK: c_long = 1 << 6; + +pub type curl_progress_callback = extern fn(*mut c_void, + c_double, + c_double, + c_double, + c_double) -> c_int; +pub type curl_xferinfo_callback = extern fn(*mut c_void, + curl_off_t, + curl_off_t, + curl_off_t, + curl_off_t) -> c_int; + +pub const CURL_WRITEFUNC_PAUSE: size_t = 0x10000001; + +pub type curl_write_callback = extern fn(*mut c_char, + size_t, + size_t, + *mut c_void) -> size_t; + +pub type curlfiletype = __enum_ty; +pub const CURLFILETYPE_FILE: curlfiletype = 0; +pub const CURLFILETYPE_DIRECTORY: curlfiletype = 1; +pub const CURLFILETYPE_SYMLINK: curlfiletype = 2; +pub const CURLFILETYPE_DEVICE_BLOCK: curlfiletype = 3; +pub const CURLFILETYPE_DEVICE_CHAR: curlfiletype = 4; +pub const CURLFILETYPE_NAMEDPIPE: curlfiletype = 5; +pub const CURLFILETYPE_SOCKET: curlfiletype = 6; +pub const CURLFILETYPE_DOOR: curlfiletype = 7; +pub const CURLFILETYPE_UNKNOWN: curlfiletype = 8; + +pub const CURLFINFOFLAG_KNOWN_FILENAME: c_uint = 1 << 0; +pub const CURLFINFOFLAG_KNOWN_FILETYPE: c_uint = 1 << 1; +pub const CURLFINFOFLAG_KNOWN_TIME: c_uint = 1 << 2; +pub const CURLFINFOFLAG_KNOWN_PERM: c_uint = 1 << 3; +pub const CURLFINFOFLAG_KNOWN_UID: c_uint = 1 << 4; +pub const CURLFINFOFLAG_KNOWN_GID: c_uint = 1 << 5; +pub const CURLFINFOFLAG_KNOWN_SIZE: c_uint = 1 << 6; +pub const CURLFINFOFLAG_KNOWN_HLINKCOUNT: c_uint = 1 << 7; + +#[repr(C)] +pub struct curl_fileinfo { + pub filename: *mut c_char, + pub filetype: curlfiletype, + pub time: time_t, + pub perm: c_uint, + pub uid: c_int, + pub gid: c_int, + pub size: curl_off_t, + pub hardlinks: c_long, + + pub strings_time: *mut c_char, + pub strings_perm: *mut c_char, + pub strings_user: *mut c_char, + pub strings_group: *mut c_char, + pub strings_target: *mut c_char, + + pub flags: c_uint, + pub b_data: *mut c_char, + pub b_size: size_t, + pub b_used: size_t, +} + +pub const CURL_CHUNK_BGN_FUNC_OK: c_long = 0; +pub const CURL_CHUNK_BGN_FUNC_FAIL: c_long = 1; +pub const CURL_CHUNK_BGN_FUNC_SKIP: c_long = 2; +pub type curl_chunk_bgn_callback = extern fn(*const c_void, + *mut c_void, + c_int) -> c_long; + +pub const CURL_CHUNK_END_FUNC_OK: c_long = 0; +pub const CURL_CHUNK_END_FUNC_FAIL: c_long = 1; +pub type curl_chunk_end_callback = extern fn(*mut c_void) -> c_long; + +pub const CURL_FNMATCHFUNC_MATCH: c_int = 0; +pub const CURL_FNMATCHFUNC_NOMATCH: c_int = 1; +pub const CURL_FNMATCHFUNC_FAIL: c_int = 2; +pub type curl_fnmatch_callback = extern fn(*mut c_void, + *const c_char, + *const c_char) -> c_int; + +pub const CURL_SEEKFUNC_OK: c_int = 0; +pub const CURL_SEEKFUNC_FAIL: c_int = 1; +pub const CURL_SEEKFUNC_CANTSEEK: c_int = 2; +pub type curl_seek_callback = extern fn(*mut c_void, + curl_off_t, + c_int) -> c_int; + +pub const CURL_READFUNC_ABORT: size_t = 0x10000000; +pub const CURL_READFUNC_PAUSE: size_t = 0x10000001; +pub type curl_read_callback = extern fn(*mut c_char, + size_t, + size_t, + *mut c_void) -> size_t; + +pub type curlsocktype = __enum_ty; +pub const CURLSOCKTYPE_IPCXN: curlsocktype = 0; +pub const CURLSOCKTYPE_ACCEPT: curlsocktype = 1; +pub const CURL_SOCKOPT_OK: c_int = 0; +pub const CURL_SOCKOPT_ERROR: c_int = 1; +pub const CURL_SOCKOPT_ALREADY_CONNECTED: c_int = 2; +pub type curl_sockopt_callback = extern fn(*mut c_void, + curl_socket_t, + curlsocktype) -> c_int; + +#[repr(C)] +pub struct curl_sockaddr { + pub family: c_int, + pub socktype: c_int, + pub protocol: c_int, + pub addrlen: c_uint, + pub addr: libc::sockaddr, +} + +pub type curl_opensocket_callback = extern fn(*mut c_void, + curlsocktype, + *mut curl_sockaddr) -> curl_socket_t; + +pub type curlioerr = __enum_ty; +pub const CURLIOE_OK: curlioerr = 0; +pub const CURLIOE_UNKNOWNCMD: curlioerr = 1; +pub const CURLIOE_FAILRESTART: curlioerr = 2; + +pub type curliocmd = __enum_ty; +pub const CURLIOCMD_NOP: curliocmd = 0; +pub const CURLIOCMD_RESTARTREAD: curliocmd = 1; + +pub type curl_ioctl_callback = extern fn(*mut CURL, c_int, *mut c_void) -> curlioerr; + +pub type curl_malloc_callback = extern fn(size_t) -> *mut c_void; +pub type curl_free_callback = extern fn(*mut c_void); +pub type curl_realloc_callback = extern fn(*mut c_void, size_t) -> *mut c_void; +pub type curl_strdup_callback = extern fn(*const c_char) -> *mut c_char; +pub type curl_calloc_callback = extern fn(size_t, size_t) -> *mut c_void; + +pub type curl_infotype = __enum_ty; +pub const CURLINFO_TEXT: curl_infotype = 0; +pub const CURLINFO_HEADER_IN: curl_infotype = 1; +pub const CURLINFO_HEADER_OUT: curl_infotype = 2; +pub const CURLINFO_DATA_IN: curl_infotype = 3; +pub const CURLINFO_DATA_OUT: curl_infotype = 4; +pub const CURLINFO_SSL_DATA_IN: curl_infotype = 5; +pub const CURLINFO_SSL_DATA_OUT: curl_infotype = 6; + +pub type curl_debug_callback = extern fn(*mut CURL, + curl_infotype, + *mut c_char, + size_t, + *mut c_void) -> c_int; + +pub const CURLE_OK: CURLcode = 0; +pub const CURLE_UNSUPPORTED_PROTOCOL: CURLcode = 1; +pub const CURLE_FAILED_INIT: CURLcode = 2; +pub const CURLE_URL_MALFORMAT: CURLcode = 3; +pub const CURLE_NOT_BUILT_IN: CURLcode = 4; +pub const CURLE_COULDNT_RESOLVE_PROXY: CURLcode = 5; +pub const CURLE_COULDNT_RESOLVE_HOST: CURLcode = 6; +pub const CURLE_COULDNT_CONNECT: CURLcode = 7; +pub const CURLE_FTP_WEIRD_SERVER_REPLY: CURLcode = 8; +pub const CURLE_REMOTE_ACCESS_DENIED: CURLcode = 9; +pub const CURLE_FTP_ACCEPT_FAILED: CURLcode = 10; +pub const CURLE_FTP_WEIRD_PASS_REPLY: CURLcode = 11; +pub const CURLE_FTP_ACCEPT_TIMEOUT: CURLcode = 12; +pub const CURLE_FTP_WEIRD_PASV_REPLY: CURLcode = 13; +pub const CURLE_FTP_WEIRD_227_FORMAT: CURLcode = 14; +pub const CURLE_FTP_CANT_GET_HOST: CURLcode = 15; +pub const CURLE_OBSOLETE16: CURLcode = 16; +pub const CURLE_FTP_COULDNT_SET_TYPE: CURLcode = 17; +pub const CURLE_PARTIAL_FILE: CURLcode = 18; +pub const CURLE_FTP_COULDNT_RETR_FILE: CURLcode = 19; +pub const CURLE_OBSOLETE20: CURLcode = 20; +pub const CURLE_QUOTE_ERROR: CURLcode = 21; +pub const CURLE_HTTP_RETURNED_ERROR: CURLcode = 22; +pub const CURLE_WRITE_ERROR: CURLcode = 23; +pub const CURLE_OBSOLETE24: CURLcode = 24; +pub const CURLE_UPLOAD_FAILED: CURLcode = 25; +pub const CURLE_READ_ERROR: CURLcode = 26; +pub const CURLE_OUT_OF_MEMORY: CURLcode = 27; +pub const CURLE_OPERATION_TIMEDOUT: CURLcode = 28; +pub const CURLE_OBSOLETE29: CURLcode = 29; +pub const CURLE_FTP_PORT_FAILED: CURLcode = 30; +pub const CURLE_FTP_COULDNT_USE_REST: CURLcode = 31; +pub const CURLE_OBSOLETE32: CURLcode = 32; +pub const CURLE_RANGE_ERROR: CURLcode = 33; +pub const CURLE_HTTP_POST_ERROR: CURLcode = 34; +pub const CURLE_SSL_CONNECT_ERROR: CURLcode = 35; +pub const CURLE_BAD_DOWNLOAD_RESUME: CURLcode = 36; +pub const CURLE_FILE_COULDNT_READ_FILE: CURLcode = 37; +pub const CURLE_LDAP_CANNOT_BIND: CURLcode = 38; +pub const CURLE_LDAP_SEARCH_FAILED: CURLcode = 39; +pub const CURLE_OBSOLETE40: CURLcode = 40; +pub const CURLE_FUNCTION_NOT_FOUND: CURLcode = 41; +pub const CURLE_ABORTED_BY_CALLBACK: CURLcode = 42; +pub const CURLE_BAD_FUNCTION_ARGUMENT: CURLcode = 43; +pub const CURLE_OBSOLETE44: CURLcode = 44; +pub const CURLE_INTERFACE_FAILED: CURLcode = 45; +pub const CURLE_OBSOLETE46: CURLcode = 46; +pub const CURLE_TOO_MANY_REDIRECTS : CURLcode = 47; +pub const CURLE_UNKNOWN_OPTION: CURLcode = 48; +pub const CURLE_TELNET_OPTION_SYNTAX : CURLcode = 49; +pub const CURLE_OBSOLETE50: CURLcode = 50; +pub const CURLE_PEER_FAILED_VERIFICATION: CURLcode = 51; +pub const CURLE_GOT_NOTHING: CURLcode = 52; +pub const CURLE_SSL_ENGINE_NOTFOUND: CURLcode = 53; +pub const CURLE_SSL_ENGINE_SETFAILED: CURLcode = 54; +pub const CURLE_SEND_ERROR: CURLcode = 55; +pub const CURLE_RECV_ERROR: CURLcode = 56; +pub const CURLE_OBSOLETE57: CURLcode = 57; +pub const CURLE_SSL_CERTPROBLEM: CURLcode = 58; +pub const CURLE_SSL_CIPHER: CURLcode = 59; +pub const CURLE_SSL_CACERT: CURLcode = 60; +pub const CURLE_BAD_CONTENT_ENCODING: CURLcode = 61; +pub const CURLE_LDAP_INVALID_URL: CURLcode = 62; +pub const CURLE_FILESIZE_EXCEEDED: CURLcode = 63; +pub const CURLE_USE_SSL_FAILED: CURLcode = 64; +pub const CURLE_SEND_FAIL_REWIND: CURLcode = 65; +pub const CURLE_SSL_ENGINE_INITFAILED: CURLcode = 66; +pub const CURLE_LOGIN_DENIED: CURLcode = 67; +pub const CURLE_TFTP_NOTFOUND: CURLcode = 68; +pub const CURLE_TFTP_PERM: CURLcode = 69; +pub const CURLE_REMOTE_DISK_FULL: CURLcode = 70; +pub const CURLE_TFTP_ILLEGAL: CURLcode = 71; +pub const CURLE_TFTP_UNKNOWNID: CURLcode = 72; +pub const CURLE_REMOTE_FILE_EXISTS: CURLcode = 73; +pub const CURLE_TFTP_NOSUCHUSER: CURLcode = 74; +pub const CURLE_CONV_FAILED: CURLcode = 75; +pub const CURLE_CONV_REQD: CURLcode = 76; +pub const CURLE_SSL_CACERT_BADFILE: CURLcode = 77; +pub const CURLE_REMOTE_FILE_NOT_FOUND: CURLcode = 78; +pub const CURLE_SSH: CURLcode = 79; +pub const CURLE_SSL_SHUTDOWN_FAILED: CURLcode = 80; +pub const CURLE_AGAIN: CURLcode = 81; +pub const CURLE_SSL_CRL_BADFILE: CURLcode = 82; +pub const CURLE_SSL_ISSUER_ERROR: CURLcode = 83; +pub const CURLE_FTP_PRET_FAILED: CURLcode = 84; +pub const CURLE_RTSP_CSEQ_ERROR: CURLcode = 85; +pub const CURLE_RTSP_SESSION_ERROR: CURLcode = 86; +pub const CURLE_FTP_BAD_FILE_LIST: CURLcode = 87; +pub const CURLE_CHUNK_FAILED: CURLcode = 88; +pub const CURLE_NO_CONNECTION_AVAILABLE: CURLcode = 89; + +pub type curl_conv_callback = extern fn(*mut c_char, size_t) -> CURLcode; +pub type curl_ssl_ctx_callback = extern fn(*mut CURL, + *mut c_void, + *mut c_void) -> CURLcode; + +pub type curl_proxytype = __enum_ty; +pub const CURLPROXY_HTTP: curl_proxytype = 0; +pub const CURLPROXY_HTTP_1_0: curl_proxytype = 1; +pub const CURLPROXY_SOCKS4: curl_proxytype = 4; +pub const CURLPROXY_SOCKS5: curl_proxytype = 5; +pub const CURLPROXY_SOCKS4A: curl_proxytype = 6; +pub const CURLPROXY_SOCKS5_HOSTNAME: curl_proxytype = 7; + +pub const CURLAUTH_NONE: c_ulong = 0; +pub const CURLAUTH_BASIC: c_ulong = 1 << 0; +pub const CURLAUTH_DIGEST: c_ulong = 1 << 1; +pub const CURLAUTH_GSSNEGOTIATE: c_ulong = 1 << 2; +pub const CURLAUTH_NTLM: c_ulong = 1 << 3; +pub const CURLAUTH_DIGEST_IE: c_ulong = 1 << 4; +pub const CURLAUTH_NTLM_WB: c_ulong = 1 << 5; +pub const CURLAUTH_ONLY: c_ulong = 1 << 31; +pub const CURLAUTH_ANY: c_ulong = !CURLAUTH_DIGEST_IE; +pub const CURLAUTH_ANYSAFE: c_ulong = !(CURLAUTH_BASIC | CURLAUTH_DIGEST_IE); + +pub const CURLSSH_AUTH_ANY: c_ulong = !0; +pub const CURLSSH_AUTH_NONE: c_ulong = 0; +pub const CURLSSH_AUTH_PUBLICKEY: c_ulong = 1 << 0; +pub const CURLSSH_AUTH_PASSWORD: c_ulong = 1 << 1; +pub const CURLSSH_AUTH_HOST: c_ulong = 1 << 2; +pub const CURLSSH_AUTH_KEYBOARD: c_ulong = 1 << 3; +pub const CURLSSH_AUTH_AGENT: c_ulong = 1 << 4; +pub const CURLSSH_AUTH_DEFAULT: c_ulong = CURLSSH_AUTH_ANY; + +pub const CURLGSSAPI_DELEGATION_NONE: c_ulong = 0; +pub const CURLGSSAPI_DELEGATION_POLICY_FLAG: c_ulong = 1 << 0; +pub const CURLGSSAPI_DELEGATION_FLAG: c_ulong = 1 << 1; + +pub type curl_khtype = __enum_ty; +pub const CURLKHTYPE_UNKNOWN: curl_khtype = 0; +pub const CURLKHTYPE_RSA1: curl_khtype = 1; +pub const CURLKHTYPE_RSA: curl_khtype = 2; +pub const CURLKHTYPE_DSS: curl_khtype = 3; + +#[repr(C)] +pub struct curl_khkey { + pub key: *const c_char, + pub len: size_t, + pub keytype: curl_khtype, +} + +pub type curl_khstat = __enum_ty; +pub const CURLKHSTAT_FINE_ADD_TO_FILE: curl_khstat = 0; +pub const CURLKHSTAT_FINE: curl_khstat = 1; +pub const CURLKHSTAT_REJECT: curl_khstat = 2; +pub const CURLKHSTAT_DEFER: curl_khstat = 3; + +pub type curl_khmatch = __enum_ty; +pub const CURLKHMATCH_OK: curl_khmatch = 0; +pub const CURLKHMATCH_MISMATCH: curl_khmatch = 1; +pub const CURLKHMATCH_MISSING: curl_khmatch = 2; + +pub type curl_sshkeycallback = extern fn(*mut CURL, + *const curl_khkey, + *const curl_khkey, + curl_khmatch, + *mut c_void) -> c_int; + +pub type curl_usessl = __enum_ty; +pub const CURLUSESSL_NONE: curl_usessl = 0; +pub const CURLUSESSL_TRY: curl_usessl = 1; +pub const CURLUSESSL_CONTROL: curl_usessl = 2; +pub const CURLUSESSL_ALL: curl_usessl = 3; + +pub const CURLPROTO_HTTP: c_int = 1 << 0; +pub const CURLPROTO_HTTPS: c_int = 1 << 1; +pub const CURLPROTO_FILE: c_int = 1 << 10; + +pub const CURLOPTTYPE_LONG: CURLoption = 0; +pub const CURLOPTTYPE_OBJECTPOINT: CURLoption = 10_000; +pub const CURLOPTTYPE_FUNCTIONPOINT: CURLoption = 20_000; +pub const CURLOPTTYPE_OFF_T: CURLoption = 30_000; + +pub const CURLOPT_FILE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 1; +pub const CURLOPT_URL: CURLoption = CURLOPTTYPE_OBJECTPOINT + 2; +pub const CURLOPT_PORT: CURLoption = CURLOPTTYPE_LONG + 3; +pub const CURLOPT_PROXY: CURLoption = CURLOPTTYPE_OBJECTPOINT + 4; +pub const CURLOPT_USERPWD: CURLoption = CURLOPTTYPE_OBJECTPOINT + 5; +pub const CURLOPT_PROXYUSERPWD: CURLoption = CURLOPTTYPE_OBJECTPOINT + 6; +pub const CURLOPT_RANGE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 7; +pub const CURLOPT_INFILE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 9; +pub const CURLOPT_ERRORBUFFER: CURLoption = CURLOPTTYPE_OBJECTPOINT + 10; +pub const CURLOPT_WRITEFUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 11; +pub const CURLOPT_READFUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 12; +pub const CURLOPT_TIMEOUT: CURLoption = CURLOPTTYPE_LONG + 13; +pub const CURLOPT_INFILESIZE: CURLoption = CURLOPTTYPE_LONG + 14; +pub const CURLOPT_POSTFIELDS: CURLoption = CURLOPTTYPE_OBJECTPOINT + 15; +pub const CURLOPT_REFERER: CURLoption = CURLOPTTYPE_OBJECTPOINT + 16; +pub const CURLOPT_FTPPORT: CURLoption = CURLOPTTYPE_OBJECTPOINT + 17; +pub const CURLOPT_USERAGENT: CURLoption = CURLOPTTYPE_OBJECTPOINT + 18; +pub const CURLOPT_LOW_SPEED_LIMIT: CURLoption = CURLOPTTYPE_LONG + 19; +pub const CURLOPT_LOW_SPEED_TIME: CURLoption = CURLOPTTYPE_LONG + 20; +pub const CURLOPT_RESUME_FROM: CURLoption = CURLOPTTYPE_LONG + 21; +pub const CURLOPT_COOKIE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 22; +pub const CURLOPT_HTTPHEADER: CURLoption = CURLOPTTYPE_OBJECTPOINT + 23; +pub const CURLOPT_HTTPPOST: CURLoption = CURLOPTTYPE_OBJECTPOINT + 24; +pub const CURLOPT_SSLCERT: CURLoption = CURLOPTTYPE_OBJECTPOINT + 25; +pub const CURLOPT_KEYPASSWD: CURLoption = CURLOPTTYPE_OBJECTPOINT + 26; +pub const CURLOPT_CRLF: CURLoption = CURLOPTTYPE_LONG + 27; +pub const CURLOPT_QUOTE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 28; +pub const CURLOPT_WRITEHEADER: CURLoption = CURLOPTTYPE_OBJECTPOINT + 29; +pub const CURLOPT_COOKIEFILE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 31; +pub const CURLOPT_SSLVERSION: CURLoption = CURLOPTTYPE_LONG + 32; +pub const CURLOPT_TIMECONDITION: CURLoption = CURLOPTTYPE_LONG + 33; +pub const CURLOPT_TIMEVALUE: CURLoption = CURLOPTTYPE_LONG + 34; +pub const CURLOPT_CUSTOMREQUEST: CURLoption = CURLOPTTYPE_OBJECTPOINT + 36; +pub const CURLOPT_STDERR: CURLoption = CURLOPTTYPE_OBJECTPOINT + 37; +pub const CURLOPT_POSTQUOTE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 39; +pub const CURLOPT_WRITEINFO: CURLoption = CURLOPTTYPE_OBJECTPOINT + 40; +pub const CURLOPT_VERBOSE: CURLoption = CURLOPTTYPE_LONG + 41; +pub const CURLOPT_HEADER: CURLoption = CURLOPTTYPE_LONG + 42; +pub const CURLOPT_NOPROGRESS: CURLoption = CURLOPTTYPE_LONG + 43; +pub const CURLOPT_NOBODY: CURLoption = CURLOPTTYPE_LONG + 44; +pub const CURLOPT_FAILONERROR: CURLoption = CURLOPTTYPE_LONG + 45; +pub const CURLOPT_UPLOAD: CURLoption = CURLOPTTYPE_LONG + 46; +pub const CURLOPT_POST: CURLoption = CURLOPTTYPE_LONG + 47; +pub const CURLOPT_DIRLISTONLY: CURLoption = CURLOPTTYPE_LONG + 48; +pub const CURLOPT_APPEND: CURLoption = CURLOPTTYPE_LONG + 50; +pub const CURLOPT_NETRC: CURLoption = CURLOPTTYPE_LONG + 51; +pub const CURLOPT_FOLLOWLOCATION: CURLoption = CURLOPTTYPE_LONG + 52; +pub const CURLOPT_TRANSFERTEXT: CURLoption = CURLOPTTYPE_LONG + 53; +pub const CURLOPT_PUT: CURLoption = CURLOPTTYPE_LONG + 54; +pub const CURLOPT_PROGRESSFUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 56; +pub const CURLOPT_PROGRESSDATA: CURLoption = CURLOPTTYPE_OBJECTPOINT + 57; +pub const CURLOPT_AUTOREFERER: CURLoption = CURLOPTTYPE_LONG + 58; +pub const CURLOPT_PROXYPORT: CURLoption = CURLOPTTYPE_LONG + 59; +pub const CURLOPT_POSTFIELDSIZE: CURLoption = CURLOPTTYPE_LONG + 60; +pub const CURLOPT_HTTPPROXYTUNNEL: CURLoption = CURLOPTTYPE_LONG + 61; +pub const CURLOPT_INTERFACE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 62; +pub const CURLOPT_KRBLEVEL: CURLoption = CURLOPTTYPE_OBJECTPOINT + 63; +pub const CURLOPT_SSL_VERIFYPEER: CURLoption = CURLOPTTYPE_LONG + 64; +pub const CURLOPT_CAINFO: CURLoption = CURLOPTTYPE_OBJECTPOINT + 65; +pub const CURLOPT_MAXREDIRS: CURLoption = CURLOPTTYPE_LONG + 68; +pub const CURLOPT_FILETIME: CURLoption = CURLOPTTYPE_LONG + 69; +pub const CURLOPT_TELNETOPTIONS: CURLoption = CURLOPTTYPE_OBJECTPOINT + 70; +pub const CURLOPT_MAXCONNECTS: CURLoption = CURLOPTTYPE_LONG + 71; +pub const CURLOPT_CLOSEPOLICY: CURLoption = CURLOPTTYPE_LONG + 72; +pub const CURLOPT_FRESH_CONNECT: CURLoption = CURLOPTTYPE_LONG + 74; +pub const CURLOPT_FORBID_REUSE: CURLoption = CURLOPTTYPE_LONG + 75; +pub const CURLOPT_RANDOM_FILE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 76; +pub const CURLOPT_EGDSOCKET: CURLoption = CURLOPTTYPE_OBJECTPOINT + 77; +pub const CURLOPT_CONNECTTIMEOUT: CURLoption = CURLOPTTYPE_LONG + 78; +pub const CURLOPT_HEADERFUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 79; +pub const CURLOPT_HTTPGET: CURLoption = CURLOPTTYPE_LONG + 80; +pub const CURLOPT_SSL_VERIFYHOST: CURLoption = CURLOPTTYPE_LONG + 81; +pub const CURLOPT_COOKIEJAR: CURLoption = CURLOPTTYPE_OBJECTPOINT + 82; +pub const CURLOPT_SSL_CIPHER_LIST: CURLoption = CURLOPTTYPE_OBJECTPOINT + 83; +pub const CURLOPT_HTTP_VERSION: CURLoption = CURLOPTTYPE_LONG + 84; +pub const CURLOPT_FTP_USE_EPSV: CURLoption = CURLOPTTYPE_LONG + 85; +pub const CURLOPT_SSLCERTTYPE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 86; +pub const CURLOPT_SSLKEY: CURLoption = CURLOPTTYPE_OBJECTPOINT + 87; +pub const CURLOPT_SSLKEYTYPE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 88; +pub const CURLOPT_SSLENGINE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 89; +pub const CURLOPT_SSLENGINE_DEFAULT: CURLoption = CURLOPTTYPE_LONG + 90; +pub const CURLOPT_DNS_USE_GLOBAL_CACHE: CURLoption = CURLOPTTYPE_LONG + 91; +pub const CURLOPT_DNS_CACHE_TIMEOUT: CURLoption = CURLOPTTYPE_LONG + 92; +pub const CURLOPT_PREQUOTE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 93; +pub const CURLOPT_DEBUGFUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 94; +pub const CURLOPT_DEBUGDATA: CURLoption = CURLOPTTYPE_OBJECTPOINT + 95; +pub const CURLOPT_COOKIESESSION: CURLoption = CURLOPTTYPE_LONG + 96; +pub const CURLOPT_CAPATH: CURLoption = CURLOPTTYPE_OBJECTPOINT + 97; +pub const CURLOPT_BUFFERSIZE: CURLoption = CURLOPTTYPE_LONG + 98; +pub const CURLOPT_NOSIGNAL: CURLoption = CURLOPTTYPE_LONG + 99; +pub const CURLOPT_SHARE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 100; +pub const CURLOPT_PROXYTYPE: CURLoption = CURLOPTTYPE_LONG + 101; +pub const CURLOPT_ACCEPT_ENCODING: CURLoption = CURLOPTTYPE_OBJECTPOINT + 102; +pub const CURLOPT_PRIVATE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 103; +pub const CURLOPT_HTTP200ALIASES: CURLoption = CURLOPTTYPE_OBJECTPOINT + 104; +pub const CURLOPT_UNRESTRICTED_AUTH: CURLoption = CURLOPTTYPE_LONG + 105; +pub const CURLOPT_FTP_USE_EPRT: CURLoption = CURLOPTTYPE_LONG + 106; +pub const CURLOPT_HTTPAUTH: CURLoption = CURLOPTTYPE_LONG + 107; +pub const CURLOPT_SSL_CTX_FUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 108; +pub const CURLOPT_SSL_CTX_DATA: CURLoption = CURLOPTTYPE_OBJECTPOINT + 109; +pub const CURLOPT_FTP_CREATE_MISSING_DIRS: CURLoption = CURLOPTTYPE_LONG + 110; +pub const CURLOPT_PROXYAUTH: CURLoption = CURLOPTTYPE_LONG + 111; +pub const CURLOPT_FTP_RESPONSE_TIMEOUT: CURLoption = CURLOPTTYPE_LONG + 112; +pub const CURLOPT_IPRESOLVE: CURLoption = CURLOPTTYPE_LONG + 113; +pub const CURLOPT_MAXFILESIZE: CURLoption = CURLOPTTYPE_LONG + 114; +pub const CURLOPT_INFILESIZE_LARGE: CURLoption = CURLOPTTYPE_OFF_T + 115; +pub const CURLOPT_RESUME_FROM_LARGE: CURLoption = CURLOPTTYPE_OFF_T + 116; +pub const CURLOPT_MAXFILESIZE_LARGE: CURLoption = CURLOPTTYPE_OFF_T + 117; +pub const CURLOPT_NETRC_FILE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 118; +pub const CURLOPT_USE_SSL: CURLoption = CURLOPTTYPE_LONG + 119; +pub const CURLOPT_POSTFIELDSIZE_LARGE: CURLoption = CURLOPTTYPE_OFF_T + 120; +pub const CURLOPT_TCP_NODELAY: CURLoption = CURLOPTTYPE_LONG + 121; +pub const CURLOPT_FTPSSLAUTH: CURLoption = CURLOPTTYPE_LONG + 129; +pub const CURLOPT_IOCTLFUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 130; +pub const CURLOPT_IOCTLDATA: CURLoption = CURLOPTTYPE_OBJECTPOINT + 131; +pub const CURLOPT_FTP_ACCOUNT: CURLoption = CURLOPTTYPE_OBJECTPOINT + 134; +pub const CURLOPT_COOKIELIST: CURLoption = CURLOPTTYPE_OBJECTPOINT + 135; +pub const CURLOPT_IGNORE_CONTENT_LENGTH: CURLoption = CURLOPTTYPE_LONG + 136; +pub const CURLOPT_FTP_SKIP_PASV_IP: CURLoption = CURLOPTTYPE_LONG + 137; +pub const CURLOPT_FTP_FILEMETHOD: CURLoption = CURLOPTTYPE_LONG + 138; +pub const CURLOPT_LOCALPORT: CURLoption = CURLOPTTYPE_LONG + 139; +pub const CURLOPT_LOCALPORTRANGE: CURLoption = CURLOPTTYPE_LONG + 140; +pub const CURLOPT_CONNECT_ONLY: CURLoption = CURLOPTTYPE_LONG + 141; +pub const CURLOPT_CONV_FROM_NETWORK_FUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 142; +pub const CURLOPT_CONV_TO_NETWORK_FUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 143; +pub const CURLOPT_CONV_FROM_UTF8_FUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 144; +pub const CURLOPT_MAX_SEND_SPEED_LARGE: CURLoption = CURLOPTTYPE_OFF_T + 145; +pub const CURLOPT_MAX_RECV_SPEED_LARGE: CURLoption = CURLOPTTYPE_OFF_T + 146; +pub const CURLOPT_FTP_ALTERNATIVE_TO_USER: CURLoption = CURLOPTTYPE_OBJECTPOINT + 147; +pub const CURLOPT_SOCKOPTFUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 148; +pub const CURLOPT_SOCKOPTDATA: CURLoption = CURLOPTTYPE_OBJECTPOINT + 149; +pub const CURLOPT_SSL_SESSIONID_CACHE: CURLoption = CURLOPTTYPE_LONG + 150; +pub const CURLOPT_SSH_AUTH_TYPES: CURLoption = CURLOPTTYPE_LONG + 151; +pub const CURLOPT_SSH_PUBLIC_KEYFILE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 152; +pub const CURLOPT_SSH_PRIVATE_KEYFILE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 153; +pub const CURLOPT_FTP_SSL_CCC: CURLoption = CURLOPTTYPE_LONG + 154; +pub const CURLOPT_TIMEOUT_MS: CURLoption = CURLOPTTYPE_LONG + 155; +pub const CURLOPT_CONNECTTIMEOUT_MS: CURLoption = CURLOPTTYPE_LONG + 156; +pub const CURLOPT_HTTP_TRANSFER_DECODING: CURLoption = CURLOPTTYPE_LONG + 157; +pub const CURLOPT_HTTP_CONTENT_DECODING: CURLoption = CURLOPTTYPE_LONG + 158; +pub const CURLOPT_NEW_FILE_PERMS: CURLoption = CURLOPTTYPE_LONG + 159; +pub const CURLOPT_NEW_DIRECTORY_PERMS: CURLoption = CURLOPTTYPE_LONG + 160; +pub const CURLOPT_POSTREDIR: CURLoption = CURLOPTTYPE_LONG + 161; +pub const CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: CURLoption = CURLOPTTYPE_OBJECTPOINT + 162; +pub const CURLOPT_OPENSOCKETFUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 163; +pub const CURLOPT_OPENSOCKETDATA: CURLoption = CURLOPTTYPE_OBJECTPOINT + 164; +pub const CURLOPT_COPYPOSTFIELDS: CURLoption = CURLOPTTYPE_OBJECTPOINT + 165; +pub const CURLOPT_PROXY_TRANSFER_MODE: CURLoption = CURLOPTTYPE_LONG + 166; +pub const CURLOPT_SEEKFUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 167; +pub const CURLOPT_SEEKDATA: CURLoption = CURLOPTTYPE_OBJECTPOINT + 168; +pub const CURLOPT_CRLFILE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 169; +pub const CURLOPT_ISSUERCERT: CURLoption = CURLOPTTYPE_OBJECTPOINT + 170; +pub const CURLOPT_ADDRESS_SCOPE: CURLoption = CURLOPTTYPE_LONG + 171; +pub const CURLOPT_CERTINFO: CURLoption = CURLOPTTYPE_LONG + 172; +pub const CURLOPT_USERNAME: CURLoption = CURLOPTTYPE_OBJECTPOINT + 173; +pub const CURLOPT_PASSWORD: CURLoption = CURLOPTTYPE_OBJECTPOINT + 174; +pub const CURLOPT_PROXYUSERNAME: CURLoption = CURLOPTTYPE_OBJECTPOINT + 175; +pub const CURLOPT_PROXYPASSWORD: CURLoption = CURLOPTTYPE_OBJECTPOINT + 176; +pub const CURLOPT_NOPROXY: CURLoption = CURLOPTTYPE_OBJECTPOINT + 177; +pub const CURLOPT_TFTP_BLKSIZE: CURLoption = CURLOPTTYPE_LONG + 178; +pub const CURLOPT_SOCKS5_GSSAPI_SERVICE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 179; +pub const CURLOPT_SOCKS5_GSSAPI_NEC: CURLoption = CURLOPTTYPE_LONG + 180; +pub const CURLOPT_PROTOCOLS: CURLoption = CURLOPTTYPE_LONG + 181; +pub const CURLOPT_REDIR_PROTOCOLS: CURLoption = CURLOPTTYPE_LONG + 182; +pub const CURLOPT_SSH_KNOWNHOSTS: CURLoption = CURLOPTTYPE_OBJECTPOINT + 183; +pub const CURLOPT_SSH_KEYFUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 184; +pub const CURLOPT_SSH_KEYDATA: CURLoption = CURLOPTTYPE_OBJECTPOINT + 185; +pub const CURLOPT_MAIL_FROM: CURLoption = CURLOPTTYPE_OBJECTPOINT + 186; +pub const CURLOPT_MAIL_RCPT: CURLoption = CURLOPTTYPE_OBJECTPOINT + 187; +pub const CURLOPT_FTP_USE_PRET: CURLoption = CURLOPTTYPE_LONG + 188; +pub const CURLOPT_RTSP_REQUEST: CURLoption = CURLOPTTYPE_LONG + 189; +pub const CURLOPT_RTSP_SESSION_ID: CURLoption = CURLOPTTYPE_OBJECTPOINT + 190; +pub const CURLOPT_RTSP_STREAM_URI: CURLoption = CURLOPTTYPE_OBJECTPOINT + 191; +pub const CURLOPT_RTSP_TRANSPORT: CURLoption = CURLOPTTYPE_OBJECTPOINT + 192; +pub const CURLOPT_RTSP_CLIENT_CSEQ: CURLoption = CURLOPTTYPE_LONG + 193; +pub const CURLOPT_RTSP_SERVER_CSEQ: CURLoption = CURLOPTTYPE_LONG + 194; +pub const CURLOPT_INTERLEAVEDATA: CURLoption = CURLOPTTYPE_OBJECTPOINT + 195; +pub const CURLOPT_INTERLEAVEFUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 196; +pub const CURLOPT_WILDCARDMATCH: CURLoption = CURLOPTTYPE_LONG + 197; +pub const CURLOPT_CHUNK_BGN_FUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 198; +pub const CURLOPT_CHUNK_END_FUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 199; +pub const CURLOPT_FNMATCH_FUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 200; +pub const CURLOPT_CHUNK_DATA: CURLoption = CURLOPTTYPE_OBJECTPOINT + 201; +pub const CURLOPT_FNMATCH_DATA: CURLoption = CURLOPTTYPE_OBJECTPOINT + 202; +pub const CURLOPT_RESOLVE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 203; +pub const CURLOPT_TLSAUTH_USERNAME: CURLoption = CURLOPTTYPE_OBJECTPOINT + 204; +pub const CURLOPT_TLSAUTH_PASSWORD: CURLoption = CURLOPTTYPE_OBJECTPOINT + 205; +pub const CURLOPT_TLSAUTH_TYPE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 206; +pub const CURLOPT_TRANSFER_ENCODING: CURLoption = CURLOPTTYPE_LONG + 207; +pub const CURLOPT_CLOSESOCKETFUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 208; +pub const CURLOPT_CLOSESOCKETDATA: CURLoption = CURLOPTTYPE_OBJECTPOINT + 209; +pub const CURLOPT_GSSAPI_DELEGATION: CURLoption = CURLOPTTYPE_LONG + 210; +pub const CURLOPT_DNS_SERVERS: CURLoption = CURLOPTTYPE_OBJECTPOINT + 211; +pub const CURLOPT_ACCEPTTIMEOUT_MS: CURLoption = CURLOPTTYPE_LONG + 212; +pub const CURLOPT_TCP_KEEPALIVE: CURLoption = CURLOPTTYPE_LONG + 213; +pub const CURLOPT_TCP_KEEPIDLE: CURLoption = CURLOPTTYPE_LONG + 214; +pub const CURLOPT_TCP_KEEPINTVL: CURLoption = CURLOPTTYPE_LONG + 215; +pub const CURLOPT_SSL_OPTIONS: CURLoption = CURLOPTTYPE_LONG + 216; +pub const CURLOPT_MAIL_AUTH: CURLoption = CURLOPTTYPE_OBJECTPOINT + 217; +pub const CURLOPT_SASL_IR: CURLoption = CURLOPTTYPE_LONG + 218; +pub const CURLOPT_XFERINFOFUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 219; +pub const CURLOPT_XOAUTH2_BEARER: CURLoption = CURLOPTTYPE_OBJECTPOINT + 220; +pub const CURLOPT_DNS_INTERFACE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 221; +pub const CURLOPT_DNS_LOCAL_IP4: CURLoption = CURLOPTTYPE_OBJECTPOINT + 222; +pub const CURLOPT_DNS_LOCAL_IP6: CURLoption = CURLOPTTYPE_OBJECTPOINT + 223; +pub const CURLOPT_LOGIN_OPTIONS: CURLoption = CURLOPTTYPE_OBJECTPOINT + 224; + +pub const CURL_IPRESOLVE_WHATEVER: c_int = 0; +pub const CURL_IPRESOLVE_V4: c_int = 1; +pub const CURL_IPRESOLVE_V6: c_int = 2; + +pub const CURLOPT_READDATA: CURLoption = CURLOPT_INFILE; +pub const CURLOPT_WRITEDATA: CURLoption = CURLOPT_FILE; +pub const CURLOPT_HEADERDATA: CURLoption = CURLOPT_WRITEHEADER; + +pub type CURLformoption = __enum_ty; +pub const CURLFORM_NOTHING: CURLformoption = 0; +pub const CURLFORM_COPYNAME: CURLformoption = 1; +pub const CURLFORM_PTRNAME: CURLformoption = 2; +pub const CURLFORM_NAMELENGTH: CURLformoption = 3; +pub const CURLFORM_COPYCONTENTS: CURLformoption = 4; +pub const CURLFORM_PTRCONTENTS: CURLformoption = 5; +pub const CURLFORM_CONTENTSLENGTH: CURLformoption = 6; +pub const CURLFORM_FILECONTENT: CURLformoption = 7; +pub const CURLFORM_ARRAY: CURLformoption = 8; +pub const CURLFORM_OBSOLETE: CURLformoption = 9; +pub const CURLFORM_FILE: CURLformoption = 10; +pub const CURLFORM_BUFFER: CURLformoption = 11; +pub const CURLFORM_BUFFERPTR: CURLformoption = 12; +pub const CURLFORM_BUFFERLENGTH: CURLformoption = 13; +pub const CURLFORM_CONTENTTYPE: CURLformoption = 14; +pub const CURLFORM_CONTENTHEADER: CURLformoption = 15; +pub const CURLFORM_FILENAME: CURLformoption = 16; +pub const CURLFORM_END: CURLformoption = 17; +pub const CURLFORM_STREAM: CURLformoption = 19; + +pub type CURLFORMcode = __enum_ty; +pub const CURL_FORMADD_OK: CURLFORMcode = 0; +pub const CURL_FORMADD_MEMORY: CURLFORMcode = 1; +pub const CURL_FORMADD_OPTION_TWICE: CURLFORMcode = 2; +pub const CURL_FORMADD_NULL: CURLFORMcode = 3; +pub const CURL_FORMADD_UNKNOWN_OPTION: CURLFORMcode = 4; +pub const CURL_FORMADD_INCOMPLETE: CURLFORMcode = 5; +pub const CURL_FORMADD_ILLEGAL_ARRAY: CURLFORMcode = 6; +pub const CURL_FORMADD_DISABLED: CURLFORMcode = 7; + +#[repr(C)] +pub struct curl_forms { + pub option: CURLformoption, + pub value: *const c_char, +} + +pub type curl_formget_callback = extern fn(*mut c_void, + *const c_char, + size_t) -> size_t; + +#[repr(C)] +pub struct curl_slist { + pub data: *mut c_char, + pub next: *mut curl_slist, +} + +#[repr(C)] +pub struct curl_certinfo { + pub num_of_certs: c_int, + pub certinfo: *mut *mut curl_slist, +} + +pub type curl_sslbackend = __enum_ty; +pub const CURLSSLBACKEND_NONE: curl_sslbackend = 0; +pub const CURLSSLBACKEND_OPENSSL: curl_sslbackend = 1; +pub const CURLSSLBACKEND_GNUTLS: curl_sslbackend = 2; +pub const CURLSSLBACKEND_NSS: curl_sslbackend = 3; +pub const CURLSSLBACKEND_QSOSSL: curl_sslbackend = 4; +pub const CURLSSLBACKEND_GSKIT: curl_sslbackend = 5; +pub const CURLSSLBACKEND_POLARSSL: curl_sslbackend = 6; +pub const CURLSSLBACKEND_CYASSL: curl_sslbackend = 7; +pub const CURLSSLBACKEND_SCHANNEL: curl_sslbackend = 8; +pub const CURLSSLBACKEND_DARWINSSL: curl_sslbackend = 9; #[repr(C)] -#[derive(Clone, Copy)] -pub enum CURLversion { - CURL_VERSION_FIRST, - CURL_VERSION_SECOND, - CURL_VERSION_THIRD, - CURL_VERSION_FOURTH, - CURL_VERSION_LAST /* never actually use this */ +pub struct curl_tlssessioninfo { + pub backend: curl_sslbackend, + pub internals: *mut c_void, } +pub const CURLINFO_STRING: CURLINFO = 0x100000; +pub const CURLINFO_LONG: CURLINFO = 0x200000; +pub const CURLINFO_DOUBLE: CURLINFO = 0x300000; +pub const CURLINFO_SLIST: CURLINFO = 0x400000; +pub const CURLINFO_MASK: CURLINFO = 0x0fffff; +pub const CURLINFO_TYPEMASK: CURLINFO = 0xf00000; + +pub const CURLINFO_EFFECTIVE_URL: CURLINFO = CURLINFO_STRING + 1; +pub const CURLINFO_RESPONSE_CODE: CURLINFO = CURLINFO_LONG + 2; +pub const CURLINFO_TOTAL_TIME: CURLINFO = CURLINFO_DOUBLE + 3; +pub const CURLINFO_NAMELOOKUP_TIME: CURLINFO = CURLINFO_DOUBLE + 4; +pub const CURLINFO_CONNECT_TIME: CURLINFO = CURLINFO_DOUBLE + 5; +pub const CURLINFO_PRETRANSFER_TIME: CURLINFO = CURLINFO_DOUBLE + 6; +pub const CURLINFO_SIZE_UPLOAD: CURLINFO = CURLINFO_DOUBLE + 7; +pub const CURLINFO_SIZE_DOWNLOAD: CURLINFO = CURLINFO_DOUBLE + 8; +pub const CURLINFO_SPEED_DOWNLOAD: CURLINFO = CURLINFO_DOUBLE + 9; +pub const CURLINFO_SPEED_UPLOAD: CURLINFO = CURLINFO_DOUBLE + 10; +pub const CURLINFO_HEADER_SIZE: CURLINFO = CURLINFO_LONG + 11; +pub const CURLINFO_REQUEST_SIZE: CURLINFO = CURLINFO_LONG + 12; +pub const CURLINFO_SSL_VERIFYRESULT: CURLINFO = CURLINFO_LONG + 13; +pub const CURLINFO_FILETIME: CURLINFO = CURLINFO_LONG + 14; +pub const CURLINFO_CONTENT_LENGTH_DOWNLOAD: CURLINFO = CURLINFO_DOUBLE + 15; +pub const CURLINFO_CONTENT_LENGTH_UPLOAD: CURLINFO = CURLINFO_DOUBLE + 16; +pub const CURLINFO_STARTTRANSFER_TIME: CURLINFO = CURLINFO_DOUBLE + 17; +pub const CURLINFO_CONTENT_TYPE: CURLINFO = CURLINFO_STRING + 18; +pub const CURLINFO_REDIRECT_TIME: CURLINFO = CURLINFO_DOUBLE + 19; +pub const CURLINFO_REDIRECT_COUNT: CURLINFO = CURLINFO_LONG + 20; +pub const CURLINFO_PRIVATE: CURLINFO = CURLINFO_STRING + 21; +pub const CURLINFO_HTTP_CONNECTCODE: CURLINFO = CURLINFO_LONG + 22; +pub const CURLINFO_HTTPAUTH_AVAIL: CURLINFO = CURLINFO_LONG + 23; +pub const CURLINFO_PROXYAUTH_AVAIL: CURLINFO = CURLINFO_LONG + 24; +pub const CURLINFO_OS_ERRNO: CURLINFO = CURLINFO_LONG + 25; +pub const CURLINFO_NUM_CONNECTS: CURLINFO = CURLINFO_LONG + 26; +pub const CURLINFO_SSL_ENGINES: CURLINFO = CURLINFO_SLIST + 27; +pub const CURLINFO_COOKIELIST: CURLINFO = CURLINFO_SLIST + 28; +pub const CURLINFO_LASTSOCKET: CURLINFO = CURLINFO_LONG + 29; +pub const CURLINFO_FTP_ENTRY_PATH: CURLINFO = CURLINFO_STRING + 30; +pub const CURLINFO_REDIRECT_URL: CURLINFO = CURLINFO_STRING + 31; +pub const CURLINFO_PRIMARY_IP: CURLINFO = CURLINFO_STRING + 32; +pub const CURLINFO_APPCONNECT_TIME: CURLINFO = CURLINFO_DOUBLE + 33; +pub const CURLINFO_CERTINFO: CURLINFO = CURLINFO_SLIST + 34; +pub const CURLINFO_CONDITION_UNMET: CURLINFO = CURLINFO_LONG + 35; +pub const CURLINFO_RTSP_SESSION_ID: CURLINFO = CURLINFO_STRING + 36; +pub const CURLINFO_RTSP_CLIENT_CSEQ: CURLINFO = CURLINFO_LONG + 37; +pub const CURLINFO_RTSP_SERVER_CSEQ: CURLINFO = CURLINFO_LONG + 38; +pub const CURLINFO_RTSP_CSEQ_RECV: CURLINFO = CURLINFO_LONG + 39; +pub const CURLINFO_PRIMARY_PORT: CURLINFO = CURLINFO_LONG + 40; +pub const CURLINFO_LOCAL_IP: CURLINFO = CURLINFO_STRING + 41; +pub const CURLINFO_LOCAL_PORT: CURLINFO = CURLINFO_LONG + 42; +pub const CURLINFO_TLS_SESSION: CURLINFO = CURLINFO_SLIST + 43; + +pub type curl_closepolicy = __enum_ty; +pub const CURLCLOSEPOLICY_NONE: curl_closepolicy = 0; +pub const CURLCLOSEPOLICY_OLDEST: curl_closepolicy = 1; +pub const CURLCLOSEPOLICY_LEAST_RECENTLY_USED: curl_closepolicy = 2; +pub const CURLCLOSEPOLICY_LEAST_TRAFFIC: curl_closepolicy = 3; +pub const CURLCLOSEPOLICY_SLOWEST: curl_closepolicy = 4; +pub const CURLCLOSEPOLICY_CALLBACK: curl_closepolicy = 5; + +pub const CURL_GLOBAL_SSL: c_long = 1 << 0; +pub const CURL_GLOBAL_WIN32: c_long = 1 << 1; +pub const CURL_GLOBAL_ALL: c_long = CURL_GLOBAL_SSL | CURL_GLOBAL_WIN32; +pub const CURL_GLOBAL_NOTHING: c_long = 0; +pub const CURL_GLOBAL_DEFAULT: c_long = CURL_GLOBAL_ALL; +pub const CURL_GLOBAL_ACK_EINTR: c_long = 1 << 2; + +pub type curl_lock_data = __enum_ty; +pub const CURL_LOCK_DATA_NONE: curl_lock_data = 0; +pub const CURL_LOCK_DATA_SHARE: curl_lock_data = 1; +pub const CURL_LOCK_DATA_COOKIE: curl_lock_data = 2; +pub const CURL_LOCK_DATA_DNS: curl_lock_data = 3; +pub const CURL_LOCK_DATA_SSL_SESSION: curl_lock_data = 4; +pub const CURL_LOCK_DATA_CONNECT: curl_lock_data = 5; + +pub type curl_lock_access = __enum_ty; +pub const CURL_LOCK_ACCESS_NONE: curl_lock_access = 0; +pub const CURL_LOCK_ACCESS_SHARED: curl_lock_access = 1; +pub const CURL_LOCK_ACCESS_SINGLE: curl_lock_access = 2; + +pub type curl_lock_function = extern fn(*mut CURL, + curl_lock_data, + curl_lock_access, + *mut c_void); +pub type curl_unlock_function = extern fn(*mut CURL, + curl_lock_data, + *mut c_void); + +pub enum CURLSH {} + +pub type CURLSHcode = __enum_ty; +pub const CURLSHE_OK: CURLSHcode = 0; +pub const CURLSHE_BAD_OPTION: CURLSHcode = 1; +pub const CURLSHE_IN_USE: CURLSHcode = 2; +pub const CURLSHE_INVALID: CURLSHcode = 3; +pub const CURLSHE_NOMEM: CURLSHcode = 4; +pub const CURLSHE_NOT_BUILT_IN: CURLSHcode = 5; + +pub type CURLSHoption = __enum_ty; +pub const CURLSHOPT_NONE: CURLSHoption = 0; +pub const CURLSHOPT_SHARE: CURLSHoption = 1; +pub const CURLSHOPT_UNSHARE: CURLSHoption = 2; +pub const CURLSHOPT_LOCKFUNC: CURLSHoption = 3; +pub const CURLSHOPT_UNLOCKFUNC: CURLSHoption = 4; +pub const CURLSHOPT_USERDATA: CURLSHoption = 5; + +pub const CURLVERSION_FIRST: CURLversion = 0; +pub const CURLVERSION_SECOND: CURLversion = 1; +pub const CURLVERSION_THIRD: CURLversion = 2; +pub const CURLVERSION_FOURTH: CURLversion = 3; + #[repr(C)] -#[derive(Copy)] pub struct curl_version_info_data { pub age: CURLversion, - pub version: *const c_char, pub version_num: c_uint, pub host: *const c_char, pub features: c_int, pub ssl_version: *const c_char, - pub ssl_version_num: c_long, - pub libz_version: *const c_char, - - /* protocols is terminated by an entry with a NULL protoname */ pub protocols: *const *const c_char, - - /* The fields below this were added in CURL_VERSION_SECOND */ pub ares: *const c_char, pub ares_num: c_int, - - /* This field was added in CURL_VERSION_THIRD */ pub libidn: *const c_char, - - /* These field were added in CURL_VERSION_FOURTH */ pub iconv_ver_num: c_int, pub libssh_version: *const c_char, } -impl Clone for curl_version_info_data { - fn clone(&self) -> Self { *self } -} +pub const CURL_VERSION_IPV6: c_int = 1 << 0; +pub const CURL_VERSION_KERBEROS4: c_int = 1 << 1; +pub const CURL_VERSION_SSL: c_int = 1 << 2; +pub const CURL_VERSION_LIBZ: c_int = 1 << 3; +pub const CURL_VERSION_NTLM: c_int = 1 << 4; +pub const CURL_VERSION_GSSNEGOTIATE: c_int = 1 << 5; +pub const CURL_VERSION_DEBUG: c_int = 1 << 6; +pub const CURL_VERSION_ASYNCHDNS: c_int = 1 << 7; +pub const CURL_VERSION_SPNEGO: c_int = 1 << 8; +pub const CURL_VERSION_LARGEFILE: c_int = 1 << 9; +pub const CURL_VERSION_IDN: c_int = 1 << 10; +pub const CURL_VERSION_SSPI: c_int = 1 << 11; +pub const CURL_VERSION_CONV: c_int = 1 << 12; +pub const CURL_VERSION_CURLDEBUG: c_int = 1 << 13; +pub const CURL_VERSION_TLSAUTH_SRP: c_int = 1 << 14; +pub const CURL_VERSION_NTLM_WB: c_int = 1 << 15; +pub const CURL_VERSION_HTTP2: c_int = 1 << 16; -pub const CURL_READFUNC_ABORT: c_int = 0x10000000; +pub const CURLPAUSE_RECV: c_int = 1 << 0; +pub const CURLPAUSE_RECV_CONT: c_int = 0; +pub const CURLPAUSE_SEND: c_int = 1 << 2; +pub const CURLPAUSE_SEND_CONT: c_int = 0; -pub const CURLINFO_STRING: c_int = 0x100000; -pub const CURLINFO_LONG: c_int = 0x200000; -pub const CURLINFO_DOUBLE: c_int = 0x300000; -pub const CURLINFO_SLIST: c_int = 0x400000; -pub const CURLINFO_MASK: c_int = 0x0fffff; -pub const CURLINFO_TYPEMASK: c_int = 0xf00000; +pub enum CURLM {} -pub const CURLINFO_EFFECTIVE_URL: CURLINFO = CURLINFO_STRING + 1; -pub const CURLINFO_RESPONSE_CODE: CURLINFO = CURLINFO_LONG + 2; -pub const CURLINFO_TOTAL_TIME: CURLINFO = CURLINFO_DOUBLE + 5; - -pub const CURLOPTTYPE_LONG: c_int = 0; -pub const CURLOPTTYPE_OBJECTPOINT: c_int = 10_000; -pub const CURLOPTTYPE_FUNCTIONPOINT: c_int = 20_000; -pub const CURLOPTTYPE_OFF_T: c_int = 30_000; - -pub const CURL_VERSION_NOW: CURLversion = CURLversion::CURL_VERSION_FOURTH; -pub const CURL_VERSION_IPV6: c_int = (1 << 0); -pub const CURL_VERSION_KERBEROS4: c_int = (1 << 1); -pub const CURL_VERSION_SSL: c_int = (1 << 2); -pub const CURL_VERSION_LIBZ: c_int = (1 << 3); -pub const CURL_VERSION_NTLM: c_int = (1 << 4); -pub const CURL_VERSION_GSSNEGOTIATE: c_int = (1 << 5); -pub const CURL_VERSION_DEBUG: c_int = (1 << 6); -pub const CURL_VERSION_ASYNCHDNS: c_int = (1 << 7); -pub const CURL_VERSION_SPNEGO: c_int = (1 << 8); -pub const CURL_VERSION_LARGEFILE: c_int = (1 << 9); -pub const CURL_VERSION_IDN: c_int = (1 << 10); -pub const CURL_VERSION_SSPI: c_int = (1 << 11); -pub const CURL_VERSION_CONV: c_int = (1 << 12); -pub const CURL_VERSION_CURLDEBUG: c_int = (1 << 13); -pub const CURL_VERSION_TLSAUTH_SRP: c_int = (1 << 14); -pub const CURL_VERSION_NTLM_WB: c_int = (1 << 15); -pub const CURL_VERSION_HTTP2: c_int = (1 << 16); +pub type CURLMcode = c_int; +pub const CURLM_CALL_MULTI_PERFORM: CURLMcode = -1; +pub const CURLM_OK: CURLMcode = 0; +pub const CURLM_BAD_HANDLE: CURLMcode = 1; +pub const CURLM_BAD_EASY_HANDLE: CURLMcode = 2; +pub const CURLM_OUT_OF_MEMORY: CURLMcode = 3; +pub const CURLM_INTERNAL_ERROR: CURLMcode = 4; +pub const CURLM_BAD_SOCKET: CURLMcode = 5; +pub const CURLM_UNKNOWN_OPTION: CURLMcode = 6; +pub const CURLM_ADDED_ALREADY: CURLMcode = 7; + +pub type CURLMSG = __enum_ty; +pub const CURLMSG_NONE: CURLMSG = 0; +pub const CURLMSG_DONE: CURLMSG = 1; #[repr(C)] -#[derive(Copy, Clone, Debug)] -pub enum CURLcode { - CURLE_OK = 0, - CURLE_UNSUPPORTED_PROTOCOL, /* 1 */ - CURLE_FAILED_INIT, /* 2 */ - CURLE_URL_MALFORMAT, /* 3 */ - CURLE_NOT_BUILT_IN, /* 4 - [was obsoleted in August 2007 for - 7.17.0, reused in April 2011 for 7.21.5] */ - CURLE_COULDNT_RESOLVE_PROXY, /* 5 */ - CURLE_COULDNT_RESOLVE_HOST, /* 6 */ - CURLE_COULDNT_CONNECT, /* 7 */ - CURLE_FTP_WEIRD_SERVER_REPLY, /* 8 */ - CURLE_REMOTE_ACCESS_DENIED, /* 9 a service was denied by the server - due to lack of access - when login fails - this is not returned. */ - CURLE_FTP_ACCEPT_FAILED, /* 10 - [was obsoleted in April 2006 for - 7.15.4, reused in Dec 2011 for 7.24.0]*/ - CURLE_FTP_WEIRD_PASS_REPLY, /* 11 */ - CURLE_FTP_ACCEPT_TIMEOUT, /* 12 - timeout occurred accepting server - [was obsoleted in August 2007 for 7.17.0, - reused in Dec 2011 for 7.24.0]*/ - CURLE_FTP_WEIRD_PASV_REPLY, /* 13 */ - CURLE_FTP_WEIRD_227_FORMAT, /* 14 */ - CURLE_FTP_CANT_GET_HOST, /* 15 */ - CURLE_OBSOLETE16, /* 16 - NOT USED */ - CURLE_FTP_COULDNT_SET_TYPE, /* 17 */ - CURLE_PARTIAL_FILE, /* 18 */ - CURLE_FTP_COULDNT_RETR_FILE, /* 19 */ - CURLE_OBSOLETE20, /* 20 - NOT USED */ - CURLE_QUOTE_ERROR, /* 21 - quote command failure */ - CURLE_HTTP_RETURNED_ERROR, /* 22 */ - CURLE_WRITE_ERROR, /* 23 */ - CURLE_OBSOLETE24, /* 24 - NOT USED */ - CURLE_UPLOAD_FAILED, /* 25 - failed upload "command" */ - CURLE_READ_ERROR, /* 26 - couldn't open/read from file */ - CURLE_OUT_OF_MEMORY, /* 27 */ - /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error - instead of a memory allocation error if CURL_DOES_CONVERSIONS - is defined - */ - CURLE_OPERATION_TIMEDOUT, /* 28 - the timeout time was reached */ - CURLE_OBSOLETE29, /* 29 - NOT USED */ - CURLE_FTP_PORT_FAILED, /* 30 - FTP PORT operation failed */ - CURLE_FTP_COULDNT_USE_REST, /* 31 - the REST command failed */ - CURLE_OBSOLETE32, /* 32 - NOT USED */ - CURLE_RANGE_ERROR, /* 33 - RANGE "command" didn't work */ - CURLE_HTTP_POST_ERROR, /* 34 */ - CURLE_SSL_CONNECT_ERROR, /* 35 - wrong when connecting with SSL */ - CURLE_BAD_DOWNLOAD_RESUME, /* 36 - couldn't resume download */ - CURLE_FILE_COULDNT_READ_FILE, /* 37 */ - CURLE_LDAP_CANNOT_BIND, /* 38 */ - CURLE_LDAP_SEARCH_FAILED, /* 39 */ - CURLE_OBSOLETE40, /* 40 - NOT USED */ - CURLE_FUNCTION_NOT_FOUND, /* 41 */ - CURLE_ABORTED_BY_CALLBACK, /* 42 */ - CURLE_BAD_FUNCTION_ARGUMENT, /* 43 */ - CURLE_OBSOLETE44, /* 44 - NOT USED */ - CURLE_INTERFACE_FAILED, /* 45 - CURLOPT_INTERFACE failed */ - CURLE_OBSOLETE46, /* 46 - NOT USED */ - CURLE_TOO_MANY_REDIRECTS , /* 47 - catch endless re-direct loops */ - CURLE_UNKNOWN_OPTION, /* 48 - User specified an unknown option */ - CURLE_TELNET_OPTION_SYNTAX , /* 49 - Malformed telnet option */ - CURLE_OBSOLETE50, /* 50 - NOT USED */ - CURLE_PEER_FAILED_VERIFICATION, /* 51 - peer's certificate or fingerprint - wasn't verified fine */ - CURLE_GOT_NOTHING, /* 52 - when this is a specific error */ - CURLE_SSL_ENGINE_NOTFOUND, /* 53 - SSL crypto engine not found */ - CURLE_SSL_ENGINE_SETFAILED, /* 54 - can not set SSL crypto engine as - default */ - CURLE_SEND_ERROR, /* 55 - failed sending network data */ - CURLE_RECV_ERROR, /* 56 - failure in receiving network data */ - CURLE_OBSOLETE57, /* 57 - NOT IN USE */ - CURLE_SSL_CERTPROBLEM, /* 58 - problem with the local certificate */ - CURLE_SSL_CIPHER, /* 59 - couldn't use specified cipher */ - CURLE_SSL_CACERT, /* 60 - problem with the CA cert (path?) */ - CURLE_BAD_CONTENT_ENCODING, /* 61 - Unrecognized/bad encoding */ - CURLE_LDAP_INVALID_URL, /* 62 - Invalid LDAP URL */ - CURLE_FILESIZE_EXCEEDED, /* 63 - Maximum file size exceeded */ - CURLE_USE_SSL_FAILED, /* 64 - Requested FTP SSL level failed */ - CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind - that failed */ - CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */ - CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not - accepted and we failed to login */ - CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */ - CURLE_TFTP_PERM, /* 69 - permission problem on server */ - CURLE_REMOTE_DISK_FULL, /* 70 - out of disk space on server */ - CURLE_TFTP_ILLEGAL, /* 71 - Illegal TFTP operation */ - CURLE_TFTP_UNKNOWNID, /* 72 - Unknown transfer ID */ - CURLE_REMOTE_FILE_EXISTS, /* 73 - File already exists */ - CURLE_TFTP_NOSUCHUSER, /* 74 - No such user */ - CURLE_CONV_FAILED, /* 75 - conversion failed */ - CURLE_CONV_REQD, /* 76 - caller must register conversion - callbacks using curl_easy_setopt options - CURLOPT_CONV_FROM_NETWORK_FUNCTION, - CURLOPT_CONV_TO_NETWORK_FUNCTION, and - CURLOPT_CONV_FROM_UTF8_FUNCTION */ - CURLE_SSL_CACERT_BADFILE, /* 77 - could not load CACERT file, missing - or wrong format */ - CURLE_REMOTE_FILE_NOT_FOUND, /* 78 - remote file not found */ - CURLE_SSH, /* 79 - error from the SSH layer, somewhat - generic so the error message will be of - interest when this has happened */ - - CURLE_SSL_SHUTDOWN_FAILED, /* 80 - Failed to shut down the SSL - connection */ - CURLE_AGAIN, /* 81 - socket is not ready for send/recv, - wait till it's ready and try again (Added - in 7.18.2) */ - CURLE_SSL_CRL_BADFILE, /* 82 - could not load CRL file, missing or - wrong format (Added in 7.19.0) */ - CURLE_SSL_ISSUER_ERROR, /* 83 - Issuer check failed. (Added in - 7.19.0) */ - CURLE_FTP_PRET_FAILED, /* 84 - a PRET command failed */ - CURLE_RTSP_CSEQ_ERROR, /* 85 - mismatch of RTSP CSeq numbers */ - CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Ids */ - CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */ - CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */ - CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the - session will be queued */ - CURLE_LAST /* never use! */ +pub struct CURLMsg { + pub msg: CURLMSG, + pub easy_handle: *mut CURL, + pub data: *mut c_void, } -macro_rules! DEFOPT { - ($name:ident, $ty:ident, $num:expr) => ( - #[allow(dead_code)] - pub const $name: CURLoption = $ty + $num; - ) -} +pub const CURL_WAIT_POLLIN: c_short = 0x1; +pub const CURL_WAIT_POLLPRI: c_short = 0x2; +pub const CURL_WAIT_POLLOUT: c_short = 0x4; -macro_rules! ALIAS { - ($name:ident, $to:ident) => ( - #[allow(dead_code)] - pub const $name: CURLoption = $to; - ) +#[repr(C)] +pub struct curl_waitfd { + pub fd: curl_socket_t, + pub events: c_short, + pub revents: c_short, } -DEFOPT!(CURLOPT_FILE, CURLOPTTYPE_OBJECTPOINT, 1); -DEFOPT!(CURLOPT_URL, CURLOPTTYPE_OBJECTPOINT, 2); -DEFOPT!(CURLOPT_PORT, CURLOPTTYPE_LONG, 3); -DEFOPT!(CURLOPT_PROXY, CURLOPTTYPE_OBJECTPOINT, 4); -DEFOPT!(CURLOPT_USERPWD, CURLOPTTYPE_OBJECTPOINT, 5); -DEFOPT!(CURLOPT_PROXYUSERPWD, CURLOPTTYPE_OBJECTPOINT, 6); -DEFOPT!(CURLOPT_RANGE, CURLOPTTYPE_OBJECTPOINT, 7); -/* 8: not used */ -DEFOPT!(CURLOPT_INFILE, CURLOPTTYPE_OBJECTPOINT, 9); -DEFOPT!(CURLOPT_ERRORBUFFER, CURLOPTTYPE_OBJECTPOINT, 10); -DEFOPT!(CURLOPT_WRITEFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 11); -DEFOPT!(CURLOPT_READFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 12); -DEFOPT!(CURLOPT_TIMEOUT, CURLOPTTYPE_LONG, 13); -DEFOPT!(CURLOPT_INFILESIZE, CURLOPTTYPE_LONG, 14); -DEFOPT!(CURLOPT_POSTFIELDS, CURLOPTTYPE_OBJECTPOINT, 15); -DEFOPT!(CURLOPT_REFERER, CURLOPTTYPE_OBJECTPOINT, 16); -DEFOPT!(CURLOPT_FTPPORT, CURLOPTTYPE_OBJECTPOINT, 17); -DEFOPT!(CURLOPT_USERAGENT, CURLOPTTYPE_OBJECTPOINT, 18); -DEFOPT!(CURLOPT_LOW_SPEED_LIMIT, CURLOPTTYPE_LONG, 19); -DEFOPT!(CURLOPT_LOW_SPEED_TIME, CURLOPTTYPE_LONG, 20); -DEFOPT!(CURLOPT_RESUME_FROM, CURLOPTTYPE_LONG, 21); -DEFOPT!(CURLOPT_COOKIE, CURLOPTTYPE_OBJECTPOINT, 22); -DEFOPT!(CURLOPT_HTTPHEADER, CURLOPTTYPE_OBJECTPOINT, 23); -DEFOPT!(CURLOPT_HTTPPOST, CURLOPTTYPE_OBJECTPOINT, 24); -DEFOPT!(CURLOPT_SSLCERT, CURLOPTTYPE_OBJECTPOINT, 25); -DEFOPT!(CURLOPT_KEYPASSWD, CURLOPTTYPE_OBJECTPOINT, 26); -DEFOPT!(CURLOPT_CRLF, CURLOPTTYPE_LONG, 27); -DEFOPT!(CURLOPT_QUOTE, CURLOPTTYPE_OBJECTPOINT, 28); -DEFOPT!(CURLOPT_WRITEHEADER, CURLOPTTYPE_OBJECTPOINT, 29); -/* 30: not used */ -DEFOPT!(CURLOPT_COOKIEFILE, CURLOPTTYPE_OBJECTPOINT, 31); -DEFOPT!(CURLOPT_SSLVERSION, CURLOPTTYPE_LONG, 32); -DEFOPT!(CURLOPT_TIMECONDITION, CURLOPTTYPE_LONG, 33); -DEFOPT!(CURLOPT_TIMEVALUE, CURLOPTTYPE_LONG, 34); -/* 35: not used */ -DEFOPT!(CURLOPT_CUSTOMREQUEST, CURLOPTTYPE_OBJECTPOINT, 36); -DEFOPT!(CURLOPT_STDERR, CURLOPTTYPE_OBJECTPOINT, 37); -/* 38: not used */ -DEFOPT!(CURLOPT_POSTQUOTE, CURLOPTTYPE_OBJECTPOINT, 39); -DEFOPT!(CURLOPT_WRITEINFO, CURLOPTTYPE_OBJECTPOINT, 40); -DEFOPT!(CURLOPT_VERBOSE, CURLOPTTYPE_LONG, 41); -DEFOPT!(CURLOPT_HEADER, CURLOPTTYPE_LONG, 42); -DEFOPT!(CURLOPT_NOPROGRESS, CURLOPTTYPE_LONG, 43); -DEFOPT!(CURLOPT_NOBODY, CURLOPTTYPE_LONG, 44); -DEFOPT!(CURLOPT_FAILONERROR, CURLOPTTYPE_LONG, 45); -DEFOPT!(CURLOPT_UPLOAD, CURLOPTTYPE_LONG, 46); -DEFOPT!(CURLOPT_POST, CURLOPTTYPE_LONG, 47); -DEFOPT!(CURLOPT_DIRLISTONLY, CURLOPTTYPE_LONG, 48); -DEFOPT!(CURLOPT_APPEND, CURLOPTTYPE_LONG, 50); -DEFOPT!(CURLOPT_NETRC, CURLOPTTYPE_LONG, 51); -DEFOPT!(CURLOPT_FOLLOWLOCATION, CURLOPTTYPE_LONG, 52); -DEFOPT!(CURLOPT_TRANSFERTEXT, CURLOPTTYPE_LONG, 53); -DEFOPT!(CURLOPT_PUT, CURLOPTTYPE_LONG, 54); -/* 55: not used */ -DEFOPT!(CURLOPT_PROGRESSFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 56); -DEFOPT!(CURLOPT_PROGRESSDATA, CURLOPTTYPE_OBJECTPOINT, 57); -DEFOPT!(CURLOPT_AUTOREFERER, CURLOPTTYPE_LONG, 58); -DEFOPT!(CURLOPT_PROXYPORT, CURLOPTTYPE_LONG, 59); -DEFOPT!(CURLOPT_POSTFIELDSIZE, CURLOPTTYPE_LONG, 60); -DEFOPT!(CURLOPT_HTTPPROXYTUNNEL, CURLOPTTYPE_LONG, 61); -DEFOPT!(CURLOPT_INTERFACE, CURLOPTTYPE_OBJECTPOINT, 62); -DEFOPT!(CURLOPT_KRBLEVEL, CURLOPTTYPE_OBJECTPOINT, 63); -DEFOPT!(CURLOPT_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 64); -DEFOPT!(CURLOPT_CAINFO, CURLOPTTYPE_OBJECTPOINT, 65); -/* 66: not used */ -/* 67: not used */ -DEFOPT!(CURLOPT_MAXREDIRS, CURLOPTTYPE_LONG, 68); -DEFOPT!(CURLOPT_FILETIME, CURLOPTTYPE_LONG, 69); -DEFOPT!(CURLOPT_TELNETOPTIONS, CURLOPTTYPE_OBJECTPOINT, 70); -DEFOPT!(CURLOPT_MAXCONNECTS, CURLOPTTYPE_LONG, 71); -DEFOPT!(CURLOPT_CLOSEPOLICY, CURLOPTTYPE_LONG, 72); -/* 73: not used */ -DEFOPT!(CURLOPT_FRESH_CONNECT, CURLOPTTYPE_LONG, 74); -DEFOPT!(CURLOPT_FORBID_REUSE, CURLOPTTYPE_LONG, 75); -DEFOPT!(CURLOPT_RANDOM_FILE, CURLOPTTYPE_OBJECTPOINT, 76); -DEFOPT!(CURLOPT_EGDSOCKET, CURLOPTTYPE_OBJECTPOINT, 77); -DEFOPT!(CURLOPT_CONNECTTIMEOUT, CURLOPTTYPE_LONG, 78); -DEFOPT!(CURLOPT_HEADERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 79); -DEFOPT!(CURLOPT_HTTPGET, CURLOPTTYPE_LONG, 80); -DEFOPT!(CURLOPT_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 81); -DEFOPT!(CURLOPT_COOKIEJAR, CURLOPTTYPE_OBJECTPOINT, 82); -DEFOPT!(CURLOPT_SSL_CIPHER_LIST, CURLOPTTYPE_OBJECTPOINT, 83); -DEFOPT!(CURLOPT_HTTP_VERSION, CURLOPTTYPE_LONG, 84); -DEFOPT!(CURLOPT_FTP_USE_EPSV, CURLOPTTYPE_LONG, 85); -DEFOPT!(CURLOPT_SSLCERTTYPE, CURLOPTTYPE_OBJECTPOINT, 86); -DEFOPT!(CURLOPT_SSLKEY, CURLOPTTYPE_OBJECTPOINT, 87); -DEFOPT!(CURLOPT_SSLKEYTYPE, CURLOPTTYPE_OBJECTPOINT, 88); -DEFOPT!(CURLOPT_SSLENGINE, CURLOPTTYPE_OBJECTPOINT, 89); -DEFOPT!(CURLOPT_SSLENGINE_DEFAULT, CURLOPTTYPE_LONG, 90); -DEFOPT!(CURLOPT_DNS_USE_GLOBAL_CACHE, CURLOPTTYPE_LONG, 91); -DEFOPT!(CURLOPT_DNS_CACHE_TIMEOUT, CURLOPTTYPE_LONG, 92); -DEFOPT!(CURLOPT_PREQUOTE, CURLOPTTYPE_OBJECTPOINT, 93); -DEFOPT!(CURLOPT_DEBUGFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 94); -DEFOPT!(CURLOPT_DEBUGDATA, CURLOPTTYPE_OBJECTPOINT, 95); -DEFOPT!(CURLOPT_COOKIESESSION, CURLOPTTYPE_LONG, 96); -DEFOPT!(CURLOPT_CAPATH, CURLOPTTYPE_OBJECTPOINT, 97); -DEFOPT!(CURLOPT_BUFFERSIZE, CURLOPTTYPE_LONG, 98); -DEFOPT!(CURLOPT_NOSIGNAL, CURLOPTTYPE_LONG, 99); -DEFOPT!(CURLOPT_SHARE, CURLOPTTYPE_OBJECTPOINT, 100); -DEFOPT!(CURLOPT_PROXYTYPE, CURLOPTTYPE_LONG, 101); -DEFOPT!(CURLOPT_ACCEPT_ENCODING, CURLOPTTYPE_OBJECTPOINT, 102); -DEFOPT!(CURLOPT_PRIVATE, CURLOPTTYPE_OBJECTPOINT, 103); -DEFOPT!(CURLOPT_HTTP200ALIASES, CURLOPTTYPE_OBJECTPOINT, 104); -DEFOPT!(CURLOPT_UNRESTRICTED_AUTH, CURLOPTTYPE_LONG, 105); -DEFOPT!(CURLOPT_FTP_USE_EPRT, CURLOPTTYPE_LONG, 106); -DEFOPT!(CURLOPT_HTTPAUTH, CURLOPTTYPE_LONG, 107); -DEFOPT!(CURLOPT_SSL_CTX_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 108); -DEFOPT!(CURLOPT_SSL_CTX_DATA, CURLOPTTYPE_OBJECTPOINT, 109); -DEFOPT!(CURLOPT_FTP_CREATE_MISSING_DIRS, CURLOPTTYPE_LONG, 110); -DEFOPT!(CURLOPT_PROXYAUTH, CURLOPTTYPE_LONG, 111); -DEFOPT!(CURLOPT_FTP_RESPONSE_TIMEOUT, CURLOPTTYPE_LONG, 112); -DEFOPT!(CURLOPT_IPRESOLVE, CURLOPTTYPE_LONG, 113); -DEFOPT!(CURLOPT_MAXFILESIZE, CURLOPTTYPE_LONG, 114); -DEFOPT!(CURLOPT_INFILESIZE_LARGE, CURLOPTTYPE_OFF_T, 115); -DEFOPT!(CURLOPT_RESUME_FROM_LARGE, CURLOPTTYPE_OFF_T, 116); -DEFOPT!(CURLOPT_MAXFILESIZE_LARGE, CURLOPTTYPE_OFF_T, 117); -DEFOPT!(CURLOPT_NETRC_FILE, CURLOPTTYPE_OBJECTPOINT, 118); -DEFOPT!(CURLOPT_USE_SSL, CURLOPTTYPE_LONG, 119); -DEFOPT!(CURLOPT_POSTFIELDSIZE_LARGE, CURLOPTTYPE_OFF_T, 120); -DEFOPT!(CURLOPT_TCP_NODELAY, CURLOPTTYPE_LONG, 121); -/* 122 - 128: not used */ -DEFOPT!(CURLOPT_FTPSSLAUTH, CURLOPTTYPE_LONG, 129); -DEFOPT!(CURLOPT_IOCTLFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 130); -DEFOPT!(CURLOPT_IOCTLDATA, CURLOPTTYPE_OBJECTPOINT, 131); -/* 132, CURLOPTTYPE_133: not used */ -DEFOPT!(CURLOPT_FTP_ACCOUNT, CURLOPTTYPE_OBJECTPOINT, 134); -DEFOPT!(CURLOPT_COOKIELIST, CURLOPTTYPE_OBJECTPOINT, 135); -DEFOPT!(CURLOPT_IGNORE_CONTENT_LENGTH, CURLOPTTYPE_LONG, 136); -DEFOPT!(CURLOPT_FTP_SKIP_PASV_IP, CURLOPTTYPE_LONG, 137); -DEFOPT!(CURLOPT_FTP_FILEMETHOD, CURLOPTTYPE_LONG, 138); -DEFOPT!(CURLOPT_LOCALPORT, CURLOPTTYPE_LONG, 139); -DEFOPT!(CURLOPT_LOCALPORTRANGE, CURLOPTTYPE_LONG, 140); -DEFOPT!(CURLOPT_CONNECT_ONLY, CURLOPTTYPE_LONG, 141); -DEFOPT!(CURLOPT_CONV_FROM_NETWORK_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 142); -DEFOPT!(CURLOPT_CONV_TO_NETWORK_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 143); -DEFOPT!(CURLOPT_CONV_FROM_UTF8_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 144); -DEFOPT!(CURLOPT_MAX_SEND_SPEED_LARGE, CURLOPTTYPE_OFF_T, 145); -DEFOPT!(CURLOPT_MAX_RECV_SPEED_LARGE, CURLOPTTYPE_OFF_T, 146); -DEFOPT!(CURLOPT_FTP_ALTERNATIVE_TO_USER, CURLOPTTYPE_OBJECTPOINT, 147); -DEFOPT!(CURLOPT_SOCKOPTFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 148); -DEFOPT!(CURLOPT_SOCKOPTDATA, CURLOPTTYPE_OBJECTPOINT, 149); -DEFOPT!(CURLOPT_SSL_SESSIONID_CACHE, CURLOPTTYPE_LONG, 150); -DEFOPT!(CURLOPT_SSH_AUTH_TYPES, CURLOPTTYPE_LONG, 151); -DEFOPT!(CURLOPT_SSH_PUBLIC_KEYFILE, CURLOPTTYPE_OBJECTPOINT, 152); -DEFOPT!(CURLOPT_SSH_PRIVATE_KEYFILE, CURLOPTTYPE_OBJECTPOINT, 153); -DEFOPT!(CURLOPT_FTP_SSL_CCC, CURLOPTTYPE_LONG, 154); -DEFOPT!(CURLOPT_TIMEOUT_MS, CURLOPTTYPE_LONG, 155); -DEFOPT!(CURLOPT_CONNECTTIMEOUT_MS, CURLOPTTYPE_LONG, 156); -DEFOPT!(CURLOPT_HTTP_TRANSFER_DECODING, CURLOPTTYPE_LONG, 157); -DEFOPT!(CURLOPT_HTTP_CONTENT_DECODING, CURLOPTTYPE_LONG, 158); -DEFOPT!(CURLOPT_NEW_FILE_PERMS, CURLOPTTYPE_LONG, 159); -DEFOPT!(CURLOPT_NEW_DIRECTORY_PERMS, CURLOPTTYPE_LONG, 160); -DEFOPT!(CURLOPT_POSTREDIR, CURLOPTTYPE_LONG, 161); -DEFOPT!(CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, CURLOPTTYPE_OBJECTPOINT, 162); -DEFOPT!(CURLOPT_OPENSOCKETFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 163); -DEFOPT!(CURLOPT_OPENSOCKETDATA, CURLOPTTYPE_OBJECTPOINT, 164); -DEFOPT!(CURLOPT_COPYPOSTFIELDS, CURLOPTTYPE_OBJECTPOINT, 165); -DEFOPT!(CURLOPT_PROXY_TRANSFER_MODE, CURLOPTTYPE_LONG, 166); -DEFOPT!(CURLOPT_SEEKFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 167); -DEFOPT!(CURLOPT_SEEKDATA, CURLOPTTYPE_OBJECTPOINT, 168); -DEFOPT!(CURLOPT_CRLFILE, CURLOPTTYPE_OBJECTPOINT, 169); -DEFOPT!(CURLOPT_ISSUERCERT, CURLOPTTYPE_OBJECTPOINT, 170); -DEFOPT!(CURLOPT_ADDRESS_SCOPE, CURLOPTTYPE_LONG, 171); -DEFOPT!(CURLOPT_CERTINFO, CURLOPTTYPE_LONG, 172); -DEFOPT!(CURLOPT_USERNAME, CURLOPTTYPE_OBJECTPOINT, 173); -DEFOPT!(CURLOPT_PASSWORD, CURLOPTTYPE_OBJECTPOINT, 174); -DEFOPT!(CURLOPT_PROXYUSERNAME, CURLOPTTYPE_OBJECTPOINT, 175); -DEFOPT!(CURLOPT_PROXYPASSWORD, CURLOPTTYPE_OBJECTPOINT, 176); -DEFOPT!(CURLOPT_NOPROXY, CURLOPTTYPE_OBJECTPOINT, 177); -DEFOPT!(CURLOPT_TFTP_BLKSIZE, CURLOPTTYPE_LONG, 178); -DEFOPT!(CURLOPT_SOCKS5_GSSAPI_SERVICE, CURLOPTTYPE_OBJECTPOINT, 179); -DEFOPT!(CURLOPT_SOCKS5_GSSAPI_NEC, CURLOPTTYPE_LONG, 180); -DEFOPT!(CURLOPT_PROTOCOLS, CURLOPTTYPE_LONG, 181); -DEFOPT!(CURLOPT_REDIR_PROTOCOLS, CURLOPTTYPE_LONG, 182); -DEFOPT!(CURLOPT_SSH_KNOWNHOSTS, CURLOPTTYPE_OBJECTPOINT, 183); -DEFOPT!(CURLOPT_SSH_KEYFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 184); -DEFOPT!(CURLOPT_SSH_KEYDATA, CURLOPTTYPE_OBJECTPOINT, 185); -DEFOPT!(CURLOPT_MAIL_FROM, CURLOPTTYPE_OBJECTPOINT, 186); -DEFOPT!(CURLOPT_MAIL_RCPT, CURLOPTTYPE_OBJECTPOINT, 187); -DEFOPT!(CURLOPT_FTP_USE_PRET, CURLOPTTYPE_LONG, 188); -DEFOPT!(CURLOPT_RTSP_REQUEST, CURLOPTTYPE_LONG, 189); -DEFOPT!(CURLOPT_RTSP_SESSION_ID, CURLOPTTYPE_OBJECTPOINT, 190); -DEFOPT!(CURLOPT_RTSP_STREAM_URI, CURLOPTTYPE_OBJECTPOINT, 191); -DEFOPT!(CURLOPT_RTSP_TRANSPORT, CURLOPTTYPE_OBJECTPOINT, 192); -DEFOPT!(CURLOPT_RTSP_CLIENT_CSEQ, CURLOPTTYPE_LONG, 193); -DEFOPT!(CURLOPT_RTSP_SERVER_CSEQ, CURLOPTTYPE_LONG, 194); -DEFOPT!(CURLOPT_INTERLEAVEDATA, CURLOPTTYPE_OBJECTPOINT, 195); -DEFOPT!(CURLOPT_INTERLEAVEFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 196); -DEFOPT!(CURLOPT_WILDCARDMATCH, CURLOPTTYPE_LONG, 197); -DEFOPT!(CURLOPT_CHUNK_BGN_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 198); -DEFOPT!(CURLOPT_CHUNK_END_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 199); -DEFOPT!(CURLOPT_FNMATCH_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 200); -DEFOPT!(CURLOPT_CHUNK_DATA, CURLOPTTYPE_OBJECTPOINT, 201); -DEFOPT!(CURLOPT_FNMATCH_DATA, CURLOPTTYPE_OBJECTPOINT, 202); -DEFOPT!(CURLOPT_RESOLVE, CURLOPTTYPE_OBJECTPOINT, 203); -DEFOPT!(CURLOPT_TLSAUTH_USERNAME, CURLOPTTYPE_OBJECTPOINT, 204); -DEFOPT!(CURLOPT_TLSAUTH_PASSWORD, CURLOPTTYPE_OBJECTPOINT, 205); -DEFOPT!(CURLOPT_TLSAUTH_TYPE, CURLOPTTYPE_OBJECTPOINT, 206); -DEFOPT!(CURLOPT_TRANSFER_ENCODING, CURLOPTTYPE_LONG, 207); -DEFOPT!(CURLOPT_CLOSESOCKETFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 208); -DEFOPT!(CURLOPT_CLOSESOCKETDATA, CURLOPTTYPE_OBJECTPOINT, 209); -DEFOPT!(CURLOPT_GSSAPI_DELEGATION, CURLOPTTYPE_LONG, 210); -DEFOPT!(CURLOPT_DNS_SERVERS, CURLOPTTYPE_OBJECTPOINT, 211); -DEFOPT!(CURLOPT_ACCEPTTIMEOUT_MS, CURLOPTTYPE_LONG, 212); -DEFOPT!(CURLOPT_TCP_KEEPALIVE, CURLOPTTYPE_LONG, 213); -DEFOPT!(CURLOPT_TCP_KEEPIDLE, CURLOPTTYPE_LONG, 214); -DEFOPT!(CURLOPT_TCP_KEEPINTVL, CURLOPTTYPE_LONG, 215); -DEFOPT!(CURLOPT_SSL_OPTIONS, CURLOPTTYPE_LONG, 216); -DEFOPT!(CURLOPT_MAIL_AUTH, CURLOPTTYPE_OBJECTPOINT, 217); -DEFOPT!(CURLOPT_SASL_IR, CURLOPTTYPE_LONG, 218); -DEFOPT!(CURLOPT_XFERINFOFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 219); -DEFOPT!(CURLOPT_XOAUTH2_BEARER, CURLOPTTYPE_OBJECTPOINT, 220); -DEFOPT!(CURLOPT_DNS_INTERFACE, CURLOPTTYPE_OBJECTPOINT, 221); -DEFOPT!(CURLOPT_DNS_LOCAL_IP4, CURLOPTTYPE_OBJECTPOINT, 222); -DEFOPT!(CURLOPT_DNS_LOCAL_IP6, CURLOPTTYPE_OBJECTPOINT, 223); -DEFOPT!(CURLOPT_LOGIN_OPTIONS, CURLOPTTYPE_OBJECTPOINT, 224); -DEFOPT!(CURLOPT_SSL_ENABLE_NPN, CURLOPTTYPE_LONG, 225); -DEFOPT!(CURLOPT_SSL_ENABLE_ALPN, CURLOPTTYPE_LONG, 226); -DEFOPT!(CURLOPT_EXPECT_100_TIMEOUT_MS, CURLOPTTYPE_LONG, 227); -DEFOPT!(CURLOPT_PROXYHEADER, CURLOPTTYPE_OBJECTPOINT, 228); -DEFOPT!(CURLOPT_HEADEROPT, CURLOPTTYPE_LONG, 229); - -// Option aliases -ALIAS!(CURLOPT_POST301, CURLOPT_POSTREDIR); -ALIAS!(CURLOPT_SSLKEYPASSWD, CURLOPT_KEYPASSWD); -ALIAS!(CURLOPT_FTPAPPEND, CURLOPT_APPEND); -ALIAS!(CURLOPT_FTPLISTONLY, CURLOPT_DIRLISTONLY); -ALIAS!(CURLOPT_FTP_SSL, CURLOPT_USE_SSL); -ALIAS!(CURLOPT_SSLCERTPASSWD, CURLOPT_KEYPASSWD); -ALIAS!(CURLOPT_KRB4LEVEL, CURLOPT_KRBLEVEL); -ALIAS!(CURLOPT_READDATA, CURLOPT_INFILE); -ALIAS!(CURLOPT_WRITEDATA, CURLOPT_FILE); -ALIAS!(CURLOPT_HEADERDATA, CURLOPT_WRITEHEADER); -ALIAS!(CURLOPT_XFERINFODATA, CURLOPT_PROGRESSDATA); +pub const CURL_POLL_NONE: c_int = 0; +pub const CURL_POLL_IN: c_int = 1; +pub const CURL_POLL_OUT: c_int = 2; +pub const CURL_POLL_INOUT: c_int = 3; +pub const CURL_POLL_REMOVE: c_int = 4; +pub const CURL_SOCKET_TIMEOUT: curl_socket_t = CURL_SOCKET_BAD; + +pub type curl_socket_callback = extern fn(*mut CURL, + curl_socket_t, + c_int, + *mut c_void, + *mut c_void) -> c_int; +pub type curl_multi_timer_callback = extern fn(*mut CURLM, + c_long, + *mut c_void) -> c_int; + +pub type CURLMoption = __enum_ty; +pub const CURLMOPT_SOCKETFUNCTION: CURLMoption = CURLOPTTYPE_FUNCTIONPOINT + 1; +pub const CURLMOPT_SOCKETDATA: CURLMoption = CURLOPTTYPE_OBJECTPOINT + 2; +pub const CURLMOPT_PIPELINING: CURLMoption = CURLOPTTYPE_LONG + 3; +pub const CURLMOPT_TIMERFUNCTION: CURLMoption = CURLOPTTYPE_FUNCTIONPOINT + 4; +pub const CURLMOPT_TIMERDATA: CURLMoption = CURLOPTTYPE_OBJECTPOINT + 5; +pub const CURLMOPT_MAXCONNECTS: CURLMoption = CURLOPTTYPE_LONG + 6; +pub const CURLMOPT_MAX_HOST_CONNECTIONS: CURLMoption = CURLOPTTYPE_LONG + 7; +pub const CURLMOPT_MAX_PIPELINE_LENGTH: CURLMoption = CURLOPTTYPE_LONG + 8; +pub const CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: CURLMoption = CURLOPTTYPE_OFF_T + 9; +pub const CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: CURLMoption = CURLOPTTYPE_OFF_T + 10; +pub const CURLMOPT_PIPELINING_SITE_BL: CURLMoption = CURLOPTTYPE_OBJECTPOINT + 11; +pub const CURLMOPT_PIPELINING_SERVER_BL: CURLMoption = CURLOPTTYPE_OBJECTPOINT + 12; +pub const CURLMOPT_MAX_TOTAL_CONNECTIONS: CURLMoption = CURLOPTTYPE_LONG + 13; extern { - pub fn curl_easy_strerror(code: CURLcode) -> *const c_char; - pub fn curl_easy_init() -> *mut CURL; - pub fn curl_easy_setopt(curl: *mut CURL, option: CURLoption, ...) -> CURLcode; - pub fn curl_easy_perform(curl: *mut CURL) -> CURLcode; - pub fn curl_easy_cleanup(curl: *mut CURL); - pub fn curl_easy_getinfo(curl: *const CURL, info: CURLINFO, ...) -> CURLcode; + pub fn curl_formadd(httppost: *mut *mut curl_httppost, + last_post: *mut *mut curl_httppost, + ...) -> CURLFORMcode; + pub fn curl_formget(form: *mut curl_httppost, + arg: *mut c_void, + append: curl_formget_callback) -> c_int; + pub fn curl_formfree(form: *mut curl_httppost); + + pub fn curl_version() -> *mut c_char; + + pub fn curl_easy_escape(handle: *mut CURL, + string: *const c_char, + length: c_int) -> *mut c_char; + pub fn curl_easy_unescape(handle: *mut CURL, + string: *const c_char, + length: c_int, + outlength: *mut c_int) -> *mut c_char; + pub fn curl_free(p: *mut c_void); + + pub fn curl_global_init(flags: c_long) -> CURLcode; + pub fn curl_global_init_mem(flags: c_long, + m: curl_malloc_callback, + f: curl_free_callback, + r: curl_realloc_callback, + s: curl_strdup_callback, + c: curl_calloc_callback) -> CURLcode; pub fn curl_global_cleanup(); pub fn curl_slist_append(list: *mut curl_slist, - val: *const u8) -> *mut curl_slist; + val: *const c_char) -> *mut curl_slist; pub fn curl_slist_free_all(list: *mut curl_slist); - pub fn curl_version() -> *const c_char; + pub fn curl_getdate(p: *const c_char, _: *const time_t) -> time_t; + + pub fn curl_share_init() -> *mut CURLSH; + pub fn curl_share_setopt(sh: *mut CURLSH, + opt: CURLSHoption, + ...) -> CURLSHcode; + pub fn curl_share_cleanup(sh: *mut CURLSH) -> CURLSHcode; + pub fn curl_version_info(t: CURLversion) -> *mut curl_version_info_data; + + pub fn curl_easy_strerror(code: CURLcode) -> *const c_char; + pub fn curl_share_strerror(code: CURLSHcode) -> *const c_char; + pub fn curl_easy_pause(handle: *mut CURL, bitmask: c_int) -> CURLcode; + + pub fn curl_easy_init() -> *mut CURL; + pub fn curl_easy_setopt(curl: *mut CURL, option: CURLoption, ...) -> CURLcode; + pub fn curl_easy_perform(curl: *mut CURL) -> CURLcode; + pub fn curl_easy_cleanup(curl: *mut CURL); + pub fn curl_easy_getinfo(curl: *mut CURL, info: CURLINFO, ...) -> CURLcode; + pub fn curl_easy_duphandle(curl: *mut CURL) -> *mut CURL; + pub fn curl_easy_reset(curl: *mut CURL); + pub fn curl_easy_recv(curl: *mut CURL, + buffer: *mut c_void, + buflen: size_t, + n: *mut size_t) -> CURLcode; + pub fn curl_easy_send(curl: *mut CURL, + buffer: *const c_void, + buflen: size_t, + n: *mut size_t) -> CURLcode; + + pub fn curl_multi_init() -> *mut CURLM; + pub fn curl_multi_add_handle(multi_handle: *mut CURLM, + curl_handle: *mut CURL) -> CURLMcode; + pub fn curl_multi_remove_handle(multi_handle: *mut CURLM, + curl_handle: *mut CURL) -> CURLMcode; + pub fn curl_multi_fdset(multi_handle: *mut CURLM, + read_fd_set: *mut libc::fd_set, + write_fd_set: *mut libc::fd_set, + exc_fd_set: *mut libc::fd_set, + max_fd: *mut c_int) -> CURLMcode; + pub fn curl_multi_wait(multi_handle: *mut CURLM, + extra_fds: *mut curl_waitfd, + extra_nfds: c_uint, + timeout_ms: c_int, + ret: *mut c_int) -> CURLMcode; + pub fn curl_multi_perform(multi_handle: *mut CURLM, + running_handles: *mut c_int) -> CURLMcode; + pub fn curl_multi_cleanup(multi_handle: *mut CURLM) -> CURLMcode; + pub fn curl_multi_info_read(multi_handle: *mut CURLM, + msgs_in_queue: *mut c_int) -> *mut CURLMsg; + pub fn curl_multi_strerror(code: CURLMcode) -> *const c_char; + pub fn curl_multi_socket(multi_handle: *mut CURLM, + s: curl_socket_t, + running_handles: *mut c_int) -> CURLMcode; + pub fn curl_multi_socket_action(multi_handle: *mut CURLM, + s: curl_socket_t, + ev_bitmask: c_int, + running_handles: *mut c_int) -> CURLMcode; + pub fn curl_multi_socket_all(multi_handle: *mut CURLM, + running_handles: *mut c_int) -> CURLMcode; + pub fn curl_multi_timeout(multi_handle: *mut CURLM, + milliseconds: *mut c_long) -> CURLMcode; + pub fn curl_multi_setopt(multi_handle: *mut CURLM, + option: CURLMoption, + ...) -> CURLMcode; + pub fn curl_multi_assign(multi_handle: *mut CURLM, + sockfd: curl_socket_t, + sockp: *mut c_void) -> CURLMcode; } diff --git a/systest/Cargo.toml b/systest/Cargo.toml new file mode 100644 index 0000000000..05d7886d75 --- /dev/null +++ b/systest/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "systest" +version = "0.1.0" +authors = ["Alex Crichton "] +build = "build.rs" + +[dependencies] +curl-sys = { path = "../curl-sys" } +libc = "0.2" + +[build-dependencies] +ctest = { git = "https://github.com/alexcrichton/ctest" } diff --git a/systest/build.rs b/systest/build.rs new file mode 100644 index 0000000000..56bc080e83 --- /dev/null +++ b/systest/build.rs @@ -0,0 +1,41 @@ +extern crate ctest; + +use std::env; + +fn main() { + let mut cfg = ctest::TestGenerator::new(); + + cfg.header("curl/curl.h"); + if let Ok(out) = env::var("DEP_CURL_INCLUDE") { + cfg.include(&out); + } + cfg.field_name(|s, field| { + if s == "curl_fileinfo" { + field.replace("strings_", "strings.") + } else if s == "CURLMsg" && field == "data" { + "data.whatever".to_string() + } else { + field.to_string() + } + }); + cfg.type_name(|s, is_struct| { + match s { + "CURL" | + "CURLM" | + "CURLSH" | + "curl_version_info_data" => s.to_string(), + "curl_khtype" | + "curl_khstat" | + "curl_khmatch" => format!("enum {}", s), + s if is_struct => format!("struct {}", s), + "sockaddr" => format!("struct sockaddr"), + s => s.to_string(), + } + }); + // cfg.fn_cname(|s, l| l.unwrap_or(s).to_string()); + cfg.skip_type(|n| n == "__enum_ty"); + cfg.skip_signededness(|s| { + s.ends_with("callback") || s.ends_with("function") + }); + cfg.generate("../curl-sys/lib.rs", "all.rs"); +} diff --git a/systest/src/main.rs b/systest/src/main.rs new file mode 100644 index 0000000000..45cf64ac1e --- /dev/null +++ b/systest/src/main.rs @@ -0,0 +1,9 @@ +#![allow(bad_style)] + +extern crate curl_sys; +extern crate libc; + +use libc::*; +use curl_sys::*; + +include!(concat!(env!("OUT_DIR"), "/all.rs")); From e48939025e8ccb3ff6b73b451c0053a3485d9bbb Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 30 Mar 2016 12:01:45 -0700 Subject: [PATCH 02/71] Update travis config and add appveyor --- .travis.yml | 22 +++++++++++++++++----- appveyor.yml | 21 +++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 appveyor.yml diff --git a/.travis.yml b/.travis.yml index 8927d7706c..9202454ed8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,22 @@ language: rust -sudo: false rust: - stable - -os: - - linux - + - beta + - nightly +sudo: false +before_script: + - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH script: + - export CARGO_TARGET_DIR=`pwd`/target - cargo test + - cargo run --manifest-path systest/Cargo.toml + - cargo doc --no-deps + - cargo doc --no-deps --manifest-path=curl-sys/Cargo.toml +after_success: + - travis-cargo --only nightly doc-upload +notifications: + email: + on_success: never +os: + - linux + - osx diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000000..b4ec4e75bb --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,21 @@ +environment: + matrix: + - TARGET: x86_64-pc-windows-gnu + MSYS_BITS: 64 + - TARGET: i686-pc-windows-gnu + MSYS_BITS: 32 + - TARGET: x86_64-pc-windows-msvc + - TARGET: i686-pc-windows-msvc +install: + - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:TARGET}.exe" + - rust-nightly-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust" + - set PATH=%PATH%;C:\Program Files (x86)\Rust\bin + - if defined MSYS_BITS set PATH=C:\msys64\mingw%MSYS_BITS%\bin;C:\msys64\usr\bin;%PATH% + - rustc -V + - cargo -V + +build: false + +test_script: + - cargo test + - cargo run --manifest-path systest/Cargo.toml From 1fac049ea57e1ccb4249391a8fbfa841e20971c6 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 30 Mar 2016 13:05:05 -0700 Subject: [PATCH 03/71] Only build curl-sys for now on travis/appveyor --- .travis.yml | 6 +++--- appveyor.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9202454ed8..1bb2e8bf50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,10 +8,10 @@ before_script: - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH script: - export CARGO_TARGET_DIR=`pwd`/target - - cargo test + - cargo test -p curl-sys - cargo run --manifest-path systest/Cargo.toml - - cargo doc --no-deps - - cargo doc --no-deps --manifest-path=curl-sys/Cargo.toml + #- cargo doc --no-deps + - cargo doc --no-deps -p curl-sys after_success: - travis-cargo --only nightly doc-upload notifications: diff --git a/appveyor.yml b/appveyor.yml index b4ec4e75bb..ed205ae152 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,5 +17,5 @@ install: build: false test_script: - - cargo test + - cargo test -p curl-sys - cargo run --manifest-path systest/Cargo.toml From f0d9016e7946edf19f226ccbd0532869f2b27547 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 30 Mar 2016 22:35:24 -0700 Subject: [PATCH 04/71] Try to fix the windows build --- curl-sys/lib.rs | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/curl-sys/lib.rs b/curl-sys/lib.rs index 4d70728c64..b04508af9d 100644 --- a/curl-sys/lib.rs +++ b/curl-sys/lib.rs @@ -26,6 +26,7 @@ pub enum CURL {} #[cfg(unix)] pub type curl_socket_t = libc::c_int; +#[cfg(unix)] pub const CURL_SOCKET_BAD: curl_socket_t = -1; #[cfg(all(windows, target_pointer_width = "32"))] pub type curl_socket_t = libc::c_uint; @@ -162,18 +163,19 @@ pub type curl_sockopt_callback = extern fn(*mut c_void, curl_socket_t, curlsocktype) -> c_int; -#[repr(C)] -pub struct curl_sockaddr { - pub family: c_int, - pub socktype: c_int, - pub protocol: c_int, - pub addrlen: c_uint, - pub addr: libc::sockaddr, -} - -pub type curl_opensocket_callback = extern fn(*mut c_void, - curlsocktype, - *mut curl_sockaddr) -> curl_socket_t; +// TODO: sort out libc::sockaddr on windows +// #[repr(C)] +// pub struct curl_sockaddr { +// pub family: c_int, +// pub socktype: c_int, +// pub protocol: c_int, +// pub addrlen: c_uint, +// pub addr: libc::sockaddr, +// } +// +// pub type curl_opensocket_callback = extern fn(*mut c_void, +// curlsocktype, +// *mut curl_sockaddr) -> curl_socket_t; pub type curlioerr = __enum_ty; pub const CURLIOE_OK: curlioerr = 0; @@ -951,11 +953,12 @@ extern { curl_handle: *mut CURL) -> CURLMcode; pub fn curl_multi_remove_handle(multi_handle: *mut CURLM, curl_handle: *mut CURL) -> CURLMcode; - pub fn curl_multi_fdset(multi_handle: *mut CURLM, - read_fd_set: *mut libc::fd_set, - write_fd_set: *mut libc::fd_set, - exc_fd_set: *mut libc::fd_set, - max_fd: *mut c_int) -> CURLMcode; + // TODO: sort out libc::fd_set on Windows + // pub fn curl_multi_fdset(multi_handle: *mut CURLM, + // read_fd_set: *mut libc::fd_set, + // write_fd_set: *mut libc::fd_set, + // exc_fd_set: *mut libc::fd_set, + // max_fd: *mut c_int) -> CURLMcode; pub fn curl_multi_wait(multi_handle: *mut CURLM, extra_fds: *mut curl_waitfd, extra_nfds: c_uint, From a1a55f2c40cce0abf141e6ef2e41a1792a0cc2a3 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 30 Mar 2016 22:37:38 -0700 Subject: [PATCH 05/71] Try to fix tests on travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1bb2e8bf50..61d97d38ce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ before_script: script: - export CARGO_TARGET_DIR=`pwd`/target - cargo test -p curl-sys - - cargo run --manifest-path systest/Cargo.toml + - CURL_NO_PKG_CONFIG=1 cargo run --manifest-path systest/Cargo.toml #- cargo doc --no-deps - cargo doc --no-deps -p curl-sys after_success: From ecc9f579e04818d5720bce962688ed5cc57cd0c1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 30 Mar 2016 22:41:34 -0700 Subject: [PATCH 06/71] Fix compile of curl crate --- Cargo.toml | 35 +-------- src/ffi/easy.rs | 2 +- src/ffi/err.rs | 181 ++++++++++++++++++++++----------------------- src/ffi/list.rs | 4 +- src/ffi/opt.rs | 26 +++---- src/ffi/version.rs | 2 +- src/lib.rs | 3 - 7 files changed, 110 insertions(+), 143 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c7eaf49201..f3bc030320 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,14 +2,14 @@ name = "curl" version = "0.2.18" -authors = ["Carl Lerche "] +authors = ["Carl Lerche ", + "Alex Crichton "] license = "MIT" repository = "https://github.com/carllerche/curl-rust" description = "Rust bindings to libcurl for making HTTP requests" [dependencies] url = "0.5.0" -log = "0.3.0" libc = "0.2" curl-sys = { path = "curl-sys", version = "0.1.0" } @@ -19,41 +19,10 @@ env_logger = "0.3.0" # Unix platforms use OpenSSL for now to provide SSL functionality [target."cfg(all(unix, not(target_os = \"macos\")))".dependencies] openssl-sys = "0.7.0" - [target.i686-unknown-linux-gnu.dependencies] openssl-sys = "0.7.0" -[target.i586-unknown-linux-gnu.dependencies] -openssl-sys = "0.7.0" -[target.i686-linux-android.dependencies] -openssl-sys = "0.7.0" [target.x86_64-unknown-linux-gnu.dependencies] openssl-sys = "0.7.0" -[target.x86_64-unknown-linux-musl.dependencies] -openssl-sys = "0.7.0" -[target.arm-unknown-linux-gnueabihf.dependencies] -openssl-sys = "0.7.0" -[target.armv7-unknown-linux-gnueabihf.dependencies] -openssl-sys = "0.7.0" -[target.arm-linux-androideabi.dependencies] -openssl-sys = "0.7.0" -[target.aarch64-unknown-linux-gnu.dependencies] -openssl-sys = "0.7.0" -[target.powerpc-unknown-linux-gnu.dependencies] -openssl-sys = "0.7.0" -[target.powerpc64-unknown-linux-gnu.dependencies] -openssl-sys = "0.7.0" -[target.powerpc64le-unknown-linux-gnu.dependencies] -openssl-sys = "0.7.0" -[target.i686-unknown-freebsd.dependencies] -openssl-sys = "0.7.0" -[target.x86_64-unknown-freebsd.dependencies] -openssl-sys = "0.7.0" -[target.x86_64-unknown-bitrig.dependencies] -openssl-sys = "0.7.0" -[target.x86_64-unknown-openbsd.dependencies] -openssl-sys = "0.7.0" -[target.x86_64-unknown-dragonfly.dependencies] -openssl-sys = "0.7.0" [[test]] diff --git a/src/ffi/easy.rs b/src/ffi/easy.rs index 1a70ac4f8e..6fe27d8cf5 100644 --- a/src/ffi/easy.rs +++ b/src/ffi/easy.rs @@ -107,7 +107,7 @@ impl Easy { fn get_info_long(&self, key: info::Key) -> Result { let v: c_long = 0; let res = err::ErrCode(unsafe { - ffi::curl_easy_getinfo(self.curl as *const _, key, &v) + ffi::curl_easy_getinfo(self.curl, key, &v) }); if !res.is_success() { diff --git a/src/ffi/err.rs b/src/ffi/err.rs index a76cfd7a27..80d46d579e 100644 --- a/src/ffi/err.rs +++ b/src/ffi/err.rs @@ -5,97 +5,96 @@ use std::str; use curl_ffi as ffi; -pub use curl_ffi::CURLcode::CURLE_OK as OK; -pub use curl_ffi::CURLcode::CURLE_UNSUPPORTED_PROTOCOL as UNSUPPORTED_PROTOCOL; -pub use curl_ffi::CURLcode::CURLE_FAILED_INIT as FAILED_INIT; -pub use curl_ffi::CURLcode::CURLE_URL_MALFORMAT as URL_MALFORMAT; -pub use curl_ffi::CURLcode::CURLE_NOT_BUILT_IN as NOT_BUILT_IN; -pub use curl_ffi::CURLcode::CURLE_COULDNT_RESOLVE_PROXY as COULDNT_RESOLVE_PROXY; -pub use curl_ffi::CURLcode::CURLE_COULDNT_RESOLVE_HOST as COULDNT_RESOLVE_HOST; -pub use curl_ffi::CURLcode::CURLE_COULDNT_CONNECT as COULDNT_CONNECT; -pub use curl_ffi::CURLcode::CURLE_FTP_WEIRD_SERVER_REPLY as FTP_WEIRD_SERVER_REPLY; -pub use curl_ffi::CURLcode::CURLE_REMOTE_ACCESS_DENIED as REMOTE_ACCESS_DENIED; -pub use curl_ffi::CURLcode::CURLE_FTP_ACCEPT_FAILED as FTP_ACCEPT_FAILED; -pub use curl_ffi::CURLcode::CURLE_FTP_WEIRD_PASS_REPLY as FTP_WEIRD_PASS_REPLY; -pub use curl_ffi::CURLcode::CURLE_FTP_ACCEPT_TIMEOUT as FTP_ACCEPT_TIMEOUT; -pub use curl_ffi::CURLcode::CURLE_FTP_WEIRD_PASV_REPLY as FTP_WEIRD_PASV_REPLY; -pub use curl_ffi::CURLcode::CURLE_FTP_WEIRD_227_FORMAT as FTP_WEIRD_227_FORMAT; -pub use curl_ffi::CURLcode::CURLE_FTP_CANT_GET_HOST as FTP_CANT_GET_HOST; -pub use curl_ffi::CURLcode::CURLE_OBSOLETE16 as OBSOLETE16; -pub use curl_ffi::CURLcode::CURLE_FTP_COULDNT_SET_TYPE as FTP_COULDNT_SET_TYPE; -pub use curl_ffi::CURLcode::CURLE_PARTIAL_FILE as PARTIAL_FILE; -pub use curl_ffi::CURLcode::CURLE_FTP_COULDNT_RETR_FILE as FTP_COULDNT_RETR_FILE; -pub use curl_ffi::CURLcode::CURLE_OBSOLETE20 as OBSOLETE20; -pub use curl_ffi::CURLcode::CURLE_QUOTE_ERROR as QUOTE_ERROR; -pub use curl_ffi::CURLcode::CURLE_HTTP_RETURNED_ERROR as HTTP_RETURNED_ERROR; -pub use curl_ffi::CURLcode::CURLE_WRITE_ERROR as WRITE_ERROR; -pub use curl_ffi::CURLcode::CURLE_OBSOLETE24 as OBSOLETE24; -pub use curl_ffi::CURLcode::CURLE_UPLOAD_FAILED as UPLOAD_FAILED; -pub use curl_ffi::CURLcode::CURLE_READ_ERROR as READ_ERROR; -pub use curl_ffi::CURLcode::CURLE_OUT_OF_MEMORY as OUT_OF_MEMORY; -pub use curl_ffi::CURLcode::CURLE_OPERATION_TIMEDOUT as OPERATION_TIMEDOUT; -pub use curl_ffi::CURLcode::CURLE_OBSOLETE29 as OBSOLETE29; -pub use curl_ffi::CURLcode::CURLE_FTP_PORT_FAILED as FTP_PORT_FAILED; -pub use curl_ffi::CURLcode::CURLE_FTP_COULDNT_USE_REST as FTP_COULDNT_USE_REST; -pub use curl_ffi::CURLcode::CURLE_OBSOLETE32 as OBSOLETE32; -pub use curl_ffi::CURLcode::CURLE_RANGE_ERROR as RANGE_ERROR; -pub use curl_ffi::CURLcode::CURLE_HTTP_POST_ERROR as HTTP_POST_ERROR; -pub use curl_ffi::CURLcode::CURLE_SSL_CONNECT_ERROR as SSL_CONNECT_ERROR; -pub use curl_ffi::CURLcode::CURLE_BAD_DOWNLOAD_RESUME as BAD_DOWNLOAD_RESUME; -pub use curl_ffi::CURLcode::CURLE_FILE_COULDNT_READ_FILE as FILE_COULDNT_READ_FILE; -pub use curl_ffi::CURLcode::CURLE_LDAP_CANNOT_BIND as LDAP_CANNOT_BIND; -pub use curl_ffi::CURLcode::CURLE_LDAP_SEARCH_FAILED as LDAP_SEARCH_FAILED; -pub use curl_ffi::CURLcode::CURLE_OBSOLETE40 as OBSOLETE40; -pub use curl_ffi::CURLcode::CURLE_FUNCTION_NOT_FOUND as FUNCTION_NOT_FOUND; -pub use curl_ffi::CURLcode::CURLE_ABORTED_BY_CALLBACK as ABORTED_BY_CALLBACK; -pub use curl_ffi::CURLcode::CURLE_BAD_FUNCTION_ARGUMENT as BAD_FUNCTION_ARGUMENT; -pub use curl_ffi::CURLcode::CURLE_OBSOLETE44 as OBSOLETE44; -pub use curl_ffi::CURLcode::CURLE_INTERFACE_FAILED as INTERFACE_FAILED; -pub use curl_ffi::CURLcode::CURLE_OBSOLETE46 as OBSOLETE46; -pub use curl_ffi::CURLcode::CURLE_TOO_MANY_REDIRECTS as TOO_MANY_REDIRECTS ; -pub use curl_ffi::CURLcode::CURLE_UNKNOWN_OPTION as UNKNOWN_OPTION; -pub use curl_ffi::CURLcode::CURLE_TELNET_OPTION_SYNTAX as TELNET_OPTION_SYNTAX ; -pub use curl_ffi::CURLcode::CURLE_OBSOLETE50 as OBSOLETE50; -pub use curl_ffi::CURLcode::CURLE_PEER_FAILED_VERIFICATION as PEER_FAILED_VERIFICATION; -pub use curl_ffi::CURLcode::CURLE_GOT_NOTHING as GOT_NOTHING; -pub use curl_ffi::CURLcode::CURLE_SSL_ENGINE_NOTFOUND as SSL_ENGINE_NOTFOUND; -pub use curl_ffi::CURLcode::CURLE_SSL_ENGINE_SETFAILED as SSL_ENGINE_SETFAILED; -pub use curl_ffi::CURLcode::CURLE_SEND_ERROR as SEND_ERROR; -pub use curl_ffi::CURLcode::CURLE_RECV_ERROR as RECV_ERROR; -pub use curl_ffi::CURLcode::CURLE_OBSOLETE57 as OBSOLETE57; -pub use curl_ffi::CURLcode::CURLE_SSL_CERTPROBLEM as SSL_CERTPROBLEM; -pub use curl_ffi::CURLcode::CURLE_SSL_CIPHER as SSL_CIPHER; -pub use curl_ffi::CURLcode::CURLE_SSL_CACERT as SSL_CACERT; -pub use curl_ffi::CURLcode::CURLE_BAD_CONTENT_ENCODING as BAD_CONTENT_ENCODING; -pub use curl_ffi::CURLcode::CURLE_LDAP_INVALID_URL as LDAP_INVALID_URL; -pub use curl_ffi::CURLcode::CURLE_FILESIZE_EXCEEDED as FILESIZE_EXCEEDED; -pub use curl_ffi::CURLcode::CURLE_USE_SSL_FAILED as USE_SSL_FAILED; -pub use curl_ffi::CURLcode::CURLE_SEND_FAIL_REWIND as SEND_FAIL_REWIND; -pub use curl_ffi::CURLcode::CURLE_SSL_ENGINE_INITFAILED as SSL_ENGINE_INITFAILED; -pub use curl_ffi::CURLcode::CURLE_LOGIN_DENIED as LOGIN_DENIED; -pub use curl_ffi::CURLcode::CURLE_TFTP_NOTFOUND as TFTP_NOTFOUND; -pub use curl_ffi::CURLcode::CURLE_TFTP_PERM as TFTP_PERM; -pub use curl_ffi::CURLcode::CURLE_REMOTE_DISK_FULL as REMOTE_DISK_FULL; -pub use curl_ffi::CURLcode::CURLE_TFTP_ILLEGAL as TFTP_ILLEGAL; -pub use curl_ffi::CURLcode::CURLE_TFTP_UNKNOWNID as TFTP_UNKNOWNID; -pub use curl_ffi::CURLcode::CURLE_REMOTE_FILE_EXISTS as REMOTE_FILE_EXISTS; -pub use curl_ffi::CURLcode::CURLE_TFTP_NOSUCHUSER as TFTP_NOSUCHUSER; -pub use curl_ffi::CURLcode::CURLE_CONV_FAILED as CONV_FAILED; -pub use curl_ffi::CURLcode::CURLE_CONV_REQD as CONV_REQD; -pub use curl_ffi::CURLcode::CURLE_SSL_CACERT_BADFILE as SSL_CACERT_BADFILE; -pub use curl_ffi::CURLcode::CURLE_REMOTE_FILE_NOT_FOUND as REMOTE_FILE_NOT_FOUND; -pub use curl_ffi::CURLcode::CURLE_SSH as SSH; -pub use curl_ffi::CURLcode::CURLE_SSL_SHUTDOWN_FAILED as SSL_SHUTDOWN_FAILED; -pub use curl_ffi::CURLcode::CURLE_AGAIN as AGAIN; -pub use curl_ffi::CURLcode::CURLE_SSL_CRL_BADFILE as SSL_CRL_BADFILE; -pub use curl_ffi::CURLcode::CURLE_SSL_ISSUER_ERROR as SSL_ISSUER_ERROR; -pub use curl_ffi::CURLcode::CURLE_FTP_PRET_FAILED as FTP_PRET_FAILED; -pub use curl_ffi::CURLcode::CURLE_RTSP_CSEQ_ERROR as RTSP_CSEQ_ERROR; -pub use curl_ffi::CURLcode::CURLE_RTSP_SESSION_ERROR as RTSP_SESSION_ERROR; -pub use curl_ffi::CURLcode::CURLE_FTP_BAD_FILE_LIST as FTP_BAD_FILE_LIST; -pub use curl_ffi::CURLcode::CURLE_CHUNK_FAILED as CHUNK_FAILED; -pub use curl_ffi::CURLcode::CURLE_NO_CONNECTION_AVAILABLE as NO_CONNECTION_AVAILABLE; -pub use curl_ffi::CURLcode::CURLE_LAST as LAST; +pub use curl_ffi::CURLE_OK as OK; +pub use curl_ffi::CURLE_UNSUPPORTED_PROTOCOL as UNSUPPORTED_PROTOCOL; +pub use curl_ffi::CURLE_FAILED_INIT as FAILED_INIT; +pub use curl_ffi::CURLE_URL_MALFORMAT as URL_MALFORMAT; +pub use curl_ffi::CURLE_NOT_BUILT_IN as NOT_BUILT_IN; +pub use curl_ffi::CURLE_COULDNT_RESOLVE_PROXY as COULDNT_RESOLVE_PROXY; +pub use curl_ffi::CURLE_COULDNT_RESOLVE_HOST as COULDNT_RESOLVE_HOST; +pub use curl_ffi::CURLE_COULDNT_CONNECT as COULDNT_CONNECT; +pub use curl_ffi::CURLE_FTP_WEIRD_SERVER_REPLY as FTP_WEIRD_SERVER_REPLY; +pub use curl_ffi::CURLE_REMOTE_ACCESS_DENIED as REMOTE_ACCESS_DENIED; +pub use curl_ffi::CURLE_FTP_ACCEPT_FAILED as FTP_ACCEPT_FAILED; +pub use curl_ffi::CURLE_FTP_WEIRD_PASS_REPLY as FTP_WEIRD_PASS_REPLY; +pub use curl_ffi::CURLE_FTP_ACCEPT_TIMEOUT as FTP_ACCEPT_TIMEOUT; +pub use curl_ffi::CURLE_FTP_WEIRD_PASV_REPLY as FTP_WEIRD_PASV_REPLY; +pub use curl_ffi::CURLE_FTP_WEIRD_227_FORMAT as FTP_WEIRD_227_FORMAT; +pub use curl_ffi::CURLE_FTP_CANT_GET_HOST as FTP_CANT_GET_HOST; +pub use curl_ffi::CURLE_OBSOLETE16 as OBSOLETE16; +pub use curl_ffi::CURLE_FTP_COULDNT_SET_TYPE as FTP_COULDNT_SET_TYPE; +pub use curl_ffi::CURLE_PARTIAL_FILE as PARTIAL_FILE; +pub use curl_ffi::CURLE_FTP_COULDNT_RETR_FILE as FTP_COULDNT_RETR_FILE; +pub use curl_ffi::CURLE_OBSOLETE20 as OBSOLETE20; +pub use curl_ffi::CURLE_QUOTE_ERROR as QUOTE_ERROR; +pub use curl_ffi::CURLE_HTTP_RETURNED_ERROR as HTTP_RETURNED_ERROR; +pub use curl_ffi::CURLE_WRITE_ERROR as WRITE_ERROR; +pub use curl_ffi::CURLE_OBSOLETE24 as OBSOLETE24; +pub use curl_ffi::CURLE_UPLOAD_FAILED as UPLOAD_FAILED; +pub use curl_ffi::CURLE_READ_ERROR as READ_ERROR; +pub use curl_ffi::CURLE_OUT_OF_MEMORY as OUT_OF_MEMORY; +pub use curl_ffi::CURLE_OPERATION_TIMEDOUT as OPERATION_TIMEDOUT; +pub use curl_ffi::CURLE_OBSOLETE29 as OBSOLETE29; +pub use curl_ffi::CURLE_FTP_PORT_FAILED as FTP_PORT_FAILED; +pub use curl_ffi::CURLE_FTP_COULDNT_USE_REST as FTP_COULDNT_USE_REST; +pub use curl_ffi::CURLE_OBSOLETE32 as OBSOLETE32; +pub use curl_ffi::CURLE_RANGE_ERROR as RANGE_ERROR; +pub use curl_ffi::CURLE_HTTP_POST_ERROR as HTTP_POST_ERROR; +pub use curl_ffi::CURLE_SSL_CONNECT_ERROR as SSL_CONNECT_ERROR; +pub use curl_ffi::CURLE_BAD_DOWNLOAD_RESUME as BAD_DOWNLOAD_RESUME; +pub use curl_ffi::CURLE_FILE_COULDNT_READ_FILE as FILE_COULDNT_READ_FILE; +pub use curl_ffi::CURLE_LDAP_CANNOT_BIND as LDAP_CANNOT_BIND; +pub use curl_ffi::CURLE_LDAP_SEARCH_FAILED as LDAP_SEARCH_FAILED; +pub use curl_ffi::CURLE_OBSOLETE40 as OBSOLETE40; +pub use curl_ffi::CURLE_FUNCTION_NOT_FOUND as FUNCTION_NOT_FOUND; +pub use curl_ffi::CURLE_ABORTED_BY_CALLBACK as ABORTED_BY_CALLBACK; +pub use curl_ffi::CURLE_BAD_FUNCTION_ARGUMENT as BAD_FUNCTION_ARGUMENT; +pub use curl_ffi::CURLE_OBSOLETE44 as OBSOLETE44; +pub use curl_ffi::CURLE_INTERFACE_FAILED as INTERFACE_FAILED; +pub use curl_ffi::CURLE_OBSOLETE46 as OBSOLETE46; +pub use curl_ffi::CURLE_TOO_MANY_REDIRECTS as TOO_MANY_REDIRECTS ; +pub use curl_ffi::CURLE_UNKNOWN_OPTION as UNKNOWN_OPTION; +pub use curl_ffi::CURLE_TELNET_OPTION_SYNTAX as TELNET_OPTION_SYNTAX ; +pub use curl_ffi::CURLE_OBSOLETE50 as OBSOLETE50; +pub use curl_ffi::CURLE_PEER_FAILED_VERIFICATION as PEER_FAILED_VERIFICATION; +pub use curl_ffi::CURLE_GOT_NOTHING as GOT_NOTHING; +pub use curl_ffi::CURLE_SSL_ENGINE_NOTFOUND as SSL_ENGINE_NOTFOUND; +pub use curl_ffi::CURLE_SSL_ENGINE_SETFAILED as SSL_ENGINE_SETFAILED; +pub use curl_ffi::CURLE_SEND_ERROR as SEND_ERROR; +pub use curl_ffi::CURLE_RECV_ERROR as RECV_ERROR; +pub use curl_ffi::CURLE_OBSOLETE57 as OBSOLETE57; +pub use curl_ffi::CURLE_SSL_CERTPROBLEM as SSL_CERTPROBLEM; +pub use curl_ffi::CURLE_SSL_CIPHER as SSL_CIPHER; +pub use curl_ffi::CURLE_SSL_CACERT as SSL_CACERT; +pub use curl_ffi::CURLE_BAD_CONTENT_ENCODING as BAD_CONTENT_ENCODING; +pub use curl_ffi::CURLE_LDAP_INVALID_URL as LDAP_INVALID_URL; +pub use curl_ffi::CURLE_FILESIZE_EXCEEDED as FILESIZE_EXCEEDED; +pub use curl_ffi::CURLE_USE_SSL_FAILED as USE_SSL_FAILED; +pub use curl_ffi::CURLE_SEND_FAIL_REWIND as SEND_FAIL_REWIND; +pub use curl_ffi::CURLE_SSL_ENGINE_INITFAILED as SSL_ENGINE_INITFAILED; +pub use curl_ffi::CURLE_LOGIN_DENIED as LOGIN_DENIED; +pub use curl_ffi::CURLE_TFTP_NOTFOUND as TFTP_NOTFOUND; +pub use curl_ffi::CURLE_TFTP_PERM as TFTP_PERM; +pub use curl_ffi::CURLE_REMOTE_DISK_FULL as REMOTE_DISK_FULL; +pub use curl_ffi::CURLE_TFTP_ILLEGAL as TFTP_ILLEGAL; +pub use curl_ffi::CURLE_TFTP_UNKNOWNID as TFTP_UNKNOWNID; +pub use curl_ffi::CURLE_REMOTE_FILE_EXISTS as REMOTE_FILE_EXISTS; +pub use curl_ffi::CURLE_TFTP_NOSUCHUSER as TFTP_NOSUCHUSER; +pub use curl_ffi::CURLE_CONV_FAILED as CONV_FAILED; +pub use curl_ffi::CURLE_CONV_REQD as CONV_REQD; +pub use curl_ffi::CURLE_SSL_CACERT_BADFILE as SSL_CACERT_BADFILE; +pub use curl_ffi::CURLE_REMOTE_FILE_NOT_FOUND as REMOTE_FILE_NOT_FOUND; +pub use curl_ffi::CURLE_SSH as SSH; +pub use curl_ffi::CURLE_SSL_SHUTDOWN_FAILED as SSL_SHUTDOWN_FAILED; +pub use curl_ffi::CURLE_AGAIN as AGAIN; +pub use curl_ffi::CURLE_SSL_CRL_BADFILE as SSL_CRL_BADFILE; +pub use curl_ffi::CURLE_SSL_ISSUER_ERROR as SSL_ISSUER_ERROR; +pub use curl_ffi::CURLE_FTP_PRET_FAILED as FTP_PRET_FAILED; +pub use curl_ffi::CURLE_RTSP_CSEQ_ERROR as RTSP_CSEQ_ERROR; +pub use curl_ffi::CURLE_RTSP_SESSION_ERROR as RTSP_SESSION_ERROR; +pub use curl_ffi::CURLE_FTP_BAD_FILE_LIST as FTP_BAD_FILE_LIST; +pub use curl_ffi::CURLE_CHUNK_FAILED as CHUNK_FAILED; +pub use curl_ffi::CURLE_NO_CONNECTION_AVAILABLE as NO_CONNECTION_AVAILABLE; #[derive(Copy, Clone)] pub struct ErrCode(pub ffi::CURLcode); diff --git a/src/ffi/list.rs b/src/ffi/list.rs index c8afe05687..e0e273431b 100644 --- a/src/ffi/list.rs +++ b/src/ffi/list.rs @@ -22,7 +22,9 @@ impl List { pub fn push_bytes(&mut self, val: &[u8]) { assert!(val[val.len() - 1] == 0); self.len += 1; - self.head = unsafe { ffi::curl_slist_append(self.head, val.as_ptr()) }; + self.head = unsafe { + ffi::curl_slist_append(self.head, val.as_ptr() as *mut _) + }; } pub fn len(&self) -> usize { diff --git a/src/ffi/opt.rs b/src/ffi/opt.rs index ad9a427335..b8e1d9e006 100644 --- a/src/ffi/opt.rs +++ b/src/ffi/opt.rs @@ -212,22 +212,22 @@ pub use curl_ffi::CURLOPT_DNS_INTERFACE as DNS_INTERFACE; pub use curl_ffi::CURLOPT_DNS_LOCAL_IP4 as DNS_LOCAL_IP4; pub use curl_ffi::CURLOPT_DNS_LOCAL_IP6 as DNS_LOCAL_IP6; pub use curl_ffi::CURLOPT_LOGIN_OPTIONS as LOGIN_OPTIONS; -pub use curl_ffi::CURLOPT_SSL_ENABLE_NPN as SSL_ENABLE_NPN; -pub use curl_ffi::CURLOPT_SSL_ENABLE_ALPN as SSL_ENABLE_ALPN; -pub use curl_ffi::CURLOPT_EXPECT_100_TIMEOUT_MS as EXPECT_100_TIMEOUT_MS; -pub use curl_ffi::CURLOPT_PROXYHEADER as PROXYHEADER; -pub use curl_ffi::CURLOPT_HEADEROPT as HEADEROPT; -pub use curl_ffi::CURLOPT_POST301 as POST301; -pub use curl_ffi::CURLOPT_SSLKEYPASSWD as SSLKEYPASSWD; -pub use curl_ffi::CURLOPT_FTPAPPEND as FTPAPPEND; -pub use curl_ffi::CURLOPT_FTPLISTONLY as FTPLISTONLY; -pub use curl_ffi::CURLOPT_FTP_SSL as FTP_SSL; -pub use curl_ffi::CURLOPT_SSLCERTPASSWD as SSLCERTPASSWD; -pub use curl_ffi::CURLOPT_KRB4LEVEL as KRB4LEVEL; +// pub use curl_ffi::CURLOPT_SSL_ENABLE_NPN as SSL_ENABLE_NPN; +// pub use curl_ffi::CURLOPT_SSL_ENABLE_ALPN as SSL_ENABLE_ALPN; +// pub use curl_ffi::CURLOPT_EXPECT_100_TIMEOUT_MS as EXPECT_100_TIMEOUT_MS; +// pub use curl_ffi::CURLOPT_PROXYHEADER as PROXYHEADER; +// pub use curl_ffi::CURLOPT_HEADEROPT as HEADEROPT; +// pub use curl_ffi::CURLOPT_POST301 as POST301; +// pub use curl_ffi::CURLOPT_SSLKEYPASSWD as SSLKEYPASSWD; +// pub use curl_ffi::CURLOPT_FTPAPPEND as FTPAPPEND; +// pub use curl_ffi::CURLOPT_FTPLISTONLY as FTPLISTONLY; +// pub use curl_ffi::CURLOPT_FTP_SSL as FTP_SSL; +// pub use curl_ffi::CURLOPT_SSLCERTPASSWD as SSLCERTPASSWD; +// pub use curl_ffi::CURLOPT_KRB4LEVEL as KRB4LEVEL; pub use curl_ffi::CURLOPT_READDATA as READDATA; pub use curl_ffi::CURLOPT_WRITEDATA as WRITEDATA; pub use curl_ffi::CURLOPT_HEADERDATA as HEADERDATA; -pub use curl_ffi::CURLOPT_XFERINFODATA as XFERINFODATA; +// pub use curl_ffi::CURLOPT_XFERINFODATA as XFERINFODATA; pub type Opt = ffi::CURLoption; diff --git a/src/ffi/version.rs b/src/ffi/version.rs index e703a09c4a..05043a9e79 100644 --- a/src/ffi/version.rs +++ b/src/ffi/version.rs @@ -205,7 +205,7 @@ fn as_str<'a>(p: *const c_char) -> Option<&'a str> { pub fn version_info() -> Version { Version { - inner: unsafe { ffi::curl_version_info(ffi::CURL_VERSION_NOW) }, + inner: unsafe { ffi::curl_version_info(ffi::CURLVERSION_FOURTH) }, } } diff --git a/src/lib.rs b/src/lib.rs index 99089cfb20..b6df681086 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,6 @@ extern crate libc; extern crate url; -#[macro_use] -extern crate log; - extern crate curl_sys as curl_ffi; #[cfg(all(unix, not(target_os = "macos")))] From 63f8a9eabe4b3662e74fda69bcbaff01795dbfdf Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 30 Mar 2016 23:21:15 -0700 Subject: [PATCH 07/71] Fill out the error module --- src/error.rs | 448 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 40 +++-- 2 files changed, 472 insertions(+), 16 deletions(-) create mode 100644 src/error.rs diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000000..6e77a1b508 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,448 @@ +use std::error; +use std::ffi::CStr; +use std::fmt; +use std::str; +use std::io; + +use curl_sys; + +/// An error returned from various "easy" operations. +/// +/// This structure wraps a `CURLcode`. +#[derive(Debug, Clone, PartialEq)] +pub struct Error { + code: curl_sys::CURLcode, +} + +impl Error { + /// Creates a new error from the underlying code returned by libcurl. + pub fn new(code: curl_sys::CURLcode) -> Error { + Error { code: code } + } + + /// Returns whether this error corresponds to CURLE_UNSUPPORTED_PROTOCOL. + pub fn is_unsupported_protocol(&self) -> bool { + self.code == curl_sys::CURLE_UNSUPPORTED_PROTOCOL + } + + /// Returns whether this error corresponds to CURLE_FAILED_INIT. + pub fn is_failed_init(&self) -> bool { + self.code == curl_sys::CURLE_FAILED_INIT + } + + /// Returns whether this error corresponds to CURLE_URL_MALFORMAT. + pub fn is_url_malformed(&self) -> bool { + self.code == curl_sys::CURLE_URL_MALFORMAT + } + + /// Returns whether this error corresponds to CURLE_NOT_BUILT_IN. + pub fn is_not_built_in(&self) -> bool { + self.code == curl_sys::CURLE_NOT_BUILT_IN + } + + /// Returns whether this error corresponds to CURLE_COULDNT_RESOLVE_PROXY. + pub fn is_couldnt_resolve_proxy(&self) -> bool { + self.code == curl_sys::CURLE_COULDNT_RESOLVE_PROXY + } + + /// Returns whether this error corresponds to CURLE_COULDNT_RESOLVE_HOST. + pub fn is_couldnt_resolve_host(&self) -> bool { + self.code == curl_sys::CURLE_COULDNT_RESOLVE_HOST + } + + /// Returns whether this error corresponds to CURLE_COULDNT_CONNECT. + pub fn is_couldnt_connect(&self) -> bool { + self.code == curl_sys::CURLE_COULDNT_CONNECT + } + + /// Returns whether this error corresponds to CURLE_REMOTE_ACCESS_DENIED. + pub fn is_remote_access_denied(&self) -> bool { + self.code == curl_sys::CURLE_REMOTE_ACCESS_DENIED + } + + /// Returns whether this error corresponds to CURLE_PARTIAL_FILE. + pub fn is_partial_file(&self) -> bool { + self.code == curl_sys::CURLE_PARTIAL_FILE + } + + /// Returns whether this error corresponds to CURLE_QUOTE_ERROR. + pub fn is_quote_error(&self) -> bool { + self.code == curl_sys::CURLE_QUOTE_ERROR + } + + /// Returns whether this error corresponds to CURLE_HTTP_RETURNED_ERROR. + pub fn is_http_returned_error(&self) -> bool { + self.code == curl_sys::CURLE_HTTP_RETURNED_ERROR + } + + /// Returns whether this error corresponds to CURLE_READ_ERROR. + pub fn is_read_error(&self) -> bool { + self.code == curl_sys::CURLE_READ_ERROR + } + + /// Returns whether this error corresponds to CURLE_WRITE_ERROR. + pub fn is_write_error(&self) -> bool { + self.code == curl_sys::CURLE_WRITE_ERROR + } + + /// Returns whether this error corresponds to CURLE_UPLOAD_FAILED. + pub fn is_upload_failed(&self) -> bool { + self.code == curl_sys::CURLE_UPLOAD_FAILED + } + + /// Returns whether this error corresponds to CURLE_OUT_OF_MEMORY. + pub fn is_out_of_memory(&self) -> bool { + self.code == curl_sys::CURLE_OUT_OF_MEMORY + } + + /// Returns whether this error corresponds to CURLE_OPERATION_TIMEDOUT. + pub fn is_operation_timedout(&self) -> bool { + self.code == curl_sys::CURLE_OPERATION_TIMEDOUT + } + + /// Returns whether this error corresponds to CURLE_RANGE_ERROR. + pub fn is_range_error(&self) -> bool { + self.code == curl_sys::CURLE_RANGE_ERROR + } + + /// Returns whether this error corresponds to CURLE_HTTP_POST_ERROR. + pub fn is_http_post_error(&self) -> bool { + self.code == curl_sys::CURLE_HTTP_POST_ERROR + } + + /// Returns whether this error corresponds to CURLE_SSL_CONNECT_ERROR. + pub fn is_ssl_connect_error(&self) -> bool { + self.code == curl_sys::CURLE_SSL_CONNECT_ERROR + } + + /// Returns whether this error corresponds to CURLE_BAD_DOWNLOAD_RESUME. + pub fn is_bad_download_resume(&self) -> bool { + self.code == curl_sys::CURLE_BAD_DOWNLOAD_RESUME + } + + /// Returns whether this error corresponds to CURLE_FILE_COULDNT_READ_FILE. + pub fn is_file_couldnt_read_file(&self) -> bool { + self.code == curl_sys::CURLE_FILE_COULDNT_READ_FILE + } + + /// Returns whether this error corresponds to CURLE_FUNCTION_NOT_FOUND. + pub fn is_function_not_found(&self) -> bool { + self.code == curl_sys::CURLE_FUNCTION_NOT_FOUND + } + + /// Returns whether this error corresponds to CURLE_ABORTED_BY_CALLBACK. + pub fn is_aborted_by_callback(&self) -> bool { + self.code == curl_sys::CURLE_ABORTED_BY_CALLBACK + } + + /// Returns whether this error corresponds to CURLE_BAD_FUNCTION_ARGUMENT. + pub fn is_bad_function_argument(&self) -> bool { + self.code == curl_sys::CURLE_BAD_FUNCTION_ARGUMENT + } + + /// Returns whether this error corresponds to CURLE_INTERFACE_FAILED. + pub fn is_interface_failed(&self) -> bool { + self.code == curl_sys::CURLE_INTERFACE_FAILED + } + + /// Returns whether this error corresponds to CURLE_TOO_MANY_REDIRECTS. + pub fn is_too_many_redirects(&self) -> bool { + self.code == curl_sys::CURLE_TOO_MANY_REDIRECTS + } + + /// Returns whether this error corresponds to CURLE_UNKNOWN_OPTION. + pub fn is_unknown_option(&self) -> bool { + self.code == curl_sys::CURLE_UNKNOWN_OPTION + } + + /// Returns whether this error corresponds to CURLE_PEER_FAILED_VERIFICATION. + pub fn is_peer_failed_verification(&self) -> bool { + self.code == curl_sys::CURLE_PEER_FAILED_VERIFICATION + } + + /// Returns whether this error corresponds to CURLE_GOT_NOTHING. + pub fn is_got_nothing(&self) -> bool { + self.code == curl_sys::CURLE_GOT_NOTHING + } + + /// Returns whether this error corresponds to CURLE_SSL_ENGINE_NOTFOUND. + pub fn is_ssl_engine_notfound(&self) -> bool { + self.code == curl_sys::CURLE_SSL_ENGINE_NOTFOUND + } + + /// Returns whether this error corresponds to CURLE_SSL_ENGINE_SETFAILED. + pub fn is_ssl_engine_setfailed(&self) -> bool { + self.code == curl_sys::CURLE_SSL_ENGINE_SETFAILED + } + + /// Returns whether this error corresponds to CURLE_SEND_ERROR. + pub fn is_send_error(&self) -> bool { + self.code == curl_sys::CURLE_SEND_ERROR + } + + /// Returns whether this error corresponds to CURLE_RECV_ERROR. + pub fn is_recv_error(&self) -> bool { + self.code == curl_sys::CURLE_RECV_ERROR + } + + /// Returns whether this error corresponds to CURLE_SSL_CERTPROBLEM. + pub fn is_ssl_certproblem(&self) -> bool { + self.code == curl_sys::CURLE_SSL_CERTPROBLEM + } + + /// Returns whether this error corresponds to CURLE_SSL_CIPHER. + pub fn is_ssl_cipher(&self) -> bool { + self.code == curl_sys::CURLE_SSL_CIPHER + } + + /// Returns whether this error corresponds to CURLE_SSL_CACERT. + pub fn is_ssl_cacert(&self) -> bool { + self.code == curl_sys::CURLE_SSL_CACERT + } + + /// Returns whether this error corresponds to CURLE_BAD_CONTENT_ENCODING. + pub fn is_bad_content_encoding(&self) -> bool { + self.code == curl_sys::CURLE_BAD_CONTENT_ENCODING + } + + /// Returns whether this error corresponds to CURLE_FILESIZE_EXCEEDED. + pub fn is_filesize_exceeded(&self) -> bool { + self.code == curl_sys::CURLE_FILESIZE_EXCEEDED + } + + /// Returns whether this error corresponds to CURLE_USE_SSL_FAILED. + pub fn is_use_ssl_failed(&self) -> bool { + self.code == curl_sys::CURLE_USE_SSL_FAILED + } + + /// Returns whether this error corresponds to CURLE_SEND_FAIL_REWIND. + pub fn is_send_fail_rewind(&self) -> bool { + self.code == curl_sys::CURLE_SEND_FAIL_REWIND + } + + /// Returns whether this error corresponds to CURLE_SSL_ENGINE_INITFAILED. + pub fn is_ssl_engine_initfailed(&self) -> bool { + self.code == curl_sys::CURLE_SSL_ENGINE_INITFAILED + } + + /// Returns whether this error corresponds to CURLE_LOGIN_DENIED. + pub fn is_login_denied(&self) -> bool { + self.code == curl_sys::CURLE_LOGIN_DENIED + } + + /// Returns whether this error corresponds to CURLE_CONV_FAILED. + pub fn is_conv_failed(&self) -> bool { + self.code == curl_sys::CURLE_CONV_FAILED + } + + /// Returns whether this error corresponds to CURLE_CONV_REQD. + pub fn is_conv_required(&self) -> bool { + self.code == curl_sys::CURLE_CONV_REQD + } + + /// Returns whether this error corresponds to CURLE_SSL_CACERT_BADFILE. + pub fn is_ssl_cacert_badfile(&self) -> bool { + self.code == curl_sys::CURLE_SSL_CACERT_BADFILE + } + + /// Returns whether this error corresponds to CURLE_SSL_CRL_BADFILE. + pub fn is_ssl_crl_badfile(&self) -> bool { + self.code == curl_sys::CURLE_SSL_CRL_BADFILE + } + + /// Returns whether this error corresponds to CURLE_SSL_SHUTDOWN_FAILED. + pub fn is_ssl_shutdown_failed(&self) -> bool { + self.code == curl_sys::CURLE_SSL_SHUTDOWN_FAILED + } + + /// Returns whether this error corresponds to CURLE_AGAIN. + pub fn is_again(&self) -> bool { + self.code == curl_sys::CURLE_AGAIN + } + + /// Returns whether this error corresponds to CURLE_SSL_ISSUER_ERROR. + pub fn is_ssl_issuer_error(&self) -> bool { + self.code == curl_sys::CURLE_SSL_ISSUER_ERROR + } + + /// Returns whether this error corresponds to CURLE_CHUNK_FAILED. + pub fn is_chunk_failed(&self) -> bool { + self.code == curl_sys::CURLE_CHUNK_FAILED + } + + /// Returns whether this error corresponds to CURLE_NO_CONNECTION_AVAILABLE. + pub fn is_no_connection_available(&self) -> bool { + self.code == curl_sys::CURLE_NO_CONNECTION_AVAILABLE + } + + /// Returns the value of the underlying error corresponding to libcurl. + pub fn code(&self) -> curl_sys::CURLcode { + self.code + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + error::Error::description(self).fmt(f) + } +} + +impl error::Error for Error { + fn description(&self) -> &str { + unsafe { + let s = curl_sys::curl_easy_strerror(self.code); + assert!(!s.is_null()); + str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap() + } + } +} + +/// An error returned from "share" operations. +/// +/// This structure wraps a `CURLSHcode`. +#[derive(Debug, Clone, PartialEq)] +pub struct ShareError { + code: curl_sys::CURLSHcode, +} + +impl ShareError { + /// Creates a new error from the underlying code returned by libcurl. + pub fn new(code: curl_sys::CURLSHcode) -> ShareError { + ShareError { code: code } + } + + /// Returns whether this error corresponds to CURLSHE_BAD_OPTION. + pub fn is_bad_option(&self) -> bool { + self.code == curl_sys::CURLSHE_BAD_OPTION + } + + /// Returns whether this error corresponds to CURLSHE_IN_USE. + pub fn is_in_use(&self) -> bool { + self.code == curl_sys::CURLSHE_IN_USE + } + + /// Returns whether this error corresponds to CURLSHE_INVALID. + pub fn is_invalid(&self) -> bool { + self.code == curl_sys::CURLSHE_INVALID + } + + /// Returns whether this error corresponds to CURLSHE_NOMEM. + pub fn is_nomem(&self) -> bool { + self.code == curl_sys::CURLSHE_NOMEM + } + + /// Returns whether this error corresponds to CURLSHE_NOT_BUILT_IN. + pub fn is_not_built_in(&self) -> bool { + self.code == curl_sys::CURLSHE_NOT_BUILT_IN + } + + /// Returns the value of the underlying error corresponding to libcurl. + pub fn code(&self) -> curl_sys::CURLSHcode { + self.code + } +} + +impl fmt::Display for ShareError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + error::Error::description(self).fmt(f) + } +} + +impl error::Error for ShareError { + fn description(&self) -> &str { + unsafe { + let s = curl_sys::curl_share_strerror(self.code); + assert!(!s.is_null()); + str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap() + } + } +} + +/// An error from "multi" operations. +/// +/// THis structure wraps a `CURLMcode`. +#[derive(Debug, Clone, PartialEq)] +pub struct MultiError { + code: curl_sys::CURLMcode, +} + +impl MultiError { + /// Creates a new error from the underlying code returned by libcurl. + pub fn new(code: curl_sys::CURLMcode) -> MultiError { + MultiError { code: code } + } + + /// Returns whether this error corresponds to CURLM_BAD_HANDLE. + pub fn is_bad_handle(&self) -> bool { + self.code == curl_sys::CURLM_BAD_HANDLE + } + + /// Returns whether this error corresponds to CURLM_BAD_EASY_HANDLE. + pub fn is_bad_easy_handle(&self) -> bool { + self.code == curl_sys::CURLM_BAD_EASY_HANDLE + } + + /// Returns whether this error corresponds to CURLM_OUT_OF_MEMORY. + pub fn is_out_of_memory(&self) -> bool { + self.code == curl_sys::CURLM_OUT_OF_MEMORY + } + + /// Returns whether this error corresponds to CURLM_INTERNAL_ERROR. + pub fn is_internal_error(&self) -> bool { + self.code == curl_sys::CURLM_INTERNAL_ERROR + } + + /// Returns whether this error corresponds to CURLM_BAD_SOCKET. + pub fn is_bad_socket(&self) -> bool { + self.code == curl_sys::CURLM_BAD_SOCKET + } + + /// Returns whether this error corresponds to CURLM_UNKNOWN_OPTION. + pub fn is_unknown_option(&self) -> bool { + self.code == curl_sys::CURLM_UNKNOWN_OPTION + } + + /// Returns whether this error corresponds to CURLM_ADDED_ALREADY. + pub fn is_added_already(&self) -> bool { + self.code == curl_sys::CURLM_ADDED_ALREADY + } + + /// Returns the value of the underlying error corresponding to libcurl. + pub fn code(&self) -> curl_sys::CURLMcode { + self.code + } +} + +impl fmt::Display for MultiError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + error::Error::description(self).fmt(f) + } +} + +impl error::Error for MultiError { + fn description(&self) -> &str { + unsafe { + let s = curl_sys::curl_multi_strerror(self.code); + assert!(!s.is_null()); + str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap() + } + } +} + +impl From for io::Error { + fn from(e: Error) -> io::Error { + io::Error::new(io::ErrorKind::Other, e) + } +} + +impl From for io::Error { + fn from(e: ShareError) -> io::Error { + io::Error::new(io::ErrorKind::Other, e) + } +} + +impl From for io::Error { + fn from(e: MultiError) -> io::Error { + io::Error::new(io::ErrorKind::Other, e) + } +} diff --git a/src/lib.rs b/src/lib.rs index b6df681086..e5c741d984 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,21 +1,29 @@ -extern crate libc; -extern crate url; +//! TODO: dox -extern crate curl_sys as curl_ffi; +#![deny(missing_docs)] -#[cfg(all(unix, not(target_os = "macos")))] -extern crate openssl_sys as openssl; +// extern crate libc; +// extern crate url; -pub use ffi::easy::ProgressCb; -pub use ffi::err::ErrCode; +extern crate curl_sys; -// Version accessors -pub use ffi::version::{ - Version, - version, - version_info, - Protocols -}; +// #[cfg(all(unix, not(target_os = "macos")))] +// extern crate openssl_sys as openssl; +// +// pub use ffi::easy::ProgressCb; +// pub use ffi::err::ErrCode; +// +// pub use error::Error; +// +// // Version accessors +// pub use ffi::version::{ +// Version, +// version, +// version_info, +// Protocols +// }; -mod ffi; -pub mod http; +pub use error::{Error, ShareError, MultiError}; +mod error; +// mod ffi; +// pub mod http; From 4fce3582bcde5b4faddbd2ead493fd4459c6e5ef Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 30 Mar 2016 23:22:50 -0700 Subject: [PATCH 08/71] Try to fix travis again --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 61d97d38ce..f76175b450 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,8 +8,9 @@ before_script: - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH script: - export CARGO_TARGET_DIR=`pwd`/target + - export CURL_NO_PKG_CONFIG=1 - cargo test -p curl-sys - - CURL_NO_PKG_CONFIG=1 cargo run --manifest-path systest/Cargo.toml + - cargo run --manifest-path systest/Cargo.toml #- cargo doc --no-deps - cargo doc --no-deps -p curl-sys after_success: From 2a87a2bf57761744edc2edb2137a703064bf4bbf Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 30 Mar 2016 23:23:07 -0700 Subject: [PATCH 09/71] Run the full test suite --- .travis.yml | 4 +-- appveyor.yml | 2 +- test/test.rs | 76 ++++++++++++++++++++++++++-------------------------- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/.travis.yml b/.travis.yml index f76175b450..d4e4f4ee8d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,9 +9,9 @@ before_script: script: - export CARGO_TARGET_DIR=`pwd`/target - export CURL_NO_PKG_CONFIG=1 - - cargo test -p curl-sys + - cargo test - cargo run --manifest-path systest/Cargo.toml - #- cargo doc --no-deps + - cargo doc --no-deps - cargo doc --no-deps -p curl-sys after_success: - travis-cargo --only nightly doc-upload diff --git a/appveyor.yml b/appveyor.yml index ed205ae152..b4ec4e75bb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,5 +17,5 @@ install: build: false test_script: - - cargo test -p curl-sys + - cargo test - cargo run --manifest-path systest/Cargo.toml diff --git a/test/test.rs b/test/test.rs index adcf66feff..8e8fd4dc92 100644 --- a/test/test.rs +++ b/test/test.rs @@ -1,38 +1,38 @@ -extern crate curl; -extern crate env_logger; - -#[macro_use] -extern crate log; - -macro_rules! server { - ($($ops:expr),+) => (server::setup(ops!($($ops),+))); -} - -macro_rules! ops { - ($op:expr) => (server::OpSequence::new($op)); - ($op:expr, $($res:expr),+) => ( - server::OpSequence::concat($op, ops!($($res),+)) - ); -} - -macro_rules! send { - ($e:expr) => (server::Op::SendBytes($e)); -} - -macro_rules! recv { - ($e:expr) => (server::Op::ReceiveBytes($e)); -} - -macro_rules! wait { - ($dur:expr) => (server::Op::Wait($dur)); -} - -mod server; -mod test_delete; -mod test_get; -mod test_head; -mod test_keep_alive; -mod test_patch; -mod test_post; -mod test_proxy; -mod test_put; +// extern crate curl; +// extern crate env_logger; +// +// #[macro_use] +// extern crate log; +// +// macro_rules! server { +// ($($ops:expr),+) => (server::setup(ops!($($ops),+))); +// } +// +// macro_rules! ops { +// ($op:expr) => (server::OpSequence::new($op)); +// ($op:expr, $($res:expr),+) => ( +// server::OpSequence::concat($op, ops!($($res),+)) +// ); +// } +// +// macro_rules! send { +// ($e:expr) => (server::Op::SendBytes($e)); +// } +// +// macro_rules! recv { +// ($e:expr) => (server::Op::ReceiveBytes($e)); +// } +// +// macro_rules! wait { +// ($dur:expr) => (server::Op::Wait($dur)); +// } +// +// mod server; +// mod test_delete; +// mod test_get; +// mod test_head; +// mod test_keep_alive; +// mod test_patch; +// mod test_post; +// mod test_proxy; +// mod test_put; From 81cd4b96ffdc7598551eaac0131d7c98728ca96f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 30 Mar 2016 23:57:46 -0700 Subject: [PATCH 10/71] Add bindings to version information. --- src/ffi/version.rs | 217 ---------------------------------------- src/lib.rs | 53 ++++++---- src/version.rs | 243 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 276 insertions(+), 237 deletions(-) delete mode 100644 src/ffi/version.rs create mode 100644 src/version.rs diff --git a/src/ffi/version.rs b/src/ffi/version.rs deleted file mode 100644 index 05043a9e79..0000000000 --- a/src/ffi/version.rs +++ /dev/null @@ -1,217 +0,0 @@ -#![allow(non_camel_case_types)] -#![allow(dead_code)] - -use std::marker; -use std::ffi::CStr; -use std::{fmt, ptr, str}; -use libc::{c_char, c_int}; - -use curl_ffi as ffi; - -#[allow(missing_copy_implementations)] -pub struct Version { inner: *mut ffi::curl_version_info_data } - -impl Version { - - pub fn version_str<'a>(&'a self) -> &'a str { - as_str(unsafe { (*self.inner).version }).unwrap() - } - - pub fn version_major(&self) -> u32 { - (unsafe { (*self.inner).version_num } as u32 & 0xFF0000) >> 16 - } - - pub fn version_minor(&self) -> u32 { - (unsafe { (*self.inner).version_num } as u32 & 0xFF00) >> 8 - } - - pub fn version_patch(&self) -> u32 { - (unsafe { (*self.inner).version_num } as u32 & 0xFF) - } - - pub fn host<'a>(&'a self) -> &'a str { - as_str(unsafe { (*self.inner).host }).unwrap() - } - - fn features(&self) -> c_int { unsafe { (*self.inner).features } } - - pub fn is_ipv6_enabled(&self) -> bool { - (self.features() & ffi::CURL_VERSION_IPV6) == ffi::CURL_VERSION_IPV6 - } - - pub fn is_kerbos4_enabled(&self) -> bool { - (self.features() & ffi::CURL_VERSION_KERBEROS4) == ffi::CURL_VERSION_KERBEROS4 - } - - pub fn is_ssl_enabled(&self) -> bool { - (self.features() & ffi::CURL_VERSION_SSL) == ffi::CURL_VERSION_SSL - } - - pub fn is_libz_enabled(&self) -> bool { - (self.features() & ffi::CURL_VERSION_LIBZ) == ffi::CURL_VERSION_LIBZ - } - - pub fn is_ntlm_enabled(&self) -> bool { - (self.features() & ffi::CURL_VERSION_NTLM) == ffi::CURL_VERSION_NTLM - } - - pub fn is_gss_negotiate_enabled(&self) -> bool { - (self.features() & ffi::CURL_VERSION_GSSNEGOTIATE) == ffi::CURL_VERSION_GSSNEGOTIATE - } - - pub fn is_debug_enabled(&self) -> bool { - (self.features() & ffi::CURL_VERSION_DEBUG) == ffi::CURL_VERSION_DEBUG - } - - pub fn is_async_dns_enabled(&self) -> bool { - (self.features() & ffi::CURL_VERSION_ASYNCHDNS) == ffi::CURL_VERSION_ASYNCHDNS - } - - pub fn is_spengo_enabled(&self) -> bool { - (self.features() & ffi::CURL_VERSION_SPNEGO) == ffi::CURL_VERSION_SPNEGO - } - - pub fn is_large_file_enabled(&self) -> bool { - (self.features() & ffi::CURL_VERSION_LARGEFILE) == ffi::CURL_VERSION_LARGEFILE - } - - pub fn is_idn_enabled(&self) -> bool { - (self.features() & ffi::CURL_VERSION_IDN) == ffi::CURL_VERSION_IDN - } - - pub fn is_sspi_enabled(&self) -> bool { - (self.features() & ffi::CURL_VERSION_SSPI) == ffi::CURL_VERSION_SSPI - } - - pub fn is_conv_enabled(&self) -> bool { - (self.features() & ffi::CURL_VERSION_CONV) == ffi::CURL_VERSION_CONV - } - - pub fn is_curl_debug_enabled(&self) -> bool { - (self.features() & ffi::CURL_VERSION_CURLDEBUG) == ffi::CURL_VERSION_CURLDEBUG - } - - pub fn is_tls_auth_srp_enabled(&self) -> bool { - (self.features() & ffi::CURL_VERSION_TLSAUTH_SRP) == ffi::CURL_VERSION_TLSAUTH_SRP - } - - pub fn is_ntlm_wb_enabled(&self) -> bool { - (self.features() & ffi::CURL_VERSION_NTLM_WB) == ffi::CURL_VERSION_NTLM_WB - } - - pub fn is_http2_enabled(&self) -> bool { - (self.features() & ffi::CURL_VERSION_HTTP2) == ffi::CURL_VERSION_HTTP2 - } - - pub fn ssl_version<'a>(&'a self) -> Option<&'a str> { - as_str(unsafe { (*self.inner).ssl_version }) - } - - pub fn libz_version<'a>(&'a self) -> Option<&'a str> { - as_str(unsafe { (*self.inner).libz_version }) - } - - pub fn protocols<'a>(&'a self) -> Protocols<'a> { - Protocols { - curr: unsafe { (*self.inner).protocols }, - _marker: marker::PhantomData - } - } - - pub fn ares_version<'a>(&'a self) -> Option<&'a str> { - as_str(unsafe { (*self.inner).ares }) - } - - pub fn ares_version_num(&self) -> Option { - match self.ares_version() { - Some(_) => Some(unsafe { (*self.inner).ares_num } as u32), - None => None - } - } - - pub fn idn_version<'a>(&'a self) -> Option<&'a str> { - if self.is_idn_enabled() { - as_str(unsafe { (*self.inner).libidn }) - } - else { - None - } - } - - pub fn iconv_version(self) -> Option { - if self.is_conv_enabled() { - Some(unsafe { (*self.inner).iconv_ver_num } as u32) - } - else { - None - } - } - - pub fn ssh_version<'a>(&'a self) -> Option<&'a str> { - as_str(unsafe { (*self.inner).libssh_version }) - } -} - -#[derive(Copy, Clone)] -pub struct Protocols<'a> { - curr: *const *const c_char, - _marker: marker::PhantomData<&'a str>, -} - -impl<'a> Iterator for Protocols<'a> { - type Item = &'a str; - fn next(&mut self) -> Option<&'a str> { - unsafe { - let proto = *self.curr; - - if proto == ptr::null() { - return None; - } - - self.curr = self.curr.offset(1); - as_str(proto) - } - } -} - -impl<'a> fmt::Display for Protocols<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let mut i = self.clone(); - - try!(write!(fmt, "[")); - - match i.next() { - Some(proto) => try!(write!(fmt, "{}", proto)), - None => return write!(fmt, "]") - } - - for proto in i { - try!(write!(fmt, ", {}", proto)); - } - - write!(fmt, "]") - } -} - -fn as_str<'a>(p: *const c_char) -> Option<&'a str> { - if p == ptr::null() { - return None; - } - - unsafe { - str::from_utf8(CStr::from_ptr(p as *const _).to_bytes()).ok() - } -} - -pub fn version_info() -> Version { - Version { - inner: unsafe { ffi::curl_version_info(ffi::CURLVERSION_FOURTH) }, - } -} - -pub fn version() -> &'static str { - unsafe { - let version = ffi::curl_version(); - str::from_utf8(CStr::from_ptr(version as *const _).to_bytes()).unwrap() - } -} diff --git a/src/lib.rs b/src/lib.rs index e5c741d984..40a3936b9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,28 +2,41 @@ #![deny(missing_docs)] -// extern crate libc; -// extern crate url; - extern crate curl_sys; +extern crate libc; -// #[cfg(all(unix, not(target_os = "macos")))] -// extern crate openssl_sys as openssl; -// -// pub use ffi::easy::ProgressCb; -// pub use ffi::err::ErrCode; -// -// pub use error::Error; -// -// // Version accessors -// pub use ffi::version::{ -// Version, -// version, -// version_info, -// Protocols -// }; +use std::ffi::CStr; +use std::str; +use std::sync::{Once, ONCE_INIT}; pub use error::{Error, ShareError, MultiError}; mod error; -// mod ffi; -// pub mod http; + +pub use version::{Version, Protocols}; +mod version; + +/// Initializes the underlying libcurl library. +/// +/// It's not required to call this before the library is used, but it's +/// recommended to do so as soon as the program starts. +pub fn init() { + static INIT: Once = ONCE_INIT; + INIT.call_once(|| { + unsafe { + curl_sys::curl_global_init(curl_sys::CURL_GLOBAL_ALL); + libc::atexit(cleanup); + } + }); + + extern fn cleanup() { + unsafe { curl_sys::curl_global_cleanup(); } + } +} + +unsafe fn opt_str<'a>(ptr: *const libc::c_char) -> Option<&'a str> { + if ptr.is_null() { + None + } else { + Some(str::from_utf8(CStr::from_ptr(ptr).to_bytes()).unwrap()) + } +} diff --git a/src/version.rs b/src/version.rs new file mode 100644 index 0000000000..57e28a297b --- /dev/null +++ b/src/version.rs @@ -0,0 +1,243 @@ +use std::ffi::CStr; +use std::str; + +use curl_sys; +use libc::{c_int, c_char}; + +/// Version information about libcurl and the capabilities that it supports. +pub struct Version { + inner: *mut curl_sys::curl_version_info_data, +} + +unsafe impl Send for Version {} +unsafe impl Sync for Version {} + +/// An iterator over the list of protocols a version supports. +pub struct Protocols<'a> { + cur: *const *const c_char, + _inner: &'a Version, +} + +impl Version { + /// Returns the libcurl version that this library is currently linked against. + pub fn num() -> &'static str { + unsafe { + let s = CStr::from_ptr(curl_sys::curl_version() as *const _); + str::from_utf8(s.to_bytes()).unwrap() + } + } + + /// Returns the libcurl version that this library is currently linked against. + pub fn get() -> Version { + unsafe { + let ptr = curl_sys::curl_version_info(curl_sys::CURLVERSION_FOURTH); + assert!(!ptr.is_null()); + Version { inner: ptr } + } + } + + /// Returns the human readable version string, + pub fn version(&self) -> &str { + unsafe { + ::opt_str((*self.inner).version).unwrap() + } + } + + /// Returns a numeric representation of the version number + /// + /// This is a 24 bit number made up of the major number, minor, and then + /// patch number. For example 7.9.8 willr eturn 0x070908. + pub fn version_num(&self) -> u32 { + unsafe { + (*self.inner).version_num as u32 + } + } + + /// Returns a human readable string of the host libcurl is built for. + /// + /// This is discovered as part of the build environment. + pub fn host(&self) -> &str { + unsafe { + ::opt_str((*self.inner).host).unwrap() + } + } + + /// Returns whether libcurl supports IPv6 + pub fn feature_ipv6(&self) -> bool { + self.flag(curl_sys::CURL_VERSION_IPV6) + } + + /// Returns whether libcurl supports SSL + pub fn feature_ssl(&self) -> bool { + self.flag(curl_sys::CURL_VERSION_SSL) + } + + /// Returns whether libcurl supports HTTP deflate via libz + pub fn feature_libz(&self) -> bool { + self.flag(curl_sys::CURL_VERSION_LIBZ) + } + + /// Returns whether libcurl supports HTTP NTLM + pub fn feature_ntlm(&self) -> bool { + self.flag(curl_sys::CURL_VERSION_NTLM) + } + + /// Returns whether libcurl supports HTTP GSSNEGOTIATE + pub fn feature_gss_negotiate(&self) -> bool { + self.flag(curl_sys::CURL_VERSION_GSSNEGOTIATE) + } + + /// Returns whether libcurl was built with debug capabilities + pub fn feature_debug(&self) -> bool { + self.flag(curl_sys::CURL_VERSION_DEBUG) + } + + /// Returns whether libcurl was built with SPNEGO authentication + pub fn feature_spnego(&self) -> bool { + self.flag(curl_sys::CURL_VERSION_SPNEGO) + } + + /// Returns whether libcurl was built with large file support + pub fn feature_largefile(&self) -> bool { + self.flag(curl_sys::CURL_VERSION_LARGEFILE) + } + + /// Returns whether libcurl was built with support for IDNA, domain names + /// with international letters. + pub fn feature_idn(&self) -> bool { + self.flag(curl_sys::CURL_VERSION_IDN) + } + + /// Returns whether libcurl was built with support for SSPI. + pub fn feature_sspi(&self) -> bool { + self.flag(curl_sys::CURL_VERSION_SSPI) + } + + /// Returns whether libcurl was built with asynchronous name lookups. + pub fn feature_async_dns(&self) -> bool { + self.flag(curl_sys::CURL_VERSION_ASYNCHDNS) + } + + /// Returns whether libcurl was built with support for character + /// conversions. + pub fn feature_conv(&self) -> bool { + self.flag(curl_sys::CURL_VERSION_CONV) + } + + /// Returns whether libcurl was built with support for TLS-SRP. + pub fn feature_tlsauth_srp(&self) -> bool { + self.flag(curl_sys::CURL_VERSION_TLSAUTH_SRP) + } + + /// Returns whether libcurl was built with support for NTLM delegation to + /// winbind helper. + pub fn feature_ntlm_wb(&self) -> bool { + self.flag(curl_sys::CURL_VERSION_NTLM_WB) + } + + /// Returns whether libcurl was built with support for HTTP2. + pub fn feature_http2(&self) -> bool { + self.flag(curl_sys::CURL_VERSION_HTTP2) + } + + fn flag(&self, flag: c_int) -> bool { + unsafe { + (*self.inner).features & flag != 0 + } + } + + /// Returns the version of OpenSSL that is used, or None if there is no SSL + /// support. + pub fn ssl_version(&self) -> Option<&str> { + unsafe { + ::opt_str((*self.inner).ssl_version) + } + } + + /// Returns the version of libz that is used, or None if there is no libz + /// support. + pub fn libz_version(&self) -> Option<&str> { + unsafe { + ::opt_str((*self.inner).libz_version) + } + } + + /// Returns an iterator over the list of protocols that this build of + /// libcurl supports. + pub fn protocols(&self) -> Protocols { + unsafe { + Protocols { _inner: self, cur: (*self.inner).protocols } + } + } + + /// If available, the human readable version of ares that libcurl is linked + /// against. + pub fn ares_version(&self) -> Option<&str> { + unsafe { + if (*self.inner).age >= 1 { + ::opt_str((*self.inner).ares) + } else { + None + } + } + } + + /// If available, the version of ares that libcurl is linked against. + pub fn ares_version_num(&self) -> Option { + unsafe { + if (*self.inner).age >= 1 { + Some((*self.inner).ares_num as u32) + } else { + None + } + } + } + + /// If available, the version of libidn that libcurl is linked against. + pub fn libidn_version(&self) -> Option<&str> { + unsafe { + if (*self.inner).age >= 2 { + ::opt_str((*self.inner).libidn) + } else { + None + } + } + } + + /// If available, the version of iconv libcurl is linked against. + pub fn iconv_version_num(&self) -> Option { + unsafe { + if (*self.inner).age >= 3 { + Some((*self.inner).iconv_ver_num as u32) + } else { + None + } + } + } + + /// If available, the version of iconv libcurl is linked against. + pub fn libssh_version(&self) -> Option<&str> { + unsafe { + if (*self.inner).age >= 3 { + ::opt_str((*self.inner).libssh_version) + } else { + None + } + } + } +} + +impl<'a> Iterator for Protocols<'a> { + type Item = &'a str; + + fn next(&mut self) -> Option<&'a str> { + unsafe { + if (*self.cur).is_null() { + return None + } + let ret = ::opt_str(*self.cur).unwrap(); + self.cur = self.cur.offset(1); + Some(ret) + } + } +} From 7aca2f266e684fd615ae6a5a63967eddf96d0f94 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 19 Apr 2016 23:31:26 -0700 Subject: [PATCH 11/71] Start filling out raw Easy bindings --- Cargo.toml | 21 +- curl-sys/lib.rs | 16 +- foo.rs | 1 + src/easy.rs | 1996 +++++++++++++++++++++++++++++++++++++++++++ src/error.rs | 38 +- src/lib.rs | 3 + src/main.rs | 8 + src/panic.rs | 22 + tests/easy.rs | 503 +++++++++++ tests/server/mod.rs | 89 ++ 10 files changed, 2677 insertions(+), 20 deletions(-) create mode 100644 foo.rs create mode 100644 src/easy.rs create mode 100644 src/main.rs create mode 100644 src/panic.rs create mode 100644 tests/easy.rs create mode 100644 tests/server/mod.rs diff --git a/Cargo.toml b/Cargo.toml index f3bc030320..963d3b3ba6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,22 +9,13 @@ repository = "https://github.com/carllerche/curl-rust" description = "Rust bindings to libcurl for making HTTP requests" [dependencies] -url = "0.5.0" libc = "0.2" curl-sys = { path = "curl-sys", version = "0.1.0" } -[dev-dependencies] -env_logger = "0.3.0" - # Unix platforms use OpenSSL for now to provide SSL functionality -[target."cfg(all(unix, not(target_os = \"macos\")))".dependencies] -openssl-sys = "0.7.0" -[target.i686-unknown-linux-gnu.dependencies] -openssl-sys = "0.7.0" -[target.x86_64-unknown-linux-gnu.dependencies] -openssl-sys = "0.7.0" - -[[test]] - -name = "test" -path = "test/test.rs" +# [target."cfg(all(unix, not(target_os = \"macos\")))".dependencies] +# openssl-sys = "0.7.0" +# [target.i686-unknown-linux-gnu.dependencies] +# openssl-sys = "0.7.0" +# [target.x86_64-unknown-linux-gnu.dependencies] +# openssl-sys = "0.7.0" diff --git a/curl-sys/lib.rs b/curl-sys/lib.rs index b04508af9d..114856b227 100644 --- a/curl-sys/lib.rs +++ b/curl-sys/lib.rs @@ -1,4 +1,4 @@ -#![allow(non_camel_case_types)] +#![allow(bad_style)] extern crate libc; #[cfg(not(target_env = "msvc"))] @@ -593,10 +593,24 @@ pub const CURL_IPRESOLVE_WHATEVER: c_int = 0; pub const CURL_IPRESOLVE_V4: c_int = 1; pub const CURL_IPRESOLVE_V6: c_int = 2; +pub const CURL_SSLVERSION_DEFAULT: __enum_ty = 0; +pub const CURL_SSLVERSION_TLSv1: __enum_ty = 1; +pub const CURL_SSLVERSION_SSLv2: __enum_ty = 2; +pub const CURL_SSLVERSION_SSLv3: __enum_ty = 3; +pub const CURL_SSLVERSION_TLSv1_0: __enum_ty = 4; +pub const CURL_SSLVERSION_TLSv1_1: __enum_ty = 5; +pub const CURL_SSLVERSION_TLSv1_2: __enum_ty = 6; + pub const CURLOPT_READDATA: CURLoption = CURLOPT_INFILE; pub const CURLOPT_WRITEDATA: CURLoption = CURLOPT_FILE; pub const CURLOPT_HEADERDATA: CURLoption = CURLOPT_WRITEHEADER; +pub type curl_TimeCond = __enum_ty; +pub const CURL_TIMECOND_NONE: curl_TimeCond = 0; +pub const CURL_TIMECOND_IFMODSINCE: curl_TimeCond = 1; +pub const CURL_TIMECOND_IFUNMODSINCE: curl_TimeCond = 2; +pub const CURL_TIMECOND_LASTMOD: curl_TimeCond = 3; + pub type CURLformoption = __enum_ty; pub const CURLFORM_NOTHING: CURLformoption = 0; pub const CURLFORM_COPYNAME: CURLformoption = 1; diff --git a/foo.rs b/foo.rs new file mode 100644 index 0000000000..2fcc7b3f8f --- /dev/null +++ b/foo.rs @@ -0,0 +1 @@ +fn m diff --git a/src/easy.rs b/src/easy.rs new file mode 100644 index 0000000000..51add8e4e5 --- /dev/null +++ b/src/easy.rs @@ -0,0 +1,1996 @@ +//! dox + +use std::cell::Cell; +use std::ffi::{CString, CStr}; +use std::io::SeekFrom; +use std::marker; +use std::path::Path; +use std::slice; +use std::str; +use std::time::Duration; + +use curl_sys; +use libc::{self, c_long, c_int, c_char, c_void, size_t}; + +use Error; +use panic; + +// TODO: checked casts everywhere + +/// Raw bindings to a libcurl "easy session". +/// +/// This type corresponds to the `CURL` type in libcurl. +pub struct Easy<'a> { + handle: *mut curl_sys::CURL, + _marker: marker::PhantomData>, +} + +unsafe impl<'a> Send for Easy<'a> {} +unsafe impl<'a> Sync for Easy<'a> {} + +/// Possible proxy types that libcurl currently understands. +#[allow(missing_docs)] +pub enum ProxyType { + Http = curl_sys::CURLPROXY_HTTP as isize, + Http1 = curl_sys::CURLPROXY_HTTP_1_0 as isize, + Socks4 = curl_sys::CURLPROXY_SOCKS4 as isize, + Socks5 = curl_sys::CURLPROXY_SOCKS5 as isize, + Socks4a = curl_sys::CURLPROXY_SOCKS4A as isize, + Socks5Hostname = curl_sys::CURLPROXY_SOCKS5_HOSTNAME as isize, + + /// Hidden variant to indicate that this enum should not be matched on, it + /// may grow over time. + #[doc(hidden)] + __Nonexhaustive, +} + +/// Possible conditions for the `time_condition` method. +#[allow(missing_docs)] +pub enum TimeCondition { + None = curl_sys::CURL_TIMECOND_NONE as isize, + IfModifiedSince = curl_sys::CURL_TIMECOND_IFMODSINCE as isize, + IfUnmodifiedSince = curl_sys::CURL_TIMECOND_IFUNMODSINCE as isize, + LastModified = curl_sys::CURL_TIMECOND_LASTMOD as isize, + + /// Hidden variant to indicate that this enum should not be matched on, it + /// may grow over time. + #[doc(hidden)] + __Nonexhaustive, +} + +/// Possible values to pass to the `ip_resolve` method. +#[allow(missing_docs)] +pub enum IpResolve { + V4 = curl_sys::CURL_IPRESOLVE_V4 as isize, + V6 = curl_sys::CURL_IPRESOLVE_V6 as isize, + Any = curl_sys::CURL_IPRESOLVE_WHATEVER as isize, + + /// Hidden variant to indicate that this enum should not be matched on, it + /// may grow over time. + #[doc(hidden)] + __Nonexhaustive = 500, +} + +/// Possible values to pass to the `ip_resolve` method. +#[allow(missing_docs)] +pub enum SslVersion { + Default = curl_sys::CURL_SSLVERSION_DEFAULT as isize, + Tlsv1 = curl_sys::CURL_SSLVERSION_TLSv1 as isize, + Sslv2 = curl_sys::CURL_SSLVERSION_SSLv2 as isize, + Sslv3 = curl_sys::CURL_SSLVERSION_SSLv3 as isize, + Tlsv10 = curl_sys::CURL_SSLVERSION_TLSv1_0 as isize, + Tlsv11 = curl_sys::CURL_SSLVERSION_TLSv1_1 as isize, + Tlsv12 = curl_sys::CURL_SSLVERSION_TLSv1_2 as isize, + + /// Hidden variant to indicate that this enum should not be matched on, it + /// may grow over time. + #[doc(hidden)] + __Nonexhaustive = 500, +} + +/// Possible return values from the `seek_function` callback. +pub enum SeekResult { + /// Indicates that the seek operation was a success + Ok = curl_sys::CURL_SEEKFUNC_OK as isize, + + /// Indicates that the seek operation failed, and the entire request should + /// fail as a result. + Fail = curl_sys::CURL_SEEKFUNC_FAIL as isize, + + /// Indicates that although the seek failed libcurl should attempt to keep + /// working if possible (for example "seek" through reading). + CantSeek = curl_sys::CURL_SEEKFUNC_CANTSEEK as isize, + + /// Hidden variant to indicate that this enum should not be matched on, it + /// may grow over time. + #[doc(hidden)] + __Nonexhaustive = 500, +} + +/// Possible data chunks that can be witnessed as part of the `debug_function` +/// callback. +pub enum InfoType { + /// The data is informational text. + Text, + + /// The data is header (or header-like) data received from the peer. + HeaderIn, + + /// The data is header (or header-like) data sent to the peer. + HeaderOut, + + /// The data is protocol data received from the peer. + DataIn, + + /// The data is protocol data sent to the peer. + DataOut, + + /// The data is SSL/TLS (binary) data received from the peer. + SslDataIn, + + /// The data is SSL/TLS (binary) data sent to the peer. + SslDataOut, + + /// Hidden variant to indicate that this enum should not be matched on, it + /// may grow over time. + #[doc(hidden)] + __Nonexhaustive, +} + +/// A linked list of a strings +pub struct List { + raw: *mut curl_sys::curl_slist, +} + +unsafe impl Send for List {} +unsafe impl Sync for List {} + +fn cvt(r: curl_sys::CURLcode) -> Result<(), Error> { + if r == curl_sys::CURLE_OK { + Ok(()) + } else { + Err(Error::new(r)) + } +} + +impl<'a> Easy<'a> { + /// Creates a new "easy" handle which is the core of almost all operations + /// in libcurl. + /// + /// To use a handle, applications typically configure a number of options + /// followed by a call to `perform`. Options are preserved across calls to + /// `perform` and need to be reset manually (or via the `reset` method) if + /// this is not desired. + pub fn new() -> Easy<'a> { + ::init(); + unsafe { + let handle = curl_sys::curl_easy_init(); + assert!(!handle.is_null()); + Easy { + handle: handle, + _marker: marker::PhantomData, + } + } + } + + // ========================================================================= + // Behavior options + + /// Configures this handle to have verbose output to help debug protocol + /// information. + /// + /// By default output goes to stderr, but the `stderr` function on this type + /// can configure that. You can also use the `debug_function` method to get + /// all protocol data sent and received. + /// + /// By default, this option is `false`. + pub fn verbose(&mut self, verbose: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_VERBOSE, verbose as c_long) + } + + /// Indicates whether header information is streamed to the output body of + /// this request. + /// + /// This option is only relevant for protocols which have header metadata + /// (like http or ftp). It's not generally possible to extract headers + /// from the body if using this method, that use case should be intended for + /// the `header_function` method. + /// + /// To set HTTP headers, use the `http_header` method. + /// + /// By default, this option is `false` and corresponds to + /// `CURLOPT_HEADER`. + pub fn show_header(&mut self, show: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_HEADER, show as c_long) + } + + /// Indicates whether a progress meter will be shown for requests done with + /// this handle. + /// + /// This will also prevent the `progress_function` from being called. + /// + /// By default this option is `false` and corresponds to + /// `CURLOPT_NOPROGRESS`. + pub fn progress(&mut self, progress: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_NOPROGRESS, + (!progress) as c_long) + } + + /// Inform libcurl whether or not it should install signal handlers or + /// attempt to use signals to perform library functions. + /// + /// If this option is disabled then timeouts during name resolution will not + /// work unless libcurl is built against c-ares. + /// + /// By default this option is `true` and corresponds to `CURLOPT_NOSIGNAL`. + pub fn signal(&mut self, signal: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_NOSIGNAL, + (!signal) as c_long) + } + + /// Indicates whether multiple files will be transferred based on the file + /// name pattern. + /// + /// The last part of a filename uses fnmatch-like pattern matching. + /// + /// By default this option is `false` and corresponds to + /// `CURLOPT_WILDCARDMATCH`. + pub fn wildcard_match(&mut self, m: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_WILDCARDMATCH, m as c_long) + } + + // ========================================================================= + // Callback options + + /// Set callback for writing received data. + /// + /// This callback function gets called by libcurl as soon as there is data + /// received that needs to be saved. + /// + /// The callback function will be passed as much data as possible in all + /// invokes, but you must not make any assumptions. It may be one byte, it + /// may be thousands. If `show_header` is enabled, which makes header data + /// get passed to the write callback, you can get up to + /// `CURL_MAX_HTTP_HEADER` bytes of header data passed into it. This + /// usually means 100K. + /// + /// This function may be called with zero bytes data if the transferred file + /// is empty. + /// + /// The callback should return the number of bytes actually taken care of. + /// If that amount differs from the amount passed to your callback function, + /// it'll signal an error condition to the library. This will cause the + /// transfer to get aborted and the libcurl function used will return + /// an error with `is_write_error`. + /// + /// If your callback function returns `CURL_WRITEFUNC_PAUSE` it will cause + /// this transfer to become paused. See `pause` for further details. + /// + /// By default data is written to stdout, and this corresponds to the + /// `CURLOPT_WRITEFUNCTION` and `CURLOPT_WRITEDATA` options. + pub fn write_function(&mut self, f: &'a mut F) -> Result<(), Error> + where F: FnMut(&[u8]) -> usize + { + try!(self.setopt_ptr(curl_sys::CURLOPT_WRITEFUNCTION, + cb:: as usize as *const c_char)); + try!(self.setopt_ptr(curl_sys::CURLOPT_WRITEDATA, + f as *mut F as *const c_char)); + return Ok(()); + + unsafe extern fn cb(ptr: *mut c_char, + size: size_t, + nmemb: size_t, + data: *mut c_void) -> size_t + where F: FnMut(&[u8]) -> usize, + { + let input = slice::from_raw_parts(ptr as *const u8, size * nmemb); + panic::catch(|| { + (*(data as *mut F))(input) + }).unwrap_or(!0) + } + } + + /// Read callback for data uploads. + /// + /// This callback function gets called by libcurl as soon as it needs to + /// read data in order to send it to the peer - like if you ask it to upload + /// or post data to the server. + /// + /// Your function must then return the actual number of bytes that it stored + /// in that memory area. Returning 0 will signal end-of-file to the library + /// and cause it to stop the current transfer. + /// + /// If you stop the current transfer by returning 0 "pre-maturely" (i.e + /// before the server expected it, like when you've said you will upload N + /// bytes and you upload less than N bytes), you may experience that the + /// server "hangs" waiting for the rest of the data that won't come. + /// + /// The read callback may return `CURL_READFUNC_ABORT` to stop the current + /// operation immediately, resulting in a `is_aborted_by_callback` error + /// code from the transfer. + /// + /// The callback can return `CURL_READFUNC_PAUSE` to cause reading from this + /// connection to pause. See `pause` for further details. + /// + /// By default data is read from stdin, and this corresponds to the + /// `CURLOPT_READFUNCTION` and `CURLOPT_READDATA` options. + pub fn read_function(&mut self, f: &'a mut F) -> Result<(), Error> + where F: FnMut(&mut [u8]) -> usize + { + try!(self.setopt_ptr(curl_sys::CURLOPT_READFUNCTION, + cb:: as usize as *const c_char)); + try!(self.setopt_ptr(curl_sys::CURLOPT_READDATA, + f as *mut F as *const c_char)); + return Ok(()); + + unsafe extern fn cb(ptr: *mut c_char, + size: size_t, + nmemb: size_t, + data: *mut c_void) -> size_t + where F: FnMut(&mut [u8]) -> usize, + { + let input = slice::from_raw_parts_mut(ptr as *mut u8, size * nmemb); + panic::catch(|| { + (*(data as *mut F))(input) + }).unwrap_or(!0) + } + } + + /// User callback for seeking in input stream. + /// + /// This function gets called by libcurl to seek to a certain position in + /// the input stream and can be used to fast forward a file in a resumed + /// upload (instead of reading all uploaded bytes with the normal read + /// function/callback). It is also called to rewind a stream when data has + /// already been sent to the server and needs to be sent again. This may + /// happen when doing a HTTP PUT or POST with a multi-pass authentication + /// method, or when an existing HTTP connection is reused too late and the + /// server closes the connection. + /// + /// The callback function must return `SeekResult::Ok` on success, + /// `SeekResult::Fail` to cause the upload operation to fail or + /// `SeekResult::CantSeek` to indicate that while the seek failed, libcurl + /// is free to work around the problem if possible. The latter can sometimes + /// be done by instead reading from the input or similar. + /// + /// By default data this option is not set, and this corresponds to the + /// `CURLOPT_SEEKFUNCTION` and `CURLOPT_SEEKDATA` options. + pub fn seek_function(&mut self, f: &'a mut F) -> Result<(), Error> + where F: FnMut(SeekFrom) -> SeekResult + { + try!(self.setopt_ptr(curl_sys::CURLOPT_SEEKFUNCTION, + cb:: as usize as *const c_char)); + try!(self.setopt_ptr(curl_sys::CURLOPT_SEEKDATA, + f as *mut F as *const c_char)); + return Ok(()); + + unsafe extern fn cb(data: *mut c_void, + offset: curl_sys::curl_off_t, + origin: c_int) -> c_int + where F: FnMut(SeekFrom) -> SeekResult + { + panic::catch(|| { + let from = if origin == libc::SEEK_SET { + SeekFrom::Start(offset as u64) + } else { + panic!("unknown origin from libcurl: {}", origin); + }; + (*(data as *mut F))(from) as c_int + }).unwrap_or(!0) + } + } + + /// Callback to progress meter function + /// + /// This function gets called by libcurl instead of its internal equivalent + /// with a frequent interval. While data is being transferred it will be + /// called very frequently, and during slow periods like when nothing is + /// being transferred it can slow down to about one call per second. + /// + /// The callback gets told how much data libcurl will transfer and has + /// transferred, in number of bytes. The first argument is the total number + /// of bytes libcurl expects to download in this transfer. The second + /// argument is the number of bytes downloaded so far. The third argument is + /// the total number of bytes libcurl expects to upload in this transfer. + /// The fourth argument is the number of bytes uploaded so far. + /// + /// Unknown/unused argument values passed to the callback will be set to + /// zero (like if you only download data, the upload size will remain 0). + /// Many times the callback will be called one or more times first, before + /// it knows the data sizes so a program must be made to handle that. + /// + /// Returning `false` from this callback will cause libcurl to abort the + /// transfer and return `is_aborted_by_callback`. + /// + /// If you transfer data with the multi interface, this function will not be + /// called during periods of idleness unless you call the appropriate + /// libcurl function that performs transfers. + /// + /// `noprogress` must be set to 0 to make this function actually get + /// called. + /// + /// By default this function calls an internal method and corresponds to + /// `CURLOPT_XFERINFOFUNCTION` and `CURLOPT_XFERINFODATA`. + pub fn progress_function(&mut self, f: &'a mut F) -> Result<(), Error> + where F: FnMut(u64, u64, u64, u64) -> bool + { + try!(self.setopt_ptr(curl_sys::CURLOPT_XFERINFOFUNCTION, + cb:: as usize as *const c_char)); + try!(self.setopt_ptr(curl_sys::CURLOPT_PROGRESSDATA, + f as *mut F as *const c_char)); + return Ok(()); + + unsafe extern fn cb(data: *mut c_void, + dltotal: curl_sys::curl_off_t, + dlnow: curl_sys::curl_off_t, + ultotal: curl_sys::curl_off_t, + ulnow: curl_sys::curl_off_t) -> c_int + where F: FnMut(u64, u64, u64, u64) -> bool + { + let keep_going = panic::catch(|| { + let fnptr = &mut *(data as *mut F); + fnptr(dltotal as u64, dlnow as u64, ultotal as u64, ulnow as u64) + }).unwrap_or(false); + if keep_going { + 0 + } else { + 1 + } + } + } + + /// Specify a debug callback + /// + /// `debug_function` replaces the standard debug function used when + /// `verbose` is in effect. This callback receives debug information, + /// as specified in the type argument. + /// + /// By default this option is not set and corresponds to the + /// `CURLOPT_DEBUGFUNCTION` and `CURLOPT_DEBUGDATA` options. + pub fn debug_function(&mut self, f: &'a mut F) -> Result<(), Error> + where F: FnMut(InfoType, &[u8]) + { + try!(self.setopt_ptr(curl_sys::CURLOPT_DEBUGFUNCTION, + cb:: as usize as *const c_char)); + try!(self.setopt_ptr(curl_sys::CURLOPT_DEBUGDATA, + f as *mut F as *const c_char)); + return Ok(()); + + // TODO: expose `handle`? is that safe? + unsafe extern fn cb(_handle: *mut curl_sys::CURL, + kind: curl_sys::curl_infotype, + data: *mut c_char, + size: size_t, + userptr: *mut c_void) -> c_int + where F: FnMut(InfoType, &[u8]) + { + panic::catch(|| { + let data = slice::from_raw_parts(data as *const u8, size); + let kind = match kind { + curl_sys::CURLINFO_TEXT => InfoType::Text, + curl_sys::CURLINFO_HEADER_IN => InfoType::HeaderIn, + curl_sys::CURLINFO_HEADER_OUT => InfoType::HeaderOut, + curl_sys::CURLINFO_DATA_IN => InfoType::DataIn, + curl_sys::CURLINFO_DATA_OUT => InfoType::DataOut, + curl_sys::CURLINFO_SSL_DATA_IN => InfoType::SslDataIn, + curl_sys::CURLINFO_SSL_DATA_OUT => InfoType::SslDataOut, + _ => return, + }; + (*(userptr as *mut F))(kind, data) + }); + return 0 + } + } + + /// Callback that receives header data + /// + /// This function gets called by libcurl as soon as it has received header + /// data. The header callback will be called once for each header and only + /// complete header lines are passed on to the callback. Parsing headers is + /// very easy using this. If this callback returns `false` it'll signal an + /// error to the library. This will cause the transfer to get aborted and + /// the libcurl function in progress will return `is_write_error`. + /// + /// A complete HTTP header that is passed to this function can be up to + /// CURL_MAX_HTTP_HEADER (100K) bytes. + /// + /// It's important to note that the callback will be invoked for the headers + /// of all responses received after initiating a request and not just the + /// final response. This includes all responses which occur during + /// authentication negotiation. If you need to operate on only the headers + /// from the final response, you will need to collect headers in the + /// callback yourself and use HTTP status lines, for example, to delimit + /// response boundaries. + /// + /// When a server sends a chunked encoded transfer, it may contain a + /// trailer. That trailer is identical to a HTTP header and if such a + /// trailer is received it is passed to the application using this callback + /// as well. There are several ways to detect it being a trailer and not an + /// ordinary header: 1) it comes after the response-body. 2) it comes after + /// the final header line (CR LF) 3) a Trailer: header among the regular + /// response-headers mention what header(s) to expect in the trailer. + /// + /// For non-HTTP protocols like FTP, POP3, IMAP and SMTP this function will + /// get called with the server responses to the commands that libcurl sends. + /// + /// By default this option is not set and corresponds to the + /// `CURLOPT_HEADERFUNCTION` and `CURLOPT_HEADERDATA` options. + pub fn header_function(&mut self, f: &'a mut F) -> Result<(), Error> + where F: FnMut(&[u8]) -> bool + { + try!(self.setopt_ptr(curl_sys::CURLOPT_HEADERFUNCTION, + cb:: as usize as *const c_char)); + try!(self.setopt_ptr(curl_sys::CURLOPT_HEADERDATA, + f as *mut F as *const c_char)); + return Ok(()); + + // TODO: expose `handle`? is that safe? + unsafe extern fn cb(buffer: *mut c_char, + size: size_t, + nitems: size_t, + userptr: *mut c_void) -> size_t + where F: FnMut(&[u8]) -> bool + { + let keep_going = panic::catch(|| { + let data = slice::from_raw_parts(buffer as *const u8, + size * nitems); + (*(userptr as *mut F))(data) + }).unwrap_or(false); + if keep_going { + size * nitems + } else { + !0 + } + } + } + + // ========================================================================= + // Error options + + // TODO: error buffer and stderr + + /// Indicates whether this library will fail on HTTP response codes >= 400. + /// + /// This method is not fail-safe especially when authentication is involved. + /// + /// By default this option is `false` and corresponds to + /// `CURLOPT_FAILONERROR`. + pub fn fail_on_error(&mut self, fail: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_FAILONERROR, fail as c_long) + } + + // ========================================================================= + // Network options + + /// Provides the URL which this handle will work with. + /// + /// The string provided must be URL-encoded with the format: + /// + /// ```text + /// scheme://host:port/path + /// ``` + /// + /// The syntax is not validated as part of this function and that is + /// deferred until later. + /// + /// By default this option is not set and `perform` will not work until it + /// is set. This option corresponds to `CURLOPT_URL`. + pub fn url(&mut self, url: &str) -> Result<(), Error> { + let url = try!(CString::new(url)); + self.setopt_str(curl_sys::CURLOPT_URL, &url) + } + + /// Configures the port number to connect to, instead of the one specified + /// in the URL or the default of the protocol. + pub fn port(&mut self, port: u16) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_PORT, port as c_long) + } + + // /// Indicates whether sequences of `/../` and `/./` will be squashed or not. + // /// + // /// By default this option is `false` and corresponds to + // /// `CURLOPT_PATH_AS_IS`. + // pub fn path_as_is(&mut self, as_is: bool) -> Result<(), Error> { + // } + + /// Provide the URL of a proxy to use. + /// + /// By default this option is not set and corresponds to `CURLOPT_PROXY`. + pub fn proxy(&mut self, url: &str) -> Result<(), Error> { + let url = try!(CString::new(url)); + self.setopt_str(curl_sys::CURLOPT_PROXY, &url) + } + + /// Provide port number the proxy is listening on. + /// + /// By default this option is not set (the default port for the proxy + /// protocol is used) and corresponds to `CURLOPT_PROXYPORT`. + pub fn proxy_port(&mut self, port: u16) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_PROXYPORT, port as c_long) + } + + /// Indicates the type of proxy being used. + /// + /// By default this option is `ProxyType::Http` and corresponds to + /// `CURLOPT_PROXYTYPE`. + pub fn proxy_type(&mut self, kind: ProxyType) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_PROXYTYPE, kind as c_long) + } + + /// Provide a list of hosts that should not be proxied to. + /// + /// This string is a comma-separated list of hosts which should not use the + /// proxy specified for connections. A single `*` character is also accepted + /// as a wildcard for all hosts. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_NOPROXY`. + pub fn noproxy(&mut self, skip: &str) -> Result<(), Error> { + let skip = try!(CString::new(skip)); + self.setopt_str(curl_sys::CURLOPT_PROXYTYPE, &skip) + } + + /// Inform curl whether it should tunnel all operations through the proxy. + /// + /// This essentially means that a `CONNECT` is sent to the proxy for all + /// outbound requests. + /// + /// By default this option is `false` and corresponds to + /// `CURLOPT_HTTPPROXYTUNNEL`. + pub fn http_proxy_tunnel(&mut self, tunnel: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_HTTPPROXYTUNNEL, + tunnel as c_long) + } + + /// Tell curl which interface to bind to for an outgoing network interface. + /// + /// The interface name, IP address, or host name can be specified here. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_INTERFACE`. + pub fn interface(&mut self, interface: &str) -> Result<(), Error> { + let s = try!(CString::new(interface)); + self.setopt_str(curl_sys::CURLOPT_INTERFACE, &s) + } + + /// Indicate which port should be bound to locally for this connection. + /// + /// By default this option is 0 (any port) and corresponds to + /// `CURLOPT_LOCALPORT`. + pub fn local_port(&mut self, port: u16) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_LOCALPORT, port as c_long) + } + + /// Indicates the number of attempts libcurl will perform to find a working + /// port number. + /// + /// By default this option is 1 and corresponds to + /// `CURLOPT_LOCALPORTRANGE`. + pub fn local_port_range(&mut self, range: u16) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_LOCALPORTRANGE, + range as c_long) + } + + /// Sets the timeout of how long name resolves will be kept in memory. + /// + /// This is distinct from DNS TTL options and is entirely speculative. + /// + /// By default this option is 60s and corresponds to + /// `CURLOPT_DNS_CACHE_TIMEOUT`. + pub fn dns_cache_timeout(&mut self, dur: Duration) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_DNS_CACHE_TIMEOUT, + dur.as_secs() as c_long) + } + + /// Specify the preferred receive buffer size, in bytes. + /// + /// This is treated as a request, not an order, and the main point of this + /// is that the write callback may get called more often with smaller + /// chunks. + /// + /// By default this option is the maximum write size and corresopnds to + /// `CURLOPT_BUFFERSIZE`. + pub fn buffer_size(&mut self, size: usize) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_BUFFERSIZE, size as c_long) + } + + // /// Enable or disable TCP Fast Open + // /// + // /// By default this options defaults to `false` and corresponds to + // /// `CURLOPT_TCP_FASTOPEN` + // pub fn fast_open(&mut self, enable: bool) -> Result<(), Error> { + // } + + /// Configures whether the TCP_NODELAY option is set, or Nagle's algorithm + /// is disabled. + /// + /// The purpose of Nagle's algorithm is to minimize the number of small + /// packet's on the network, and disabling this may be less efficient in + /// some situations. + /// + /// By default this option is `false` and corresponds to + /// `CURLOPT_TCP_NODELAY`. + pub fn tcp_nodelay(&mut self, enable: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_TCP_NODELAY, enable as c_long) + } + + /// Configures whether TCP keepalive probes will be sent. + /// + /// The delay and frequency of these probes is controlled by `tcp_keepidle` + /// and `tcp_keepintvl`. + /// + /// By default this option is `false` and corresponds to + /// `CURLOPT_TCP_KEEPALIVE`. + pub fn tcp_keepalive(&mut self, enable: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_TCP_KEEPALIVE, enable as c_long) + } + + /// Configures the TCP keepalive idle time wait. + /// + /// This is the delay, after which the connection is idle, keepalive probes + /// will be sent. Not all operating systems support this. + /// + /// By default this corresponds to `CURLOPT_TCP_KEEPIDLE`. + pub fn tcp_keepidle(&mut self, amt: Duration) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_TCP_KEEPIDLE, + amt.as_secs() as c_long) + } + + /// Configures the delay between keepalive probes. + /// + /// By default this corresponds to `CURLOPT_TCP_KEEPINTVL`. + pub fn tcp_keepintvl(&mut self, amt: Duration) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_TCP_KEEPINTVL, + amt.as_secs() as c_long) + } + + /// Configures the scope for local IPv6 addresses. + /// + /// Sets the scope_id value to use when connecting to IPv6 or link-local + /// addresses. + /// + /// By default this value is 0 and corresponds to `CURLOPT_ADDRESS_SCOPE` + pub fn address_scope(&mut self, scope: u32) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_TCP_KEEPINTVL, + scope as c_long) + } + + // ========================================================================= + // Names and passwords + + /// Configures the username to pass as authentication for this connection. + /// + /// By default this value is not set and corresponds to `CURLOPT_USERNAME`. + pub fn username(&mut self, user: &str) -> Result<(), Error> { + let user = try!(CString::new(user)); + self.setopt_str(curl_sys::CURLOPT_USERNAME, &user) + } + + /// Configures the password to pass as authentication for this connection. + /// + /// By default this value is not set and corresponds to `CURLOPT_PASSWORD`. + pub fn password(&mut self, pass: &str) -> Result<(), Error> { + let pass = try!(CString::new(pass)); + self.setopt_str(curl_sys::CURLOPT_PASSWORD, &pass) + } + + /// Configures the proxy username to pass as authentication for this + /// connection. + /// + /// By default this value is not set and corresponds to + /// `CURLOPT_PROXYUSERNAME`. + pub fn proxy_username(&mut self, user: &str) -> Result<(), Error> { + let user = try!(CString::new(user)); + self.setopt_str(curl_sys::CURLOPT_PROXYUSERNAME, &user) + } + + /// Configures the proxy password to pass as authentication for this + /// connection. + /// + /// By default this value is not set and corresponds to + /// `CURLOPT_PROXYPASSWORD`. + pub fn proxy_password(&mut self, pass: &str) -> Result<(), Error> { + let pass = try!(CString::new(pass)); + self.setopt_str(curl_sys::CURLOPT_PROXYPASSWORD, &pass) + } + + // ========================================================================= + // HTTP Options + + /// Indicates whether the referer header is automatically updated + /// + /// By default this option is `false` and corresponds to + /// `CURLOPT_AUTOREFERER`. + pub fn autoreferer(&mut self, enable: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_AUTOREFERER, enable as c_long) + } + + /// Enables automatic decompression of HTTP downloads. + /// + /// Sets the contents of the Accept-Encoding header sent in an HTTP request. + /// This enables decoding of a response with Content-Encoding. + /// + /// Currently supported encoding are `identity`, `zlib`, and `gzip`. A + /// zero-length string passed in will send all accepted encodings. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_ACCEPT_ENCODING`. + pub fn accept_encoding(&mut self, encoding: &str) -> Result<(), Error> { + let encoding = try!(CString::new(encoding)); + self.setopt_str(curl_sys::CURLOPT_ACCEPT_ENCODING, &encoding) + } + + /// Request the HTTP Transfer Encoding. + /// + /// By default this option is `false` and corresponds to + /// `CURLOPT_TRANSFER_ENCODING`. + pub fn transfer_encoding(&mut self, enable: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_TRANSFER_ENCODING, enable as c_long) + } + + /// Follow HTTP 3xx redirects. + /// + /// Indicates whether any `Location` headers in the response should get + /// followed. + /// + /// By default this option is `false` and corresponds to + /// `CURLOPT_FOLLOWLOCATION`. + pub fn follow_location(&mut self, enable: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_FOLLOWLOCATION, enable as c_long) + } + + /// Send credentials to hosts other than the first as well. + /// + /// Sends username/password credentials even when the host changes as part + /// of a redirect. + /// + /// By default this option is `false` and corresponds to + /// `CURLOPT_UNRESTRICTED_AUTH`. + pub fn unrestricted_auth(&mut self, enable: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_UNRESTRICTED_AUTH, enable as c_long) + } + + /// Set the maximum number of redirects allowed. + /// + /// A value of 0 will refuse any redirect. + /// + /// By default this option is `-1` (unlimited) and corresponds to + /// `CURLOPT_MAXREDIRS`. + pub fn max_redirections(&mut self, max: u32) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_MAXREDIRS, max as c_long) + } + + // TODO: post_redirections + + /// Make an HTTP PUT request. + /// + /// By default this option is `false` and corresponds to `CURLOPT_PUT`. + pub fn put(&mut self, enable: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_PUT, enable as c_long) + } + + /// Make an HTTP POST request. + /// + /// This will also make the library use the + /// `Content-Type: application/x-www-form-urlencoded` header. + /// + /// POST data can be specified through `post_fields` or by specifying a read + /// function. + /// + /// By default this option is `false` and corresponds to `CURLOPT_POST`. + pub fn post(&mut self, enable: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_POST, enable as c_long) + } + + /// Configures the data that will be uploaded as part of a POST. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_POSTFIELDS`. + pub fn post_fields(&mut self, data: &'a [u8]) -> Result<(), Error> { + // Set the length before the pointer so libcurl knows how much to read + try!(self.post_field_size(data.len() as u64)); + self.setopt_ptr(curl_sys::CURLOPT_POSTFIELDS, + data.as_ptr() as *const _) + } + + /// Configures the data that will be uploaded as part of a POST. + /// + /// Note that the data is copied into this handle and if that's not desired + /// then the read callbacks can be used instead. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_COPYPOSTFIELDS`. + pub fn post_fields_copy(&mut self, data: &[u8]) -> Result<(), Error> { + // Set the length before the pointer so libcurl knows how much to read + try!(self.post_field_size(data.len() as u64)); + self.setopt_ptr(curl_sys::CURLOPT_COPYPOSTFIELDS, + data.as_ptr() as *const _) + } + + /// Configures the size of data that's going to be uploaded as part of a + /// POST operation. + /// + /// This is called automaticsally as part of `post_fields` and should only + /// be called if data is being provided in a read callback (and even then + /// it's optional). + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_POSTFIELDSIZE_LARGE`. + pub fn post_field_size(&mut self, size: u64) -> Result<(), Error> { + // Clear anything previous to ensure we don't read past a buffer + try!(self.setopt_ptr(curl_sys::CURLOPT_POSTFIELDS, 0 as *const _)); + self.setopt_off_t(curl_sys::CURLOPT_POSTFIELDSIZE_LARGE, + size as curl_sys::curl_off_t) + } + + // TODO: httppost (multipart post) + + /// Sets the HTTP referer header + /// + /// By default this option is not set and corresponds to `CURLOPT_REFERER`. + pub fn referer(&mut self, referer: &str) -> Result<(), Error> { + let referer = try!(CString::new(referer)); + self.setopt_str(curl_sys::CURLOPT_REFERER, &referer) + } + + /// Sets the HTTP user-agent header + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_USERAGENT`. + pub fn useragent(&mut self, useragent: &str) -> Result<(), Error> { + let useragent = try!(CString::new(useragent)); + self.setopt_str(curl_sys::CURLOPT_USERAGENT, &useragent) + } + + /// Add some headers to this HTTP request. + /// + /// If you add a header that is otherwise used internally, the value here + /// takes precedence. If a header is added with no content (like `Accept:`) + /// the internally the header will get disabled. To add a header with no + /// content, use the form `MyHeader;` (not the trailing semicolon). + /// + /// Headers must not be CRLF terminated. Many replaced headers have common + /// shortcuts which should be prefered. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_HTTPHEADER` + pub fn http_headers(&mut self, list: &'a List) -> Result<(), Error> { + self.setopt_ptr(curl_sys::CURLOPT_HTTPHEADER, list.raw as *const _) + } + + // /// Add some headers to send to the HTTP proxy. + // /// + // /// This function is essentially the same as `http_headers`. + // /// + // /// By default this option is not set and corresponds to + // /// `CURLOPT_PROXYHEADER` + // pub fn proxy_headers(&mut self, list: &'a List) -> Result<(), Error> { + // self.setopt_ptr(curl_sys::CURLOPT_PROXYHEADER, list.raw as *const _) + // } + + /// Set the contents of the HTTP Cookie header. + /// + /// Pass a string of the form `name=contents` for one cookie value or + /// `name1=val1; name2=val2` for multiple values. + /// + /// Using this option multiple times will only make the latest string + /// override the previous ones. This option will not enable the cookie + /// engine, use `cookie_file` or `cookie_jar` to do that. + /// + /// By default this option is not set and corresponds to `CURLOPT_COOKIE`. + pub fn cookie(&mut self, cookie: &str) -> Result<(), Error> { + let cookie = try!(CString::new(cookie)); + self.setopt_str(curl_sys::CURLOPT_COOKIE, &cookie) + } + + /// Set the file name to read cookies from. + /// + /// The cookie data can be in either the old Netscape / Mozilla cookie data + /// format or just regular HTTP headers (Set-Cookie style) dumped to a file. + /// + /// This also enables the cookie engine, making libcurl parse and send + /// cookies on subsequent requests with this handle. + /// + /// Given an empty or non-existing file or by passing the empty string ("") + /// to this option, you can enable the cookie engine without reading any + /// initial cookies. + /// + /// If you use this option multiple times, you just add more files to read. + /// Subsequent files will add more cookies. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_COOKIEFILE`. + pub fn cookie_file>(&mut self, file: P) -> Result<(), Error> { + self.setopt_path(curl_sys::CURLOPT_COOKIEFILE, file.as_ref()) + } + + /// Set the file name to store cookies to. + /// + /// This will make libcurl write all internally known cookies to the file + /// when this handle is dropped. If no cookies are known, no file will be + /// created. Specify "-" as filename to instead have the cookies written to + /// stdout. Using this option also enables cookies for this session, so if + /// you for example follow a location it will make matching cookies get sent + /// accordingly. + /// + /// Note that libcurl doesn't read any cookies from the cookie jar. If you + /// want to read cookies from a file, use `cookie_file`. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_COOKIEJAR`. + pub fn cookie_jar>(&mut self, file: P) -> Result<(), Error> { + self.setopt_path(curl_sys::CURLOPT_COOKIEJAR, file.as_ref()) + } + + /// Start a new cookie session + /// + /// Marks this as a new cookie "session". It will force libcurl to ignore + /// all cookies it is about to load that are "session cookies" from the + /// previous session. By default, libcurl always stores and loads all + /// cookies, independent if they are session cookies or not. Session cookies + /// are cookies without expiry date and they are meant to be alive and + /// existing for this "session" only. + /// + /// By default this option is `false` and corresponds to + /// `CURLOPT_COOKIESESSION`. + pub fn cookie_session(&mut self, session: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_COOKIESESSION, session as c_long) + } + + /// Add to or manipulate cookies held in memory. + /// + /// Such a cookie can be either a single line in Netscape / Mozilla format + /// or just regular HTTP-style header (Set-Cookie: ...) format. This will + /// also enable the cookie engine. This adds that single cookie to the + /// internal cookie store. + /// + /// Exercise caution if you are using this option and multiple transfers may + /// occur. If you use the Set-Cookie format and don't specify a domain then + /// the cookie is sent for any domain (even after redirects are followed) + /// and cannot be modified by a server-set cookie. If a server sets a cookie + /// of the same name (or maybe you've imported one) then both will be sent + /// on a future transfer to that server, likely not what you intended. + /// address these issues set a domain in Set-Cookie or use the Netscape + /// format. + /// + /// Additionally, there are commands available that perform actions if you + /// pass in these exact strings: + /// + /// * "ALL" - erases all cookies held in memory + /// * "SESS" - erases all session cookies held in memory + /// * "FLUSH" - write all known cookies to the specified cookie jar + /// * "RELOAD" - reread all cookies from the cookie file + /// + /// By default this options corresponds to `CURLOPT_COOKIELIST` + pub fn cookie_list(&mut self, cookie: &str) -> Result<(), Error> { + let cookie = try!(CString::new(cookie)); + self.setopt_str(curl_sys::CURLOPT_COOKIELIST, &cookie) + } + + /// Ask for a HTTP GET request. + /// + /// By default this option is `false` and corresponds to `CURLOPT_HTTPGET`. + pub fn get(&mut self, enable: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_HTTPGET, enable as c_long) + } + + // /// Ask for a HTTP GET request. + // /// + // /// By default this option is `false` and corresponds to `CURLOPT_HTTPGET`. + // pub fn http_version(&mut self, vers: &str) -> Result<(), Error> { + // self.setopt_long(curl_sys::CURLOPT_HTTPGET, enable as c_long) + // } + + /// Ignore the content-length header. + /// + /// By default this option is `false` and corresponds to + /// `CURLOPT_IGNORE_CONTENT_LENGTH`. + pub fn ignore_content_length(&mut self, ignore: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_IGNORE_CONTENT_LENGTH, + ignore as c_long) + } + + /// Enable or disable HTTP content decoding. + /// + /// By default this option is `true` and corresponds to + /// `CURLOPT_HTTP_CONTENT_DECODING`. + pub fn http_content_decoding(&mut self, enable: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_HTTP_CONTENT_DECODING, + enable as c_long) + } + + /// Enable or disable HTTP transfer decoding. + /// + /// By default this option is `true` and corresponds to + /// `CURLOPT_HTTP_TRANSFER_DECODING`. + pub fn http_transfer_decoding(&mut self, enable: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_HTTP_TRANSFER_DECODING, + enable as c_long) + } + + // /// Timeout for the Expect: 100-continue response + // /// + // /// By default this option is 1s and corresponds to + // /// `CURLOPT_EXPECT_100_TIMEOUT_MS`. + // pub fn expect_100_timeout(&mut self, enable: bool) -> Result<(), Error> { + // self.setopt_long(curl_sys::CURLOPT_HTTP_TRANSFER_DECODING, + // enable as c_long) + // } + + // /// Wait for pipelining/multiplexing. + // /// + // /// Tells libcurl to prefer to wait for a connection to confirm or deny that + // /// it can do pipelining or multiplexing before continuing. + // /// + // /// When about to perform a new transfer that allows pipelining or + // /// multiplexing, libcurl will check for existing connections to re-use and + // /// pipeline on. If no such connection exists it will immediately continue + // /// and create a fresh new connection to use. + // /// + // /// By setting this option to `true` - having `pipeline` enabled for the + // /// multi handle this transfer is associated with - libcurl will instead + // /// wait for the connection to reveal if it is possible to + // /// pipeline/multiplex on before it continues. This enables libcurl to much + // /// better keep the number of connections to a minimum when using pipelining + // /// or multiplexing protocols. + // /// + // /// The effect thus becomes that with this option set, libcurl prefers to + // /// wait and re-use an existing connection for pipelining rather than the + // /// opposite: prefer to open a new connection rather than waiting. + // /// + // /// The waiting time is as long as it takes for the connection to get up and + // /// for libcurl to get the necessary response back that informs it about its + // /// protocol and support level. + // pub fn http_pipewait(&mut self, enable: bool) -> Result<(), Error> { + // } + + + // ========================================================================= + // Protocol Options + + /// Indicates the range that this request should retrieve. + /// + /// The string provided should be of the form `N-M` where either `N` or `M` + /// can be left out. For HTTP transfers multiple ranges separated by commas + /// are also accepted. + /// + /// By default this option is not set and corresponds to `CURLOPT_RANGE`. + pub fn range(&mut self, range: &str) -> Result<(), Error> { + let range = try!(CString::new(range)); + self.setopt_str(curl_sys::CURLOPT_RANGE, &range) + } + + /// Set a point to resume transfer from + /// + /// Specify the offset in bytes you want the transfer to start from. + /// + /// By default this option is 0 and corresponds to + /// `CURLOPT_RESUME_FROM_LARGE`. + pub fn resume_from(&mut self, from: u64) -> Result<(), Error> { + self.setopt_off_t(curl_sys::CURLOPT_RESUME_FROM_LARGE, + from as curl_sys::curl_off_t) + } + + /// Set a custom request string + /// + /// Specifies that a custom request will be made (e.g. a custom HTTP + /// method). This does not change how libcurl performs internally, just + /// changes the string sent to the server. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_CUSTOMREQUEST`. + pub fn custom_request(&mut self, request: &str) -> Result<(), Error> { + let request = try!(CString::new(request)); + self.setopt_str(curl_sys::CURLOPT_CUSTOMREQUEST, &request) + } + + /// Indicate whether to download the request without getting the body + /// + /// This is useful, for example, for doing a HEAD request. + /// + /// By default this option is `false` and corresponds to `CURLOPT_NOBODY`. + pub fn nobody(&mut self, enable: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_NOBODY, enable as c_long) + } + + /// Set the size of the input file to send off. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_INFILESIZE_LARGE`. + pub fn in_filesize(&mut self, size: u64) -> Result<(), Error> { + self.setopt_off_t(curl_sys::CURLOPT_INFILESIZE_LARGE, + size as curl_sys::curl_off_t) + } + + /// Enable or disable data upload. + /// + /// This means that a PUT request will be made for HTTP and probably wants + /// to be combined with the read callback as well as the `in_filesize` + /// method. + /// + /// By default this option is `false` and corresponds to `CURLOPT_UPLOAD`. + pub fn upload(&mut self, enable: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_UPLOAD, enable as c_long) + } + + /// Configure the maximum file size to download. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_MAXFILESIZE_LARGE`. + pub fn max_filesize(&mut self, size: u64) -> Result<(), Error> { + self.setopt_off_t(curl_sys::CURLOPT_MAXFILESIZE_LARGE, + size as curl_sys::curl_off_t) + } + + /// Selects a condition for a time request. + /// + /// This value indicates how the `time_value` option is interpreted. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_TIMECONDITION`. + pub fn time_condition(&mut self, cond: TimeCondition) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_TIMECONDITION, cond as c_long) + } + + /// Sets the time value for a conditional request. + /// + /// The value here should be the number of seconds elapsed since January 1, + /// 1970. To pass how to interpret this value, use `time_condition`. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_TIMEVALUE`. + pub fn time_value(&mut self, val: i64) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_TIMEVALUE, val as c_long) + } + + // ========================================================================= + // Connection Options + + /// Set maximum time the request is allowed to take. + /// + /// Normally, name lookups can take a considerable time and limiting + /// operations to less than a few minutes risk aborting perfectly normal + /// operations. + /// + /// If libcurl is built to use the standard system name resolver, that + /// portion of the transfer will still use full-second resolution for + /// timeouts with a minimum timeout allowed of one second. + /// + /// In unix-like systems, this might cause signals to be used unless + /// `nosignal` is set. + /// + /// Since this puts a hard limit for how long time a request is allowed to + /// take, it has limited use in dynamic use cases with varying transfer + /// times. You are then advised to explore `low_speed_limit`, + /// `low_speed_time` or using `progress_function` to implement your own + /// timeout logic. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_TIMEOUT_MS`. + pub fn timeout(&mut self, timeout: Duration) -> Result<(), Error> { + // TODO: checked arithmetic and casts + // TODO: use CURLOPT_TIMEOUT if the timeout is too great + let ms = timeout.as_secs() * 1000 + + (timeout.subsec_nanos() / 1_000_000) as u64; + self.setopt_long(curl_sys::CURLOPT_TIMEOUT_MS, ms as c_long) + + } + + /// Set the low speed limit in bytes per second. + /// + /// This specifies the average transfer speed in bytes per second that the + /// transfer should be below during `low_speed_time` for libcurl to consider + /// it to be too slow and abort. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_LOW_SPEED_LIMIT`. + pub fn low_speed_limit(&mut self, limit: u32) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_LOW_SPEED_LIMIT, limit as c_long) + } + + /// Set the low speed time period. + /// + /// Specifies the window of time for which if the transfer rate is below + /// `low_speed_limit` the request will be aborted. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_LOW_SPEED_TIME`. + pub fn low_speed_time(&mut self, dur: Duration) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_LOW_SPEED_TIME, + dur.as_secs() as c_long) + } + + /// Rate limit data upload speed + /// + /// If an upload exceeds this speed (counted in bytes per second) on + /// cumulative average during the transfer, the transfer will pause to keep + /// the average rate less than or equal to the parameter value. + /// + /// By default this option is not set (unlimited speed) and corresponds to + /// `CURLOPT_MAX_SEND_SPEED_LARGE`. + pub fn max_send_speed(&mut self, speed: u64) -> Result<(), Error> { + self.setopt_off_t(curl_sys::CURLOPT_MAX_SEND_SPEED_LARGE, + speed as curl_sys::curl_off_t) + } + + /// Rate limit data download speed + /// + /// If a download exceeds this speed (counted in bytes per second) on + /// cumulative average during the transfer, the transfer will pause to keep + /// the average rate less than or equal to the parameter value. + /// + /// By default this option is not set (unlimited speed) and corresponds to + /// `CURLOPT_MAX_RECV_SPEED_LARGE`. + pub fn max_recv_speed(&mut self, speed: u64) -> Result<(), Error> { + self.setopt_off_t(curl_sys::CURLOPT_MAX_RECV_SPEED_LARGE, + speed as curl_sys::curl_off_t) + } + + /// Set the maximum connection cache size. + /// + /// The set amount will be the maximum number of simultaneously open + /// persistent connections that libcurl may cache in the pool associated + /// with this handle. The default is 5, and there isn't much point in + /// changing this value unless you are perfectly aware of how this works and + /// changes libcurl's behaviour. This concerns connections using any of the + /// protocols that support persistent connections. + /// + /// When reaching the maximum limit, curl closes the oldest one in the cache + /// to prevent increasing the number of open connections. + /// + /// By default this option is set to 5 and corresponds to + /// `CURLOPT_MAXCONNECTS` + pub fn max_connects(&mut self, max: u32) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_MAXCONNECTS, max as c_long) + } + + /// Force a new connection to be used. + /// + /// Makes the next transfer use a new (fresh) connection by force instead of + /// trying to re-use an existing one. This option should be used with + /// caution and only if you understand what it does as it may seriously + /// impact performance. + /// + /// By default this option is `false` and corresponds to + /// `CURLOPT_FRESH_CONNECT`. + pub fn fresh_connect(&mut self, enable: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_FRESH_CONNECT, enable as c_long) + } + + /// Make connection get closed at once after use. + /// + /// Makes libcurl explicitly close the connection when done with the + /// transfer. Normally, libcurl keeps all connections alive when done with + /// one transfer in case a succeeding one follows that can re-use them. + /// This option should be used with caution and only if you understand what + /// it does as it can seriously impact performance. + /// + /// By default this option is `false` and corresponds to + /// `CURLOPT_FORBID_REUSE`. + pub fn forbid_reuse(&mut self, enable: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_FORBID_REUSE, enable as c_long) + } + + /// Timeout for the connect phase + /// + /// This is the maximum time that you allow the connection phase to the + /// server to take. This only limits the connection phase, it has no impact + /// once it has connected. + /// + /// By default this value is 300 seconds and corresponds to + /// `CURLOPT_CONNECTTIMEOUT_MS`. + pub fn connect_timeout(&mut self, timeout: Duration) -> Result<(), Error> { + let ms = timeout.as_secs() * 1000 + + (timeout.subsec_nanos() / 1_000_000) as u64; + self.setopt_long(curl_sys::CURLOPT_CONNECTTIMEOUT_MS, ms as c_long) + } + + /// Specify which IP protocol version to use + /// + /// Allows an application to select what kind of IP addresses to use when + /// resolving host names. This is only interesting when using host names + /// that resolve addresses using more than one version of IP. + /// + /// By default this value is "any" and corresponds to `CURLOPT_IPRESOLVE`. + pub fn ip_resolve(&mut self, resolve: IpResolve) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_IPRESOLVE, resolve as c_long) + } + + /// Configure whether to stop when connected to target server + /// + /// When enabled it tells the library to perform all the required proxy + /// authentication and connection setup, but no data transfer, and then + /// return. + /// + /// The option can be used to simply test a connection to a server. + /// + /// By default this value is `false` and corresponds to + /// `CURLOPT_CONNECT_ONLY`. + pub fn connect_only(&mut self, enable: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_CONNECT_ONLY, enable as c_long) + } + + /// Set interface to speak DNS over. + /// + /// Set the name of the network interface that the DNS resolver should bind + /// to. This must be an interface name (not an address). + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_DNS_INTERFACE`. + pub fn dns_interface(&mut self, interface: &str) -> Result<(), Error> { + let interface = try!(CString::new(interface)); + self.setopt_str(curl_sys::CURLOPT_DNS_INTERFACE, &interface) + } + + /// IPv4 address to bind DNS resolves to + /// + /// Set the local IPv4 address that the resolver should bind to. The + /// argument should be of type char * and contain a single numerical IPv4 + /// address as a string. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_DNS_LOCAL_IP4`. + pub fn dns_local_ip4(&mut self, ip: &str) -> Result<(), Error> { + let ip = try!(CString::new(ip)); + self.setopt_str(curl_sys::CURLOPT_DNS_LOCAL_IP4, &ip) + } + + /// IPv6 address to bind DNS resolves to + /// + /// Set the local IPv6 address that the resolver should bind to. The + /// argument should be of type char * and contain a single numerical IPv6 + /// address as a string. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_DNS_LOCAL_IP6`. + pub fn dns_local_ip6(&mut self, ip: &str) -> Result<(), Error> { + let ip = try!(CString::new(ip)); + self.setopt_str(curl_sys::CURLOPT_DNS_LOCAL_IP6, &ip) + } + + /// Set preferred DNS servers. + /// + /// Provides a list of DNS servers to be used instead of the system default. + /// The format of the dns servers option is: + /// + /// ```text + /// host[:port],[host[:port]]... + /// ``` + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_DNS_SERVERS`. + pub fn dns_servers(&mut self, servers: &str) -> Result<(), Error> { + let servers = try!(CString::new(servers)); + self.setopt_str(curl_sys::CURLOPT_DNS_SERVERS, &servers) + } + + // ========================================================================= + // SSL/Security Options + + /// Sets the SSL client certificate. + /// + /// The string should be the file name of your client certificate. The + /// default format is "P12" on Secure Transport and "PEM" on other engines, + /// and can be changed with `ssl_cert_type`. + /// + /// With NSS or Secure Transport, this can also be the nickname of the + /// certificate you wish to authenticate with as it is named in the security + /// database. If you want to use a file from the current directory, please + /// precede it with "./" prefix, in order to avoid confusion with a + /// nickname. + /// + /// When using a client certificate, you most likely also need to provide a + /// private key with `ssl_key`. + /// + /// By default this option is not set and corresponds to `CURLOPT_SSLCERT`. + pub fn ssl_cert>(&mut self, cert: P) -> Result<(), Error> { + self.setopt_path(curl_sys::CURLOPT_SSLCERT, cert.as_ref()) + } + + /// Specify type of the client SSL certificate. + /// + /// The string should be the format of your certificate. Supported formats + /// are "PEM" and "DER", except with Secure Transport. OpenSSL (versions + /// 0.9.3 and later) and Secure Transport (on iOS 5 or later, or OS X 10.7 + /// or later) also support "P12" for PKCS#12-encoded files. + /// + /// By default this option is "PEM" and corresponds to + /// `CURLOPT_SSLCERTTYPE`. + pub fn ssl_cert_type(&mut self, kind: &str) -> Result<(), Error> { + let kind = try!(CString::new(kind)); + self.setopt_str(curl_sys::CURLOPT_SSLCERTTYPE, &kind) + } + + /// Specify private keyfile for TLS and SSL client cert. + /// + /// The string should be the file name of your private key. The default + /// format is "PEM" and can be changed with `ssl_key_type`. + /// + /// (iOS and Mac OS X only) This option is ignored if curl was built against + /// Secure Transport. Secure Transport expects the private key to be already + /// present in the keychain or PKCS#12 file containing the certificate. + /// + /// By default this option is not set and corresponds to `CURLOPT_SSLKEY`. + pub fn ssl_key>(&mut self, key: P) -> Result<(), Error> { + self.setopt_path(curl_sys::CURLOPT_SSLKEY, key.as_ref()) + } + + /// Set type of the private key file. + /// + /// The string should be the format of your private key. Supported formats + /// are "PEM", "DER" and "ENG". + /// + /// The format "ENG" enables you to load the private key from a crypto + /// engine. In this case `ssl_key` is used as an identifier passed to + /// the engine. You have to set the crypto engine with `ssl_engine`. + /// "DER" format key file currently does not work because of a bug in + /// OpenSSL. + /// + /// By default this option is "PEM" and corresponds to + /// `CURLOPT_SSLKEYTYPE`. + pub fn ssl_key_type(&mut self, kind: &str) -> Result<(), Error> { + let kind = try!(CString::new(kind)); + self.setopt_str(curl_sys::CURLOPT_SSLKEYTYPE, &kind) + } + + /// Set passphrase to private key. + /// + /// This will be used as the password required to use the `ssl_key`. + /// You never needed a pass phrase to load a certificate but you need one to + /// load your private key. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_KEYPASSWD`. + pub fn key_password(&mut self, password: &str) -> Result<(), Error> { + let password = try!(CString::new(password)); + self.setopt_str(curl_sys::CURLOPT_KEYPASSWD, &password) + } + + /// Set the SSL engine identifier. + /// + /// This will be used as the identifier for the crypto engine you want to + /// use for your private key. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_SSLENGINE`. + pub fn ssl_engine(&mut self, engine: &str) -> Result<(), Error> { + let engine = try!(CString::new(engine)); + self.setopt_str(curl_sys::CURLOPT_SSLENGINE, &engine) + } + + /// Make this handle's SSL engine the default. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_SSLENGINE_DEFAULT`. + pub fn ssl_engine_default(&mut self, enable: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_SSLENGINE_DEFAULT, enable as c_long) + } + + // /// Enable TLS false start. + // /// + // /// This option determines whether libcurl should use false start during the + // /// TLS handshake. False start is a mode where a TLS client will start + // /// sending application data before verifying the server's Finished message, + // /// thus saving a round trip when performing a full handshake. + // /// + // /// By default this option is not set and corresponds to + // /// `CURLOPT_SSL_FALSESTARTE`. + // pub fn ssl_false_start(&mut self, enable: bool) -> Result<(), Error> { + // self.setopt_long(curl_sys::CURLOPT_SSLENGINE_DEFAULT, enable as c_long) + // } + + /// Set preferred TLS/SSL version. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_SSLVERSION`. + pub fn ssl_version(&mut self, version: SslVersion) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_SSLVERSION, version as c_long) + } + + /// Verify the certificate's name against host. + /// + /// This should be disabled with great caution! It basically disables the + /// security features of SSL if it is disabled. + /// + /// By default this option is set to `true` and corresponds to + /// `CURLOPT_SSL_VERIFYHOST`. + pub fn ssl_verify_host(&mut self, verify: bool) -> Result<(), Error> { + let val = if verify {2} else {0}; + self.setopt_long(curl_sys::CURLOPT_SSL_VERIFYHOST, val) + } + + /// Verify the peer's SSL certificate. + /// + /// This should be disabled with great caution! It basically disables the + /// security features of SSL if it is disabled. + /// + /// By default this option is set to `true` and corresponds to + /// `CURLOPT_SSL_VERIFYPEER`. + pub fn ssl_verify_peer(&mut self, verify: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_SSL_VERIFYPEER, verify as c_long) + } + + // /// Verify the certificate's status. + // /// + // /// This option determines whether libcurl verifies the status of the server + // /// cert using the "Certificate Status Request" TLS extension (aka. OCSP + // /// stapling). + // /// + // /// By default this option is set to `false` and corresponds to + // /// `CURLOPT_SSL_VERIFYSTATUS`. + // pub fn ssl_verify_status(&mut self, verify: bool) -> Result<(), Error> { + // self.setopt_long(curl_sys::CURLOPT_SSL_VERIFYSTATUS, verify as c_long) + // } + + /// Specify the path to Certificate Authority (CA) bundle + /// + /// The file referenced should hold one or more certificates to verify the + /// peer with. + /// + /// This option is by default set to the system path where libcurl's cacert + /// bundle is assumed to be stored, as established at build time. + /// + /// If curl is built against the NSS SSL library, the NSS PEM PKCS#11 module + /// (libnsspem.so) needs to be available for this option to work properly. + /// + /// By default this option is the system defaults, and corresponds to + /// `CURLOPT_CAINFO`. + pub fn cainfo>(&mut self, path: P) -> Result<(), Error> { + self.setopt_path(curl_sys::CURLOPT_CAINFO, path.as_ref()) + } + + /// Set the issuer SSL certificate filename + /// + /// Specifies a file holding a CA certificate in PEM format. If the option + /// is set, an additional check against the peer certificate is performed to + /// verify the issuer is indeed the one associated with the certificate + /// provided by the option. This additional check is useful in multi-level + /// PKI where one needs to enforce that the peer certificate is from a + /// specific branch of the tree. + /// + /// This option makes sense only when used in combination with the + /// `ssl_verify_peer` option. Otherwise, the result of the check is not + /// considered as failure. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_ISSUERCERT`. + pub fn issuer_cert>(&mut self, path: P) -> Result<(), Error> { + self.setopt_path(curl_sys::CURLOPT_ISSUERCERT, path.as_ref()) + } + + /// Specify directory holding CA certificates + /// + /// Names a directory holding multiple CA certificates to verify the peer + /// with. If libcurl is built against OpenSSL, the certificate directory + /// must be prepared using the openssl c_rehash utility. This makes sense + /// only when used in combination with the `ssl_verify_peer` option. + /// + /// By default this option is not set and corresponds to `CURLOPT_CAPATH`. + pub fn capath>(&mut self, path: P) -> Result<(), Error> { + self.setopt_path(curl_sys::CURLOPT_CAPATH, path.as_ref()) + } + + /// Specify a Certificate Revocation List file + /// + /// Names a file with the concatenation of CRL (in PEM format) to use in the + /// certificate validation that occurs during the SSL exchange. + /// + /// When curl is built to use NSS or GnuTLS, there is no way to influence + /// the use of CRL passed to help in the verification process. When libcurl + /// is built with OpenSSL support, X509_V_FLAG_CRL_CHECK and + /// X509_V_FLAG_CRL_CHECK_ALL are both set, requiring CRL check against all + /// the elements of the certificate chain if a CRL file is passed. + /// + /// This option makes sense only when used in combination with the + /// `ssl_verify_peer` option. + /// + /// A specific error code (`is_ssl_crl_badfile`) is defined with the + /// option. It is returned when the SSL exchange fails because the CRL file + /// cannot be loaded. A failure in certificate verification due to a + /// revocation information found in the CRL does not trigger this specific + /// error. + /// + /// By default this option is not set and corresponds to `CURLOPT_CRLFILE`. + pub fn crlfile>(&mut self, path: P) -> Result<(), Error> { + self.setopt_path(curl_sys::CURLOPT_CRLFILE, path.as_ref()) + } + + /// Request SSL certificate information + /// + /// Enable libcurl's certificate chain info gatherer. With this enabled, + /// libcurl will extract lots of information and data about the certificates + /// in the certificate chain used in the SSL connection. + /// + /// By default this option is `false` and corresponds to + /// `CURLOPT_CERTINFO`. + pub fn certinfo(&mut self, enable: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_CERTINFO, enable as c_long) + } + + // /// Set pinned public key. + // /// + // /// Pass a pointer to a zero terminated string as parameter. The string can + // /// be the file name of your pinned public key. The file format expected is + // /// "PEM" or "DER". The string can also be any number of base64 encoded + // /// sha256 hashes preceded by "sha256//" and separated by ";" + // /// + // /// When negotiating a TLS or SSL connection, the server sends a certificate + // /// indicating its identity. A public key is extracted from this certificate + // /// and if it does not exactly match the public key provided to this option, + // /// curl will abort the connection before sending or receiving any data. + // /// + // /// By default this option is not set and corresponds to + // /// `CURLOPT_PINNEDPUBLICKEY`. + // pub fn pinned_public_key(&mut self, enable: bool) -> Result<(), Error> { + // self.setopt_long(curl_sys::CURLOPT_CERTINFO, enable as c_long) + // } + + /// Specify a source for random data + /// + /// The file will be used to read from to seed the random engine for SSL and + /// more. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_RANDOM_FILE`. + pub fn random_file>(&mut self, p: P) -> Result<(), Error> { + self.setopt_path(curl_sys::CURLOPT_RANDOM_FILE, p.as_ref()) + } + + /// Specify EGD socket path. + /// + /// Indicates the path name to the Entropy Gathering Daemon socket. It will + /// be used to seed the random engine for SSL. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_EGDSOCKET`. + pub fn egd_socket>(&mut self, p: P) -> Result<(), Error> { + self.setopt_path(curl_sys::CURLOPT_EGDSOCKET, p.as_ref()) + } + + /// Specify ciphers to use for TLS. + /// + /// Holds the list of ciphers to use for the SSL connection. The list must + /// be syntactically correct, it consists of one or more cipher strings + /// separated by colons. Commas or spaces are also acceptable separators + /// but colons are normally used, !, - and + can be used as operators. + /// + /// For OpenSSL and GnuTLS valid examples of cipher lists include 'RC4-SHA', + /// ´SHA1+DES´, 'TLSv1' and 'DEFAULT'. The default list is normally set when + /// you compile OpenSSL. + /// + /// You'll find more details about cipher lists on this URL: + /// + /// https://www.openssl.org/docs/apps/ciphers.html + /// + /// For NSS, valid examples of cipher lists include 'rsa_rc4_128_md5', + /// ´rsa_aes_128_sha´, etc. With NSS you don't add/remove ciphers. If one + /// uses this option then all known ciphers are disabled and only those + /// passed in are enabled. + /// + /// You'll find more details about the NSS cipher lists on this URL: + /// + /// http://git.fedorahosted.org/cgit/mod_nss.git/plain/docs/mod_nss.html#Directives + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_SSL_CIPHER_LIST`. + pub fn ssl_cipher_list(&mut self, ciphers: &str) -> Result<(), Error> { + let ciphers = try!(CString::new(ciphers)); + self.setopt_str(curl_sys::CURLOPT_SSL_CIPHER_LIST, &ciphers) + } + + /// Enable or disable use of the SSL session-ID cache + /// + /// By default all transfers are done using the cache enabled. While nothing + /// ever should get hurt by attempting to reuse SSL session-IDs, there seem + /// to be or have been broken SSL implementations in the wild that may + /// require you to disable this in order for you to succeed. + /// + /// This corresponds to the `CURLOPT_SSL_SESSIONID_CACHE` option. + pub fn ssl_sessionid_cache(&mut self, enable: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_SSL_SESSIONID_CACHE, + enable as c_long) + } + + // ========================================================================= + // Other methods + + /// After options have been set, this will perform the transfer described by + /// the options. + /// + /// This performs the request in a synchronous fashion. This can be used + /// multiple times for one easy handle and libcurl will attempt to re-use + /// the same connection for all transfers. + /// + /// This method will preserve all options configured in this handle for the + /// next request, and if that is not desired then the options can be + /// manually reset or the `reset` method can be called. + pub fn perform(&mut self) -> Result<(), Error> { + let ret = unsafe { + cvt(curl_sys::curl_easy_perform(self.handle)) + }; + panic::propagate(); + return ret + } + + /// URL encodes a string `s` + pub fn url_encode(&mut self, s: &[u8]) -> String { + unsafe { + let p = curl_sys::curl_easy_escape(self.handle, + s.as_ptr() as *const _, + s.len() as c_int); + assert!(!p.is_null()); + let ret = str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap(); + let ret = String::from(ret); + curl_sys::curl_free(p as *mut _); + return ret + } + } + + /// URL decodes a string `s`, returning `None` if it fails + pub fn url_decode(&mut self, s: &str) -> Vec { + unsafe { + let mut len = 0; + let p = curl_sys::curl_easy_unescape(self.handle, + s.as_ptr() as *const _, + s.len() as c_int, + &mut len); + assert!(!p.is_null()); + let slice = slice::from_raw_parts(p as *const u8, len as usize); + let ret = slice.to_vec(); + curl_sys::curl_free(p as *mut _); + return ret + } + } + + /// Attempts to clone this handle, returning a new session handle with the + /// same options set for this handle. + /// + /// Internal state info and things like persistent connections ccannot be + /// transferred. + /// + /// # Errors + /// + /// If a new handle could not be allocated or another error happens, `None` + /// is returned. + pub fn try_clone<'b>(&mut self) -> Option> { + unsafe { + let handle = curl_sys::curl_easy_duphandle(self.handle); + if handle.is_null() { + None + } else { + Some(Easy { + handle: handle, + _marker: marker::PhantomData, + }) + } + } + } + + /// Re-initializes this handle to the default values. + /// + /// This puts the handle to the same state as it was in when it was just + /// created. This does, however, keep live connections, the session id + /// cache, the dns cache, and cookies. + pub fn reset(&mut self) { + unsafe { + curl_sys::curl_easy_reset(self.handle); + } + } + + /// Receives data from a connected socket. + /// + /// Only useful after a successful `perform` with the `connect_only` option + /// set as well. + pub fn recv(&mut self, data: &mut [u8]) -> Result { + unsafe { + let mut n = 0; + let r = curl_sys::curl_easy_recv(self.handle, + data.as_mut_ptr() as *mut _, + data.len(), + &mut n); + if r == curl_sys::CURLE_OK { + Ok(n) + } else { + Err(Error::new(r)) + } + } + } + + /// Sends data over the connected socket. + /// + /// Only useful after a successful `perform` with the `connect_only` option + /// set as well. + pub fn send(&mut self, data: &[u8]) -> Result { + unsafe { + let mut n = 0; + cvt(curl_sys::curl_easy_send(self.handle, + data.as_ptr() as *const _, + data.len(), + &mut n)) + .map(|()| n) + } + } + + #[cfg(unix)] + fn setopt_path(&mut self, + opt: curl_sys::CURLoption, + val: &Path) -> Result<(), Error> { + use std::os::unix::prelude::*; + let s = try!(CString::new(val.as_os_str().as_bytes())); + self.setopt_str(opt, &s) + } + + #[cfg(windows)] + fn setopt_path(&mut self, + opt: curl_sys::CURLoption, + val: &Path) -> Result<(), Error> { + match val.to_str() { + Some(s) => self.setopt_str(opt, &try!(CString::new(s))), + None => Err(Error::new(curl_sys::CURLE_CONV_FAILED)), + } + } + + fn setopt_long(&mut self, + opt: curl_sys::CURLoption, + val: c_long) -> Result<(), Error> { + unsafe { + cvt(curl_sys::curl_easy_setopt(self.handle, opt, val)) + } + } + + fn setopt_str(&mut self, + opt: curl_sys::CURLoption, + val: &CStr) -> Result<(), Error> { + self.setopt_ptr(opt, val.as_ptr()) + } + + fn setopt_ptr(&mut self, + opt: curl_sys::CURLoption, + val: *const c_char) -> Result<(), Error> { + unsafe { + cvt(curl_sys::curl_easy_setopt(self.handle, opt, val)) + } + } + + fn setopt_off_t(&mut self, + opt: curl_sys::CURLoption, + val: curl_sys::curl_off_t) -> Result<(), Error> { + unsafe { + cvt(curl_sys::curl_easy_setopt(self.handle, opt, val)) + } + } +} + +impl<'a> Drop for Easy<'a> { + fn drop(&mut self) { + unsafe { + curl_sys::curl_easy_cleanup(self.handle); + } + } +} + +impl List { + /// Creates a new empty list of strings. + pub fn new() -> List { + List { raw: 0 as *mut _ } + } + + /// Appends some data into this list. + pub fn append(&mut self, data: &str) -> Result<(), Error> { + let data = try!(CString::new(data)); + unsafe { + let raw = curl_sys::curl_slist_append(self.raw, data.as_ptr()); + assert!(!raw.is_null()); + self.raw = raw; + Ok(()) + } + } +} + +impl Drop for List { + fn drop(&mut self) { + unsafe { + curl_sys::curl_slist_free_all(self.raw) + } + } +} diff --git a/src/error.rs b/src/error.rs index 6e77a1b508..085673912a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,5 @@ use std::error; -use std::ffi::CStr; +use std::ffi::{self, CStr}; use std::fmt; use std::str; use std::io; @@ -9,7 +9,7 @@ use curl_sys; /// An error returned from various "easy" operations. /// /// This structure wraps a `CURLcode`. -#[derive(Debug, Clone, PartialEq)] +#[derive(Clone, PartialEq)] pub struct Error { code: curl_sys::CURLcode, } @@ -287,6 +287,14 @@ impl fmt::Display for Error { } } +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Error {{ description: {:?}, code: {} }}", + error::Error::description(self), + self.code) + } +} + impl error::Error for Error { fn description(&self) -> &str { unsafe { @@ -300,7 +308,7 @@ impl error::Error for Error { /// An error returned from "share" operations. /// /// This structure wraps a `CURLSHcode`. -#[derive(Debug, Clone, PartialEq)] +#[derive(Clone, PartialEq)] pub struct ShareError { code: curl_sys::CURLSHcode, } @@ -348,6 +356,14 @@ impl fmt::Display for ShareError { } } +impl fmt::Debug for ShareError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ShareError {{ description: {:?}, code: {} }}", + error::Error::description(self), + self.code) + } +} + impl error::Error for ShareError { fn description(&self) -> &str { unsafe { @@ -361,7 +377,7 @@ impl error::Error for ShareError { /// An error from "multi" operations. /// /// THis structure wraps a `CURLMcode`. -#[derive(Debug, Clone, PartialEq)] +#[derive(Clone, PartialEq)] pub struct MultiError { code: curl_sys::CURLMcode, } @@ -419,6 +435,14 @@ impl fmt::Display for MultiError { } } +impl fmt::Debug for MultiError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "MultiError {{ description: {:?}, code: {} }}", + error::Error::description(self), + self.code) + } +} + impl error::Error for MultiError { fn description(&self) -> &str { unsafe { @@ -429,6 +453,12 @@ impl error::Error for MultiError { } } +impl From for Error { + fn from(_: ffi::NulError) -> Error { + Error { code: curl_sys::CURLE_CONV_FAILED } + } +} + impl From for io::Error { fn from(e: Error) -> io::Error { io::Error::new(io::ErrorKind::Other, e) diff --git a/src/lib.rs b/src/lib.rs index 40a3936b9c..78f55bef27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,9 @@ mod error; pub use version::{Version, Protocols}; mod version; +mod panic; +pub mod easy; + /// Initializes the underlying libcurl library. /// /// It's not required to call this before the library is used, but it's diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000000..0356fa1ad0 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,8 @@ +extern crate curl; + +fn main() { + // let data = 2; + let mut easy = curl::easy::Easy::new(); + // easy.debug_function(&mut |_, _| {}); + let _ = easy.perform(); +} diff --git a/src/panic.rs b/src/panic.rs new file mode 100644 index 0000000000..f2d070de66 --- /dev/null +++ b/src/panic.rs @@ -0,0 +1,22 @@ +struct Bomb { + armed: bool, +} + +impl Drop for Bomb { + fn drop(&mut self) { + if self.armed { + panic!("cannot panic in a callback in libcurl, panicking again to \ + abort the process safely") + } + } +} + +pub fn catch R>(f: F) -> Option { + let mut bomb = Bomb { armed: true }; + let ret = f(); + bomb.armed = false; + Some(ret) +} + +pub fn propagate() { +} diff --git a/tests/easy.rs b/tests/easy.rs new file mode 100644 index 0000000000..f94bc9ccbf --- /dev/null +++ b/tests/easy.rs @@ -0,0 +1,503 @@ +extern crate curl; + +use std::io::Read; +use std::str; +use std::time::Duration; + +macro_rules! t { + ($e:expr) => (match $e { + Ok(e) => e, + Err(e) => panic!("{} failed with {:?}", stringify!($e), e), + }) +} + +use curl::easy::{Easy, List}; + +use server::Server; +mod server; + +fn handle<'a>() -> Easy<'a> { + let mut e = Easy::new(); + t!(e.timeout(Duration::new(1, 0))); + return e +} + +fn sink(data: &[u8]) -> usize { + data.len() +} + +#[test] +fn get_smoke() { + let s = Server::new(); + s.receive("\ +GET / HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +\r\n"); + s.send("HTTP/1.1 200 OK\r\n\r\n"); + + let mut handle = handle(); + t!(handle.url(&s.url("/"))); + t!(handle.perform()); +} + +#[test] +fn get_path() { + let s = Server::new(); + s.receive("\ +GET /foo HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +\r\n"); + s.send("HTTP/1.1 200 OK\r\n\r\n"); + + let mut handle = handle(); + t!(handle.url(&s.url("/foo"))); + t!(handle.perform()); +} + +#[test] +fn write_callback() { + let s = Server::new(); + s.receive("\ +GET / HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +\r\n"); + s.send("HTTP/1.1 200 OK\r\n\r\nhello!"); + + let mut all = Vec::::new(); + { + let mut write = |data: &[u8]| { + all.extend(data); + data.len() + }; + let mut handle = handle(); + t!(handle.url(&s.url("/"))); + t!(handle.write_function(&mut write)); + t!(handle.perform()); + } + assert_eq!(all, b"hello!"); +} + +#[test] +fn progress() { + let s = Server::new(); + s.receive("\ +GET /foo HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +\r\n"); + s.send("HTTP/1.1 200 OK\r\n\r\nHello!"); + + let mut hits = 0; + let mut dl = 0; + { + let mut cb = |_, a, _, _| { + hits += 1; + dl = a; + true + }; + let mut write = sink; + let mut handle = handle(); + t!(handle.url(&s.url("/foo"))); + t!(handle.progress(true)); + t!(handle.progress_function(&mut cb)); + t!(handle.write_function(&mut write)); + t!(handle.perform()); + } + assert!(hits > 0); + assert_eq!(dl, 6); +} + +#[test] +fn headers() { + let s = Server::new(); + s.receive("\ +GET / HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +\r\n"); + s.send("\ +HTTP/1.1 200 OK\r\n\ +Foo: bar\r\n\ +Bar: baz\r\n\ +\r\n +Hello!"); + + let mut headers = Vec::new(); + { + let mut header = |h: &[u8]| { + headers.push(str::from_utf8(h).unwrap().to_string()); + true + }; + let mut write = sink; + let mut handle = handle(); + t!(handle.url(&s.url("/"))); + t!(handle.header_function(&mut header)); + t!(handle.write_function(&mut write)); + t!(handle.perform()); + } + assert_eq!(headers, vec![ + "HTTP/1.1 200 OK\r\n".to_string(), + "Foo: bar\r\n".to_string(), + "Bar: baz\r\n".to_string(), + "\r\n".to_string(), + ]); +} + +#[test] +fn fail_on_error() { + let s = Server::new(); + s.receive("\ +GET / HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +\r\n"); + s.send("\ +HTTP/1.1 401 Not so good\r\n\ +\r\n"); + + let mut h = handle(); + t!(h.url(&s.url("/"))); + t!(h.fail_on_error(true)); + assert!(h.perform().is_err()); + + let s = Server::new(); + s.receive("\ +GET / HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +\r\n"); + s.send("\ +HTTP/1.1 401 Not so good\r\n\ +\r\n"); + + let mut h = handle(); + t!(h.url(&s.url("/"))); + t!(h.fail_on_error(false)); + t!(h.perform()); +} + +#[test] +fn port() { + let s = Server::new(); + s.receive("\ +GET / HTTP/1.1\r\n\ +Host: localhost:$PORT\r\n\ +Accept: */*\r\n\ +\r\n"); + s.send("\ +HTTP/1.1 200 OK\r\n\ +\r\n"); + + let mut h = handle(); + t!(h.url("http://localhost/")); + t!(h.port(s.addr().port())); + t!(h.perform()); +} + +#[test] +fn proxy() { + let s = Server::new(); + s.receive("\ +GET http://example.com/ HTTP/1.1\r\n\ +Host: example.com\r\n\ +Accept: */*\r\n\ +Proxy-Connection: Keep-Alive\r\n\ +\r\n"); + s.send("\ +HTTP/1.1 200 OK\r\n\ +\r\n"); + + let mut h = handle(); + t!(h.url("http://example.com/")); + t!(h.proxy(&s.url("/"))); + t!(h.perform()); +} + +#[test] +fn noproxy() { + let s = Server::new(); + s.receive("\ +GET / HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +\r\n"); + s.send("\ +HTTP/1.1 200 OK\r\n\ +\r\n"); + + let mut h = handle(); + t!(h.url(&s.url("/"))); + t!(h.proxy(&s.url("/"))); + t!(h.noproxy("127.0.0.1")); + t!(h.perform()); +} + +#[test] +fn misc() { + let mut h = handle(); + t!(h.tcp_nodelay(true)); + t!(h.tcp_keepalive(true)); + t!(h.tcp_keepidle(Duration::new(3, 0))); + t!(h.tcp_keepintvl(Duration::new(3, 0))); + t!(h.buffer_size(10)); + t!(h.dns_cache_timeout(Duration::new(1, 0))); +} + +#[test] +fn userpass() { + let s = Server::new(); + s.receive("\ +GET / HTTP/1.1\r\n\ +Authorization: Basic YmFyOg==\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +\r\n"); + s.send("\ +HTTP/1.1 200 OK\r\n\ +\r\n"); + + let mut h = handle(); + t!(h.url(&s.url("/"))); + t!(h.username("foo")); + t!(h.username("bar")); + t!(h.perform()); +} + +#[test] +fn accept_encoding() { + let s = Server::new(); + s.receive("\ +GET / HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +Accept-Encoding: gzip\r\n\ +\r\n"); + s.send("\ +HTTP/1.1 200 OK\r\n\ +\r\n"); + + let mut h = handle(); + t!(h.url(&s.url("/"))); + t!(h.accept_encoding("gzip")); + t!(h.perform()); +} + +#[test] +fn follow_location() { + let s1 = Server::new(); + let s2 = Server::new(); + s1.receive("\ +GET / HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +\r\n"); + s1.send(&format!("\ +HTTP/1.1 301 Moved Permanently\r\n\ +Location: http://{}/foo\r\n\ +\r\n", s2.addr())); + + s2.receive("\ +GET /foo HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +\r\n"); + s2.send("\ +HTTP/1.1 200 OK\r\n\ +\r\n"); + + let mut h = handle(); + t!(h.url(&s1.url("/"))); + t!(h.follow_location(true)); + t!(h.perform()); +} + +#[test] +fn put() { + let s = Server::new(); + s.receive("\ +PUT / HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +Content-Length: 5\r\n\ +\r\n\ +data\n"); + s.send("\ +HTTP/1.1 200 OK\r\n\ +\r\n"); + + let mut data = "data\n".as_bytes(); + let mut read = |buf: &mut [u8]| data.read(buf).unwrap(); + let mut list = List::new(); + t!(list.append("Expect:")); + let mut h = handle(); + t!(h.url(&s.url("/"))); + t!(h.put(true)); + t!(h.read_function(&mut read)); + t!(h.in_filesize(5)); + t!(h.upload(true)); + t!(h.http_headers(&list)); + t!(h.perform()); +} + +#[test] +fn post1() { + let s = Server::new(); + s.receive("\ +POST / HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +Content-Length: 5\r\n\ +Content-Type: application/x-www-form-urlencoded\r\n\ +\r\n\ +data\n"); + s.send("\ +HTTP/1.1 200 OK\r\n\ +\r\n"); + + let mut h = handle(); + t!(h.url(&s.url("/"))); + t!(h.post(true)); + t!(h.post_fields(b"data\n")); + t!(h.perform()); +} + +#[test] +fn post2() { + let s = Server::new(); + s.receive("\ +POST / HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +Content-Length: 5\r\n\ +Content-Type: application/x-www-form-urlencoded\r\n\ +\r\n\ +data\n"); + s.send("\ +HTTP/1.1 200 OK\r\n\ +\r\n"); + + let mut h = handle(); + t!(h.url(&s.url("/"))); + t!(h.post(true)); + t!(h.post_fields_copy(b"data\n")); + t!(h.perform()); +} + +#[test] +fn post3() { + let s = Server::new(); + s.receive("\ +POST / HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +Content-Length: 5\r\n\ +Content-Type: application/x-www-form-urlencoded\r\n\ +\r\n\ +data\n"); + s.send("\ +HTTP/1.1 200 OK\r\n\ +\r\n"); + + let mut data = "data\n".as_bytes(); + let mut read = |buf: &mut [u8]| data.read(buf).unwrap(); + let mut h = handle(); + t!(h.url(&s.url("/"))); + t!(h.post(true)); + t!(h.post_field_size(5)); + t!(h.read_function(&mut read)); + t!(h.perform()); +} + +#[test] +fn referer() { + let s = Server::new(); + s.receive("\ +GET / HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +Referer: foo\r\n\ +\r\n"); + s.send("\ +HTTP/1.1 200 OK\r\n\ +\r\n"); + + let mut h = handle(); + t!(h.url(&s.url("/"))); + t!(h.referer("foo")); + t!(h.perform()); +} + +#[test] +fn useragent() { + let s = Server::new(); + s.receive("\ +GET / HTTP/1.1\r\n\ +User-Agent: foo\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +\r\n"); + s.send("\ +HTTP/1.1 200 OK\r\n\ +\r\n"); + + let mut h = handle(); + t!(h.url(&s.url("/"))); + t!(h.useragent("foo")); + t!(h.perform()); +} + +#[test] +fn custom_headers() { + let s = Server::new(); + s.receive("\ +GET / HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Foo: bar\r\n\ +\r\n"); + s.send("\ +HTTP/1.1 200 OK\r\n\ +\r\n"); + + let mut custom = List::new(); + t!(custom.append("Foo: bar")); + t!(custom.append("Accept:")); + let mut h = handle(); + t!(h.url(&s.url("/"))); + t!(h.http_headers(&custom)); + t!(h.perform()); +} + +#[test] +fn cookie() { + let s = Server::new(); + s.receive("\ +GET / HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +Cookie: foo\r\n\ +\r\n"); + s.send("\ +HTTP/1.1 200 OK\r\n\ +\r\n"); + + let mut h = handle(); + t!(h.url(&s.url("/"))); + t!(h.cookie("foo")); + t!(h.perform()); +} + +#[test] +fn url_encoding() { + let mut h = handle(); + assert_eq!(h.url_encode(b"foo"), "foo"); + assert_eq!(h.url_encode(b"foo bar"), "foo%20bar"); + assert_eq!(h.url_encode(b"foo bar\xff"), "foo%20bar%FF"); + assert_eq!(h.url_decode("foo"), b"foo"); + assert_eq!(h.url_decode("foo%20bar"), b"foo bar"); + assert_eq!(h.url_decode("foo%2"), b"foo%2"); + assert_eq!(h.url_decode("foo%xx"), b"foo%xx"); + assert_eq!(h.url_decode("foo%ff"), b"foo\xff"); +} diff --git a/tests/server/mod.rs b/tests/server/mod.rs new file mode 100644 index 0000000000..b1e503f63c --- /dev/null +++ b/tests/server/mod.rs @@ -0,0 +1,89 @@ +use std::net::{TcpListener, SocketAddr}; +use std::io::prelude::*; +use std::thread; +use std::sync::mpsc::{Sender, Receiver, channel}; +use std::io::BufReader; + +pub struct Server { + messages: Option>, + addr: SocketAddr, + thread: Option>, +} + +enum Message { + Read(String), + Write(String), +} + +fn run(listener: &TcpListener, rx: &Receiver) { + let mut socket = BufReader::new(listener.accept().unwrap().0); + for msg in rx.iter() { + match msg { + Message::Read(ref expected) => { + let mut expected = &expected[..]; + while let Some(i) = expected.find("\n") { + let mut actual = String::new(); + t!(socket.read_line(&mut actual)); + assert_eq!(actual, &expected[..i + 1]); + expected = &expected[i + 1..]; + } + } + Message::Write(ref to_write) => { + t!(socket.get_mut().write_all(to_write.as_bytes())); + return + } + } + } + + let mut dst = Vec::new(); + t!(socket.read_to_end(&mut dst)); + assert!(dst.len() == 0); +} + +impl Server { + pub fn new() -> Server { + let listener = t!(TcpListener::bind("127.0.0.1:0")); + let addr = t!(listener.local_addr()); + let (tx, rx) = channel(); + let thread = thread::spawn(move || run(&listener, &rx)); + Server { + messages: Some(tx), + addr: addr, + thread: Some(thread), + } + } + + pub fn receive(&self, msg: &str) { + let msg = msg.replace("$PORT", &self.addr.port().to_string()); + self.msg(Message::Read(msg)); + } + + pub fn send(&self, msg: &str) { + let msg = msg.replace("$PORT", &self.addr.port().to_string()); + self.msg(Message::Write(msg)); + } + + fn msg(&self, msg: Message) { + t!(self.messages.as_ref().unwrap().send(msg)); + } + + pub fn addr(&self) -> &SocketAddr { + &self.addr + } + + pub fn url(&self, path: &str) -> String { + format!("http://{}{}", self.addr, path) + } +} + +impl Drop for Server { + fn drop(&mut self) { + drop(self.messages.take()); + let res = self.thread.take().unwrap().join(); + if !thread::panicking() { + t!(res); + } else if let Err(e) = res { + println!("child server thread also failed: {:?}", e); + } + } +} From 099290ccba01b430ed7435aa91a472b6e6669f3b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 25 Apr 2016 23:02:37 -0700 Subject: [PATCH 12/71] Apparently QSOSSL isn't on OSX --- curl-sys/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/curl-sys/lib.rs b/curl-sys/lib.rs index 114856b227..312b8c50cc 100644 --- a/curl-sys/lib.rs +++ b/curl-sys/lib.rs @@ -669,7 +669,7 @@ pub const CURLSSLBACKEND_NONE: curl_sslbackend = 0; pub const CURLSSLBACKEND_OPENSSL: curl_sslbackend = 1; pub const CURLSSLBACKEND_GNUTLS: curl_sslbackend = 2; pub const CURLSSLBACKEND_NSS: curl_sslbackend = 3; -pub const CURLSSLBACKEND_QSOSSL: curl_sslbackend = 4; +// pub const CURLSSLBACKEND_QSOSSL: curl_sslbackend = 4; pub const CURLSSLBACKEND_GSKIT: curl_sslbackend = 5; pub const CURLSSLBACKEND_POLARSSL: curl_sslbackend = 6; pub const CURLSSLBACKEND_CYASSL: curl_sslbackend = 7; From 819571cbe724b8a913745c833ccd243c639b7938 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 25 Apr 2016 23:03:59 -0700 Subject: [PATCH 13/71] Fix systest (don't have types __enum_ty) --- curl-sys/lib.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/curl-sys/lib.rs b/curl-sys/lib.rs index 312b8c50cc..15623a41c3 100644 --- a/curl-sys/lib.rs +++ b/curl-sys/lib.rs @@ -593,13 +593,14 @@ pub const CURL_IPRESOLVE_WHATEVER: c_int = 0; pub const CURL_IPRESOLVE_V4: c_int = 1; pub const CURL_IPRESOLVE_V6: c_int = 2; -pub const CURL_SSLVERSION_DEFAULT: __enum_ty = 0; -pub const CURL_SSLVERSION_TLSv1: __enum_ty = 1; -pub const CURL_SSLVERSION_SSLv2: __enum_ty = 2; -pub const CURL_SSLVERSION_SSLv3: __enum_ty = 3; -pub const CURL_SSLVERSION_TLSv1_0: __enum_ty = 4; -pub const CURL_SSLVERSION_TLSv1_1: __enum_ty = 5; -pub const CURL_SSLVERSION_TLSv1_2: __enum_ty = 6; +// Note that the type here is wrong, it's just intended to just be an enum. +pub const CURL_SSLVERSION_DEFAULT: CURLoption = 0; +pub const CURL_SSLVERSION_TLSv1: CURLoption = 1; +pub const CURL_SSLVERSION_SSLv2: CURLoption = 2; +pub const CURL_SSLVERSION_SSLv3: CURLoption = 3; +pub const CURL_SSLVERSION_TLSv1_0: CURLoption = 4; +pub const CURL_SSLVERSION_TLSv1_1: CURLoption = 5; +pub const CURL_SSLVERSION_TLSv1_2: CURLoption = 6; pub const CURLOPT_READDATA: CURLoption = CURLOPT_INFILE; pub const CURLOPT_WRITEDATA: CURLoption = CURLOPT_FILE; From a6533fa98d56998bbc7fa6f498a3762fac67e0e5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 25 Apr 2016 23:09:25 -0700 Subject: [PATCH 14/71] Work with headers out of order in tests --- tests/server/mod.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/tests/server/mod.rs b/tests/server/mod.rs index b1e503f63c..2f72ddcced 100644 --- a/tests/server/mod.rs +++ b/tests/server/mod.rs @@ -1,3 +1,4 @@ +use std::collections::HashSet; use std::net::{TcpListener, SocketAddr}; use std::io::prelude::*; use std::thread; @@ -21,11 +22,34 @@ fn run(listener: &TcpListener, rx: &Receiver) { match msg { Message::Read(ref expected) => { let mut expected = &expected[..]; + let mut expected_headers = HashSet::new(); while let Some(i) = expected.find("\n") { + let line = &expected[..i + 1]; + expected = &expected[i + 1..]; + if line == "\r" { + break + } + expected_headers.insert(line); + } + if expected.len() > 0 { + assert!(expected_headers.insert(expected)); + } + + while expected_headers.len() > 0 { let mut actual = String::new(); t!(socket.read_line(&mut actual)); - assert_eq!(actual, &expected[..i + 1]); - expected = &expected[i + 1..]; + if !expected_headers.remove(&actual[..]) { + panic!("unexpected header: {:?}", actual); + } + } + for header in expected_headers { + panic!("expected header but not found: {:?}", header); + } + + let mut buf = vec![0; expected.len()]; + if buf.len() > 0 { + t!(socket.read_exact(&mut buf)); + assert_eq!(buf, expected.as_bytes()); } } Message::Write(ref to_write) => { From bfebe8f91e928eb09f37a64d6030a4d3df25332d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 25 Apr 2016 23:17:30 -0700 Subject: [PATCH 15/71] Try to debug travis more --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d4e4f4ee8d..f4afc6bcdd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,8 @@ before_script: - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH script: - export CARGO_TARGET_DIR=`pwd`/target - - export CURL_NO_PKG_CONFIG=1 + - export LIBCURL_NO_PKG_CONFIG=1 + - export RUST_TEST_THREADS=1 - cargo test - cargo run --manifest-path systest/Cargo.toml - cargo doc --no-deps From 4536a2c69ab5f515a94b416a9b1d701a3fd07bf5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 25 Apr 2016 23:22:19 -0700 Subject: [PATCH 16/71] Remove erroneous src/main.rs --- src/main.rs | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 src/main.rs diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 0356fa1ad0..0000000000 --- a/src/main.rs +++ /dev/null @@ -1,8 +0,0 @@ -extern crate curl; - -fn main() { - // let data = 2; - let mut easy = curl::easy::Easy::new(); - // easy.debug_function(&mut |_, _| {}); - let _ = easy.perform(); -} From 95a2686c8f86bd00598feb32ba5d4359e7c2cd16 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 25 Apr 2016 23:40:13 -0700 Subject: [PATCH 17/71] Compat with older curl Just get CI passing for now --- curl-sys/lib.rs | 210 ++++++++++++++++++++++---------------------- src/easy.rs | 188 +++++++++++++++++++-------------------- src/error.rs | 32 +++---- src/version.rs | 8 +- tests/easy.rs | 10 +-- tests/server/mod.rs | 3 +- 6 files changed, 226 insertions(+), 225 deletions(-) diff --git a/curl-sys/lib.rs b/curl-sys/lib.rs index 15623a41c3..5868d56920 100644 --- a/curl-sys/lib.rs +++ b/curl-sys/lib.rs @@ -65,11 +65,11 @@ pub type curl_progress_callback = extern fn(*mut c_void, c_double, c_double, c_double) -> c_int; -pub type curl_xferinfo_callback = extern fn(*mut c_void, - curl_off_t, - curl_off_t, - curl_off_t, - curl_off_t) -> c_int; +// pub type curl_xferinfo_callback = extern fn(*mut c_void, +// curl_off_t, +// curl_off_t, +// curl_off_t, +// curl_off_t) -> c_int; pub const CURL_WRITEFUNC_PAUSE: size_t = 0x10000001; @@ -153,15 +153,15 @@ pub type curl_read_callback = extern fn(*mut c_char, size_t, *mut c_void) -> size_t; -pub type curlsocktype = __enum_ty; -pub const CURLSOCKTYPE_IPCXN: curlsocktype = 0; -pub const CURLSOCKTYPE_ACCEPT: curlsocktype = 1; -pub const CURL_SOCKOPT_OK: c_int = 0; -pub const CURL_SOCKOPT_ERROR: c_int = 1; -pub const CURL_SOCKOPT_ALREADY_CONNECTED: c_int = 2; -pub type curl_sockopt_callback = extern fn(*mut c_void, - curl_socket_t, - curlsocktype) -> c_int; +// pub type curlsocktype = __enum_ty; +// pub const CURLSOCKTYPE_IPCXN: curlsocktype = 0; +// pub const CURLSOCKTYPE_ACCEPT: curlsocktype = 1; +// pub const CURL_SOCKOPT_OK: c_int = 0; +// pub const CURL_SOCKOPT_ERROR: c_int = 1; +// pub const CURL_SOCKOPT_ALREADY_CONNECTED: c_int = 2; +// pub type curl_sockopt_callback = extern fn(*mut c_void, +// curl_socket_t, +// curlsocktype) -> c_int; // TODO: sort out libc::sockaddr on windows // #[repr(C)] @@ -213,15 +213,15 @@ pub const CURLE_OK: CURLcode = 0; pub const CURLE_UNSUPPORTED_PROTOCOL: CURLcode = 1; pub const CURLE_FAILED_INIT: CURLcode = 2; pub const CURLE_URL_MALFORMAT: CURLcode = 3; -pub const CURLE_NOT_BUILT_IN: CURLcode = 4; +// pub const CURLE_NOT_BUILT_IN: CURLcode = 4; pub const CURLE_COULDNT_RESOLVE_PROXY: CURLcode = 5; pub const CURLE_COULDNT_RESOLVE_HOST: CURLcode = 6; pub const CURLE_COULDNT_CONNECT: CURLcode = 7; pub const CURLE_FTP_WEIRD_SERVER_REPLY: CURLcode = 8; pub const CURLE_REMOTE_ACCESS_DENIED: CURLcode = 9; -pub const CURLE_FTP_ACCEPT_FAILED: CURLcode = 10; +// pub const CURLE_FTP_ACCEPT_FAILED: CURLcode = 10; pub const CURLE_FTP_WEIRD_PASS_REPLY: CURLcode = 11; -pub const CURLE_FTP_ACCEPT_TIMEOUT: CURLcode = 12; +// pub const CURLE_FTP_ACCEPT_TIMEOUT: CURLcode = 12; pub const CURLE_FTP_WEIRD_PASV_REPLY: CURLcode = 13; pub const CURLE_FTP_WEIRD_227_FORMAT: CURLcode = 14; pub const CURLE_FTP_CANT_GET_HOST: CURLcode = 15; @@ -298,7 +298,7 @@ pub const CURLE_RTSP_CSEQ_ERROR: CURLcode = 85; pub const CURLE_RTSP_SESSION_ERROR: CURLcode = 86; pub const CURLE_FTP_BAD_FILE_LIST: CURLcode = 87; pub const CURLE_CHUNK_FAILED: CURLcode = 88; -pub const CURLE_NO_CONNECTION_AVAILABLE: CURLcode = 89; +// pub const CURLE_NO_CONNECTION_AVAILABLE: CURLcode = 89; pub type curl_conv_callback = extern fn(*mut c_char, size_t) -> CURLcode; pub type curl_ssl_ctx_callback = extern fn(*mut CURL, @@ -320,7 +320,7 @@ pub const CURLAUTH_GSSNEGOTIATE: c_ulong = 1 << 2; pub const CURLAUTH_NTLM: c_ulong = 1 << 3; pub const CURLAUTH_DIGEST_IE: c_ulong = 1 << 4; pub const CURLAUTH_NTLM_WB: c_ulong = 1 << 5; -pub const CURLAUTH_ONLY: c_ulong = 1 << 31; +// pub const CURLAUTH_ONLY: c_ulong = 1 << 31; pub const CURLAUTH_ANY: c_ulong = !CURLAUTH_DIGEST_IE; pub const CURLAUTH_ANYSAFE: c_ulong = !(CURLAUTH_BASIC | CURLAUTH_DIGEST_IE); @@ -330,42 +330,42 @@ pub const CURLSSH_AUTH_PUBLICKEY: c_ulong = 1 << 0; pub const CURLSSH_AUTH_PASSWORD: c_ulong = 1 << 1; pub const CURLSSH_AUTH_HOST: c_ulong = 1 << 2; pub const CURLSSH_AUTH_KEYBOARD: c_ulong = 1 << 3; -pub const CURLSSH_AUTH_AGENT: c_ulong = 1 << 4; +// pub const CURLSSH_AUTH_AGENT: c_ulong = 1 << 4; pub const CURLSSH_AUTH_DEFAULT: c_ulong = CURLSSH_AUTH_ANY; pub const CURLGSSAPI_DELEGATION_NONE: c_ulong = 0; pub const CURLGSSAPI_DELEGATION_POLICY_FLAG: c_ulong = 1 << 0; pub const CURLGSSAPI_DELEGATION_FLAG: c_ulong = 1 << 1; -pub type curl_khtype = __enum_ty; -pub const CURLKHTYPE_UNKNOWN: curl_khtype = 0; -pub const CURLKHTYPE_RSA1: curl_khtype = 1; -pub const CURLKHTYPE_RSA: curl_khtype = 2; -pub const CURLKHTYPE_DSS: curl_khtype = 3; +// pub type curl_khtype = __enum_ty; +// pub const CURLKHTYPE_UNKNOWN: curl_khtype = 0; +// pub const CURLKHTYPE_RSA1: curl_khtype = 1; +// pub const CURLKHTYPE_RSA: curl_khtype = 2; +// pub const CURLKHTYPE_DSS: curl_khtype = 3; -#[repr(C)] -pub struct curl_khkey { - pub key: *const c_char, - pub len: size_t, - pub keytype: curl_khtype, -} +// #[repr(C)] +// pub struct curl_khkey { +// pub key: *const c_char, +// pub len: size_t, +// pub keytype: curl_khtype, +// } -pub type curl_khstat = __enum_ty; -pub const CURLKHSTAT_FINE_ADD_TO_FILE: curl_khstat = 0; -pub const CURLKHSTAT_FINE: curl_khstat = 1; -pub const CURLKHSTAT_REJECT: curl_khstat = 2; -pub const CURLKHSTAT_DEFER: curl_khstat = 3; - -pub type curl_khmatch = __enum_ty; -pub const CURLKHMATCH_OK: curl_khmatch = 0; -pub const CURLKHMATCH_MISMATCH: curl_khmatch = 1; -pub const CURLKHMATCH_MISSING: curl_khmatch = 2; - -pub type curl_sshkeycallback = extern fn(*mut CURL, - *const curl_khkey, - *const curl_khkey, - curl_khmatch, - *mut c_void) -> c_int; +// pub type curl_khstat = __enum_ty; +// pub const CURLKHSTAT_FINE_ADD_TO_FILE: curl_khstat = 0; +// pub const CURLKHSTAT_FINE: curl_khstat = 1; +// pub const CURLKHSTAT_REJECT: curl_khstat = 2; +// pub const CURLKHSTAT_DEFER: curl_khstat = 3; +// +// pub type curl_khmatch = __enum_ty; +// pub const CURLKHMATCH_OK: curl_khmatch = 0; +// pub const CURLKHMATCH_MISMATCH: curl_khmatch = 1; +// pub const CURLKHMATCH_MISSING: curl_khmatch = 2; + +// pub type curl_sshkeycallback = extern fn(*mut CURL, +// *const curl_khkey, +// *const curl_khkey, +// curl_khmatch, +// *mut c_void) -> c_int; pub type curl_usessl = __enum_ty; pub const CURLUSESSL_NONE: curl_usessl = 0; @@ -574,20 +574,20 @@ pub const CURLOPT_TRANSFER_ENCODING: CURLoption = CURLOPTTYPE_LONG + 207; pub const CURLOPT_CLOSESOCKETFUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 208; pub const CURLOPT_CLOSESOCKETDATA: CURLoption = CURLOPTTYPE_OBJECTPOINT + 209; pub const CURLOPT_GSSAPI_DELEGATION: CURLoption = CURLOPTTYPE_LONG + 210; -pub const CURLOPT_DNS_SERVERS: CURLoption = CURLOPTTYPE_OBJECTPOINT + 211; -pub const CURLOPT_ACCEPTTIMEOUT_MS: CURLoption = CURLOPTTYPE_LONG + 212; -pub const CURLOPT_TCP_KEEPALIVE: CURLoption = CURLOPTTYPE_LONG + 213; -pub const CURLOPT_TCP_KEEPIDLE: CURLoption = CURLOPTTYPE_LONG + 214; -pub const CURLOPT_TCP_KEEPINTVL: CURLoption = CURLOPTTYPE_LONG + 215; -pub const CURLOPT_SSL_OPTIONS: CURLoption = CURLOPTTYPE_LONG + 216; -pub const CURLOPT_MAIL_AUTH: CURLoption = CURLOPTTYPE_OBJECTPOINT + 217; -pub const CURLOPT_SASL_IR: CURLoption = CURLOPTTYPE_LONG + 218; +// pub const CURLOPT_DNS_SERVERS: CURLoption = CURLOPTTYPE_OBJECTPOINT + 211; +// pub const CURLOPT_ACCEPTTIMEOUT_MS: CURLoption = CURLOPTTYPE_LONG + 212; +// pub const CURLOPT_TCP_KEEPALIVE: CURLoption = CURLOPTTYPE_LONG + 213; +// pub const CURLOPT_TCP_KEEPIDLE: CURLoption = CURLOPTTYPE_LONG + 214; +// pub const CURLOPT_TCP_KEEPINTVL: CURLoption = CURLOPTTYPE_LONG + 215; +// pub const CURLOPT_SSL_OPTIONS: CURLoption = CURLOPTTYPE_LONG + 216; +// pub const CURLOPT_MAIL_AUTH: CURLoption = CURLOPTTYPE_OBJECTPOINT + 217; +// pub const CURLOPT_SASL_IR: CURLoption = CURLOPTTYPE_LONG + 218; pub const CURLOPT_XFERINFOFUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 219; -pub const CURLOPT_XOAUTH2_BEARER: CURLoption = CURLOPTTYPE_OBJECTPOINT + 220; -pub const CURLOPT_DNS_INTERFACE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 221; -pub const CURLOPT_DNS_LOCAL_IP4: CURLoption = CURLOPTTYPE_OBJECTPOINT + 222; -pub const CURLOPT_DNS_LOCAL_IP6: CURLoption = CURLOPTTYPE_OBJECTPOINT + 223; -pub const CURLOPT_LOGIN_OPTIONS: CURLoption = CURLOPTTYPE_OBJECTPOINT + 224; +// pub const CURLOPT_XOAUTH2_BEARER: CURLoption = CURLOPTTYPE_OBJECTPOINT + 220; +// pub const CURLOPT_DNS_INTERFACE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 221; +// pub const CURLOPT_DNS_LOCAL_IP4: CURLoption = CURLOPTTYPE_OBJECTPOINT + 222; +// pub const CURLOPT_DNS_LOCAL_IP6: CURLoption = CURLOPTTYPE_OBJECTPOINT + 223; +// pub const CURLOPT_LOGIN_OPTIONS: CURLoption = CURLOPTTYPE_OBJECTPOINT + 224; pub const CURL_IPRESOLVE_WHATEVER: c_int = 0; pub const CURL_IPRESOLVE_V4: c_int = 1; @@ -598,9 +598,9 @@ pub const CURL_SSLVERSION_DEFAULT: CURLoption = 0; pub const CURL_SSLVERSION_TLSv1: CURLoption = 1; pub const CURL_SSLVERSION_SSLv2: CURLoption = 2; pub const CURL_SSLVERSION_SSLv3: CURLoption = 3; -pub const CURL_SSLVERSION_TLSv1_0: CURLoption = 4; -pub const CURL_SSLVERSION_TLSv1_1: CURLoption = 5; -pub const CURL_SSLVERSION_TLSv1_2: CURLoption = 6; +// pub const CURL_SSLVERSION_TLSv1_0: CURLoption = 4; +// pub const CURL_SSLVERSION_TLSv1_1: CURLoption = 5; +// pub const CURL_SSLVERSION_TLSv1_2: CURLoption = 6; pub const CURLOPT_READDATA: CURLoption = CURLOPT_INFILE; pub const CURLOPT_WRITEDATA: CURLoption = CURLOPT_FILE; @@ -665,23 +665,23 @@ pub struct curl_certinfo { pub certinfo: *mut *mut curl_slist, } -pub type curl_sslbackend = __enum_ty; -pub const CURLSSLBACKEND_NONE: curl_sslbackend = 0; -pub const CURLSSLBACKEND_OPENSSL: curl_sslbackend = 1; -pub const CURLSSLBACKEND_GNUTLS: curl_sslbackend = 2; -pub const CURLSSLBACKEND_NSS: curl_sslbackend = 3; +// pub type curl_sslbackend = __enum_ty; +// pub const CURLSSLBACKEND_NONE: curl_sslbackend = 0; +// pub const CURLSSLBACKEND_OPENSSL: curl_sslbackend = 1; +// pub const CURLSSLBACKEND_GNUTLS: curl_sslbackend = 2; +// pub const CURLSSLBACKEND_NSS: curl_sslbackend = 3; // pub const CURLSSLBACKEND_QSOSSL: curl_sslbackend = 4; -pub const CURLSSLBACKEND_GSKIT: curl_sslbackend = 5; -pub const CURLSSLBACKEND_POLARSSL: curl_sslbackend = 6; -pub const CURLSSLBACKEND_CYASSL: curl_sslbackend = 7; -pub const CURLSSLBACKEND_SCHANNEL: curl_sslbackend = 8; -pub const CURLSSLBACKEND_DARWINSSL: curl_sslbackend = 9; +// pub const CURLSSLBACKEND_GSKIT: curl_sslbackend = 5; +// pub const CURLSSLBACKEND_POLARSSL: curl_sslbackend = 6; +// pub const CURLSSLBACKEND_CYASSL: curl_sslbackend = 7; +// pub const CURLSSLBACKEND_SCHANNEL: curl_sslbackend = 8; +// pub const CURLSSLBACKEND_DARWINSSL: curl_sslbackend = 9; -#[repr(C)] -pub struct curl_tlssessioninfo { - pub backend: curl_sslbackend, - pub internals: *mut c_void, -} +// #[repr(C)] +// pub struct curl_tlssessioninfo { +// pub backend: curl_sslbackend, +// pub internals: *mut c_void, +// } pub const CURLINFO_STRING: CURLINFO = 0x100000; pub const CURLINFO_LONG: CURLINFO = 0x200000; @@ -732,7 +732,7 @@ pub const CURLINFO_RTSP_CSEQ_RECV: CURLINFO = CURLINFO_LONG + 39; pub const CURLINFO_PRIMARY_PORT: CURLINFO = CURLINFO_LONG + 40; pub const CURLINFO_LOCAL_IP: CURLINFO = CURLINFO_STRING + 41; pub const CURLINFO_LOCAL_PORT: CURLINFO = CURLINFO_LONG + 42; -pub const CURLINFO_TLS_SESSION: CURLINFO = CURLINFO_SLIST + 43; +// pub const CURLINFO_TLS_SESSION: CURLINFO = CURLINFO_SLIST + 43; pub type curl_closepolicy = __enum_ty; pub const CURLCLOSEPOLICY_NONE: curl_closepolicy = 0; @@ -747,7 +747,7 @@ pub const CURL_GLOBAL_WIN32: c_long = 1 << 1; pub const CURL_GLOBAL_ALL: c_long = CURL_GLOBAL_SSL | CURL_GLOBAL_WIN32; pub const CURL_GLOBAL_NOTHING: c_long = 0; pub const CURL_GLOBAL_DEFAULT: c_long = CURL_GLOBAL_ALL; -pub const CURL_GLOBAL_ACK_EINTR: c_long = 1 << 2; +// pub const CURL_GLOBAL_ACK_EINTR: c_long = 1 << 2; pub type curl_lock_data = __enum_ty; pub const CURL_LOCK_DATA_NONE: curl_lock_data = 0; @@ -778,7 +778,7 @@ pub const CURLSHE_BAD_OPTION: CURLSHcode = 1; pub const CURLSHE_IN_USE: CURLSHcode = 2; pub const CURLSHE_INVALID: CURLSHcode = 3; pub const CURLSHE_NOMEM: CURLSHcode = 4; -pub const CURLSHE_NOT_BUILT_IN: CURLSHcode = 5; +// pub const CURLSHE_NOT_BUILT_IN: CURLSHcode = 5; pub type CURLSHoption = __enum_ty; pub const CURLSHOPT_NONE: CURLSHoption = 0; @@ -827,7 +827,7 @@ pub const CURL_VERSION_CONV: c_int = 1 << 12; pub const CURL_VERSION_CURLDEBUG: c_int = 1 << 13; pub const CURL_VERSION_TLSAUTH_SRP: c_int = 1 << 14; pub const CURL_VERSION_NTLM_WB: c_int = 1 << 15; -pub const CURL_VERSION_HTTP2: c_int = 1 << 16; +// pub const CURL_VERSION_HTTP2: c_int = 1 << 16; pub const CURLPAUSE_RECV: c_int = 1 << 0; pub const CURLPAUSE_RECV_CONT: c_int = 0; @@ -845,7 +845,7 @@ pub const CURLM_OUT_OF_MEMORY: CURLMcode = 3; pub const CURLM_INTERNAL_ERROR: CURLMcode = 4; pub const CURLM_BAD_SOCKET: CURLMcode = 5; pub const CURLM_UNKNOWN_OPTION: CURLMcode = 6; -pub const CURLM_ADDED_ALREADY: CURLMcode = 7; +// pub const CURLM_ADDED_ALREADY: CURLMcode = 7; pub type CURLMSG = __enum_ty; pub const CURLMSG_NONE: CURLMSG = 0; @@ -858,16 +858,16 @@ pub struct CURLMsg { pub data: *mut c_void, } -pub const CURL_WAIT_POLLIN: c_short = 0x1; -pub const CURL_WAIT_POLLPRI: c_short = 0x2; -pub const CURL_WAIT_POLLOUT: c_short = 0x4; +// pub const CURL_WAIT_POLLIN: c_short = 0x1; +// pub const CURL_WAIT_POLLPRI: c_short = 0x2; +// pub const CURL_WAIT_POLLOUT: c_short = 0x4; -#[repr(C)] -pub struct curl_waitfd { - pub fd: curl_socket_t, - pub events: c_short, - pub revents: c_short, -} +// #[repr(C)] +// pub struct curl_waitfd { +// pub fd: curl_socket_t, +// pub events: c_short, +// pub revents: c_short, +// } pub const CURL_POLL_NONE: c_int = 0; pub const CURL_POLL_IN: c_int = 1; @@ -891,14 +891,14 @@ pub const CURLMOPT_SOCKETDATA: CURLMoption = CURLOPTTYPE_OBJECTPOINT + 2; pub const CURLMOPT_PIPELINING: CURLMoption = CURLOPTTYPE_LONG + 3; pub const CURLMOPT_TIMERFUNCTION: CURLMoption = CURLOPTTYPE_FUNCTIONPOINT + 4; pub const CURLMOPT_TIMERDATA: CURLMoption = CURLOPTTYPE_OBJECTPOINT + 5; -pub const CURLMOPT_MAXCONNECTS: CURLMoption = CURLOPTTYPE_LONG + 6; -pub const CURLMOPT_MAX_HOST_CONNECTIONS: CURLMoption = CURLOPTTYPE_LONG + 7; -pub const CURLMOPT_MAX_PIPELINE_LENGTH: CURLMoption = CURLOPTTYPE_LONG + 8; -pub const CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: CURLMoption = CURLOPTTYPE_OFF_T + 9; -pub const CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: CURLMoption = CURLOPTTYPE_OFF_T + 10; -pub const CURLMOPT_PIPELINING_SITE_BL: CURLMoption = CURLOPTTYPE_OBJECTPOINT + 11; -pub const CURLMOPT_PIPELINING_SERVER_BL: CURLMoption = CURLOPTTYPE_OBJECTPOINT + 12; -pub const CURLMOPT_MAX_TOTAL_CONNECTIONS: CURLMoption = CURLOPTTYPE_LONG + 13; +// pub const CURLMOPT_MAXCONNECTS: CURLMoption = CURLOPTTYPE_LONG + 6; +// pub const CURLMOPT_MAX_HOST_CONNECTIONS: CURLMoption = CURLOPTTYPE_LONG + 7; +// pub const CURLMOPT_MAX_PIPELINE_LENGTH: CURLMoption = CURLOPTTYPE_LONG + 8; +// pub const CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: CURLMoption = CURLOPTTYPE_OFF_T + 9; +// pub const CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: CURLMoption = CURLOPTTYPE_OFF_T + 10; +// pub const CURLMOPT_PIPELINING_SITE_BL: CURLMoption = CURLOPTTYPE_OBJECTPOINT + 11; +// pub const CURLMOPT_PIPELINING_SERVER_BL: CURLMoption = CURLOPTTYPE_OBJECTPOINT + 12; +// pub const CURLMOPT_MAX_TOTAL_CONNECTIONS: CURLMoption = CURLOPTTYPE_LONG + 13; extern { pub fn curl_formadd(httppost: *mut *mut curl_httppost, @@ -974,11 +974,11 @@ extern { // write_fd_set: *mut libc::fd_set, // exc_fd_set: *mut libc::fd_set, // max_fd: *mut c_int) -> CURLMcode; - pub fn curl_multi_wait(multi_handle: *mut CURLM, - extra_fds: *mut curl_waitfd, - extra_nfds: c_uint, - timeout_ms: c_int, - ret: *mut c_int) -> CURLMcode; + // pub fn curl_multi_wait(multi_handle: *mut CURLM, + // extra_fds: *mut curl_waitfd, + // extra_nfds: c_uint, + // timeout_ms: c_int, + // ret: *mut c_int) -> CURLMcode; pub fn curl_multi_perform(multi_handle: *mut CURLM, running_handles: *mut c_int) -> CURLMcode; pub fn curl_multi_cleanup(multi_handle: *mut CURLM) -> CURLMcode; diff --git a/src/easy.rs b/src/easy.rs index 51add8e4e5..a9a758a21c 100644 --- a/src/easy.rs +++ b/src/easy.rs @@ -10,7 +10,7 @@ use std::str; use std::time::Duration; use curl_sys; -use libc::{self, c_long, c_int, c_char, c_void, size_t}; +use libc::{self, c_long, c_int, c_char, c_void, size_t, c_double}; use Error; use panic; @@ -78,9 +78,9 @@ pub enum SslVersion { Tlsv1 = curl_sys::CURL_SSLVERSION_TLSv1 as isize, Sslv2 = curl_sys::CURL_SSLVERSION_SSLv2 as isize, Sslv3 = curl_sys::CURL_SSLVERSION_SSLv3 as isize, - Tlsv10 = curl_sys::CURL_SSLVERSION_TLSv1_0 as isize, - Tlsv11 = curl_sys::CURL_SSLVERSION_TLSv1_1 as isize, - Tlsv12 = curl_sys::CURL_SSLVERSION_TLSv1_2 as isize, + // Tlsv10 = curl_sys::CURL_SSLVERSION_TLSv1_0 as isize, + // Tlsv11 = curl_sys::CURL_SSLVERSION_TLSv1_1 as isize, + // Tlsv12 = curl_sys::CURL_SSLVERSION_TLSv1_2 as isize, /// Hidden variant to indicate that this enum should not be matched on, it /// may grow over time. @@ -412,24 +412,24 @@ impl<'a> Easy<'a> { /// By default this function calls an internal method and corresponds to /// `CURLOPT_XFERINFOFUNCTION` and `CURLOPT_XFERINFODATA`. pub fn progress_function(&mut self, f: &'a mut F) -> Result<(), Error> - where F: FnMut(u64, u64, u64, u64) -> bool + where F: FnMut(f64, f64, f64, f64) -> bool { - try!(self.setopt_ptr(curl_sys::CURLOPT_XFERINFOFUNCTION, + try!(self.setopt_ptr(curl_sys::CURLOPT_PROGRESSFUNCTION, cb:: as usize as *const c_char)); try!(self.setopt_ptr(curl_sys::CURLOPT_PROGRESSDATA, f as *mut F as *const c_char)); return Ok(()); unsafe extern fn cb(data: *mut c_void, - dltotal: curl_sys::curl_off_t, - dlnow: curl_sys::curl_off_t, - ultotal: curl_sys::curl_off_t, - ulnow: curl_sys::curl_off_t) -> c_int - where F: FnMut(u64, u64, u64, u64) -> bool + dltotal: c_double, + dlnow: c_double, + ultotal: c_double, + ulnow: c_double) -> c_int + where F: FnMut(f64, f64, f64, f64) -> bool { let keep_going = panic::catch(|| { let fnptr = &mut *(data as *mut F); - fnptr(dltotal as u64, dlnow as u64, ultotal as u64, ulnow as u64) + fnptr(dltotal, dlnow, ultotal, ulnow) }).unwrap_or(false); if keep_going { 0 @@ -714,35 +714,35 @@ impl<'a> Easy<'a> { self.setopt_long(curl_sys::CURLOPT_TCP_NODELAY, enable as c_long) } - /// Configures whether TCP keepalive probes will be sent. - /// - /// The delay and frequency of these probes is controlled by `tcp_keepidle` - /// and `tcp_keepintvl`. - /// - /// By default this option is `false` and corresponds to - /// `CURLOPT_TCP_KEEPALIVE`. - pub fn tcp_keepalive(&mut self, enable: bool) -> Result<(), Error> { - self.setopt_long(curl_sys::CURLOPT_TCP_KEEPALIVE, enable as c_long) - } - - /// Configures the TCP keepalive idle time wait. - /// - /// This is the delay, after which the connection is idle, keepalive probes - /// will be sent. Not all operating systems support this. - /// - /// By default this corresponds to `CURLOPT_TCP_KEEPIDLE`. - pub fn tcp_keepidle(&mut self, amt: Duration) -> Result<(), Error> { - self.setopt_long(curl_sys::CURLOPT_TCP_KEEPIDLE, - amt.as_secs() as c_long) - } + // /// Configures whether TCP keepalive probes will be sent. + // /// + // /// The delay and frequency of these probes is controlled by `tcp_keepidle` + // /// and `tcp_keepintvl`. + // /// + // /// By default this option is `false` and corresponds to + // /// `CURLOPT_TCP_KEEPALIVE`. + // pub fn tcp_keepalive(&mut self, enable: bool) -> Result<(), Error> { + // self.setopt_long(curl_sys::CURLOPT_TCP_KEEPALIVE, enable as c_long) + // } - /// Configures the delay between keepalive probes. - /// - /// By default this corresponds to `CURLOPT_TCP_KEEPINTVL`. - pub fn tcp_keepintvl(&mut self, amt: Duration) -> Result<(), Error> { - self.setopt_long(curl_sys::CURLOPT_TCP_KEEPINTVL, - amt.as_secs() as c_long) - } + // /// Configures the TCP keepalive idle time wait. + // /// + // /// This is the delay, after which the connection is idle, keepalive probes + // /// will be sent. Not all operating systems support this. + // /// + // /// By default this corresponds to `CURLOPT_TCP_KEEPIDLE`. + // pub fn tcp_keepidle(&mut self, amt: Duration) -> Result<(), Error> { + // self.setopt_long(curl_sys::CURLOPT_TCP_KEEPIDLE, + // amt.as_secs() as c_long) + // } + // + // /// Configures the delay between keepalive probes. + // /// + // /// By default this corresponds to `CURLOPT_TCP_KEEPINTVL`. + // pub fn tcp_keepintvl(&mut self, amt: Duration) -> Result<(), Error> { + // self.setopt_long(curl_sys::CURLOPT_TCP_KEEPINTVL, + // amt.as_secs() as c_long) + // } /// Configures the scope for local IPv6 addresses. /// @@ -751,7 +751,7 @@ impl<'a> Easy<'a> { /// /// By default this value is 0 and corresponds to `CURLOPT_ADDRESS_SCOPE` pub fn address_scope(&mut self, scope: u32) -> Result<(), Error> { - self.setopt_long(curl_sys::CURLOPT_TCP_KEEPINTVL, + self.setopt_long(curl_sys::CURLOPT_ADDRESS_SCOPE, scope as c_long) } @@ -1410,59 +1410,59 @@ impl<'a> Easy<'a> { self.setopt_long(curl_sys::CURLOPT_CONNECT_ONLY, enable as c_long) } - /// Set interface to speak DNS over. - /// - /// Set the name of the network interface that the DNS resolver should bind - /// to. This must be an interface name (not an address). - /// - /// By default this option is not set and corresponds to - /// `CURLOPT_DNS_INTERFACE`. - pub fn dns_interface(&mut self, interface: &str) -> Result<(), Error> { - let interface = try!(CString::new(interface)); - self.setopt_str(curl_sys::CURLOPT_DNS_INTERFACE, &interface) - } - - /// IPv4 address to bind DNS resolves to - /// - /// Set the local IPv4 address that the resolver should bind to. The - /// argument should be of type char * and contain a single numerical IPv4 - /// address as a string. - /// - /// By default this option is not set and corresponds to - /// `CURLOPT_DNS_LOCAL_IP4`. - pub fn dns_local_ip4(&mut self, ip: &str) -> Result<(), Error> { - let ip = try!(CString::new(ip)); - self.setopt_str(curl_sys::CURLOPT_DNS_LOCAL_IP4, &ip) - } - - /// IPv6 address to bind DNS resolves to - /// - /// Set the local IPv6 address that the resolver should bind to. The - /// argument should be of type char * and contain a single numerical IPv6 - /// address as a string. - /// - /// By default this option is not set and corresponds to - /// `CURLOPT_DNS_LOCAL_IP6`. - pub fn dns_local_ip6(&mut self, ip: &str) -> Result<(), Error> { - let ip = try!(CString::new(ip)); - self.setopt_str(curl_sys::CURLOPT_DNS_LOCAL_IP6, &ip) - } - - /// Set preferred DNS servers. - /// - /// Provides a list of DNS servers to be used instead of the system default. - /// The format of the dns servers option is: - /// - /// ```text - /// host[:port],[host[:port]]... - /// ``` - /// - /// By default this option is not set and corresponds to - /// `CURLOPT_DNS_SERVERS`. - pub fn dns_servers(&mut self, servers: &str) -> Result<(), Error> { - let servers = try!(CString::new(servers)); - self.setopt_str(curl_sys::CURLOPT_DNS_SERVERS, &servers) - } + // /// Set interface to speak DNS over. + // /// + // /// Set the name of the network interface that the DNS resolver should bind + // /// to. This must be an interface name (not an address). + // /// + // /// By default this option is not set and corresponds to + // /// `CURLOPT_DNS_INTERFACE`. + // pub fn dns_interface(&mut self, interface: &str) -> Result<(), Error> { + // let interface = try!(CString::new(interface)); + // self.setopt_str(curl_sys::CURLOPT_DNS_INTERFACE, &interface) + // } + // + // /// IPv4 address to bind DNS resolves to + // /// + // /// Set the local IPv4 address that the resolver should bind to. The + // /// argument should be of type char * and contain a single numerical IPv4 + // /// address as a string. + // /// + // /// By default this option is not set and corresponds to + // /// `CURLOPT_DNS_LOCAL_IP4`. + // pub fn dns_local_ip4(&mut self, ip: &str) -> Result<(), Error> { + // let ip = try!(CString::new(ip)); + // self.setopt_str(curl_sys::CURLOPT_DNS_LOCAL_IP4, &ip) + // } + // + // /// IPv6 address to bind DNS resolves to + // /// + // /// Set the local IPv6 address that the resolver should bind to. The + // /// argument should be of type char * and contain a single numerical IPv6 + // /// address as a string. + // /// + // /// By default this option is not set and corresponds to + // /// `CURLOPT_DNS_LOCAL_IP6`. + // pub fn dns_local_ip6(&mut self, ip: &str) -> Result<(), Error> { + // let ip = try!(CString::new(ip)); + // self.setopt_str(curl_sys::CURLOPT_DNS_LOCAL_IP6, &ip) + // } + // + // /// Set preferred DNS servers. + // /// + // /// Provides a list of DNS servers to be used instead of the system default. + // /// The format of the dns servers option is: + // /// + // /// ```text + // /// host[:port],[host[:port]]... + // /// ``` + // /// + // /// By default this option is not set and corresponds to + // /// `CURLOPT_DNS_SERVERS`. + // pub fn dns_servers(&mut self, servers: &str) -> Result<(), Error> { + // let servers = try!(CString::new(servers)); + // self.setopt_str(curl_sys::CURLOPT_DNS_SERVERS, &servers) + // } // ========================================================================= // SSL/Security Options diff --git a/src/error.rs b/src/error.rs index 085673912a..3cad8c1bbd 100644 --- a/src/error.rs +++ b/src/error.rs @@ -35,10 +35,10 @@ impl Error { self.code == curl_sys::CURLE_URL_MALFORMAT } - /// Returns whether this error corresponds to CURLE_NOT_BUILT_IN. - pub fn is_not_built_in(&self) -> bool { - self.code == curl_sys::CURLE_NOT_BUILT_IN - } + // /// Returns whether this error corresponds to CURLE_NOT_BUILT_IN. + // pub fn is_not_built_in(&self) -> bool { + // self.code == curl_sys::CURLE_NOT_BUILT_IN + // } /// Returns whether this error corresponds to CURLE_COULDNT_RESOLVE_PROXY. pub fn is_couldnt_resolve_proxy(&self) -> bool { @@ -270,10 +270,10 @@ impl Error { self.code == curl_sys::CURLE_CHUNK_FAILED } - /// Returns whether this error corresponds to CURLE_NO_CONNECTION_AVAILABLE. - pub fn is_no_connection_available(&self) -> bool { - self.code == curl_sys::CURLE_NO_CONNECTION_AVAILABLE - } + // /// Returns whether this error corresponds to CURLE_NO_CONNECTION_AVAILABLE. + // pub fn is_no_connection_available(&self) -> bool { + // self.code == curl_sys::CURLE_NO_CONNECTION_AVAILABLE + // } /// Returns the value of the underlying error corresponding to libcurl. pub fn code(&self) -> curl_sys::CURLcode { @@ -339,10 +339,10 @@ impl ShareError { self.code == curl_sys::CURLSHE_NOMEM } - /// Returns whether this error corresponds to CURLSHE_NOT_BUILT_IN. - pub fn is_not_built_in(&self) -> bool { - self.code == curl_sys::CURLSHE_NOT_BUILT_IN - } + // /// Returns whether this error corresponds to CURLSHE_NOT_BUILT_IN. + // pub fn is_not_built_in(&self) -> bool { + // self.code == curl_sys::CURLSHE_NOT_BUILT_IN + // } /// Returns the value of the underlying error corresponding to libcurl. pub fn code(&self) -> curl_sys::CURLSHcode { @@ -418,10 +418,10 @@ impl MultiError { self.code == curl_sys::CURLM_UNKNOWN_OPTION } - /// Returns whether this error corresponds to CURLM_ADDED_ALREADY. - pub fn is_added_already(&self) -> bool { - self.code == curl_sys::CURLM_ADDED_ALREADY - } + // /// Returns whether this error corresponds to CURLM_ADDED_ALREADY. + // pub fn is_added_already(&self) -> bool { + // self.code == curl_sys::CURLM_ADDED_ALREADY + // } /// Returns the value of the underlying error corresponding to libcurl. pub fn code(&self) -> curl_sys::CURLMcode { diff --git a/src/version.rs b/src/version.rs index 57e28a297b..a9ca7cd26b 100644 --- a/src/version.rs +++ b/src/version.rs @@ -135,10 +135,10 @@ impl Version { self.flag(curl_sys::CURL_VERSION_NTLM_WB) } - /// Returns whether libcurl was built with support for HTTP2. - pub fn feature_http2(&self) -> bool { - self.flag(curl_sys::CURL_VERSION_HTTP2) - } + // /// Returns whether libcurl was built with support for HTTP2. + // pub fn feature_http2(&self) -> bool { + // self.flag(curl_sys::CURL_VERSION_HTTP2) + // } fn flag(&self, flag: c_int) -> bool { unsafe { diff --git a/tests/easy.rs b/tests/easy.rs index f94bc9ccbf..d36ab223b9 100644 --- a/tests/easy.rs +++ b/tests/easy.rs @@ -91,7 +91,7 @@ Accept: */*\r\n\ s.send("HTTP/1.1 200 OK\r\n\r\nHello!"); let mut hits = 0; - let mut dl = 0; + let mut dl = 0.0; { let mut cb = |_, a, _, _| { hits += 1; @@ -107,7 +107,7 @@ Accept: */*\r\n\ t!(handle.perform()); } assert!(hits > 0); - assert_eq!(dl, 6); + assert_eq!(dl, 6.0); } #[test] @@ -239,9 +239,9 @@ HTTP/1.1 200 OK\r\n\ fn misc() { let mut h = handle(); t!(h.tcp_nodelay(true)); - t!(h.tcp_keepalive(true)); - t!(h.tcp_keepidle(Duration::new(3, 0))); - t!(h.tcp_keepintvl(Duration::new(3, 0))); + // t!(h.tcp_keepalive(true)); + // t!(h.tcp_keepidle(Duration::new(3, 0))); + // t!(h.tcp_keepintvl(Duration::new(3, 0))); t!(h.buffer_size(10)); t!(h.dns_cache_timeout(Duration::new(1, 0))); } diff --git a/tests/server/mod.rs b/tests/server/mod.rs index 2f72ddcced..a288086407 100644 --- a/tests/server/mod.rs +++ b/tests/server/mod.rs @@ -1,5 +1,5 @@ use std::collections::HashSet; -use std::net::{TcpListener, SocketAddr}; +use std::net::{TcpListener, SocketAddr, TcpStream}; use std::io::prelude::*; use std::thread; use std::sync::mpsc::{Sender, Receiver, channel}; @@ -102,6 +102,7 @@ impl Server { impl Drop for Server { fn drop(&mut self) { + drop(TcpStream::connect(&self.addr)); drop(self.messages.take()); let res = self.thread.take().unwrap().join(); if !thread::panicking() { From ed8b43aa6ca9cac2c1238dd0730232b4345320f2 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 25 Apr 2016 23:44:02 -0700 Subject: [PATCH 18/71] Comment out XFERINFO for now --- curl-sys/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/curl-sys/lib.rs b/curl-sys/lib.rs index 5868d56920..f9de3f87d7 100644 --- a/curl-sys/lib.rs +++ b/curl-sys/lib.rs @@ -582,7 +582,7 @@ pub const CURLOPT_GSSAPI_DELEGATION: CURLoption = CURLOPTTYPE_LONG + 210; // pub const CURLOPT_SSL_OPTIONS: CURLoption = CURLOPTTYPE_LONG + 216; // pub const CURLOPT_MAIL_AUTH: CURLoption = CURLOPTTYPE_OBJECTPOINT + 217; // pub const CURLOPT_SASL_IR: CURLoption = CURLOPTTYPE_LONG + 218; -pub const CURLOPT_XFERINFOFUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 219; +// pub const CURLOPT_XFERINFOFUNCTION: CURLoption = CURLOPTTYPE_FUNCTIONPOINT + 219; // pub const CURLOPT_XOAUTH2_BEARER: CURLoption = CURLOPTTYPE_OBJECTPOINT + 220; // pub const CURLOPT_DNS_INTERFACE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 221; // pub const CURLOPT_DNS_LOCAL_IP4: CURLoption = CURLOPTTYPE_OBJECTPOINT + 222; From 16f90d348a707eea65f6031dab92f2f9b7beea24 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 25 Apr 2016 23:44:12 -0700 Subject: [PATCH 19/71] Add a dedicated build for bundled build --- .travis.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f4afc6bcdd..bf6ad88f4c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,13 +3,16 @@ rust: - stable - beta - nightly +matrix: + include: + - os: linux + rust: stable + env: LIBCURL_NO_PKG_CONFIG=1 sudo: false before_script: - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH script: - export CARGO_TARGET_DIR=`pwd`/target - - export LIBCURL_NO_PKG_CONFIG=1 - - export RUST_TEST_THREADS=1 - cargo test - cargo run --manifest-path systest/Cargo.toml - cargo doc --no-deps From 43b2bfdf81b08a64e9e5404c569c3f6ae759bdd7 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 25 Apr 2016 23:49:52 -0700 Subject: [PATCH 20/71] Handle 0-length url decode/encode --- curl-sys/lib.rs | 2 +- src/easy.rs | 6 ++++++ tests/easy.rs | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/curl-sys/lib.rs b/curl-sys/lib.rs index f9de3f87d7..6a8d6311b4 100644 --- a/curl-sys/lib.rs +++ b/curl-sys/lib.rs @@ -7,7 +7,7 @@ extern crate libz_sys; extern crate openssl_sys; use libc::{c_int, c_char, c_uint, c_long, c_double, c_void, size_t, time_t}; -use libc::{c_ulong, c_short}; +use libc::c_ulong; #[cfg(target_env = "msvc")] #[doc(hidden)] diff --git a/src/easy.rs b/src/easy.rs index a9a758a21c..602f340552 100644 --- a/src/easy.rs +++ b/src/easy.rs @@ -1814,6 +1814,9 @@ impl<'a> Easy<'a> { /// URL encodes a string `s` pub fn url_encode(&mut self, s: &[u8]) -> String { + if s.len() == 0 { + return String::new() + } unsafe { let p = curl_sys::curl_easy_escape(self.handle, s.as_ptr() as *const _, @@ -1828,6 +1831,9 @@ impl<'a> Easy<'a> { /// URL decodes a string `s`, returning `None` if it fails pub fn url_decode(&mut self, s: &str) -> Vec { + if s.len() == 0 { + return Vec::new(); + } unsafe { let mut len = 0; let p = curl_sys::curl_easy_unescape(self.handle, diff --git a/tests/easy.rs b/tests/easy.rs index d36ab223b9..4bf8ae902f 100644 --- a/tests/easy.rs +++ b/tests/easy.rs @@ -495,9 +495,11 @@ fn url_encoding() { assert_eq!(h.url_encode(b"foo"), "foo"); assert_eq!(h.url_encode(b"foo bar"), "foo%20bar"); assert_eq!(h.url_encode(b"foo bar\xff"), "foo%20bar%FF"); + assert_eq!(h.url_encode(b""), ""); assert_eq!(h.url_decode("foo"), b"foo"); assert_eq!(h.url_decode("foo%20bar"), b"foo bar"); assert_eq!(h.url_decode("foo%2"), b"foo%2"); assert_eq!(h.url_decode("foo%xx"), b"foo%xx"); assert_eq!(h.url_decode("foo%ff"), b"foo\xff"); + assert_eq!(h.url_decode(""), b""); } From 0d3aecb7f06c38dafad7ca3a4816d8d3be70c0e6 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 25 Apr 2016 23:52:14 -0700 Subject: [PATCH 21/71] Try to make tests more robust? --- tests/server/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/server/mod.rs b/tests/server/mod.rs index a288086407..72f5f0ef30 100644 --- a/tests/server/mod.rs +++ b/tests/server/mod.rs @@ -26,7 +26,7 @@ fn run(listener: &TcpListener, rx: &Receiver) { while let Some(i) = expected.find("\n") { let line = &expected[..i + 1]; expected = &expected[i + 1..]; - if line == "\r" { + if line == "\r" || line == "" { break } expected_headers.insert(line); From a4dd5240e36d7e82a1f3e37e9922176468d9264f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 25 Apr 2016 23:55:58 -0700 Subject: [PATCH 22/71] Ok, debug again on travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index bf6ad88f4c..a038c3228d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,7 @@ before_script: - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH script: - export CARGO_TARGET_DIR=`pwd`/target + - export RUST_TEST_THREADS=1 - cargo test - cargo run --manifest-path systest/Cargo.toml - cargo doc --no-deps From b3c191ee127acaf38c255f9be3aa974c8fafa7ed Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 26 Apr 2016 00:04:50 -0700 Subject: [PATCH 23/71] What is wrong with OSX --- .travis.yml | 17 +++++++++-------- tests/easy.rs | 18 +++++++++--------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/.travis.yml b/.travis.yml index a038c3228d..5ec99fd646 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,19 +1,20 @@ language: rust rust: - stable - - beta - - nightly -matrix: - include: - - os: linux - rust: stable - env: LIBCURL_NO_PKG_CONFIG=1 + # - beta + # - nightly +# matrix: +# include: +# - os: linux +# rust: stable +# env: LIBCURL_NO_PKG_CONFIG=1 sudo: false before_script: - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH script: - export CARGO_TARGET_DIR=`pwd`/target - export RUST_TEST_THREADS=1 + - curl --version - cargo test - cargo run --manifest-path systest/Cargo.toml - cargo doc --no-deps @@ -24,5 +25,5 @@ notifications: email: on_success: never os: - - linux + # - linux - osx diff --git a/tests/easy.rs b/tests/easy.rs index 4bf8ae902f..83e3a392c2 100644 --- a/tests/easy.rs +++ b/tests/easy.rs @@ -492,14 +492,14 @@ HTTP/1.1 200 OK\r\n\ #[test] fn url_encoding() { let mut h = handle(); - assert_eq!(h.url_encode(b"foo"), "foo"); - assert_eq!(h.url_encode(b"foo bar"), "foo%20bar"); - assert_eq!(h.url_encode(b"foo bar\xff"), "foo%20bar%FF"); - assert_eq!(h.url_encode(b""), ""); - assert_eq!(h.url_decode("foo"), b"foo"); - assert_eq!(h.url_decode("foo%20bar"), b"foo bar"); + // assert_eq!(h.url_encode(b"foo"), "foo"); + // assert_eq!(h.url_encode(b"foo bar"), "foo%20bar"); + // assert_eq!(h.url_encode(b"foo bar\xff"), "foo%20bar%FF"); + // assert_eq!(h.url_encode(b""), ""); + // assert_eq!(h.url_decode("foo"), b"foo"); + // assert_eq!(h.url_decode("foo%20bar"), b"foo bar"); assert_eq!(h.url_decode("foo%2"), b"foo%2"); - assert_eq!(h.url_decode("foo%xx"), b"foo%xx"); - assert_eq!(h.url_decode("foo%ff"), b"foo\xff"); - assert_eq!(h.url_decode(""), b""); + // assert_eq!(h.url_decode("foo%xx"), b"foo%xx"); + // assert_eq!(h.url_decode("foo%ff"), b"foo\xff"); + // assert_eq!(h.url_decode(""), b""); } From ad76dd23cf080045434531ad403efd177c2a7e1b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 26 Apr 2016 00:22:19 -0700 Subject: [PATCH 24/71] Try to fix segfaults on OSX --- src/easy.rs | 17 ++++++++++++++++- tests/easy.rs | 18 +++++++++--------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/easy.rs b/src/easy.rs index 602f340552..7dd5282eda 100644 --- a/src/easy.rs +++ b/src/easy.rs @@ -1834,11 +1834,26 @@ impl<'a> Easy<'a> { if s.len() == 0 { return Vec::new(); } + + // Work around https://curl.haxx.se/docs/adv_20130622.html, a bug where + // if the last few characters are a bad escape then curl will have a + // buffer overrun. + let mut iter = s.chars().rev(); + let orig_len = s.len(); + let mut data; + let mut s = s; + if iter.next() == Some('%') || + iter.next() == Some('%') || + iter.next() == Some('%') { + data = s.to_string(); + data.push(0u8 as char); + s = &data[..]; + } unsafe { let mut len = 0; let p = curl_sys::curl_easy_unescape(self.handle, s.as_ptr() as *const _, - s.len() as c_int, + orig_len as c_int, &mut len); assert!(!p.is_null()); let slice = slice::from_raw_parts(p as *const u8, len as usize); diff --git a/tests/easy.rs b/tests/easy.rs index 83e3a392c2..4bf8ae902f 100644 --- a/tests/easy.rs +++ b/tests/easy.rs @@ -492,14 +492,14 @@ HTTP/1.1 200 OK\r\n\ #[test] fn url_encoding() { let mut h = handle(); - // assert_eq!(h.url_encode(b"foo"), "foo"); - // assert_eq!(h.url_encode(b"foo bar"), "foo%20bar"); - // assert_eq!(h.url_encode(b"foo bar\xff"), "foo%20bar%FF"); - // assert_eq!(h.url_encode(b""), ""); - // assert_eq!(h.url_decode("foo"), b"foo"); - // assert_eq!(h.url_decode("foo%20bar"), b"foo bar"); + assert_eq!(h.url_encode(b"foo"), "foo"); + assert_eq!(h.url_encode(b"foo bar"), "foo%20bar"); + assert_eq!(h.url_encode(b"foo bar\xff"), "foo%20bar%FF"); + assert_eq!(h.url_encode(b""), ""); + assert_eq!(h.url_decode("foo"), b"foo"); + assert_eq!(h.url_decode("foo%20bar"), b"foo bar"); assert_eq!(h.url_decode("foo%2"), b"foo%2"); - // assert_eq!(h.url_decode("foo%xx"), b"foo%xx"); - // assert_eq!(h.url_decode("foo%ff"), b"foo\xff"); - // assert_eq!(h.url_decode(""), b""); + assert_eq!(h.url_decode("foo%xx"), b"foo%xx"); + assert_eq!(h.url_decode("foo%ff"), b"foo\xff"); + assert_eq!(h.url_decode(""), b""); } From bddd368a01742145570e0174a385c81e83ad85bf Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 26 Apr 2016 00:22:33 -0700 Subject: [PATCH 25/71] Re-enable tests --- .travis.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5ec99fd646..d7095b6463 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,13 @@ language: rust rust: - stable - # - beta - # - nightly -# matrix: -# include: -# - os: linux -# rust: stable -# env: LIBCURL_NO_PKG_CONFIG=1 + - beta + - nightly +matrix: + include: + - os: linux + rust: stable + env: LIBCURL_NO_PKG_CONFIG=1 sudo: false before_script: - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH @@ -25,5 +25,5 @@ notifications: email: on_success: never os: - # - linux + - linux - osx From d2ded3e76fa92f635cc4e2adc16e6a4dd9b8fae5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 27 Apr 2016 22:54:04 -0700 Subject: [PATCH 26/71] Try to fix Appveyor CI --- curl-sys/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/curl-sys/lib.rs b/curl-sys/lib.rs index 6a8d6311b4..3d50b6b7f5 100644 --- a/curl-sys/lib.rs +++ b/curl-sys/lib.rs @@ -52,13 +52,13 @@ pub struct curl_httppost { pub userp: *mut c_void, } -pub const HTTPPOST_FILENAME: c_long = 1 << 0; -pub const HTTPPOST_READFILE: c_long = 1 << 1; -pub const HTTPPOST_PTRNAME: c_long = 1 << 2; -pub const HTTPPOST_PTRCONTENTS: c_long = 1 << 3; -pub const HTTPPOST_BUFFER: c_long = 1 << 4; -pub const HTTPPOST_PTRBUFFER: c_long = 1 << 5; -pub const HTTPPOST_CALLBACK: c_long = 1 << 6; +// pub const HTTPPOST_FILENAME: c_long = 1 << 0; +// pub const HTTPPOST_READFILE: c_long = 1 << 1; +// pub const HTTPPOST_PTRNAME: c_long = 1 << 2; +// pub const HTTPPOST_PTRCONTENTS: c_long = 1 << 3; +// pub const HTTPPOST_BUFFER: c_long = 1 << 4; +// pub const HTTPPOST_PTRBUFFER: c_long = 1 << 5; +// pub const HTTPPOST_CALLBACK: c_long = 1 << 6; pub type curl_progress_callback = extern fn(*mut c_void, c_double, From a046651479c27f4564417afb95a8054df2cfb840 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 27 Apr 2016 22:56:30 -0700 Subject: [PATCH 27/71] Add submodule init to build script --- curl-sys/build.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/curl-sys/build.rs b/curl-sys/build.rs index 8569f2cd79..6e37cfd7e9 100644 --- a/curl-sys/build.rs +++ b/curl-sys/build.rs @@ -4,7 +4,7 @@ extern crate gcc; use std::env; use std::ffi::OsString; use std::fs; -use std::path::PathBuf; +use std::path::{PathBuf, Path}; use std::process::Command; macro_rules! t { @@ -14,7 +14,6 @@ macro_rules! t { }) } -#[allow(deprecated)] // needed for `connect()`, since Rust 1.1 is supported fn main() { let target = env::var("TARGET").unwrap(); let host = env::var("HOST").unwrap(); @@ -35,6 +34,11 @@ fn main() { pkgconfig ({:?}), compiling it from source...", e), } + if !Path::new("curl/.git").exists() { + let _ = Command::new("git").args(&["submodule", "update", "--init"]) + .status(); + } + println!("cargo:rustc-link-search={}/lib", dst.display()); println!("cargo:rustc-link-lib=static=curl"); println!("cargo:root={}", dst.display()); From 3439cb98f40f134a9638608887383b3cac625570 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 28 Apr 2016 09:21:37 -0700 Subject: [PATCH 28/71] Tweak what CI is run --- .travis.yml | 38 +++++++++++++++++++++++++------------- curl-sys/lib.rs | 2 +- systest/build.rs | 2 +- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index d7095b6463..29191fb9c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,29 +1,41 @@ language: rust -rust: - - stable - - beta - - nightly +rust: stable +env: + - ARCH=x86_64 + - ARCH=i686 +os: + - linux + - osx matrix: include: - os: linux rust: stable - env: LIBCURL_NO_PKG_CONFIG=1 + env: ARCH=x86_64 LIBCURL_NO_PKG_CONFIG=1 + - os: linux + rust: stable + env: ARCH=i686 LIBCURL_NO_PKG_CONFIG=1 + - os: linux + rust: beta + env: ARCH=x86_64 + - os: linux + rust: nightly + env: ARCH=x86_64 sudo: false before_script: - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then OS=unknown-linux-gnu; else OS=apple-darwin; fi + - export TARGET=$ARCH-$OS + - curl https://static.rust-lang.org/rustup.sh | + sh -s -- --add-target=$TARGET --disable-sudo -y --prefix=`rustc --print sysroot` script: - export CARGO_TARGET_DIR=`pwd`/target - - export RUST_TEST_THREADS=1 - curl --version - - cargo test - - cargo run --manifest-path systest/Cargo.toml - - cargo doc --no-deps - - cargo doc --no-deps -p curl-sys + - cargo test --target $TARGET + - cargo run --manifest-path systest/Cargo.toml --target $TARGET + - cargo doc --no-deps --target $TARGET + - cargo doc --no-deps -p curl-sys --target $TARGET after_success: - travis-cargo --only nightly doc-upload notifications: email: on_success: never -os: - - linux - - osx diff --git a/curl-sys/lib.rs b/curl-sys/lib.rs index 3d50b6b7f5..93d263f118 100644 --- a/curl-sys/lib.rs +++ b/curl-sys/lib.rs @@ -20,7 +20,7 @@ pub type CURLINFO = __enum_ty; pub type CURLoption = __enum_ty; pub type CURLcode = __enum_ty; pub type CURLversion = __enum_ty; -pub type curl_off_t = c_long; +pub type curl_off_t = i64; pub enum CURL {} diff --git a/systest/build.rs b/systest/build.rs index 56bc080e83..ee5928e548 100644 --- a/systest/build.rs +++ b/systest/build.rs @@ -5,10 +5,10 @@ use std::env; fn main() { let mut cfg = ctest::TestGenerator::new(); - cfg.header("curl/curl.h"); if let Ok(out) = env::var("DEP_CURL_INCLUDE") { cfg.include(&out); } + cfg.header("curl/curl.h"); cfg.field_name(|s, field| { if s == "curl_fileinfo" { field.replace("strings_", "strings.") From b67a63a4abe446ddb94c000bdfbe678060e8d9f8 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 28 Apr 2016 09:32:19 -0700 Subject: [PATCH 29/71] Install gcc-multilib on travis --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 29191fb9c3..d0d5f30f2e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,3 +39,7 @@ after_success: notifications: email: on_success: never +addons: + apt: + packages: + - g++-multilib From 1e491362ef89da97267ddec572e8458370748439 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 28 Apr 2016 09:55:19 -0700 Subject: [PATCH 30/71] Try to install 32-bit libssl --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index d0d5f30f2e..7441c60b5f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,3 +43,4 @@ addons: apt: packages: - g++-multilib + - libssl-dev:i386 From 596267e1e2163592a4ca7fcadc9dc9d12541bfc8 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 28 Apr 2016 10:09:19 -0700 Subject: [PATCH 31/71] Only install linux apt packages on 32-bit builds --- .travis.yml | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7441c60b5f..d42552e013 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,30 +1,43 @@ language: rust -rust: stable -env: - - ARCH=x86_64 - - ARCH=i686 -os: - - linux - - osx matrix: include: - os: linux rust: stable - env: ARCH=x86_64 LIBCURL_NO_PKG_CONFIG=1 + env: TARGET=x86_64-unknown-linux-gnu - os: linux rust: stable - env: ARCH=i686 LIBCURL_NO_PKG_CONFIG=1 + env: TARGET=i686-unknown-linux-gnu + addons: + apt: + packages: + - gcc-multilib + - libssl-dev:i386 + - os: linux + rust: stable + env: TARGET=x86_64-unknown-linux-gnu LIBCURL_NO_PKG_CONFIG=1 + - os: linux + rust: stable + env: TARGET=i686-unknown-linux-gnu LIBCURL_NO_PKG_CONFIG=1 + addons: + apt: + packages: + - gcc-multilib + - libssl-dev:i386 + - os: osx + rust: stable + env: TARGET=x86_64-apple-darwin + - os: osx + rust: stable + env: TARGET=i686-apple-darwin - os: linux rust: beta - env: ARCH=x86_64 + env: TARGET=x86_64-unknown-linux-gnu - os: linux rust: nightly - env: ARCH=x86_64 + env: TARGET=x86_64-unknown-linux-gnu sudo: false before_script: - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH - - if [ "$TRAVIS_OS_NAME" = "linux" ]; then OS=unknown-linux-gnu; else OS=apple-darwin; fi - - export TARGET=$ARCH-$OS - curl https://static.rust-lang.org/rustup.sh | sh -s -- --add-target=$TARGET --disable-sudo -y --prefix=`rustc --print sysroot` script: @@ -39,8 +52,3 @@ after_success: notifications: email: on_success: never -addons: - apt: - packages: - - g++-multilib - - libssl-dev:i386 From 22c2464a32fd03433852228c535009e0da877ea0 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 28 Apr 2016 10:38:02 -0700 Subject: [PATCH 32/71] Help debug test failures --- tests/server/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/server/mod.rs b/tests/server/mod.rs index 72f5f0ef30..ff90ef6001 100644 --- a/tests/server/mod.rs +++ b/tests/server/mod.rs @@ -39,7 +39,8 @@ fn run(listener: &TcpListener, rx: &Receiver) { let mut actual = String::new(); t!(socket.read_line(&mut actual)); if !expected_headers.remove(&actual[..]) { - panic!("unexpected header: {:?}", actual); + panic!("unexpected header: {:?} (remaining headers {:?})", + actual, expected_headers); } } for header in expected_headers { From 692aa07f35321842437190c9fb08f7872a3f5d10 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 28 Apr 2016 10:43:30 -0700 Subject: [PATCH 33/71] Let's rediscover why `make install` is bad --- curl-sys/build.rs | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/curl-sys/build.rs b/curl-sys/build.rs index 6e37cfd7e9..47e31e6a3b 100644 --- a/curl-sys/build.rs +++ b/curl-sys/build.rs @@ -63,6 +63,14 @@ fn main() { cflags.push(arg); cflags.push(" "); } + + // Can't run ./configure directly on msys2 b/c we're handing in + // Windows-style paths (those starting with C:\), but it chokes on those. + // For that reason we build up a shell script with paths converted to + // posix versions hopefully... + // + // Also apparently the buildbots choke unless we manually set LD, who knows + // why?! cmd.env("CC", compiler.path()) .env("CFLAGS", cflags) .env("LD", &which("ld").unwrap()) @@ -70,6 +78,7 @@ fn main() { .arg(src.join("curl/configure").to_str().unwrap() .replace("C:\\", "/c/") .replace("\\", "/")); + if windows { cmd.arg("--with-winssl"); } else { @@ -113,32 +122,11 @@ fn main() { cmd.arg("--disable-gopher"); cmd.arg("--disable-manual"); - // Can't run ./configure directly on msys2 b/c we're handing in - // Windows-style paths (those starting with C:\), but it chokes on those. - // For that reason we build up a shell script with paths converted to - // posix versions hopefully... - // - // Also apparently the buildbots choke unless we manually set LD, who knows - // why?! run(&mut cmd); run(Command::new(make()) .arg(&format!("-j{}", env::var("NUM_JOBS").unwrap())) + .arg("install") .current_dir(&dst.join("build"))); - - // Don't run `make install` because apparently it's a little buggy on mingw - // for windows. - let _ = fs::create_dir_all(&dst.join("lib/pkgconfig")); - - // Which one does windows generate? Who knows! - let p1 = dst.join("build/lib/.libs/libcurl.a"); - let p2 = dst.join("build/lib/.libs/libcurl.lib"); - if fs::metadata(&p1).is_ok() { - t!(fs::copy(&p1, &dst.join("lib/libcurl.a"))); - } else { - t!(fs::copy(&p2, &dst.join("lib/libcurl.a"))); - } - t!(fs::copy(&dst.join("build/libcurl.pc"), - &dst.join("lib/pkgconfig/libcurl.pc"))); } fn run(cmd: &mut Command) { From 2beeb9bde20b5af2aea2cc4bad4188e0497f3f1a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 28 Apr 2016 23:27:22 -0700 Subject: [PATCH 34/71] Attempt to appease MSVC --- curl-sys/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/curl-sys/lib.rs b/curl-sys/lib.rs index 93d263f118..61d32f34f4 100644 --- a/curl-sys/lib.rs +++ b/curl-sys/lib.rs @@ -324,14 +324,14 @@ pub const CURLAUTH_NTLM_WB: c_ulong = 1 << 5; pub const CURLAUTH_ANY: c_ulong = !CURLAUTH_DIGEST_IE; pub const CURLAUTH_ANYSAFE: c_ulong = !(CURLAUTH_BASIC | CURLAUTH_DIGEST_IE); -pub const CURLSSH_AUTH_ANY: c_ulong = !0; -pub const CURLSSH_AUTH_NONE: c_ulong = 0; -pub const CURLSSH_AUTH_PUBLICKEY: c_ulong = 1 << 0; -pub const CURLSSH_AUTH_PASSWORD: c_ulong = 1 << 1; -pub const CURLSSH_AUTH_HOST: c_ulong = 1 << 2; -pub const CURLSSH_AUTH_KEYBOARD: c_ulong = 1 << 3; +// pub const CURLSSH_AUTH_ANY: c_ulong = !0; +// pub const CURLSSH_AUTH_NONE: c_ulong = 0; +// pub const CURLSSH_AUTH_PUBLICKEY: c_ulong = 1 << 0; +// pub const CURLSSH_AUTH_PASSWORD: c_ulong = 1 << 1; +// pub const CURLSSH_AUTH_HOST: c_ulong = 1 << 2; +// pub const CURLSSH_AUTH_KEYBOARD: c_ulong = 1 << 3; // pub const CURLSSH_AUTH_AGENT: c_ulong = 1 << 4; -pub const CURLSSH_AUTH_DEFAULT: c_ulong = CURLSSH_AUTH_ANY; +// pub const CURLSSH_AUTH_DEFAULT: c_ulong = CURLSSH_AUTH_ANY; pub const CURLGSSAPI_DELEGATION_NONE: c_ulong = 0; pub const CURLGSSAPI_DELEGATION_POLICY_FLAG: c_ulong = 1 << 0; From 62c34d154510fff8fd6ddff5fa831a8221113cca Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 28 Apr 2016 23:35:11 -0700 Subject: [PATCH 35/71] Print link-paths to curl with pkg-config --- curl-sys/build.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/curl-sys/build.rs b/curl-sys/build.rs index 47e31e6a3b..c12aae6083 100644 --- a/curl-sys/build.rs +++ b/curl-sys/build.rs @@ -29,7 +29,12 @@ fn main() { // Next, fall back and try to use pkg-config if its available. match pkg_config::find_library("libcurl") { - Ok(..) => return, + Ok(lib) => { + for path in lib.include_paths.iter() { + println!("cargo:include={}", path.display()); + } + return + } Err(e) => println!("Couldn't find libcurl from \ pkgconfig ({:?}), compiling it from source...", e), } From 58e020bdb6ee0a89b0b5c18542d4c0cdac4fa461 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 28 Apr 2016 23:36:29 -0700 Subject: [PATCH 36/71] Don't run pkg-config on Windows --- curl-sys/build.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/curl-sys/build.rs b/curl-sys/build.rs index c12aae6083..706eb298dc 100644 --- a/curl-sys/build.rs +++ b/curl-sys/build.rs @@ -28,15 +28,17 @@ fn main() { } // Next, fall back and try to use pkg-config if its available. - match pkg_config::find_library("libcurl") { - Ok(lib) => { - for path in lib.include_paths.iter() { - println!("cargo:include={}", path.display()); + if !target.contains("windows") { + match pkg_config::find_library("libcurl") { + Ok(lib) => { + for path in lib.include_paths.iter() { + println!("cargo:include={}", path.display()); + } + return } - return + Err(e) => println!("Couldn't find libcurl from \ + pkgconfig ({:?}), compiling it from source...", e), } - Err(e) => println!("Couldn't find libcurl from \ - pkgconfig ({:?}), compiling it from source...", e), } if !Path::new("curl/.git").exists() { From 3ce0e54d497f1e2045ddc97ef809553a6c7ab92f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 28 Apr 2016 23:38:34 -0700 Subject: [PATCH 37/71] Bump default test timeout --- tests/easy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/easy.rs b/tests/easy.rs index 4bf8ae902f..fd6bf80496 100644 --- a/tests/easy.rs +++ b/tests/easy.rs @@ -18,7 +18,7 @@ mod server; fn handle<'a>() -> Easy<'a> { let mut e = Easy::new(); - t!(e.timeout(Duration::new(1, 0))); + t!(e.timeout(Duration::new(20, 0))); return e } From 4d8488426aad9055e5a79087e31eb26804553d23 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 9 May 2016 23:01:08 -0700 Subject: [PATCH 38/71] Add a few getters [ci skip] --- src/easy.rs | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/src/easy.rs b/src/easy.rs index 7dd5282eda..b8aa66d9b2 100644 --- a/src/easy.rs +++ b/src/easy.rs @@ -1791,6 +1791,83 @@ impl<'a> Easy<'a> { enable as c_long) } + // ========================================================================= + // getters + + /// Get the last used URL + /// + /// In cases when you've asked libcurl to follow redirects, it may + /// not be the same value you set with `url`. + /// + /// This methods corresponds to the `CURLINFO_EFFECTIVE_URL` option. + /// + /// Returns `Ok(None)` if no effective url is listed or `Err` if an error + /// happens or the underlying bytes aren't valid utf-8. + pub fn effective_url(&self) -> Result, Error> { + match self.effective_url_bytes() { + Ok(None) => Ok(None), + Err(e) => Err(e), + Ok(Some(bytes)) => { + match str::from_utf8(bytes) { + Ok(s) => Ok(Some(s)), + Err(_) => Err(Error::new(curl_sys::CURLE_CONV_FAILED)), + } + } + } + } + + /// Get the last used URL, in bytes + /// + /// In cases when you've asked libcurl to follow redirects, it may + /// not be the same value you set with `url`. + /// + /// This methods corresponds to the `CURLINFO_EFFECTIVE_URL` option. + /// + /// Returns `Ok(None)` if no effective url is listed or `Err` if an error + /// happens or the underlying bytes aren't valid utf-8. + pub fn effective_url_bytes(&self) -> Result, Error> { + self.getopt_bytes(curl_sys::CURLINFO_EFFECTIVE_URL) + .map(|c| c.map(|c| c.to_bytes())) + } + + /// Get the last response code + /// + /// The stored value will be zero if no server response code has been + /// received. Note that a proxy's CONNECT response should be read with + /// `http_connectcode` and not this. + /// + /// Corresponds to `CURLINFO_RESPONSE_CODE` and returns an error if this + /// option is not supported. + pub fn response_code(&self) -> Result { + self.getopt_long(curl_sys::CURLINFO_RESPONSE_CODE).map(|c| c as u32) + } + + /// Get the CONNECT response code + /// + /// Returns the last received HTTP proxy response code to a CONNECT request. + /// The returned value will be zero if no such response code was available. + /// + /// Corresponds to `CURLINFO_HTTP_CONNECTCODE` and returns an error if this + /// option is not supported. + pub fn http_connectcode(&self) -> Result { + self.getopt_long(curl_sys::CURLINFO_HTTP_CONNECTCODE).map(|c| c as u32) + } + + /// Get the remote time of the retrieved document + /// + /// Pass a pointer to a long to receive the remote time of the retrieved + /// document (in number of seconds since 1 jan 1970 in the GMT/UTC time + /// zone). If you get -1, it can be because of many reasons (it might be + /// unknown, the server might hide it or the server doesn't support the + /// command that tells document time etc) and the time of the document is + /// unknown. + /// + /// Note that you must tell the server to collect this information before + /// the transfer is made, by using the CURLOPT_FILETIME option to + /// curl_easy_setopt or you will unconditionally get a -1 back. + pub fn filetime(&self) -> Result { + } + // ========================================================================= // Other methods @@ -1980,6 +2057,27 @@ impl<'a> Easy<'a> { cvt(curl_sys::curl_easy_setopt(self.handle, opt, val)) } } + + fn getopt_bytes(&self, opt: curl_sys::CURLINFO) + -> Result, Error> { + unsafe { + let mut p = 0 as *const c_char; + try!(cvt(curl_sys::curl_easy_getinfo(self.handle, opt, &mut p))); + if p.is_null() { + Ok(None) + } else { + Ok(Some(CStr::from_ptr(p))) + } + } + } + + fn getopt_long(&self, opt: curl_sys::CURLINFO) -> Result { + unsafe { + let mut p = 0; + try!(cvt(curl_sys::curl_easy_getinfo(self.handle, opt, &mut p))); + Ok(p) + } + } } impl<'a> Drop for Easy<'a> { From 25d8a09edbda792ef36029884ebf4fea625c727a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 9 May 2016 23:51:43 -0700 Subject: [PATCH 39/71] Flesh out more of the accessors --- src/easy.rs | 243 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 220 insertions(+), 23 deletions(-) diff --git a/src/easy.rs b/src/easy.rs index b8aa66d9b2..acd460eba9 100644 --- a/src/easy.rs +++ b/src/easy.rs @@ -142,6 +142,12 @@ pub struct List { raw: *mut curl_sys::curl_slist, } +/// An iterator over `List` +pub struct Iter<'a> { + _me: &'a List, + cur: *mut curl_sys::curl_slist, +} + unsafe impl Send for List {} unsafe impl Sync for List {} @@ -657,7 +663,7 @@ impl<'a> Easy<'a> { /// /// By default this option is 0 (any port) and corresponds to /// `CURLOPT_LOCALPORT`. - pub fn local_port(&mut self, port: u16) -> Result<(), Error> { + pub fn set_local_port(&mut self, port: u16) -> Result<(), Error> { self.setopt_long(curl_sys::CURLOPT_LOCALPORT, port as c_long) } @@ -1184,6 +1190,19 @@ impl<'a> Easy<'a> { self.setopt_str(curl_sys::CURLOPT_CUSTOMREQUEST, &request) } + /// Get the modification time of the remote resource + /// + /// If true, libcurl will attempt to get the modification time of the + /// remote document in this operation. This requires that the remote server + /// sends the time or replies to a time querying command. The `filetime` + /// function can be used after a transfer to extract the received time (if + /// any). + /// + /// By default this option is `false` and corresponds to `CURLOPT_FILETIME` + pub fn fetch_filetime(&mut self, fetch: bool) -> Result<(), Error> { + self.setopt_long(curl_sys::CURLOPT_FILETIME, fetch as c_long) + } + /// Indicate whether to download the request without getting the body /// /// This is useful, for example, for doing a HEAD request. @@ -1804,16 +1823,7 @@ impl<'a> Easy<'a> { /// Returns `Ok(None)` if no effective url is listed or `Err` if an error /// happens or the underlying bytes aren't valid utf-8. pub fn effective_url(&self) -> Result, Error> { - match self.effective_url_bytes() { - Ok(None) => Ok(None), - Err(e) => Err(e), - Ok(Some(bytes)) => { - match str::from_utf8(bytes) { - Ok(s) => Ok(Some(s)), - Err(_) => Err(Error::new(curl_sys::CURLE_CONV_FAILED)), - } - } - } + self.getopt_str(curl_sys::CURLINFO_EFFECTIVE_URL) } /// Get the last used URL, in bytes @@ -1827,7 +1837,6 @@ impl<'a> Easy<'a> { /// happens or the underlying bytes aren't valid utf-8. pub fn effective_url_bytes(&self) -> Result, Error> { self.getopt_bytes(curl_sys::CURLINFO_EFFECTIVE_URL) - .map(|c| c.map(|c| c.to_bytes())) } /// Get the last response code @@ -1855,17 +1864,170 @@ impl<'a> Easy<'a> { /// Get the remote time of the retrieved document /// - /// Pass a pointer to a long to receive the remote time of the retrieved - /// document (in number of seconds since 1 jan 1970 in the GMT/UTC time - /// zone). If you get -1, it can be because of many reasons (it might be - /// unknown, the server might hide it or the server doesn't support the - /// command that tells document time etc) and the time of the document is - /// unknown. + /// Returns the remote time of the retrieved document (in number of seconds + /// since 1 Jan 1970 in the GMT/UTC time zone). If you get `None`, it can be + /// because of many reasons (it might be unknown, the server might hide it + /// or the server doesn't support the command that tells document time etc) + /// and the time of the document is unknown. /// /// Note that you must tell the server to collect this information before - /// the transfer is made, by using the CURLOPT_FILETIME option to - /// curl_easy_setopt or you will unconditionally get a -1 back. - pub fn filetime(&self) -> Result { + /// the transfer is made, by using the `filetime` method to + /// or you will unconditionally get a `None` back. + /// + /// This corresponds to `CURLINFO_FILETIME` and may return an error if the + /// option is not supported + pub fn filetime(&self) -> Result, Error> { + self.getopt_long(curl_sys::CURLINFO_FILETIME).map(|r| { + if r == -1 { + None + } else { + Some(r as i64) + } + }) + } + + /// Get the number of redirects + /// + /// Corresponds to `CURLINFO_REDIRECT_COUNT` and may return an error if the + /// option isn't supported. + pub fn redirect_count(&self) -> Result { + self.getopt_long(curl_sys::CURLINFO_REDIRECT_COUNT).map(|c| c as u32) + } + + /// Get the URL a redirect would go to + /// + /// Returns the URL a redirect would take you to if you would enable + /// `follow_location`. This can come very handy if you think using the + /// built-in libcurl redirect logic isn't good enough for you but you would + /// still prefer to avoid implementing all the magic of figuring out the new + /// URL. + /// + /// Corresponds to `CURLINFO_REDIRECT_URL` and may return an error if the + /// url isn't valid utf-8 or an error happens. + pub fn redirect_url(&self) -> Result, Error> { + self.getopt_str(curl_sys::CURLINFO_REDIRECT_URL) + } + + /// Get the URL a redirect would go to, in bytes + /// + /// Returns the URL a redirect would take you to if you would enable + /// `follow_location`. This can come very handy if you think using the + /// built-in libcurl redirect logic isn't good enough for you but you would + /// still prefer to avoid implementing all the magic of figuring out the new + /// URL. + /// + /// Corresponds to `CURLINFO_REDIRECT_URL` and may return an error. + pub fn redirect_url_bytes(&self) -> Result, Error> { + self.getopt_bytes(curl_sys::CURLINFO_REDIRECT_URL) + } + + /// Get size of retrieved headers + /// + /// Corresponds to `CURLINFO_HEADER_SIZE` and may return an error if the + /// option isn't supported. + pub fn header_size(&self) -> Result { + self.getopt_long(curl_sys::CURLINFO_HEADER_SIZE).map(|c| c as u64) + } + + /// Get size of sent request. + /// + /// Corresponds to `CURLINFO_REQUEST_SIZE` and may return an error if the + /// option isn't supported. + pub fn request_size(&self) -> Result { + self.getopt_long(curl_sys::CURLINFO_REQUEST_SIZE).map(|c| c as u64) + } + + /// Get Content-Type + /// + /// Returns the content-type of the downloaded object. This is the value + /// read from the Content-Type: field. If you get `None`, it means that the + /// server didn't send a valid Content-Type header or that the protocol + /// used doesn't support this. + /// + /// Corresponds to `CURLINFO_CONTENT_TYPE` and may return an error if the + /// option isn't supported. + pub fn content_type(&self) -> Result, Error> { + self.getopt_str(curl_sys::CURLINFO_CONTENT_TYPE) + } + + /// Get Content-Type, in bytes + /// + /// Returns the content-type of the downloaded object. This is the value + /// read from the Content-Type: field. If you get `None`, it means that the + /// server didn't send a valid Content-Type header or that the protocol + /// used doesn't support this. + /// + /// Corresponds to `CURLINFO_CONTENT_TYPE` and may return an error if the + /// option isn't supported. + pub fn content_type_bytes(&self) -> Result, Error> { + self.getopt_bytes(curl_sys::CURLINFO_CONTENT_TYPE) + } + + /// Get errno number from last connect failure. + /// + /// Note that the value is only set on failure, it is not reset upon a + /// successful operation. The number is OS and system specific. + /// + /// Corresponds to `CURLINFO_OS_ERRNO` and may return an error if the + /// option isn't supported. + pub fn os_errno(&self) -> Result { + self.getopt_long(curl_sys::CURLINFO_OS_ERRNO).map(|c| c as i32) + } + + /// Get IP address of last connection. + /// + /// Returns a string holding the IP address of the most recent connection + /// done with this curl handle. This string may be IPv6 when that is + /// enabled. + /// + /// Corresponds to `CURLINFO_PRIMARY_IP` and may return an error if the + /// option isn't supported. + pub fn primary_ip(&self) -> Result, Error> { + self.getopt_str(curl_sys::CURLINFO_PRIMARY_IP) + } + + /// Get the latest destination port number + /// + /// Corresponds to `CURLINFO_PRIMARY_PORT` and may return an error if the + /// option isn't supported. + pub fn primary_port(&self) -> Result { + self.getopt_long(curl_sys::CURLINFO_PRIMARY_PORT).map(|c| c as u16) + } + + /// Get local IP address of last connection + /// + /// Returns a string holding the IP address of the local end of most recent + /// connection done with this curl handle. This string may be IPv6 when that + /// is enabled. + /// + /// Corresponds to `CURLINFO_LOCAL_IP` and may return an error if the + /// option isn't supported. + pub fn local_ip(&self) -> Result, Error> { + self.getopt_str(curl_sys::CURLINFO_LOCAL_IP) + } + + /// Get the latest local port number + /// + /// Corresponds to `CURLINFO_LOCAL_PORT` and may return an error if the + /// option isn't supported. + pub fn local_port(&self) -> Result { + self.getopt_long(curl_sys::CURLINFO_LOCAL_PORT).map(|c| c as u16) + } + + /// Get all known cookies + /// + /// Returns a linked-list of all cookies cURL knows (expired ones, too). + /// + /// Corresponds to the `CURLINFO_COOKIELIST` option and may return an error + /// if the option isn't supported. + pub fn cookies(&self) -> Result { + unsafe { + let mut list = 0 as *mut _; + try!(cvt(curl_sys::curl_easy_getinfo(self.handle, + curl_sys::CURLINFO_COOKIELIST, + &mut list))); + Ok(List { raw: list }) + } } // ========================================================================= @@ -2059,14 +2221,28 @@ impl<'a> Easy<'a> { } fn getopt_bytes(&self, opt: curl_sys::CURLINFO) - -> Result, Error> { + -> Result, Error> { unsafe { let mut p = 0 as *const c_char; try!(cvt(curl_sys::curl_easy_getinfo(self.handle, opt, &mut p))); if p.is_null() { Ok(None) } else { - Ok(Some(CStr::from_ptr(p))) + Ok(Some(CStr::from_ptr(p).to_bytes())) + } + } + } + + fn getopt_str(&self, opt: curl_sys::CURLINFO) + -> Result, Error> { + match self.getopt_bytes(opt) { + Ok(None) => Ok(None), + Err(e) => Err(e), + Ok(Some(bytes)) => { + match str::from_utf8(bytes) { + Ok(s) => Ok(Some(s)), + Err(_) => Err(Error::new(curl_sys::CURLE_CONV_FAILED)), + } } } } @@ -2104,6 +2280,11 @@ impl List { Ok(()) } } + + /// Returns an iterator over the nodes in this list. + pub fn iter(&self) -> Iter { + Iter { _me: self, cur: self.raw } + } } impl Drop for List { @@ -2113,3 +2294,19 @@ impl Drop for List { } } } + +impl<'a> Iterator for Iter<'a> { + type Item = &'a [u8]; + + fn next(&mut self) -> Option<&'a [u8]> { + if self.cur.is_null() { + return None + } + + unsafe { + let ret = Some(CStr::from_ptr((*self.cur).data).to_bytes()); + self.cur = (*self.cur).next; + return ret + } + } +} From d58e84d3b1ffbd833d716e38fb82905c90233b35 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 10 May 2016 00:06:41 -0700 Subject: [PATCH 40/71] Add some simple tests --- tests/easy.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/easy.rs b/tests/easy.rs index fd6bf80496..aa202ff9db 100644 --- a/tests/easy.rs +++ b/tests/easy.rs @@ -503,3 +503,34 @@ fn url_encoding() { assert_eq!(h.url_decode("foo%ff"), b"foo\xff"); assert_eq!(h.url_decode(""), b""); } + +#[test] +fn getters() { + let s = Server::new(); + s.receive("\ +GET / HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +\r\n"); + s.send("\ +HTTP/1.1 200 OK\r\n\ +\r\n"); + + let mut h = handle(); + t!(h.url(&s.url("/"))); + t!(h.cookie_file("/dev/null")); + t!(h.perform()); + assert_eq!(t!(h.response_code()), 200); + assert_eq!(t!(h.redirect_count()), 0); + assert_eq!(t!(h.redirect_url()), None); + assert_eq!(t!(h.content_type()), None); + + let addr = format!("http://{}/", s.addr()); + assert_eq!(t!(h.effective_url()), Some(&addr[..])); + + // TODO: test this + // let cookies = t!(h.cookies()).iter() + // .map(|s| s.to_vec()) + // .collect::>(); + // assert_eq!(cookies.len(), 1); +} From fa7f82c6b2db678975e7b2b17124e3ae8082e0ec Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 10 May 2016 08:37:29 -0700 Subject: [PATCH 41/71] Display error codes as well --- src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/error.rs b/src/error.rs index 3cad8c1bbd..1b4b7db63f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -283,7 +283,7 @@ impl Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - error::Error::description(self).fmt(f) + write!(f, "[{}] {}", self.code(), error::Error::description(self)) } } From 30cdee2c4806b75f8d100bc19a2f8c8919627ed9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 10 May 2016 09:47:00 -0700 Subject: [PATCH 42/71] Upgrade curl submodule --- curl-sys/curl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/curl-sys/curl b/curl-sys/curl index 9a300aa13e..7776801ddf 160000 --- a/curl-sys/curl +++ b/curl-sys/curl @@ -1 +1 @@ -Subproject commit 9a300aa13e5035a795396e429aa861229424c9dc +Subproject commit 7776801ddf93c950634b58888ea0150cc967a5b9 From a10148d31aa36922862a7781289c8e4a89c27554 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 10 May 2016 10:31:27 -0700 Subject: [PATCH 43/71] Disable more protocols --- curl-sys/build.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/curl-sys/build.rs b/curl-sys/build.rs index 706eb298dc..f006b7c3eb 100644 --- a/curl-sys/build.rs +++ b/curl-sys/build.rs @@ -81,6 +81,7 @@ fn main() { cmd.env("CC", compiler.path()) .env("CFLAGS", cflags) .env("LD", &which("ld").unwrap()) + .env("VERBOSE", "1") .current_dir(&dst.join("build")) .arg(src.join("curl/configure").to_str().unwrap() .replace("C:\\", "/c/") @@ -128,6 +129,8 @@ fn main() { cmd.arg("--disable-smtp"); cmd.arg("--disable-gopher"); cmd.arg("--disable-manual"); + cmd.arg("--disable-smb"); + cmd.arg("--disable-sspi"); run(&mut cmd); run(Command::new(make()) From 37b8f4770938437a3370841dd7ed8bec07418dd4 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 10 May 2016 10:42:51 -0700 Subject: [PATCH 44/71] Don't expose curl_httppost bindings for now --- curl-sys/lib.rs | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/curl-sys/lib.rs b/curl-sys/lib.rs index 61d32f34f4..4110880259 100644 --- a/curl-sys/lib.rs +++ b/curl-sys/lib.rs @@ -35,22 +35,22 @@ pub type curl_socket_t = u64; #[cfg(windows)] pub const CURL_SOCKET_BAD: curl_socket_t = !0; -#[repr(C)] -pub struct curl_httppost { - pub next: *mut curl_httppost, - pub name: *mut c_char, - pub namelength: c_long, - pub contents: *mut c_char, - pub contentslength: c_long, - pub buffer: *mut c_char, - pub bufferlength: c_long, - pub contenttype: *mut c_char, - pub contentheader: *mut curl_slist, - pub more: *mut curl_httppost, - pub flags: c_long, - pub showfilename: *mut c_char, - pub userp: *mut c_void, -} +// #[repr(C)] +// pub struct curl_httppost { +// pub next: *mut curl_httppost, +// pub name: *mut c_char, +// pub namelength: c_long, +// pub contents: *mut c_char, +// pub contentslength: c_long, +// pub buffer: *mut c_char, +// pub bufferlength: c_long, +// pub contenttype: *mut c_char, +// pub contentheader: *mut curl_slist, +// pub more: *mut curl_httppost, +// pub flags: c_long, +// pub showfilename: *mut c_char, +// pub userp: *mut c_void, +// } // pub const HTTPPOST_FILENAME: c_long = 1 << 0; // pub const HTTPPOST_READFILE: c_long = 1 << 1; @@ -901,13 +901,13 @@ pub const CURLMOPT_TIMERDATA: CURLMoption = CURLOPTTYPE_OBJECTPOINT + 5; // pub const CURLMOPT_MAX_TOTAL_CONNECTIONS: CURLMoption = CURLOPTTYPE_LONG + 13; extern { - pub fn curl_formadd(httppost: *mut *mut curl_httppost, - last_post: *mut *mut curl_httppost, - ...) -> CURLFORMcode; - pub fn curl_formget(form: *mut curl_httppost, - arg: *mut c_void, - append: curl_formget_callback) -> c_int; - pub fn curl_formfree(form: *mut curl_httppost); + // pub fn curl_formadd(httppost: *mut *mut curl_httppost, + // last_post: *mut *mut curl_httppost, + // ...) -> CURLFORMcode; + // pub fn curl_formget(form: *mut curl_httppost, + // arg: *mut c_void, + // append: curl_formget_callback) -> c_int; + // pub fn curl_formfree(form: *mut curl_httppost); pub fn curl_version() -> *mut c_char; From 9fa64edce1ed7f20a26c0890a8bfa4cc43bb18f3 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 10 May 2016 11:03:55 -0700 Subject: [PATCH 45/71] Fix test across multiple curl versions --- tests/easy.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/easy.rs b/tests/easy.rs index aa202ff9db..50ed78cc0f 100644 --- a/tests/easy.rs +++ b/tests/easy.rs @@ -210,9 +210,13 @@ Proxy-Connection: Keep-Alive\r\n\ HTTP/1.1 200 OK\r\n\ \r\n"); + let mut header = List::new(); + t!(header.append("Proxy-Connection: Keep-Alive")); + let mut h = handle(); t!(h.url("http://example.com/")); t!(h.proxy(&s.url("/"))); + t!(h.http_headers(&header)); t!(h.perform()); } From b90d77b1ea91a636b5d8cc4ac57131daa3d3e9c5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 10 May 2016 11:17:52 -0700 Subject: [PATCH 46/71] Try to fix more MinGW weirdness --- curl-sys/build.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/curl-sys/build.rs b/curl-sys/build.rs index f006b7c3eb..cfff02adde 100644 --- a/curl-sys/build.rs +++ b/curl-sys/build.rs @@ -83,9 +83,7 @@ fn main() { .env("LD", &which("ld").unwrap()) .env("VERBOSE", "1") .current_dir(&dst.join("build")) - .arg(src.join("curl/configure").to_str().unwrap() - .replace("C:\\", "/c/") - .replace("\\", "/")); + .arg(msys_compatible(&src.join("curl/configure"))); if windows { cmd.arg("--with-winssl"); @@ -106,7 +104,7 @@ fn main() { cmd.arg("--enable-static=yes"); cmd.arg("--enable-shared=no"); cmd.arg("--enable-optimize"); - cmd.arg(format!("--prefix={}", dst.display())); + cmd.arg(format!("--prefix={}", msys_compatible(&dst))); if target != host { cmd.arg(format!("--host={}", host)); @@ -156,6 +154,15 @@ fn which(cmd: &str) -> Option { }) } +fn msys_compatible(path: &Path) -> String { + let path = path.to_str().unwrap(); + if !cfg!(windows) { + return path.to_string() + } + path.replace("C:\\", "/c/") + .replace("\\", "/") +} + fn build_msvc(target: &str) { let cmd = gcc::windows_registry::find(target, "nmake.exe"); let mut cmd = cmd.unwrap_or(Command::new("nmake.exe")); From 1b2677f08b441e50992837ce50db3ffffe38539b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 10 May 2016 11:28:52 -0700 Subject: [PATCH 47/71] Clean out old and unused files --- foo.rs | 1 - src/ffi/consts.rs | 1 - src/ffi/easy.rs | 246 ---------------------- src/ffi/err.rs | 138 ------------ src/ffi/info.rs | 9 - src/ffi/list.rs | 51 ----- src/ffi/mod.rs | 10 - src/ffi/opt.rs | 283 ------------------------- src/http/body.rs | 53 ----- src/http/handle.rs | 449 ---------------------------------------- src/http/header.rs | 224 -------------------- src/http/mod.rs | 12 -- src/http/response.rs | 64 ------ test/server.rs | 260 ----------------------- test/test.rs | 38 ---- test/test_delete.rs | 52 ----- test/test_get.rs | 114 ---------- test/test_head.rs | 26 --- test/test_keep_alive.rs | 68 ------ test/test_patch.rs | 53 ----- test/test_post.rs | 107 ---------- test/test_proxy.rs | 30 --- test/test_put.rs | 53 ----- 23 files changed, 2342 deletions(-) delete mode 100644 foo.rs delete mode 100644 src/ffi/consts.rs delete mode 100644 src/ffi/easy.rs delete mode 100644 src/ffi/err.rs delete mode 100644 src/ffi/info.rs delete mode 100644 src/ffi/list.rs delete mode 100644 src/ffi/mod.rs delete mode 100644 src/ffi/opt.rs delete mode 100644 src/http/body.rs delete mode 100644 src/http/handle.rs delete mode 100644 src/http/header.rs delete mode 100644 src/http/mod.rs delete mode 100644 src/http/response.rs delete mode 100644 test/server.rs delete mode 100644 test/test.rs delete mode 100644 test/test_delete.rs delete mode 100644 test/test_get.rs delete mode 100644 test/test_head.rs delete mode 100644 test/test_keep_alive.rs delete mode 100644 test/test_patch.rs delete mode 100644 test/test_post.rs delete mode 100644 test/test_proxy.rs delete mode 100644 test/test_put.rs diff --git a/foo.rs b/foo.rs deleted file mode 100644 index 2fcc7b3f8f..0000000000 --- a/foo.rs +++ /dev/null @@ -1 +0,0 @@ -fn m diff --git a/src/ffi/consts.rs b/src/ffi/consts.rs deleted file mode 100644 index 339ffa08b8..0000000000 --- a/src/ffi/consts.rs +++ /dev/null @@ -1 +0,0 @@ -pub use curl_ffi::CURL_READFUNC_ABORT; diff --git a/src/ffi/easy.rs b/src/ffi/easy.rs deleted file mode 100644 index 6fe27d8cf5..0000000000 --- a/src/ffi/easy.rs +++ /dev/null @@ -1,246 +0,0 @@ -#![allow(dead_code)] - -use std::sync::{Once, ONCE_INIT}; -use std::mem; -use std::collections::HashMap; -use std::slice; -use libc::{self, c_int, c_long, c_double, size_t}; -use super::{consts, err, info, opt}; -use http::body::Body; -use http::{header, Response}; - -use curl_ffi as ffi; - -pub type ProgressCb<'a> = FnMut(usize, usize, usize, usize) + 'a; - -pub struct Easy { - curl: *mut ffi::CURL -} - -impl Easy { - pub fn new() -> Easy { - // Ensure that curl is globally initialized - global_init(); - - let handle = unsafe { - let p = ffi::curl_easy_init(); - ffi::curl_easy_setopt(p, opt::NOPROGRESS, 0); - p - }; - - Easy { curl: handle } - } - - #[inline] - pub fn setopt(&mut self, option: opt::Opt, val: T) -> Result<(), err::ErrCode> { - // TODO: Prevent setting callback related options - let mut res = err::ErrCode(err::OK); - - unsafe { - val.with_c_repr(|repr| { - res = err::ErrCode(ffi::curl_easy_setopt(self.curl, option, repr)); - }) - } - - if res.is_success() { Ok(()) } else { Err(res) } - } - - pub fn perform(&mut self, - body: Option<&mut Body>, - progress: Option>) - -> Result { - let mut builder = ResponseBuilder::new(); - - unsafe { - let resp_p: usize = mem::transmute(&builder); - let body_p: usize = match body { - Some(b) => mem::transmute(b), - None => 0 - }; - - let progress_p: usize = match progress.as_ref() { - Some(cb) => mem::transmute(cb), - None => 0 - }; - - // Set callback options - // - // Use explicit `as` casts to work around rust-lang/rust#32201 - ffi::curl_easy_setopt(self.curl, opt::READFUNCTION, - curl_read_fn as extern fn(_, _, _, _) -> _); - ffi::curl_easy_setopt(self.curl, opt::READDATA, body_p); - - ffi::curl_easy_setopt(self.curl, opt::WRITEFUNCTION, - curl_write_fn as extern fn(_, _, _, _) -> _); - ffi::curl_easy_setopt(self.curl, opt::WRITEDATA, resp_p); - - ffi::curl_easy_setopt(self.curl, opt::HEADERFUNCTION, - curl_header_fn as extern fn(_, _, _, _) -> _); - ffi::curl_easy_setopt(self.curl, opt::HEADERDATA, resp_p); - - ffi::curl_easy_setopt(self.curl, opt::PROGRESSFUNCTION, - curl_progress_fn as extern fn(_, _, _, _, _) -> _); - ffi::curl_easy_setopt(self.curl, opt::PROGRESSDATA, progress_p); - } - - let err = err::ErrCode(unsafe { ffi::curl_easy_perform(self.curl) }); - - // If the request failed, abort here - if !err.is_success() { - return Err(err); - } - - // Try to get the response code - builder.code = try!(self.get_response_code()); - - Ok(builder.build()) - } - - pub fn get_response_code(&self) -> Result { - Ok(try!(self.get_info_long(info::RESPONSE_CODE)) as u32) - } - - pub fn get_total_time(&self) -> Result { - Ok(try!(self.get_info_long(info::TOTAL_TIME)) as usize) - } - - fn get_info_long(&self, key: info::Key) -> Result { - let v: c_long = 0; - let res = err::ErrCode(unsafe { - ffi::curl_easy_getinfo(self.curl, key, &v) - }); - - if !res.is_success() { - return Err(res); - } - - Ok(v) - } -} - -#[inline] -fn global_init() { - // Schedule curl to be cleaned up after we're done with this whole process - static INIT: Once = ONCE_INIT; - INIT.call_once(|| unsafe { - assert_eq!(libc::atexit(cleanup), 0); - }); - - extern fn cleanup() { - unsafe { ffi::curl_global_cleanup() } - } -} - -impl Drop for Easy { - fn drop(&mut self) { - unsafe { ffi::curl_easy_cleanup(self.curl) } - } -} - -/* - * - * TODO: Move this into handle - * - */ - -struct ResponseBuilder { - code: u32, - hdrs: HashMap>, - body: Vec -} - -impl ResponseBuilder { - fn new() -> ResponseBuilder { - ResponseBuilder { - code: 0, - hdrs: HashMap::new(), - body: Vec::new() - } - } - - fn add_header(&mut self, name: &str, val: &str) { - // TODO: Reduce allocations - use std::ascii::AsciiExt; - let name = name.to_ascii_lowercase(); - - let inserted = match self.hdrs.get_mut(&name) { - Some(vals) => { - vals.push(val.to_string()); - true - } - None => false - }; - - if !inserted { - self.hdrs.insert(name, vec!(val.to_string())); - } - } - - fn build(self) -> Response { - let ResponseBuilder { code, hdrs, body } = self; - Response::new(code, hdrs, body) - } -} - -/* - * - * ===== Callbacks ===== - */ - -extern fn curl_read_fn(p: *mut u8, size: size_t, nmemb: size_t, - body: *mut Body) -> size_t { - if body.is_null() { - return 0; - } - - let dst = unsafe { slice::from_raw_parts_mut(p, (size * nmemb) as usize) }; - let body = unsafe { &mut *body }; - - match body.read(dst) { - Ok(len) => len as size_t, - Err(_) => consts::CURL_READFUNC_ABORT as size_t, - } -} - -extern fn curl_write_fn(p: *mut u8, size: size_t, nmemb: size_t, - resp: *mut ResponseBuilder) -> size_t { - if !resp.is_null() { - let builder: &mut ResponseBuilder = unsafe { mem::transmute(resp) }; - let chunk = unsafe { slice::from_raw_parts(p as *const u8, - (size * nmemb) as usize) }; - builder.body.extend(chunk.iter().map(|x| *x)); - } - - size * nmemb -} - -extern fn curl_header_fn(p: *mut u8, size: size_t, nmemb: size_t, - resp: &mut ResponseBuilder) -> size_t { - // TODO: Skip the first call (it seems to be the status line) - - let vec = unsafe { slice::from_raw_parts(p as *const u8, - (size * nmemb) as usize) }; - - match header::parse(&vec) { - Some((name, val)) => { - resp.add_header(name, val); - } - None => {} - } - - vec.len() as size_t -} - -pub extern "C" fn curl_progress_fn(cb: *mut Box, dltotal: c_double, dlnow: c_double, ultotal: c_double, ulnow: c_double) -> c_int { - #[inline] - fn to_usize(v: c_double) -> usize { - if v > 0.0 { v as usize } else { 0 } - } - - if !cb.is_null() { - let cb: &mut ProgressCb = unsafe { &mut **cb }; - (*cb)(to_usize(dltotal), to_usize(dlnow), to_usize(ultotal), to_usize(ulnow)); - } - - 0 -} diff --git a/src/ffi/err.rs b/src/ffi/err.rs deleted file mode 100644 index 80d46d579e..0000000000 --- a/src/ffi/err.rs +++ /dev/null @@ -1,138 +0,0 @@ -use std::ffi::CStr; -use std::error; -use std::fmt; -use std::str; - -use curl_ffi as ffi; - -pub use curl_ffi::CURLE_OK as OK; -pub use curl_ffi::CURLE_UNSUPPORTED_PROTOCOL as UNSUPPORTED_PROTOCOL; -pub use curl_ffi::CURLE_FAILED_INIT as FAILED_INIT; -pub use curl_ffi::CURLE_URL_MALFORMAT as URL_MALFORMAT; -pub use curl_ffi::CURLE_NOT_BUILT_IN as NOT_BUILT_IN; -pub use curl_ffi::CURLE_COULDNT_RESOLVE_PROXY as COULDNT_RESOLVE_PROXY; -pub use curl_ffi::CURLE_COULDNT_RESOLVE_HOST as COULDNT_RESOLVE_HOST; -pub use curl_ffi::CURLE_COULDNT_CONNECT as COULDNT_CONNECT; -pub use curl_ffi::CURLE_FTP_WEIRD_SERVER_REPLY as FTP_WEIRD_SERVER_REPLY; -pub use curl_ffi::CURLE_REMOTE_ACCESS_DENIED as REMOTE_ACCESS_DENIED; -pub use curl_ffi::CURLE_FTP_ACCEPT_FAILED as FTP_ACCEPT_FAILED; -pub use curl_ffi::CURLE_FTP_WEIRD_PASS_REPLY as FTP_WEIRD_PASS_REPLY; -pub use curl_ffi::CURLE_FTP_ACCEPT_TIMEOUT as FTP_ACCEPT_TIMEOUT; -pub use curl_ffi::CURLE_FTP_WEIRD_PASV_REPLY as FTP_WEIRD_PASV_REPLY; -pub use curl_ffi::CURLE_FTP_WEIRD_227_FORMAT as FTP_WEIRD_227_FORMAT; -pub use curl_ffi::CURLE_FTP_CANT_GET_HOST as FTP_CANT_GET_HOST; -pub use curl_ffi::CURLE_OBSOLETE16 as OBSOLETE16; -pub use curl_ffi::CURLE_FTP_COULDNT_SET_TYPE as FTP_COULDNT_SET_TYPE; -pub use curl_ffi::CURLE_PARTIAL_FILE as PARTIAL_FILE; -pub use curl_ffi::CURLE_FTP_COULDNT_RETR_FILE as FTP_COULDNT_RETR_FILE; -pub use curl_ffi::CURLE_OBSOLETE20 as OBSOLETE20; -pub use curl_ffi::CURLE_QUOTE_ERROR as QUOTE_ERROR; -pub use curl_ffi::CURLE_HTTP_RETURNED_ERROR as HTTP_RETURNED_ERROR; -pub use curl_ffi::CURLE_WRITE_ERROR as WRITE_ERROR; -pub use curl_ffi::CURLE_OBSOLETE24 as OBSOLETE24; -pub use curl_ffi::CURLE_UPLOAD_FAILED as UPLOAD_FAILED; -pub use curl_ffi::CURLE_READ_ERROR as READ_ERROR; -pub use curl_ffi::CURLE_OUT_OF_MEMORY as OUT_OF_MEMORY; -pub use curl_ffi::CURLE_OPERATION_TIMEDOUT as OPERATION_TIMEDOUT; -pub use curl_ffi::CURLE_OBSOLETE29 as OBSOLETE29; -pub use curl_ffi::CURLE_FTP_PORT_FAILED as FTP_PORT_FAILED; -pub use curl_ffi::CURLE_FTP_COULDNT_USE_REST as FTP_COULDNT_USE_REST; -pub use curl_ffi::CURLE_OBSOLETE32 as OBSOLETE32; -pub use curl_ffi::CURLE_RANGE_ERROR as RANGE_ERROR; -pub use curl_ffi::CURLE_HTTP_POST_ERROR as HTTP_POST_ERROR; -pub use curl_ffi::CURLE_SSL_CONNECT_ERROR as SSL_CONNECT_ERROR; -pub use curl_ffi::CURLE_BAD_DOWNLOAD_RESUME as BAD_DOWNLOAD_RESUME; -pub use curl_ffi::CURLE_FILE_COULDNT_READ_FILE as FILE_COULDNT_READ_FILE; -pub use curl_ffi::CURLE_LDAP_CANNOT_BIND as LDAP_CANNOT_BIND; -pub use curl_ffi::CURLE_LDAP_SEARCH_FAILED as LDAP_SEARCH_FAILED; -pub use curl_ffi::CURLE_OBSOLETE40 as OBSOLETE40; -pub use curl_ffi::CURLE_FUNCTION_NOT_FOUND as FUNCTION_NOT_FOUND; -pub use curl_ffi::CURLE_ABORTED_BY_CALLBACK as ABORTED_BY_CALLBACK; -pub use curl_ffi::CURLE_BAD_FUNCTION_ARGUMENT as BAD_FUNCTION_ARGUMENT; -pub use curl_ffi::CURLE_OBSOLETE44 as OBSOLETE44; -pub use curl_ffi::CURLE_INTERFACE_FAILED as INTERFACE_FAILED; -pub use curl_ffi::CURLE_OBSOLETE46 as OBSOLETE46; -pub use curl_ffi::CURLE_TOO_MANY_REDIRECTS as TOO_MANY_REDIRECTS ; -pub use curl_ffi::CURLE_UNKNOWN_OPTION as UNKNOWN_OPTION; -pub use curl_ffi::CURLE_TELNET_OPTION_SYNTAX as TELNET_OPTION_SYNTAX ; -pub use curl_ffi::CURLE_OBSOLETE50 as OBSOLETE50; -pub use curl_ffi::CURLE_PEER_FAILED_VERIFICATION as PEER_FAILED_VERIFICATION; -pub use curl_ffi::CURLE_GOT_NOTHING as GOT_NOTHING; -pub use curl_ffi::CURLE_SSL_ENGINE_NOTFOUND as SSL_ENGINE_NOTFOUND; -pub use curl_ffi::CURLE_SSL_ENGINE_SETFAILED as SSL_ENGINE_SETFAILED; -pub use curl_ffi::CURLE_SEND_ERROR as SEND_ERROR; -pub use curl_ffi::CURLE_RECV_ERROR as RECV_ERROR; -pub use curl_ffi::CURLE_OBSOLETE57 as OBSOLETE57; -pub use curl_ffi::CURLE_SSL_CERTPROBLEM as SSL_CERTPROBLEM; -pub use curl_ffi::CURLE_SSL_CIPHER as SSL_CIPHER; -pub use curl_ffi::CURLE_SSL_CACERT as SSL_CACERT; -pub use curl_ffi::CURLE_BAD_CONTENT_ENCODING as BAD_CONTENT_ENCODING; -pub use curl_ffi::CURLE_LDAP_INVALID_URL as LDAP_INVALID_URL; -pub use curl_ffi::CURLE_FILESIZE_EXCEEDED as FILESIZE_EXCEEDED; -pub use curl_ffi::CURLE_USE_SSL_FAILED as USE_SSL_FAILED; -pub use curl_ffi::CURLE_SEND_FAIL_REWIND as SEND_FAIL_REWIND; -pub use curl_ffi::CURLE_SSL_ENGINE_INITFAILED as SSL_ENGINE_INITFAILED; -pub use curl_ffi::CURLE_LOGIN_DENIED as LOGIN_DENIED; -pub use curl_ffi::CURLE_TFTP_NOTFOUND as TFTP_NOTFOUND; -pub use curl_ffi::CURLE_TFTP_PERM as TFTP_PERM; -pub use curl_ffi::CURLE_REMOTE_DISK_FULL as REMOTE_DISK_FULL; -pub use curl_ffi::CURLE_TFTP_ILLEGAL as TFTP_ILLEGAL; -pub use curl_ffi::CURLE_TFTP_UNKNOWNID as TFTP_UNKNOWNID; -pub use curl_ffi::CURLE_REMOTE_FILE_EXISTS as REMOTE_FILE_EXISTS; -pub use curl_ffi::CURLE_TFTP_NOSUCHUSER as TFTP_NOSUCHUSER; -pub use curl_ffi::CURLE_CONV_FAILED as CONV_FAILED; -pub use curl_ffi::CURLE_CONV_REQD as CONV_REQD; -pub use curl_ffi::CURLE_SSL_CACERT_BADFILE as SSL_CACERT_BADFILE; -pub use curl_ffi::CURLE_REMOTE_FILE_NOT_FOUND as REMOTE_FILE_NOT_FOUND; -pub use curl_ffi::CURLE_SSH as SSH; -pub use curl_ffi::CURLE_SSL_SHUTDOWN_FAILED as SSL_SHUTDOWN_FAILED; -pub use curl_ffi::CURLE_AGAIN as AGAIN; -pub use curl_ffi::CURLE_SSL_CRL_BADFILE as SSL_CRL_BADFILE; -pub use curl_ffi::CURLE_SSL_ISSUER_ERROR as SSL_ISSUER_ERROR; -pub use curl_ffi::CURLE_FTP_PRET_FAILED as FTP_PRET_FAILED; -pub use curl_ffi::CURLE_RTSP_CSEQ_ERROR as RTSP_CSEQ_ERROR; -pub use curl_ffi::CURLE_RTSP_SESSION_ERROR as RTSP_SESSION_ERROR; -pub use curl_ffi::CURLE_FTP_BAD_FILE_LIST as FTP_BAD_FILE_LIST; -pub use curl_ffi::CURLE_CHUNK_FAILED as CHUNK_FAILED; -pub use curl_ffi::CURLE_NO_CONNECTION_AVAILABLE as NO_CONNECTION_AVAILABLE; - -#[derive(Copy, Clone)] -pub struct ErrCode(pub ffi::CURLcode); - -impl ErrCode { - pub fn is_success(self) -> bool { - self.code() as i32 == OK as i32 - } - - pub fn code(self) -> ffi::CURLcode { let ErrCode(c) = self; c } -} - -impl fmt::Debug for ErrCode { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - -impl fmt::Display for ErrCode { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let s = unsafe { - let ptr = ffi::curl_easy_strerror(self.code()); - CStr::from_ptr(ptr as *const _).to_bytes() - }; - - match str::from_utf8(s) { - Ok(s) => write!(fmt, "{}", s), - Err(err) => write!(fmt, "{}", err) - } - } -} - -impl error::Error for ErrCode { - fn description(&self) -> &str { - let code = self.code(); - let s = unsafe { - CStr::from_ptr(ffi::curl_easy_strerror(code) as *const _).to_bytes() - }; - str::from_utf8(s).unwrap() - } -} diff --git a/src/ffi/info.rs b/src/ffi/info.rs deleted file mode 100644 index a1c509d212..0000000000 --- a/src/ffi/info.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![allow(dead_code)] - -use curl_ffi as ffi; - -pub use curl_ffi::CURLINFO_EFFECTIVE_URL as EFFECTIVE_URL; -pub use curl_ffi::CURLINFO_RESPONSE_CODE as RESPONSE_CODE; -pub use curl_ffi::CURLINFO_TOTAL_TIME as TOTAL_TIME; - -pub type Key = ffi::CURLINFO; diff --git a/src/ffi/list.rs b/src/ffi/list.rs deleted file mode 100644 index e0e273431b..0000000000 --- a/src/ffi/list.rs +++ /dev/null @@ -1,51 +0,0 @@ -#![allow(dead_code)] - -use std::ptr; -use libc::c_void; -use super::opt::OptVal; - -use curl_ffi as ffi; - -pub struct List { - len: usize, - head: *mut ffi::curl_slist, -} - -impl List { - pub fn new() -> List { - List { - len: 0, - head: ptr::null_mut() - } - } - - pub fn push_bytes(&mut self, val: &[u8]) { - assert!(val[val.len() - 1] == 0); - self.len += 1; - self.head = unsafe { - ffi::curl_slist_append(self.head, val.as_ptr() as *mut _) - }; - } - - pub fn len(&self) -> usize { - self.len - } - - pub fn is_empty(&self) -> bool { - self.len() == 0 - } -} - -impl Drop for List { - fn drop(&mut self) { - if !self.is_empty() { - unsafe { ffi::curl_slist_free_all(self.head) } - } - } -} - -impl<'a> OptVal for &'a List { - fn with_c_repr(self, f: F) where F: FnOnce(*const c_void) { - f(self.head as *const c_void) - } -} diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs deleted file mode 100644 index cb01dc2a0b..0000000000 --- a/src/ffi/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub use self::list::List; -pub use self::version::version; - -pub mod consts; -pub mod easy; -pub mod err; -pub mod info; -pub mod list; -pub mod opt; -pub mod version; diff --git a/src/ffi/opt.rs b/src/ffi/opt.rs deleted file mode 100644 index b8e1d9e006..0000000000 --- a/src/ffi/opt.rs +++ /dev/null @@ -1,283 +0,0 @@ -#![allow(dead_code)] - -use std::ffi::CString; -use std::path::Path; -use libc::{c_void}; - -use curl_ffi as ffi; - -pub use curl_ffi::CURLOPT_FILE as FILE; -pub use curl_ffi::CURLOPT_URL as URL; -pub use curl_ffi::CURLOPT_PORT as PORT; -pub use curl_ffi::CURLOPT_PROXY as PROXY; -pub use curl_ffi::CURLOPT_USERPWD as USERPWD; -pub use curl_ffi::CURLOPT_PROXYUSERPWD as PROXYUSERPWD; -pub use curl_ffi::CURLOPT_RANGE as RANGE; -pub use curl_ffi::CURLOPT_INFILE as INFILE; -pub use curl_ffi::CURLOPT_ERRORBUFFER as ERRORBUFFER; -pub use curl_ffi::CURLOPT_WRITEFUNCTION as WRITEFUNCTION; -pub use curl_ffi::CURLOPT_READFUNCTION as READFUNCTION; -pub use curl_ffi::CURLOPT_TIMEOUT as TIMEOUT; -pub use curl_ffi::CURLOPT_INFILESIZE as INFILESIZE; -pub use curl_ffi::CURLOPT_POSTFIELDS as POSTFIELDS; -pub use curl_ffi::CURLOPT_REFERER as REFERER; -pub use curl_ffi::CURLOPT_FTPPORT as FTPPORT; -pub use curl_ffi::CURLOPT_USERAGENT as USERAGENT; -pub use curl_ffi::CURLOPT_LOW_SPEED_LIMIT as LOW_SPEED_LIMIT; -pub use curl_ffi::CURLOPT_LOW_SPEED_TIME as LOW_SPEED_TIME; -pub use curl_ffi::CURLOPT_RESUME_FROM as RESUME_FROM; -pub use curl_ffi::CURLOPT_COOKIE as COOKIE; -pub use curl_ffi::CURLOPT_HTTPHEADER as HTTPHEADER; -pub use curl_ffi::CURLOPT_HTTPPOST as HTTPPOST; -pub use curl_ffi::CURLOPT_SSLCERT as SSLCERT; -pub use curl_ffi::CURLOPT_KEYPASSWD as KEYPASSWD; -pub use curl_ffi::CURLOPT_CRLF as CRLF; -pub use curl_ffi::CURLOPT_QUOTE as QUOTE; -pub use curl_ffi::CURLOPT_WRITEHEADER as WRITEHEADER; -pub use curl_ffi::CURLOPT_COOKIEFILE as COOKIEFILE; -pub use curl_ffi::CURLOPT_SSLVERSION as SSLVERSION; -pub use curl_ffi::CURLOPT_TIMECONDITION as TIMECONDITION; -pub use curl_ffi::CURLOPT_TIMEVALUE as TIMEVALUE; -pub use curl_ffi::CURLOPT_CUSTOMREQUEST as CUSTOMREQUEST; -pub use curl_ffi::CURLOPT_STDERR as STDERR; -pub use curl_ffi::CURLOPT_POSTQUOTE as POSTQUOTE; -pub use curl_ffi::CURLOPT_WRITEINFO as WRITEINFO; -pub use curl_ffi::CURLOPT_VERBOSE as VERBOSE; -pub use curl_ffi::CURLOPT_HEADER as HEADER; -pub use curl_ffi::CURLOPT_NOPROGRESS as NOPROGRESS; -pub use curl_ffi::CURLOPT_NOBODY as NOBODY; -pub use curl_ffi::CURLOPT_FAILONERROR as FAILONERROR; -pub use curl_ffi::CURLOPT_UPLOAD as UPLOAD; -pub use curl_ffi::CURLOPT_POST as POST; -pub use curl_ffi::CURLOPT_DIRLISTONLY as DIRLISTONLY; -pub use curl_ffi::CURLOPT_APPEND as APPEND; -pub use curl_ffi::CURLOPT_NETRC as NETRC; -pub use curl_ffi::CURLOPT_FOLLOWLOCATION as FOLLOWLOCATION; -pub use curl_ffi::CURLOPT_TRANSFERTEXT as TRANSFERTEXT; -pub use curl_ffi::CURLOPT_PUT as PUT; -pub use curl_ffi::CURLOPT_PROGRESSFUNCTION as PROGRESSFUNCTION; -pub use curl_ffi::CURLOPT_PROGRESSDATA as PROGRESSDATA; -pub use curl_ffi::CURLOPT_AUTOREFERER as AUTOREFERER; -pub use curl_ffi::CURLOPT_PROXYPORT as PROXYPORT; -pub use curl_ffi::CURLOPT_POSTFIELDSIZE as POSTFIELDSIZE; -pub use curl_ffi::CURLOPT_HTTPPROXYTUNNEL as HTTPPROXYTUNNEL; -pub use curl_ffi::CURLOPT_INTERFACE as INTERFACE; -pub use curl_ffi::CURLOPT_KRBLEVEL as KRBLEVEL; -pub use curl_ffi::CURLOPT_SSL_VERIFYPEER as SSL_VERIFYPEER; -pub use curl_ffi::CURLOPT_CAINFO as CAINFO; -pub use curl_ffi::CURLOPT_MAXREDIRS as MAXREDIRS; -pub use curl_ffi::CURLOPT_FILETIME as FILETIME; -pub use curl_ffi::CURLOPT_TELNETOPTIONS as TELNETOPTIONS; -pub use curl_ffi::CURLOPT_MAXCONNECTS as MAXCONNECTS; -pub use curl_ffi::CURLOPT_CLOSEPOLICY as CLOSEPOLICY; -pub use curl_ffi::CURLOPT_FRESH_CONNECT as FRESH_CONNECT; -pub use curl_ffi::CURLOPT_FORBID_REUSE as FORBID_REUSE; -pub use curl_ffi::CURLOPT_RANDOM_FILE as RANDOM_FILE; -pub use curl_ffi::CURLOPT_EGDSOCKET as EGDSOCKET; -pub use curl_ffi::CURLOPT_CONNECTTIMEOUT as CONNECTTIMEOUT; -pub use curl_ffi::CURLOPT_HEADERFUNCTION as HEADERFUNCTION; -pub use curl_ffi::CURLOPT_HTTPGET as HTTPGET; -pub use curl_ffi::CURLOPT_SSL_VERIFYHOST as SSL_VERIFYHOST; -pub use curl_ffi::CURLOPT_COOKIEJAR as COOKIEJAR; -pub use curl_ffi::CURLOPT_SSL_CIPHER_LIST as SSL_CIPHER_LIST; -pub use curl_ffi::CURLOPT_HTTP_VERSION as HTTP_VERSION; -pub use curl_ffi::CURLOPT_FTP_USE_EPSV as FTP_USE_EPSV; -pub use curl_ffi::CURLOPT_SSLCERTTYPE as SSLCERTTYPE; -pub use curl_ffi::CURLOPT_SSLKEY as SSLKEY; -pub use curl_ffi::CURLOPT_SSLKEYTYPE as SSLKEYTYPE; -pub use curl_ffi::CURLOPT_SSLENGINE as SSLENGINE; -pub use curl_ffi::CURLOPT_SSLENGINE_DEFAULT as SSLENGINE_DEFAULT; -pub use curl_ffi::CURLOPT_DNS_USE_GLOBAL_CACHE as DNS_USE_GLOBAL_CACHE; -pub use curl_ffi::CURLOPT_DNS_CACHE_TIMEOUT as DNS_CACHE_TIMEOUT; -pub use curl_ffi::CURLOPT_PREQUOTE as PREQUOTE; -pub use curl_ffi::CURLOPT_DEBUGFUNCTION as DEBUGFUNCTION; -pub use curl_ffi::CURLOPT_DEBUGDATA as DEBUGDATA; -pub use curl_ffi::CURLOPT_COOKIESESSION as COOKIESESSION; -pub use curl_ffi::CURLOPT_CAPATH as CAPATH; -pub use curl_ffi::CURLOPT_BUFFERSIZE as BUFFERSIZE; -pub use curl_ffi::CURLOPT_NOSIGNAL as NOSIGNAL; -pub use curl_ffi::CURLOPT_SHARE as SHARE; -pub use curl_ffi::CURLOPT_PROXYTYPE as PROXYTYPE; -pub use curl_ffi::CURLOPT_ACCEPT_ENCODING as ACCEPT_ENCODING; -pub use curl_ffi::CURLOPT_PRIVATE as PRIVATE; -pub use curl_ffi::CURLOPT_HTTP200ALIASES as HTTP200ALIASES; -pub use curl_ffi::CURLOPT_UNRESTRICTED_AUTH as UNRESTRICTED_AUTH; -pub use curl_ffi::CURLOPT_FTP_USE_EPRT as FTP_USE_EPRT; -pub use curl_ffi::CURLOPT_HTTPAUTH as HTTPAUTH; -pub use curl_ffi::CURLOPT_SSL_CTX_FUNCTION as SSL_CTX_FUNCTION; -pub use curl_ffi::CURLOPT_SSL_CTX_DATA as SSL_CTX_DATA; -pub use curl_ffi::CURLOPT_FTP_CREATE_MISSING_DIRS as FTP_CREATE_MISSING_DIRS; -pub use curl_ffi::CURLOPT_PROXYAUTH as PROXYAUTH; -pub use curl_ffi::CURLOPT_FTP_RESPONSE_TIMEOUT as FTP_RESPONSE_TIMEOUT; -pub use curl_ffi::CURLOPT_IPRESOLVE as IPRESOLVE; -pub use curl_ffi::CURLOPT_MAXFILESIZE as MAXFILESIZE; -pub use curl_ffi::CURLOPT_INFILESIZE_LARGE as INFILESIZE_LARGE; -pub use curl_ffi::CURLOPT_RESUME_FROM_LARGE as RESUME_FROM_LARGE; -pub use curl_ffi::CURLOPT_MAXFILESIZE_LARGE as MAXFILESIZE_LARGE; -pub use curl_ffi::CURLOPT_NETRC_FILE as NETRC_FILE; -pub use curl_ffi::CURLOPT_USE_SSL as USE_SSL; -pub use curl_ffi::CURLOPT_POSTFIELDSIZE_LARGE as POSTFIELDSIZE_LARGE; -pub use curl_ffi::CURLOPT_TCP_NODELAY as TCP_NODELAY; -pub use curl_ffi::CURLOPT_FTPSSLAUTH as FTPSSLAUTH; -pub use curl_ffi::CURLOPT_IOCTLFUNCTION as IOCTLFUNCTION; -pub use curl_ffi::CURLOPT_IOCTLDATA as IOCTLDATA; -pub use curl_ffi::CURLOPT_FTP_ACCOUNT as FTP_ACCOUNT; -pub use curl_ffi::CURLOPT_COOKIELIST as COOKIELIST; -pub use curl_ffi::CURLOPT_IGNORE_CONTENT_LENGTH as IGNORE_CONTENT_LENGTH; -pub use curl_ffi::CURLOPT_FTP_SKIP_PASV_IP as FTP_SKIP_PASV_IP; -pub use curl_ffi::CURLOPT_FTP_FILEMETHOD as FTP_FILEMETHOD; -pub use curl_ffi::CURLOPT_LOCALPORT as LOCALPORT; -pub use curl_ffi::CURLOPT_LOCALPORTRANGE as LOCALPORTRANGE; -pub use curl_ffi::CURLOPT_CONNECT_ONLY as CONNECT_ONLY; -pub use curl_ffi::CURLOPT_CONV_FROM_NETWORK_FUNCTION as CONV_FROM_NETWORK_FUNCTION; -pub use curl_ffi::CURLOPT_CONV_TO_NETWORK_FUNCTION as CONV_TO_NETWORK_FUNCTION; -pub use curl_ffi::CURLOPT_CONV_FROM_UTF8_FUNCTION as CONV_FROM_UTF8_FUNCTION; -pub use curl_ffi::CURLOPT_MAX_SEND_SPEED_LARGE as MAX_SEND_SPEED_LARGE; -pub use curl_ffi::CURLOPT_MAX_RECV_SPEED_LARGE as MAX_RECV_SPEED_LARGE; -pub use curl_ffi::CURLOPT_FTP_ALTERNATIVE_TO_USER as FTP_ALTERNATIVE_TO_USER; -pub use curl_ffi::CURLOPT_SOCKOPTFUNCTION as SOCKOPTFUNCTION; -pub use curl_ffi::CURLOPT_SOCKOPTDATA as SOCKOPTDATA; -pub use curl_ffi::CURLOPT_SSL_SESSIONID_CACHE as SSL_SESSIONID_CACHE; -pub use curl_ffi::CURLOPT_SSH_AUTH_TYPES as SSH_AUTH_TYPES; -pub use curl_ffi::CURLOPT_SSH_PUBLIC_KEYFILE as SSH_PUBLIC_KEYFILE; -pub use curl_ffi::CURLOPT_SSH_PRIVATE_KEYFILE as SSH_PRIVATE_KEYFILE; -pub use curl_ffi::CURLOPT_FTP_SSL_CCC as FTP_SSL_CCC; -pub use curl_ffi::CURLOPT_TIMEOUT_MS as TIMEOUT_MS; -pub use curl_ffi::CURLOPT_CONNECTTIMEOUT_MS as CONNECTTIMEOUT_MS; -pub use curl_ffi::CURLOPT_HTTP_TRANSFER_DECODING as HTTP_TRANSFER_DECODING; -pub use curl_ffi::CURLOPT_HTTP_CONTENT_DECODING as HTTP_CONTENT_DECODING; -pub use curl_ffi::CURLOPT_NEW_FILE_PERMS as NEW_FILE_PERMS; -pub use curl_ffi::CURLOPT_NEW_DIRECTORY_PERMS as NEW_DIRECTORY_PERMS; -pub use curl_ffi::CURLOPT_POSTREDIR as POSTREDIR; -pub use curl_ffi::CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 as SSH_HOST_PUBLIC_KEY_MD5; -pub use curl_ffi::CURLOPT_OPENSOCKETFUNCTION as OPENSOCKETFUNCTION; -pub use curl_ffi::CURLOPT_OPENSOCKETDATA as OPENSOCKETDATA; -pub use curl_ffi::CURLOPT_COPYPOSTFIELDS as COPYPOSTFIELDS; -pub use curl_ffi::CURLOPT_PROXY_TRANSFER_MODE as PROXY_TRANSFER_MODE; -pub use curl_ffi::CURLOPT_SEEKFUNCTION as SEEKFUNCTION; -pub use curl_ffi::CURLOPT_SEEKDATA as SEEKDATA; -pub use curl_ffi::CURLOPT_CRLFILE as CRLFILE; -pub use curl_ffi::CURLOPT_ISSUERCERT as ISSUERCERT; -pub use curl_ffi::CURLOPT_ADDRESS_SCOPE as ADDRESS_SCOPE; -pub use curl_ffi::CURLOPT_CERTINFO as CERTINFO; -pub use curl_ffi::CURLOPT_USERNAME as USERNAME; -pub use curl_ffi::CURLOPT_PASSWORD as PASSWORD; -pub use curl_ffi::CURLOPT_PROXYUSERNAME as PROXYUSERNAME; -pub use curl_ffi::CURLOPT_PROXYPASSWORD as PROXYPASSWORD; -pub use curl_ffi::CURLOPT_NOPROXY as NOPROXY; -pub use curl_ffi::CURLOPT_TFTP_BLKSIZE as TFTP_BLKSIZE; -pub use curl_ffi::CURLOPT_SOCKS5_GSSAPI_SERVICE as SOCKS5_GSSAPI_SERVICE; -pub use curl_ffi::CURLOPT_SOCKS5_GSSAPI_NEC as SOCKS5_GSSAPI_NEC; -pub use curl_ffi::CURLOPT_PROTOCOLS as PROTOCOLS; -pub use curl_ffi::CURLOPT_REDIR_PROTOCOLS as REDIR_PROTOCOLS; -pub use curl_ffi::CURLOPT_SSH_KNOWNHOSTS as SSH_KNOWNHOSTS; -pub use curl_ffi::CURLOPT_SSH_KEYFUNCTION as SSH_KEYFUNCTION; -pub use curl_ffi::CURLOPT_SSH_KEYDATA as SSH_KEYDATA; -pub use curl_ffi::CURLOPT_MAIL_FROM as MAIL_FROM; -pub use curl_ffi::CURLOPT_MAIL_RCPT as MAIL_RCPT; -pub use curl_ffi::CURLOPT_FTP_USE_PRET as FTP_USE_PRET; -pub use curl_ffi::CURLOPT_RTSP_REQUEST as RTSP_REQUEST; -pub use curl_ffi::CURLOPT_RTSP_SESSION_ID as RTSP_SESSION_ID; -pub use curl_ffi::CURLOPT_RTSP_STREAM_URI as RTSP_STREAM_URI; -pub use curl_ffi::CURLOPT_RTSP_TRANSPORT as RTSP_TRANSPORT; -pub use curl_ffi::CURLOPT_RTSP_CLIENT_CSEQ as RTSP_CLIENT_CSEQ; -pub use curl_ffi::CURLOPT_RTSP_SERVER_CSEQ as RTSP_SERVER_CSEQ; -pub use curl_ffi::CURLOPT_INTERLEAVEDATA as INTERLEAVEDATA; -pub use curl_ffi::CURLOPT_INTERLEAVEFUNCTION as INTERLEAVEFUNCTION; -pub use curl_ffi::CURLOPT_WILDCARDMATCH as WILDCARDMATCH; -pub use curl_ffi::CURLOPT_CHUNK_BGN_FUNCTION as CHUNK_BGN_FUNCTION; -pub use curl_ffi::CURLOPT_CHUNK_END_FUNCTION as CHUNK_END_FUNCTION; -pub use curl_ffi::CURLOPT_FNMATCH_FUNCTION as FNMATCH_FUNCTION; -pub use curl_ffi::CURLOPT_CHUNK_DATA as CHUNK_DATA; -pub use curl_ffi::CURLOPT_FNMATCH_DATA as FNMATCH_DATA; -pub use curl_ffi::CURLOPT_RESOLVE as RESOLVE; -pub use curl_ffi::CURLOPT_TLSAUTH_USERNAME as TLSAUTH_USERNAME; -pub use curl_ffi::CURLOPT_TLSAUTH_PASSWORD as TLSAUTH_PASSWORD; -pub use curl_ffi::CURLOPT_TLSAUTH_TYPE as TLSAUTH_TYPE; -pub use curl_ffi::CURLOPT_TRANSFER_ENCODING as TRANSFER_ENCODING; -pub use curl_ffi::CURLOPT_CLOSESOCKETFUNCTION as CLOSESOCKETFUNCTION; -pub use curl_ffi::CURLOPT_CLOSESOCKETDATA as CLOSESOCKETDATA; -pub use curl_ffi::CURLOPT_GSSAPI_DELEGATION as GSSAPI_DELEGATION; -pub use curl_ffi::CURLOPT_DNS_SERVERS as DNS_SERVERS; -pub use curl_ffi::CURLOPT_ACCEPTTIMEOUT_MS as ACCEPTTIMEOUT_MS; -pub use curl_ffi::CURLOPT_TCP_KEEPALIVE as TCP_KEEPALIVE; -pub use curl_ffi::CURLOPT_TCP_KEEPIDLE as TCP_KEEPIDLE; -pub use curl_ffi::CURLOPT_TCP_KEEPINTVL as TCP_KEEPINTVL; -pub use curl_ffi::CURLOPT_SSL_OPTIONS as SSL_OPTIONS; -pub use curl_ffi::CURLOPT_MAIL_AUTH as MAIL_AUTH; -pub use curl_ffi::CURLOPT_SASL_IR as SASL_IR; -pub use curl_ffi::CURLOPT_XFERINFOFUNCTION as XFERINFOFUNCTION; -pub use curl_ffi::CURLOPT_XOAUTH2_BEARER as XOAUTH2_BEARER; -pub use curl_ffi::CURLOPT_DNS_INTERFACE as DNS_INTERFACE; -pub use curl_ffi::CURLOPT_DNS_LOCAL_IP4 as DNS_LOCAL_IP4; -pub use curl_ffi::CURLOPT_DNS_LOCAL_IP6 as DNS_LOCAL_IP6; -pub use curl_ffi::CURLOPT_LOGIN_OPTIONS as LOGIN_OPTIONS; -// pub use curl_ffi::CURLOPT_SSL_ENABLE_NPN as SSL_ENABLE_NPN; -// pub use curl_ffi::CURLOPT_SSL_ENABLE_ALPN as SSL_ENABLE_ALPN; -// pub use curl_ffi::CURLOPT_EXPECT_100_TIMEOUT_MS as EXPECT_100_TIMEOUT_MS; -// pub use curl_ffi::CURLOPT_PROXYHEADER as PROXYHEADER; -// pub use curl_ffi::CURLOPT_HEADEROPT as HEADEROPT; -// pub use curl_ffi::CURLOPT_POST301 as POST301; -// pub use curl_ffi::CURLOPT_SSLKEYPASSWD as SSLKEYPASSWD; -// pub use curl_ffi::CURLOPT_FTPAPPEND as FTPAPPEND; -// pub use curl_ffi::CURLOPT_FTPLISTONLY as FTPLISTONLY; -// pub use curl_ffi::CURLOPT_FTP_SSL as FTP_SSL; -// pub use curl_ffi::CURLOPT_SSLCERTPASSWD as SSLCERTPASSWD; -// pub use curl_ffi::CURLOPT_KRB4LEVEL as KRB4LEVEL; -pub use curl_ffi::CURLOPT_READDATA as READDATA; -pub use curl_ffi::CURLOPT_WRITEDATA as WRITEDATA; -pub use curl_ffi::CURLOPT_HEADERDATA as HEADERDATA; -// pub use curl_ffi::CURLOPT_XFERINFODATA as XFERINFODATA; - -pub type Opt = ffi::CURLoption; - -pub trait OptVal { - fn with_c_repr(self, f: F) where F: FnOnce(*const c_void); -} - -impl OptVal for isize { - fn with_c_repr(self, f: F) where F: FnOnce(*const c_void) { - f(self as *const c_void) - } -} - -impl OptVal for i32 { - fn with_c_repr(self, f: F) where F: FnOnce(*const c_void) { - (self as isize).with_c_repr(f) - } -} - -impl OptVal for usize { - fn with_c_repr(self, f: F) where F: FnOnce(*const c_void) { - f(self as *const c_void) - } -} - -impl OptVal for bool { - fn with_c_repr(self, f: F) where F: FnOnce(*const c_void) { - f(self as usize as *const c_void) - } -} - -impl<'a> OptVal for &'a str { - fn with_c_repr(self, f: F) where F: FnOnce(*const c_void) { - let s = CString::new(self).unwrap(); - f(s.as_ptr() as *const c_void) - } -} - -impl<'a> OptVal for &'a Path { - #[cfg(unix)] - fn with_c_repr(self, f: F) where F: FnOnce(*const c_void) { - use std::ffi::OsStr; - use std::os::unix::prelude::*; - let s: &OsStr = self.as_ref(); - let s = CString::new(s.as_bytes()).unwrap(); - f(s.as_ptr() as *const c_void) - } - #[cfg(windows)] - fn with_c_repr(self, f: F) where F: FnOnce(*const c_void) { - let s = CString::new(self.to_str().unwrap()).unwrap(); - f(s.as_ptr() as *const c_void) - } -} diff --git a/src/http/body.rs b/src/http/body.rs deleted file mode 100644 index b8d8b90677..0000000000 --- a/src/http/body.rs +++ /dev/null @@ -1,53 +0,0 @@ -use std::io::prelude::*; -use std::io; - -use self::Body::{FixedBody, ChunkedBody}; - -pub enum Body<'a> { - FixedBody(&'a [u8], usize), - ChunkedBody(&'a mut (Read+'a)) -} - -impl<'a> Body<'a> { - pub fn get_size(&self) -> Option { - match self { - &FixedBody(_, len) => Some(len), - _ => None - } - } - - pub fn read(&mut self, buf: &mut [u8]) -> io::Result { - match self { - &mut FixedBody(ref mut r, _) => Read::read(r, buf), - &mut ChunkedBody(ref mut r) => r.read(buf) - } - } -} - -pub trait ToBody<'a> { - fn to_body(self) -> Body<'a>; -} - -impl<'a> ToBody<'a> for &'a str { - fn to_body(self) -> Body<'a> { - self.as_bytes().to_body() - } -} - -impl<'a> ToBody<'a> for &'a [u8] { - fn to_body(self) -> Body<'a> { - FixedBody(self, self.len()) - } -} - -impl<'a> ToBody<'a> for &'a String { - fn to_body(self) -> Body<'a> { - self[..].to_body() - } -} - -impl<'a, R: Read + 'a> ToBody<'a> for &'a mut R { - fn to_body(self) -> Body<'a> { - ChunkedBody(self) - } -} diff --git a/src/http/handle.rs b/src/http/handle.rs deleted file mode 100644 index ac3b8b0bd0..0000000000 --- a/src/http/handle.rs +++ /dev/null @@ -1,449 +0,0 @@ -use std::collections::hash_map::{HashMap, Entry}; -use std::path::Path; - -use url::Url; - -use ffi::easy::Easy; -use ffi::err; -use ffi::opt; -use ffi; -use http::Response; -use http::body::{Body,ToBody}; -use {ProgressCb,ErrCode}; - -use self::Method::{Get, Head, Post, Put, Patch, Delete}; -use self::BodyType::{Fixed, Chunked}; - -const DEFAULT_TIMEOUT_MS: usize = 30_000; - -pub struct Handle { - easy: Easy, -} - -impl Handle { - pub fn new() -> Handle { - return configure(Handle { easy: Easy::new() } - .timeout(DEFAULT_TIMEOUT_MS) - .connect_timeout(DEFAULT_TIMEOUT_MS)); - - #[cfg(all(unix, not(target_os = "macos")))] - fn configure(mut handle: Handle) -> Handle { - let probe = ::openssl::probe::probe(); - if let Some(ref path) = probe.cert_file { - set_path(&mut handle, opt::CAINFO, path); - } - if let Some(ref path) = probe.cert_dir { - set_path(&mut handle, opt::CAPATH, path); - } - return handle; - - fn set_path(handle: &mut Handle, opt: opt::Opt, path: &Path) { - if let Err(e) = handle.easy.setopt(opt, path) { - if let err::NOT_BUILT_IN = e.code() { - return - } - panic!("failed to set {:?}: {}", opt, e) - } - } - } - - #[cfg(any(not(unix), target_os = "macos"))] - fn configure(handle: Handle) -> Handle { handle } - } - - pub fn timeout(mut self, ms: usize) -> Handle { - self.easy.setopt(opt::TIMEOUT_MS, ms).unwrap(); - self - } - - pub fn connect_timeout(mut self, ms: usize) -> Handle { - self.easy.setopt(opt::CONNECTTIMEOUT_MS, ms).unwrap(); - self - } - - /// Set the time in seconds that the transfer speed should be below - /// the `low_speed_limit` rate of bytes per second for the library to - /// consider it too slow and abort. - /// - /// The default for this option is 0 which means that this option is - /// disabled. - pub fn low_speed_timeout(mut self, seconds: usize) -> Handle { - self.easy.setopt(opt::LOW_SPEED_TIME, seconds).unwrap(); - self - } - - /// Set the average transfer speed in bytes per second that the - /// transfer should be below during `low_speed_timeout` seconds for - /// libcurl to consider it to be too slow and abort. - /// - /// The default for this option is 0 which means that this option is - /// disabled. - pub fn low_speed_limit(mut self, bytes_per_second: usize) -> Handle { - self.easy.setopt(opt::LOW_SPEED_LIMIT, bytes_per_second).unwrap(); - self - } - - pub fn ssl_verifypeer(mut self, value: bool) -> Handle { - self.easy.setopt(opt::SSL_VERIFYPEER, value).unwrap(); - self - } - - pub fn follow_location(mut self, value: isize) -> Handle { - self.easy.setopt(opt::FOLLOWLOCATION, value).unwrap(); - self - } - - pub fn userpwd(mut self, userpwd: &str) -> Handle { - self.easy.setopt(opt::USERPWD, userpwd).unwrap(); - self - } - - pub fn verbose(mut self) -> Handle { - self.easy.setopt(opt::VERBOSE, 1).unwrap(); - self - } - - pub fn proxy(mut self, proxy: U) -> Handle { - proxy.with_url_str(|s| { - self.easy.setopt(opt::PROXY, s).unwrap(); - }); - - self - } - - pub fn ssl_ca_path(mut self, path: &Path) -> Handle { - self.easy.setopt(opt::CAPATH, path).unwrap(); - self - } - - pub fn ssl_ca_info(mut self, path: &Path) -> Handle { - self.easy.setopt(opt::CAINFO, path).unwrap(); - self - } - - pub fn cookie_jar(mut self, path: &Path) -> Handle { - self.easy.setopt(opt::COOKIEJAR, path).unwrap(); - self - } - - pub fn cookie_file(mut self, path: &Path) -> Handle { - self.easy.setopt(opt::COOKIEFILE, path).unwrap(); - self - } - - pub fn cookies(self, path: &Path) -> Handle { - self.cookie_jar(path).cookie_file(path) - } - - pub fn cookie(mut self, cookie: &str) -> Handle { - self.easy.setopt(opt::COOKIELIST, cookie).unwrap(); - self - } - - pub fn get<'a, 'b, U: ToUrl>(&'a mut self, uri: U) -> Request<'a, 'b> { - Request::new(self, Get).uri(uri) - } - - pub fn head<'a, 'b, U: ToUrl>(&'a mut self, uri: U) -> Request<'a, 'b> { - Request::new(self, Head).uri(uri) - } - - pub fn post<'a, 'b, U: ToUrl, B: ToBody<'b>>(&'a mut self, uri: U, body: B) -> Request<'a, 'b> { - Request::new(self, Post).uri(uri).body(body) - } - - pub fn put<'a, 'b, U: ToUrl, B: ToBody<'b>>(&'a mut self, uri: U, body: B) -> Request<'a, 'b> { - Request::new(self, Put).uri(uri).body(body) - } - - pub fn patch<'a, 'b, U: ToUrl, B: ToBody<'b>>(&'a mut self, uri: U, body: B) -> Request<'a, 'b> { - Request::new(self, Patch).uri(uri).body(body) - } - - pub fn delete<'a, 'b, U: ToUrl>(&'a mut self, uri: U) -> Request<'a, 'b> { - Request::new(self, Delete).uri(uri) - } -} - -#[derive(Copy, Clone)] -pub enum Method { - Options, - Get, - Head, - Post, - Put, - Patch, - Delete, - Trace, - Connect -} - -pub struct Request<'a, 'b> { - err: Option, - handle: &'a mut Handle, - method: Method, - headers: HashMap>, - body: Option>, - body_type: Option, - content_type: bool, // whether or not the content type was set - expect_continue: bool, // whether to expect a 100 continue from the server - progress: Option>>, - follow: bool, -} - -enum BodyType { - Fixed(usize), - Chunked, -} - -impl<'a, 'b> Request<'a, 'b> { - pub fn new(handle: &'a mut Handle, method: Method) -> Request<'a, 'b> { - Request { - err: None, - handle: handle, - method: method, - headers: HashMap::new(), - body: None, - body_type: None, - content_type: false, - expect_continue: false, - progress: None, - follow: false, - } - } - - pub fn uri(mut self, uri: U) -> Request<'a, 'b> { - uri.with_url_str(|s| { - match self.handle.easy.setopt(opt::URL, s) { - Ok(_) => {} - Err(e) => self.err = Some(e) - } - }); - - self - } - - pub fn body>(mut self, body: B) -> Request<'a, 'b> { - self.body = Some(body.to_body()); - self - } - - pub fn content_type(mut self, ty: &str) -> Request<'a, 'b> { - if !self.content_type { - self.content_type = true; - append_header(&mut self.headers, "Content-Type", ty); - } - - self - } - - pub fn content_length(mut self, len: usize) -> Request<'a, 'b> { - self.body_type = Some(Fixed(len)); - self - } - - pub fn chunked(mut self) -> Request<'a, 'b> { - self.body_type = Some(Chunked); - self - } - - pub fn expect_continue(mut self) -> Request<'a, 'b> { - self.expect_continue = true; - self - } - - pub fn header(mut self, name: &str, val: &str) -> Request<'a, 'b> { - append_header(&mut self.headers, name, val); - self - } - - pub fn get_header(&self, name: &str) -> Option<&[String]> { - self.headers.get(name).map(|a| &a[..]) - } - - pub fn headers<'c, 'd, I: Iterator>(mut self, hdrs: I) -> Request<'a, 'b> { - for (name, val) in hdrs { - append_header(&mut self.headers, name, val); - } - - self - } - - pub fn progress(mut self, cb: F) -> Request<'a, 'b> - where F: FnMut(usize, usize, usize, usize) + 'b - { - self.progress = Some(Box::new(cb) as Box>); - self - } - - pub fn follow_redirects(mut self, follow: bool) -> Request<'a, 'b> { - self.follow = follow; - self - } - - pub fn exec(self) -> Result { - // Deconstruct the struct - let Request { - err, - handle, - method, - mut headers, - mut body, - body_type, - content_type, - expect_continue, - progress, - follow, - .. - } = self; - - if follow { - try!(handle.easy.setopt(opt::FOLLOWLOCATION, 1)); - } - - match err { - Some(e) => return Err(e), - None => {} - } - - // Clear custom headers set from the previous request - try!(handle.easy.setopt(opt::HTTPHEADER, 0)); - - match method { - Get => try!(handle.easy.setopt(opt::HTTPGET, 1)), - Head => try!(handle.easy.setopt(opt::NOBODY, 1)), - Post => try!(handle.easy.setopt(opt::POST, 1)), - Put => try!(handle.easy.setopt(opt::UPLOAD, 1)), - Patch => { - try!(handle.easy.setopt(opt::CUSTOMREQUEST, "PATCH")); - try!(handle.easy.setopt(opt::UPLOAD, 1)); - }, - Delete => { - if body.is_some() { - try!(handle.easy.setopt(opt::UPLOAD, 1)); - } - - try!(handle.easy.setopt(opt::CUSTOMREQUEST, "DELETE")); - } - _ => unimplemented!() - } - - match body.as_ref() { - None => {} - Some(body) => { - let body_type = body_type.unwrap_or(match body.get_size() { - Some(len) => Fixed(len), - None => Chunked, - }); - - match body_type { - Fixed(len) => { - match method { - Post => try!(handle.easy.setopt(opt::POSTFIELDSIZE, len)), - Put | Patch | Delete => try!(handle.easy.setopt(opt::INFILESIZE, len)), - _ => { - append_header(&mut headers, "Content-Length", - &len.to_string()); - } - } - } - Chunked => { - append_header(&mut headers, "Transfer-Encoding", - "chunked"); - } - - } - - if !content_type { - append_header(&mut headers, "Content-Type", "application/octet-stream"); - } - - if !expect_continue { - append_header(&mut headers, "Expect", ""); - } - } - } - - let mut ffi_headers = ffi::List::new(); - - if !headers.is_empty() { - let mut buf = Vec::new(); - - for (k, v) in headers.iter() { - buf.extend(k.bytes()); - buf.extend(": ".bytes()); - - for v in v.iter() { - buf.extend(v.bytes()); - buf.push(0); - ffi_headers.push_bytes(&buf); - buf.truncate(k.len() + 2); - } - - buf.truncate(0); - } - - try!(handle.easy.setopt(opt::HTTPHEADER, &ffi_headers)); - } - - // According to libcurl's thread safety docs [1]: - // - // > When using multiple threads you should set the CURLOPT_NOSIGNAL - // > option to 1L for all handles - // - // This library is likely to be used in a multithreaded situation, so - // set this option as such. The implication of this is that timeouts are - // not honored during the DNS lookup phase, but using c-ares can fix - // that and it seems a small price to pay for thread safety! - // - // [1]: http://curl.haxx.se/libcurl/c/threadsafe.html - try!(handle.easy.setopt(opt::NOSIGNAL, 1)); - - handle.easy.perform(body.as_mut(), progress) - } -} - -fn append_header(map: &mut HashMap>, key: &str, val: &str) { - match map.entry(key.to_string()) { - Entry::Vacant(entry) => { - let mut values = Vec::new(); - values.push(val.to_string()); - entry.insert(values) - }, - Entry::Occupied(entry) => entry.into_mut() - }; -} - -pub trait ToUrl{ - fn with_url_str(self, f: F) where F: FnOnce(&str); -} - -impl<'a> ToUrl for &'a str { - fn with_url_str(self, f: F) where F: FnOnce(&str) { - f(self); - } -} - -impl<'a> ToUrl for &'a Url { - fn with_url_str(self, f: F) where F: FnOnce(&str) { - self.to_string().with_url_str(f); - } -} - -impl ToUrl for String { - fn with_url_str(self, f: F) where F: FnOnce(&str) { - self[..].with_url_str(f); - } -} - -#[cfg(test)] -mod tests { - use super::Handle; - - #[test] - fn get_header() { - let mut h = Handle::new(); - let r = h.get("/foo").header("foo", "bar"); - assert_eq!(r.get_header("foo"), Some(&["bar".to_string()][..])); - } -} diff --git a/src/http/header.rs b/src/http/header.rs deleted file mode 100644 index d50a9b9249..0000000000 --- a/src/http/header.rs +++ /dev/null @@ -1,224 +0,0 @@ -use std::str; - -use self::State::{HdrNameStart, HdrName, HdrValStart, HdrNameDiscardWs, HdrVal, - HdrValDiscardWs}; - -enum State { - HdrNameStart, - HdrName, - HdrNameDiscardWs, - HdrValStart, - HdrVal, - HdrValDiscardWs -} - -/** - * Simple header parser extracts the header name and value, stripping out - * starting and trailing white space. It does not, however, normalize header - * value whitespace - */ -pub fn parse<'a>(buf: &'a [u8]) -> Option<(&'a str, &'a str)> { - let mut i = 0; - let mut name_begin = 0; - let mut name_end = 0; - let mut val_begin = 0; - let mut val_end = 0; - let mut state = HdrNameStart; - - while i < buf.len() { - let c = buf[i]; - - match state { - HdrNameStart => { - if is_token(c) { - name_begin = i; - name_end = i; - state = HdrName; - } - else if c == COLON { - name_end = i; - state = HdrValStart; - } - else if is_space(c) { - name_end = i; - } - else { - return None; // error - } - } - HdrName => { - if c == COLON { - name_end = i; - state = HdrValStart; - } - else if is_space(c) { - name_end = i; - state = HdrNameDiscardWs; - } - else if !is_token(c) { - return None; // error - } - } - HdrNameDiscardWs => { - if is_token(c) { - state = HdrName; - } - else if c == COLON { - state = HdrValStart; - } - else if !is_space(c) { - return None; // error - } - } - HdrValStart => { - if !is_lws(c) { - val_begin = i; - val_end = i + 1; - state = HdrVal; - } - } - HdrVal => { - if is_lws(c) { - val_end = i; - state = HdrValDiscardWs; - } - else { - val_end = i + 1; - } - } - HdrValDiscardWs => { - if !is_lws(c) { - val_end = i + 1; - state = HdrVal; - } - } - } - - i += 1; - } - - let name = match str::from_utf8(&buf[name_begin..name_end]) { - Ok(v) => v, - Err(..) => return None - }; - - let val = match str::from_utf8(&buf[val_begin..val_end]) { - Ok(v) => v, - Err(..) => return None - }; - - Some((name, val)) -} - -static COLON: u8 = 58; - -static TOKEN: &'static [u8] = &[ -/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ - 0, 33, 0, 35, 36, 37, 38, 39, -/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ - 0, 0, 42, 43, 44, 45, 46, 47, -/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ - 48, 49, 50, 51, 52, 53, 54, 55, -/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ - 56, 57, 0, 0, 0, 0, 0, 0, -/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ - 0, 97, 98, 99, 100, 101, 102, 103, -/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ - 104, 105, 106, 107, 108, 109, 110, 111, -/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ - 112, 113, 114, 115, 116, 117, 118, 119, -/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ - 120, 121, 122, 0, 0, 0, 94, 95, -/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ - 96, 97, 98, 99, 100, 101, 102, 103, -/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ - 104, 105, 106, 107, 108, 109, 110, 111, -/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ - 112, 113, 114, 115, 116, 117, 118, 119, -/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ - 120, 121, 122, 123, 124, 0, 126, 0 -]; - -#[inline] -fn is_token(c: u8) -> bool { - c < 128 && TOKEN[c as usize] > 0 -} - -#[inline] -fn is_space(c: u8) -> bool { - c == (' ' as u8) || c == ('\t' as u8) -} - -#[inline] -fn is_lws(c: u8) -> bool { - is_space(c) || c == ('\n' as u8) || c == ('\r' as u8) -} - -#[cfg(test)] -mod test { - use super::parse; - - fn parse_str<'a>(s: &'a str) -> (&'a str, &'a str) { - parse(s.as_bytes()).unwrap() - } - - #[test] - pub fn test_basic_header() { - let (name, val) = parse_str("foo: bar"); - - assert!(name == "foo"); - assert!(val == "bar"); - } - - #[test] - pub fn test_basic_header_with_crlf() { - let (name, val) = parse_str("foo: bar\r\n"); - - assert!(name == "foo"); - assert!(val == "bar"); - } - - #[test] - pub fn test_header_with_extra_spacing() { - let (name, val) = parse_str(" \tfoo :bar \t\r"); - - assert!(name == "foo"); - assert!(val == "bar"); - } - - #[test] - pub fn test_header_without_value() { - let (name, val) = parse_str("foo:"); - - assert!(name == "foo"); - assert!(val == ""); - } - - #[test] - pub fn test_header_value_with_spacing_characters() { - let (name, val) = parse_str("foo: blah@example.com\r\n"); - - assert!(name == "foo"); - assert!(val == "blah@example.com"); - } - - #[test] - pub fn test_parsing_empty_line() { - let res = parse("\r\n\r\n".as_bytes()); - assert!(res.is_none()); - } - - #[test] - pub fn test_parsing_invalid_bytes() { - let res = parse(b"fo\x9co: zomg"); - assert!(res.is_none()); - } -} diff --git a/src/http/mod.rs b/src/http/mod.rs deleted file mode 100644 index 10f4d60f8f..0000000000 --- a/src/http/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub use self::handle::{Handle,Request}; -pub use self::response::{Headers,Response}; - -pub mod body; -pub mod handle; -pub mod header; -mod response; - -#[inline] -pub fn handle() -> Handle { - Handle::new() -} diff --git a/src/http/response.rs b/src/http/response.rs deleted file mode 100644 index bf8d0053a8..0000000000 --- a/src/http/response.rs +++ /dev/null @@ -1,64 +0,0 @@ -use std::{fmt,str}; -use std::collections::HashMap; - -pub type Headers = HashMap>; - -#[derive(Debug)] -pub struct Response { - code: u32, - hdrs: Headers, - body: Vec -} - -impl Response { - pub fn new(code: u32, hdrs: Headers, body: Vec) -> Response { - Response { - code: code, - hdrs: hdrs, - body: body - } - } - - pub fn get_code(&self) -> u32 { - self.code - } - - pub fn get_headers<'a>(&'a self) -> &'a Headers { - &self.hdrs - } - - pub fn get_header<'a>(&'a self, name: &str) -> &'a [String] { - self.hdrs - .get(name) - .map(|v| &v[..]) - .unwrap_or(&[]) - } - - pub fn get_body<'a>(&'a self) -> &'a [u8] { - &self.body - } - - pub fn move_body(self) -> Vec { - self.body - } -} - -impl fmt::Display for Response { - #[allow(deprecated)] // needed for `connect()`, since Rust 1.1 is supported - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - try!(write!(fmt, "Response {{{}, ", self.code)); - - for (name, val) in self.hdrs.iter() { - try!(write!(fmt, "{}: {}, ", name, val.connect(", "))); - } - - match str::from_utf8(&self.body) { - Ok(b) => try!(write!(fmt, "{}", b)), - Err(..) => try!(write!(fmt, "bytes[{}]", self.body.len())) - } - - try!(write!(fmt, "]")); - - Ok(()) - } -} diff --git a/test/server.rs b/test/server.rs deleted file mode 100644 index d8e30a3e46..0000000000 --- a/test/server.rs +++ /dev/null @@ -1,260 +0,0 @@ -use std::collections::HashSet; -use std::io::prelude::*; -use std::iter::repeat; -use std::net::{TcpStream, TcpListener}; -use std::str; -use std::sync::mpsc::{channel, Sender, Receiver}; -use std::thread; -use std::time::Duration; - -use self::Op::{SendBytes, ReceiveBytes, Wait, Shutdown}; - -// Global handle to the running test HTTP server -thread_local!(static HANDLE: Handle = start_server()); - -// Setup an op sequence with the test HTTP server -pub fn setup(ops: OpSequence) -> OpSequenceResult { - // Setup a channel to receive the response on - let (tx, rx) = channel(); - - // Send the op sequence to the server task - HANDLE.with(move |h| { - h.send(ops, tx); - }); - - OpSequenceResult::new(rx) -} - -pub fn url(path: &str) -> String { - format!("http://localhost:{}{}", port(), path) -} - -fn port() -> usize { - HANDLE.with(|h| { - h.port() - }) -} - -/* Handle to the running HTTP server task. Communication with the server - * happens over channels. - */ -struct Handle { - port: u16, - tx: Sender<(OpSequence, Sender>)> -} - -/* Operations for the test server to perform: - * - Send some bytes - * - Expect to receive bytes - * - Wait for a certain amount of time - * - Shutdown the server (allows a clean exit at the end of the tests) - */ -#[derive(Clone,PartialEq,Debug)] -pub enum Op { - SendBytes(&'static [u8]), - ReceiveBytes(&'static [u8]), - #[allow(dead_code)] - Wait(usize), - Shutdown -} - -/* An ordered sequence of operations for the HTTP server to perform -*/ -#[derive(Debug)] -pub struct OpSequence { - ops: Vec -} - -/* Represents the completion of the of the op sequence by the HTTP - * server. - */ -pub struct OpSequenceResult { - rx: Receiver> -} - -impl OpSequence { - pub fn new(op: Op) -> OpSequence { - OpSequence { ops: vec!(op) } - } - - pub fn concat(op: Op, seq: OpSequence) -> OpSequence { - let mut ops = vec!(op); - ops.extend(seq.ops.iter().cloned()); - OpSequence { ops: ops } - } - - pub fn is_shutdown(&self) -> bool { - self.ops.len() == 1 && self.ops[0] == Shutdown - } - - pub fn apply(&self, sock: &mut TcpStream, port: usize) - -> Result<(), String> { - for op in self.ops.iter() { - match op { - &SendBytes(b) => { - let b = insert_port(b, port); - match sock.write_all(&b) { - Ok(_) => {} - Err(e) => return Err(e.to_string()) - } - } - &ReceiveBytes(b) => { - let b = insert_port(b, port); - let mut rem = b.len(); - let mut act = repeat(0u8).take(rem).collect::>(); - - while rem > 0 { - match sock.read(&mut act[b.len() - rem..]) { - Ok(i) => rem = rem - i, - Err(e) => { - debug!("aborting due to error; error={}; \ - remaining={}; bytes=\n{}", e, rem, - to_debug_str(&act)); - return Err(e.to_string()) - } - } - } - - debug!("server received bytes; rem={}; bytes=\n{}\n~~~~~~~~~~~~~~", - rem, to_debug_str(&act)); - - let req1 = parse_request(&b); - let req2 = parse_request(&act); - - if req1 != req2 { - return Err(format!( - "received unexpected byte sequence.\n\n\ - Expected:\n{}\n\nReceived:\n{}", - to_debug_str(&b), to_debug_str(&act))); - } - } - &Wait(ms) => thread::sleep(Duration::from_millis(ms as u64)), - &Shutdown => { - return Err("Shutdown must be sent on its own".to_string()) - } - } - } - - return Ok(()); - - fn insert_port(bytes: &'static [u8], port: usize) -> Vec { - let s = str::from_utf8(bytes).unwrap(); - let p = port.to_string(); - s.replace("{PORT}", &p).into_bytes() - } - - fn parse_request<'a>(req: &'a [u8]) -> (&'a [u8], - HashSet<&'a [u8]>, - &'a [u8]) { - let mut start = None; - let mut headers = HashSet::new(); - let mut taken = 0; - - for part in req.split(|a| *a == b'\n') { - taken += part.len() + 1; - - if start.is_none() { - start = Some(part); - } else if part.len() == 1 { - break; - } else { - headers.insert(part); - } - } - - if taken > req.len() { - taken = req.len(); - } - - (start.unwrap(), headers, &req[taken..]) - } - } -} - -fn to_debug_str(bytes: &[u8]) -> String { - let mut ret = String::new(); - - for b in bytes.iter() { - let b = *b as char; - - if b >= ' ' && b <= '~' { - ret.push(b); - } - else if b == '\n' { - ret.push_str("\\n\n"); - } - else if b == '\r' { - ret.push_str("\\r"); - } - else { - ret.push('?'); - } - } - - ret -} - -impl Handle { - fn new(tx: Sender<(OpSequence, Sender>)>, port: u16) - -> Handle { - Handle { tx: tx, port: port } - } - - fn send(&self, ops: OpSequence, resp: Sender>) { - self.tx.send((ops, resp)).unwrap(); - } - - fn port(&self) -> usize { - self.port as usize - } -} - -impl Drop for Handle { - fn drop(&mut self) { - let (tx, _) = channel(); - self.send(OpSequence::new(Shutdown), tx); - } -} - -impl OpSequenceResult { - pub fn new(rx: Receiver>) -> OpSequenceResult { - OpSequenceResult { rx: rx } - } - - pub fn assert(&self) { - match self.rx.recv().unwrap() { - Ok(_) => {} - Err(e) => panic!("http exchange did not proceed as expected: {}", e) - } - } -} - -fn start_server() -> Handle { - let (ops_tx, ops_rx) = channel(); - - let srv = TcpListener::bind("127.0.0.1:0").unwrap(); - let port = srv.local_addr().unwrap().port(); - - thread::spawn(move || { - loop { - let (ops, resp_tx): (OpSequence, Sender>) = - ops_rx.recv().unwrap(); - - if ops.is_shutdown() { - return; - } - - let mut sock = match srv.accept() { - Ok(s) => s.0, - Err(e) => { - resp_tx.send(Err(format!("server accept err: {}", e))).unwrap(); - return; - } - }; - - resp_tx.send(ops.apply(&mut sock, port as usize)).unwrap(); - } - }); - - Handle::new(ops_tx, port) -} diff --git a/test/test.rs b/test/test.rs deleted file mode 100644 index 8e8fd4dc92..0000000000 --- a/test/test.rs +++ /dev/null @@ -1,38 +0,0 @@ -// extern crate curl; -// extern crate env_logger; -// -// #[macro_use] -// extern crate log; -// -// macro_rules! server { -// ($($ops:expr),+) => (server::setup(ops!($($ops),+))); -// } -// -// macro_rules! ops { -// ($op:expr) => (server::OpSequence::new($op)); -// ($op:expr, $($res:expr),+) => ( -// server::OpSequence::concat($op, ops!($($res),+)) -// ); -// } -// -// macro_rules! send { -// ($e:expr) => (server::Op::SendBytes($e)); -// } -// -// macro_rules! recv { -// ($e:expr) => (server::Op::ReceiveBytes($e)); -// } -// -// macro_rules! wait { -// ($dur:expr) => (server::Op::Wait($dur)); -// } -// -// mod server; -// mod test_delete; -// mod test_get; -// mod test_head; -// mod test_keep_alive; -// mod test_patch; -// mod test_post; -// mod test_proxy; -// mod test_put; diff --git a/test/test_delete.rs b/test/test_delete.rs deleted file mode 100644 index e233af5e04..0000000000 --- a/test/test_delete.rs +++ /dev/null @@ -1,52 +0,0 @@ -use curl::http; -use super::server; - -#[test] -pub fn test_delete_with_no_body() { - let srv = server!( - recv!(b"DELETE / HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\ - \r\n"), - send!(b"HTTP/1.1 200 OK\r\n\ - Content-Length: 5\r\n\r\n\ - Hello\r\n\r\n") - ); - - let res = http::handle() - .delete(server::url("/")) - .exec(); - - srv.assert(); - let res = res.unwrap(); - - assert!(res.get_code() == 200); - assert!(res.get_body() == "Hello".as_bytes()); -} - -#[test] -pub fn test_delete_binary_with_slice() { - let srv = server!( - recv!(b"DELETE / HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\ - Content-Type: application/octet-stream\r\n\ - Content-Length: 11\r\n\ - \r\n\ - Foo Bar Baz"), - send!(b"HTTP/1.1 200 OK\r\n\ - Content-Length: 5\r\n\r\n\ - Hello\r\n\r\n") - ); - - let res = http::handle() - .delete(server::url("/")) - .body("Foo Bar Baz") - .exec(); // .unwrap(); - - srv.assert(); - let res = res.unwrap(); - - assert!(res.get_code() == 200); - assert!(res.get_body() == "Hello".as_bytes()); -} diff --git a/test/test_get.rs b/test/test_get.rs deleted file mode 100644 index 6c4ef291c0..0000000000 --- a/test/test_get.rs +++ /dev/null @@ -1,114 +0,0 @@ -use curl::http::handle; -use super::server; - -#[test] -pub fn test_simple_get() { - let srv = server!( - recv!( - b"GET / HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\r\n"), // Send the data - send!( - b"HTTP/1.1 200 OK\r\n\ - Content-Length: 5\r\n\r\n\ - Hello\r\n")); // Sends - - let res = handle() - .get(server::url("/")) - .exec().unwrap(); - - srv.assert(); - - assert!(res.get_code() == 200, "code is {}", res.get_code()); - assert!(res.get_body() == "Hello".as_bytes()); - assert!(res.get_headers().len() == 1); - assert!(res.get_header("content-length") == ["5".to_string()]); -} - -#[test] -pub fn test_get_with_custom_headers() { - let srv = server!( - recv!( - b"GET / HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\ - User-Agent: Zomg Test\r\n\r\n"), - send!( - b"HTTP/1.1 200 OK\r\n\ - Content-Length: 5\r\n\r\n\ - Hello\r\n\r\n")); - - let res = handle() - .get(server::url("/")) - .header("User-Agent", "Zomg Test") - .exec().unwrap(); - - srv.assert(); - - assert!(res.get_code() == 200); - assert!(res.get_body() == "Hello".as_bytes()); - assert!(res.get_headers().len() == 1); - assert!(res.get_header("content-length") == ["5".to_string()]); -} - -#[test] -pub fn test_get_tracking_progress() { - let srv = server!( - recv!( - b"GET / HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\r\n"), - send!( - b"HTTP/1.1 200 OK\r\n\ - Content-Length: 5\r\n\r\n\ - Hello\r\n")); - - let mut dl = 0; - let mut cnt = 0; - - let res = handle() - .get(server::url("/")) - .progress(|_, dlnow, _, _| { - cnt = cnt + 1; - dl = dlnow - }) - .exec().unwrap(); - - srv.assert(); - - assert!(res.get_code() == 200); - assert!(dl == 5); - assert!(cnt > 1); -} - -#[test] -pub fn follows_redirects() { - let srv1 = server!( - recv!( - b"GET / HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\r\n"), - send!( - b"HTTP/1.1 301 Moved Permanently\r\n\ - Location: http://localhost:{PORT}/test\r\n\r\n")); - let srv2 = server!( - recv!( - b"GET /test HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\r\n"), - send!( - b"HTTP/1.1 200 OK\r\n\r\n\ - response!")); - - let res = handle() - .get(server::url("/")) - .follow_redirects(true) - .exec() - .unwrap(); - - srv1.assert(); - srv2.assert(); - - assert!(res.get_code() == 200); - assert_eq!(res.get_body(), b"response!"); -} diff --git a/test/test_head.rs b/test/test_head.rs deleted file mode 100644 index a4d33b8deb..0000000000 --- a/test/test_head.rs +++ /dev/null @@ -1,26 +0,0 @@ -use curl::http::handle; -use super::server; - -#[test] -pub fn test_simple_head() { - let srv = server!( - recv!(b"HEAD / HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\ - \r\n"), // Send the data - send!(b"HTTP/1.1 200 OK\r\n\ - Content-Length: 5\r\n\r\n") - ); - - let res = handle() - .head(server::url("/")) - .exec(); - - srv.assert(); - let res = res.unwrap(); - - assert!(res.get_code() == 200, "code is {}", res.get_code()); - assert!(res.get_body().len() == 0); - assert!(res.get_headers().len() == 1); - assert!(res.get_header("content-length") == ["5".to_string()]); -} diff --git a/test/test_keep_alive.rs b/test/test_keep_alive.rs deleted file mode 100644 index 719941cfdc..0000000000 --- a/test/test_keep_alive.rs +++ /dev/null @@ -1,68 +0,0 @@ -use curl::http::handle; -use super::server; - -#[test] -pub fn test_get_requests() { - let srv = server!( - recv!(b"GET / HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\r\n"), - send!(b"HTTP/1.1 200 OK\r\n\ - Content-Length: 5\r\n\r\n\ - Hello\r\n\r\n"), - recv!(b"GET /next HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\r\n"), - send!(b"HTTP/1.1 200 OK\r\n\ - Content-Length: 5\r\n\r\n\ - World\r\n\r\n") - ); - - let mut handle = handle(); - let res1 = handle.get(server::url("/")).exec().unwrap(); - let res2 = handle.get(server::url("/next")).exec().unwrap(); - - srv.assert(); - - assert!(res1.get_code() == 200); - assert!(res1.get_body() == "Hello".as_bytes()); - - assert!(res2.get_code() == 200); - assert!(res2.get_body() == "World".as_bytes()); -} - -#[test] -pub fn test_post_get_requests() { - let srv = server!( - recv!(b"POST / HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\ - Content-Type: application/octet-stream\r\n\ - Content-Length: 5\r\n\ - \r\n\ - Hello"), - send!(b"HTTP/1.1 200 OK\r\n\ - Content-Length: 5\r\n\r\n\ - World\r\n\r\n"), - recv!(b"GET /next HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\r\n"), - send!(b"HTTP/1.1 200 OK\r\n\ - Content-Length: 4\r\n\r\n\ - NEXT\r\n\r\n") - ); - - let mut handle = handle().timeout(1000); - let res1 = handle.post(server::url("/"), "Hello").exec().unwrap(); - let res2 = handle.get(server::url("/next")).exec().unwrap(); - - srv.assert(); - - assert!(res1.get_code() == 200); - assert!(res1.get_body() == "World".as_bytes(), "actual={}", - String::from_utf8_lossy(res1.get_body())); - - assert!(res2.get_code() == 200); - assert!(res2.get_body() == "NEXT".as_bytes(), "actual={}", - String::from_utf8_lossy(res2.get_body())); -} diff --git a/test/test_patch.rs b/test/test_patch.rs deleted file mode 100644 index 621c6e4e76..0000000000 --- a/test/test_patch.rs +++ /dev/null @@ -1,53 +0,0 @@ -use curl::http; -use super::server; - -#[test] -pub fn test_patch_binary_with_slice() { - let srv = server!( - recv!(b"PATCH / HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\ - Content-Type: application/octet-stream\r\n\ - Content-Length: 11\r\n\ - \r\n\ - Foo Bar Baz"), - send!(b"HTTP/1.1 200 OK\r\n\ - Content-Length: 5\r\n\r\n\ - Hello\r\n\r\n") - ); - - let res = http::handle() - .patch(server::url("/"), "Foo Bar Baz") - .exec().unwrap(); - - srv.assert(); - - assert!(res.get_code() == 200); - assert!(res.get_body() == "Hello".as_bytes()); -} - -#[test] -pub fn test_patch_binary_with_content_type() { - let srv = server!( - recv!(b"PATCH / HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\ - Content-Type: application/foobar\r\n\ - Content-Length: 11\r\n\ - \r\n\ - Foo Bar Baz"), - send!(b"HTTP/1.1 200 OK\r\n\ - Content-Length: 5\r\n\r\n\ - Hello\r\n\r\n") - ); - - let res = http::handle() - .patch(server::url("/"), "Foo Bar Baz") - .content_type("application/foobar") - .exec().unwrap(); - - srv.assert(); - - assert!(res.get_code() == 200); - assert!(res.get_body() == "Hello".as_bytes()); -} diff --git a/test/test_post.rs b/test/test_post.rs deleted file mode 100644 index 65c682f97f..0000000000 --- a/test/test_post.rs +++ /dev/null @@ -1,107 +0,0 @@ -use curl::http; -use super::server; - -#[test] -pub fn test_post_binary_with_slice() { - let srv = server!( - recv!(b"POST / HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\ - Content-Type: application/octet-stream\r\n\ - Content-Length: 11\r\n\ - \r\n\ - Foo Bar Baz"), - send!(b"HTTP/1.1 200 OK\r\n\ - Content-Length: 5\r\n\r\n\ - Hello\r\n\r\n") - ); - - let res = http::handle() - .post(server::url("/"), "Foo Bar Baz") - .exec().unwrap(); - - srv.assert(); - - assert!(res.get_code() == 200); - assert!(res.get_body() == "Hello".as_bytes()); -} - -#[test] -pub fn test_post_binary_with_string() { - let srv = server!( - recv!(b"POST / HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\ - Content-Type: application/octet-stream\r\n\ - Content-Length: 11\r\n\ - \r\n\ - Foo Bar Baz"), - send!(b"HTTP/1.1 200 OK\r\n\ - Content-Length: 5\r\n\r\n\ - Hello\r\n\r\n") - ); - - let body = "Foo Bar Baz".to_string(); - let res = http::handle() - .post(server::url("/"), &body) - .exec().unwrap(); - - srv.assert(); - - assert!(res.get_code() == 200); - assert!(res.get_body() == "Hello".as_bytes()); -} - -#[test] -pub fn test_post_binary_with_reader() { - let srv = server!( - recv!(b"POST / HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\ - Transfer-Encoding: chunked\r\n\ - Content-Type: application/octet-stream\r\n\ - \r\n\ - b\r\n\ - Foo Bar Baz"), - send!(b"HTTP/1.1 200 OK\r\n\ - Content-Length: 5\r\n\r\n\ - Hello\r\n\r\n") - ); - - let mut body = "Foo Bar Baz".as_bytes(); - let res = http::handle() - .post(server::url("/"), &mut body) - .exec().unwrap(); - - srv.assert(); - - assert!(res.get_code() == 200); - assert!(res.get_body() == "Hello".as_bytes()); -} - -#[test] -pub fn test_post_binary_with_reader_and_content_length() { - let srv = server!( - recv!(b"POST / HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\ - Content-Length: 11\r\n\ - Content-Type: application/octet-stream\r\n\ - \r\n\ - Foo Bar Baz"), - send!(b"HTTP/1.1 200 OK\r\n\ - Content-Length: 5\r\n\r\n\ - Hello\r\n\r\n") - ); - - let mut body = "Foo Bar Baz".as_bytes(); - let res = http::handle() - .post(server::url("/"), &mut body) - .content_length(11) - .exec().unwrap(); - - srv.assert(); - - assert!(res.get_code() == 200); - assert!(res.get_body() == "Hello".as_bytes()); -} diff --git a/test/test_proxy.rs b/test/test_proxy.rs deleted file mode 100644 index c4a63533ed..0000000000 --- a/test/test_proxy.rs +++ /dev/null @@ -1,30 +0,0 @@ -use curl::http; -use super::server; - -#[test] -pub fn test_proxy() { - ::env_logger::init().unwrap(); - - let srv = server!( - recv!(b"POST http://www.google.com/ HTTP/1.1\r\n\ - Host: www.google.com\r\n\ - Accept: */*\r\n\ - Proxy-Connection: Keep-Alive\r\n\ - Content-Type: application/octet-stream\r\n\ - Content-Length: 11\r\n\ - \r\n\ - Foo Bar Baz"), - send!(b"HTTP/1.1 200 OK\r\n\ - Content-Length: 5\r\n\r\n\ - Hello\r\n\r\n")); - - let res = http::handle() - .proxy(server::url("/")) - .post("http://www.google.com/", "Foo Bar Baz") - .exec().unwrap(); - - srv.assert(); - - assert!(res.get_code() == 200); - assert!(res.get_body() == "Hello".as_bytes()); -} diff --git a/test/test_put.rs b/test/test_put.rs deleted file mode 100644 index 5898a125bd..0000000000 --- a/test/test_put.rs +++ /dev/null @@ -1,53 +0,0 @@ -use curl::http; -use super::server; - -#[test] -pub fn test_put_binary_with_slice() { - let srv = server!( - recv!(b"PUT / HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\ - Content-Type: application/octet-stream\r\n\ - Content-Length: 11\r\n\ - \r\n\ - Foo Bar Baz"), - send!(b"HTTP/1.1 200 OK\r\n\ - Content-Length: 5\r\n\r\n\ - Hello\r\n\r\n") - ); - - let res = http::handle() - .put(server::url("/"), "Foo Bar Baz") - .exec().unwrap(); - - srv.assert(); - - assert!(res.get_code() == 200); - assert!(res.get_body() == "Hello".as_bytes()); -} - -#[test] -pub fn test_put_binary_with_content_type() { - let srv = server!( - recv!(b"PUT / HTTP/1.1\r\n\ - Host: localhost:{PORT}\r\n\ - Accept: */*\r\n\ - Content-Type: application/foobar\r\n\ - Content-Length: 11\r\n\ - \r\n\ - Foo Bar Baz"), - send!(b"HTTP/1.1 200 OK\r\n\ - Content-Length: 5\r\n\r\n\ - Hello\r\n\r\n") - ); - - let res = http::handle() - .put(server::url("/"), "Foo Bar Baz") - .content_type("application/foobar") - .exec().unwrap(); - - srv.assert(); - - assert!(res.get_code() == 200); - assert!(res.get_body() == "Hello".as_bytes()); -} From 17f9e0f01fc930cf7b5d657da79dcacb32f722b5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 10 May 2016 11:30:48 -0700 Subject: [PATCH 48/71] Remove stray comment --- src/easy.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/easy.rs b/src/easy.rs index acd460eba9..49ce3a4070 100644 --- a/src/easy.rs +++ b/src/easy.rs @@ -530,7 +530,6 @@ impl<'a> Easy<'a> { f as *mut F as *const c_char)); return Ok(()); - // TODO: expose `handle`? is that safe? unsafe extern fn cb(buffer: *mut c_char, size: size_t, nitems: size_t, From 241b0a408686b48850a9d91dab9c8f924db8877a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 10 May 2016 11:51:42 -0700 Subject: [PATCH 49/71] Add a function to reset the lifetime --- src/easy.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/easy.rs b/src/easy.rs index 49ce3a4070..3c32039cbb 100644 --- a/src/easy.rs +++ b/src/easy.rs @@ -4,6 +4,7 @@ use std::cell::Cell; use std::ffi::{CString, CStr}; use std::io::SeekFrom; use std::marker; +use std::mem; use std::path::Path; use std::slice; use std::str; @@ -2136,6 +2137,24 @@ impl<'a> Easy<'a> { } } + /// Re-initializes this handle to all the default values *and* resets the + /// lifetime associated with this handle. + /// + /// The lifetime on this handle is currently used to prevent storing + /// configuration which references data that lives shorter than this handle + /// (e.g. preventing use-after-free). This can make it difficult, however, + /// to reuse an `Easy` handle as the lifetime of callbacks may be limited to + /// a smaller scope. + /// + /// This will have the same effect as `reset`, plus the additional effect of + /// returning a handle with a new arbitrary lifetime. + pub fn reset_lifetime<'b>(mut self) -> Easy<'b> { + self.reset(); + let new = Easy { handle: self.handle, _marker: marker::PhantomData }; + mem::forget(self); + new + } + /// Receives data from a connected socket. /// /// Only useful after a successful `perform` with the `connect_only` option From b00e6b5724229aaa0ab332dc3eaf4a20942c7dcf Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 10 May 2016 13:17:36 -0700 Subject: [PATCH 50/71] Update curl submodule again --- curl-sys/curl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/curl-sys/curl b/curl-sys/curl index 7776801ddf..c1f2b7d296 160000 --- a/curl-sys/curl +++ b/curl-sys/curl @@ -1 +1 @@ -Subproject commit 7776801ddf93c950634b58888ea0150cc967a5b9 +Subproject commit c1f2b7d296dd7d2834f964a9480cfd68fd3e83fd From 74341313f1e7ba213a691951651a8600af545a3a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 10 May 2016 14:40:24 -0700 Subject: [PATCH 51/71] Use std::panic and test on beta for now --- .travis.yml | 18 +++++++++--------- src/panic.rs | 38 +++++++++++++++++++++++--------------- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/.travis.yml b/.travis.yml index d42552e013..1e78fc6fc0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,10 +2,10 @@ language: rust matrix: include: - os: linux - rust: stable + rust: beta env: TARGET=x86_64-unknown-linux-gnu - os: linux - rust: stable + rust: beta env: TARGET=i686-unknown-linux-gnu addons: apt: @@ -13,10 +13,10 @@ matrix: - gcc-multilib - libssl-dev:i386 - os: linux - rust: stable + rust: beta env: TARGET=x86_64-unknown-linux-gnu LIBCURL_NO_PKG_CONFIG=1 - os: linux - rust: stable + rust: beta env: TARGET=i686-unknown-linux-gnu LIBCURL_NO_PKG_CONFIG=1 addons: apt: @@ -24,14 +24,14 @@ matrix: - gcc-multilib - libssl-dev:i386 - os: osx - rust: stable + rust: beta env: TARGET=x86_64-apple-darwin - os: osx - rust: stable - env: TARGET=i686-apple-darwin - - os: linux rust: beta - env: TARGET=x86_64-unknown-linux-gnu + env: TARGET=i686-apple-darwin + # - os: linux + # rust: beta + # env: TARGET=x86_64-unknown-linux-gnu - os: linux rust: nightly env: TARGET=x86_64-unknown-linux-gnu diff --git a/src/panic.rs b/src/panic.rs index f2d070de66..1da2179851 100644 --- a/src/panic.rs +++ b/src/panic.rs @@ -1,22 +1,30 @@ -struct Bomb { - armed: bool, -} +use std::any::Any; +use std::cell::RefCell; +use std::panic::{self, AssertUnwindSafe}; -impl Drop for Bomb { - fn drop(&mut self) { - if self.armed { - panic!("cannot panic in a callback in libcurl, panicking again to \ - abort the process safely") - } +thread_local!(static LAST_ERROR: RefCell>> = { + RefCell::new(None) +}); + +pub fn catch T>(f: F) -> Option { + if LAST_ERROR.with(|slot| slot.borrow().is_some()) { + return None } -} -pub fn catch R>(f: F) -> Option { - let mut bomb = Bomb { armed: true }; - let ret = f(); - bomb.armed = false; - Some(ret) + // Note that `AssertUnwindSafe` is used here as we prevent reentering + // arbitrary code due to the `LAST_ERROR` check above plus propagation of a + // panic after we return back to user code from C. + match panic::catch_unwind(AssertUnwindSafe(f)) { + Ok(ret) => Some(ret), + Err(e) => { + LAST_ERROR.with(|slot| *slot.borrow_mut() = Some(e)); + None + } + } } pub fn propagate() { + if let Some(t) = LAST_ERROR.with(|slot| slot.borrow_mut().take()) { + panic::resume_unwind(t) + } } From 2c8d8cf74ae1cbe7e4bfbd7a527d34c6f5add898 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 10 May 2016 14:41:38 -0700 Subject: [PATCH 52/71] Add a test where the main thread panics --- tests/easy.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/easy.rs b/tests/easy.rs index 50ed78cc0f..3ec6e93ee0 100644 --- a/tests/easy.rs +++ b/tests/easy.rs @@ -538,3 +538,23 @@ HTTP/1.1 200 OK\r\n\ // .collect::>(); // assert_eq!(cookies.len(), 1); } + +#[test] +#[should_panic] +fn panic_in_callback() { + let s = Server::new(); + s.receive("\ +GET / HTTP/1.1\r\n\ +Host: 127.0.0.1:$PORT\r\n\ +Accept: */*\r\n\ +\r\n"); + s.send("\ +HTTP/1.1 200 OK\r\n\ +\r\n"); + + let mut header = |_: &[u8]| panic!(); + let mut h = handle(); + t!(h.url(&s.url("/"))); + t!(h.header_function(&mut header)); + t!(h.perform()); +} From 3293678ba090304b7aa17e1e84d8ca656a81a540 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 10 May 2016 14:49:10 -0700 Subject: [PATCH 53/71] Add some crate docs --- src/lib.rs | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 78f55bef27..d5dd8087f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,45 @@ -//! TODO: dox +//! Rust bindings to the libcurl C library +//! +//! This crate contains bindings for an HTTP/HTTPS client which is powered by +//! [libcurl], the same library behind the `curl` command line tool. The API +//! currently closely matches that of libcurl itself, except that a Rustic layer +//! of safety is applied on top. +//! +//! [libcurl]: https://curl.haxx.se/libcurl/ +//! +//! # The "Easy" API +//! +//! The easiest way to send a request is to use the `Easy` api which corresponds +//! to `CURL` in libcurl. This handle supports a wide variety of options and can +//! be used to make a single blocking request in a thread. Callbacks can be +//! specified to deal with data as it arrives and a handle can be reused to +//! cache connections and such. +//! +//! ```rust,no_run +//! use curl::easy::Easy; +//! +//! // Write the contents of rust-lang.org to stdout +//! let mut easy = Easy::new(); +//! easy.url("https://www.rust-lang.org/").unwrap(); +//! easy.perform().unwrap(); +//! ``` +//! +//! # What about multiple concurrent HTTP requests? +//! +//! One option you have currently is to send multiple requests in multiple +//! threads, but otherwise libcurl has a "multi" interface for doing this +//! operation. This unfortunately has not yet been bound in this crate, but +//! those should be coming soon! +//! +//! # Where does libcurl come from? +//! +//! This crate links to the `curl-sys` crate which is in turn responsible for +//! acquiring and linking to the libcurl library. Currently this crate will +//! build libcurl from source if one is not already detected on the system. +//! +//! There is a large number of releases for libcurl, all with different sets of +//! capabilities. Robust programs may wish to inspect `Version::get()` to test +//! what features are implemented in the linked build of libcurl at runtime. #![deny(missing_docs)] From 4ed9f4a71a330e6f2c8efd9785f2f2eb36340d77 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 10 May 2016 15:42:09 -0700 Subject: [PATCH 54/71] Add a whole bunch more Easy docs --- src/easy.rs | 209 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 182 insertions(+), 27 deletions(-) diff --git a/src/easy.rs b/src/easy.rs index 3c32039cbb..17cd01b2b1 100644 --- a/src/easy.rs +++ b/src/easy.rs @@ -1,4 +1,11 @@ -//! dox +//! Bindings to the "easy" libcurl API. +//! +//! This module contains some simple types like `Easy` and `List` which are just +//! wrappers around the corresponding libcurl types. There's also a few enums +//! scattered about for various options here and there. +//! +//! Most simple usage of libcurl will likely use the `Easy` structure here, and +//! you can find more docs about its usage on that struct. use std::cell::Cell; use std::ffi::{CString, CStr}; @@ -20,13 +27,82 @@ use panic; /// Raw bindings to a libcurl "easy session". /// -/// This type corresponds to the `CURL` type in libcurl. +/// This type corresponds to the `CURL` type in libcurl, and is probably what +/// you want for just sending off a simple HTTP request and fetching a response. +/// Each easy handle can be thought of as a large builder before calling the +/// final `perform` function. +/// +/// There are many many configuration options for each `Easy` handle, and they +/// should all have their own documentation indicating what it affects and how +/// it interacts with other options. Some implementations of libcurl can use +/// this handle to interact with many different protocols, although by default +/// this crate only guarantees the HTTP/HTTPS protocols working. +/// +/// This struct's associated lifetime indicates the lifetime of various pieces +/// of borrowed data that can be configured. Examples of this are callbacks, +/// POST data, and HTTP headers. The associated lifetime can be "reset" through +/// the `reset_lifetime` function to allow changing the scope of parameters on a +/// handle. +/// +/// Note that almost all methods on this structure which configure various +/// properties return a `Result`. This is largely used to detect whether the +/// underlying implementation of libcurl actually implements the option being +/// requested. If you're linked to a version of libcurl which doesn't support +/// the option, then an error will be returned. Some options also perform some +/// validation when they're set, and the error is returned through this vector. +/// +/// ## Examples +/// +/// Creating a handle which can be used later +/// +/// ``` +/// use curl::easy::Easy; +/// +/// let handle = Easy::new(); +/// ``` +/// +/// Send an HTTP request, writing the response to stdout. +/// +/// ``` +/// use curl::easy::Easy; +/// +/// let mut handle = Easy::new(); +/// handle.url("https://www.rust-lang.org/").unwrap(); +/// handle.perform().unwrap(); +/// ``` +/// +/// Collect all output of an HTTP request to a vector. +/// +/// ``` +/// use curl::easy::Easy; +/// +/// let mut data = Vec::new(); +/// { +/// let mut callback = |d: &[u8]| { +/// data.extend_from_slice(d); +/// d.len() +/// }; +/// let mut handle = Easy::new(); +/// handle.url("https://www.rust-lang.org/").unwrap(); +/// handle.write_function(&mut callback).unwrap(); +/// handle.perform().unwrap(); +/// } +/// println!("{:?}", data); +/// ``` +/// +/// More examples of various properties of an HTTP request can be found on the +/// specific methods as well. pub struct Easy<'a> { handle: *mut curl_sys::CURL, _marker: marker::PhantomData>, } +// libcurl guarantees that a CURL handle is fine to be transferred so long as +// it's not used concurrently, and we do that correctly ourselves. unsafe impl<'a> Send for Easy<'a> {} + +// This type is `Sync` as it adheres to the proper `self` conventions, which in +// this case for libcurl basically means that everything takes `&mut self`. unsafe impl<'a> Sync for Easy<'a> {} /// Possible proxy types that libcurl currently understands. @@ -173,10 +249,12 @@ impl<'a> Easy<'a> { unsafe { let handle = curl_sys::curl_easy_init(); assert!(!handle.is_null()); - Easy { + let mut ret = Easy { handle: handle, _marker: marker::PhantomData, - } + }; + let _ = ret.signal(false); + ret } } @@ -227,9 +305,15 @@ impl<'a> Easy<'a> { /// attempt to use signals to perform library functions. /// /// If this option is disabled then timeouts during name resolution will not - /// work unless libcurl is built against c-ares. + /// work unless libcurl is built against c-ares. Note that enabling this + /// option, however, may not cause libcurl to work with multiple threads. /// - /// By default this option is `true` and corresponds to `CURLOPT_NOSIGNAL`. + /// By default this option is `false` and corresponds to `CURLOPT_NOSIGNAL`. + /// Note that this default is **different than libcurl** as it is intended + /// that this library is threadsafe by default. See the [libcurl docs] for + /// some more information. + /// + /// [libcurl docs]: https://curl.haxx.se/libcurl/c/threadsafe.html pub fn signal(&mut self, signal: bool) -> Result<(), Error> { self.setopt_long(curl_sys::CURLOPT_NOSIGNAL, (!signal) as c_long) @@ -275,6 +359,21 @@ impl<'a> Easy<'a> { /// /// By default data is written to stdout, and this corresponds to the /// `CURLOPT_WRITEFUNCTION` and `CURLOPT_WRITEDATA` options. + /// + /// # Examples + /// + /// ``` + /// use std::io::*; + /// use curl::easy::Easy; + /// + /// let mut callback = |d: &[u8]| { + /// stdout().write(d).unwrap_or(0) + /// }; + /// let mut handle = Easy::new(); + /// handle.url("https://www.rust-lang.org/").unwrap(); + /// handle.write_function(&mut callback).unwrap(); + /// handle.perform().unwrap(); + /// ``` pub fn write_function(&mut self, f: &'a mut F) -> Result<(), Error> where F: FnMut(&[u8]) -> usize { @@ -321,6 +420,23 @@ impl<'a> Easy<'a> { /// /// By default data is read from stdin, and this corresponds to the /// `CURLOPT_READFUNCTION` and `CURLOPT_READDATA` options. + /// + /// # Examples + /// + /// ``` + /// use std::io::*; + /// use curl::easy::Easy; + /// + /// let mut data_to_upload = &b"foobar"[..]; + /// let mut callback = |into: &mut [u8]| { + /// data_to_upload.read(into).unwrap_or(0) + /// }; + /// let mut handle = Easy::new(); + /// handle.url("https://example.com/login").unwrap(); + /// handle.read_function(&mut callback).unwrap(); + /// handle.post(true).unwrap(); + /// handle.perform().unwrap(); + /// ``` pub fn read_function(&mut self, f: &'a mut F) -> Result<(), Error> where F: FnMut(&mut [u8]) -> usize { @@ -522,6 +638,30 @@ impl<'a> Easy<'a> { /// /// By default this option is not set and corresponds to the /// `CURLOPT_HEADERFUNCTION` and `CURLOPT_HEADERDATA` options. + /// + /// # Examples + /// + /// ``` + /// use std::io::*; + /// use std::str; + /// + /// use curl::easy::Easy; + /// + /// let mut headers = Vec::new(); + /// { + /// let mut data_to_upload = b"foobar"; + /// let mut callback = |header: &[u8]| { + /// headers.push(str::from_utf8(header).unwrap().to_string()); + /// true + /// }; + /// let mut handle = Easy::new(); + /// handle.url("https://www.rust-lang.org/").unwrap(); + /// handle.header_function(&mut callback).unwrap(); + /// handle.perform().unwrap(); + /// } + /// + /// println!("{:?}", headers); + /// ``` pub fn header_function(&mut self, f: &'a mut F) -> Result<(), Error> where F: FnMut(&[u8]) -> bool { @@ -960,6 +1100,21 @@ impl<'a> Easy<'a> { /// /// By default this option is not set and corresponds to /// `CURLOPT_HTTPHEADER` + /// + /// # Examples + /// + /// ``` + /// use curl::easy::{Easy, List}; + /// + /// let mut list = List::new(); + /// list.append("Foo: bar").unwrap(); + /// list.append("Bar: baz").unwrap(); + /// + /// let mut handle = Easy::new(); + /// handle.url("https://www.rust-lang.org/").unwrap(); + /// handle.http_headers(&list).unwrap(); + /// handle.perform().unwrap(); + /// ``` pub fn http_headers(&mut self, list: &'a List) -> Result<(), Error> { self.setopt_ptr(curl_sys::CURLOPT_HTTPHEADER, list.raw as *const _) } @@ -1822,7 +1977,7 @@ impl<'a> Easy<'a> { /// /// Returns `Ok(None)` if no effective url is listed or `Err` if an error /// happens or the underlying bytes aren't valid utf-8. - pub fn effective_url(&self) -> Result, Error> { + pub fn effective_url(&mut self) -> Result, Error> { self.getopt_str(curl_sys::CURLINFO_EFFECTIVE_URL) } @@ -1835,7 +1990,7 @@ impl<'a> Easy<'a> { /// /// Returns `Ok(None)` if no effective url is listed or `Err` if an error /// happens or the underlying bytes aren't valid utf-8. - pub fn effective_url_bytes(&self) -> Result, Error> { + pub fn effective_url_bytes(&mut self) -> Result, Error> { self.getopt_bytes(curl_sys::CURLINFO_EFFECTIVE_URL) } @@ -1847,7 +2002,7 @@ impl<'a> Easy<'a> { /// /// Corresponds to `CURLINFO_RESPONSE_CODE` and returns an error if this /// option is not supported. - pub fn response_code(&self) -> Result { + pub fn response_code(&mut self) -> Result { self.getopt_long(curl_sys::CURLINFO_RESPONSE_CODE).map(|c| c as u32) } @@ -1858,7 +2013,7 @@ impl<'a> Easy<'a> { /// /// Corresponds to `CURLINFO_HTTP_CONNECTCODE` and returns an error if this /// option is not supported. - pub fn http_connectcode(&self) -> Result { + pub fn http_connectcode(&mut self) -> Result { self.getopt_long(curl_sys::CURLINFO_HTTP_CONNECTCODE).map(|c| c as u32) } @@ -1876,7 +2031,7 @@ impl<'a> Easy<'a> { /// /// This corresponds to `CURLINFO_FILETIME` and may return an error if the /// option is not supported - pub fn filetime(&self) -> Result, Error> { + pub fn filetime(&mut self) -> Result, Error> { self.getopt_long(curl_sys::CURLINFO_FILETIME).map(|r| { if r == -1 { None @@ -1890,7 +2045,7 @@ impl<'a> Easy<'a> { /// /// Corresponds to `CURLINFO_REDIRECT_COUNT` and may return an error if the /// option isn't supported. - pub fn redirect_count(&self) -> Result { + pub fn redirect_count(&mut self) -> Result { self.getopt_long(curl_sys::CURLINFO_REDIRECT_COUNT).map(|c| c as u32) } @@ -1904,7 +2059,7 @@ impl<'a> Easy<'a> { /// /// Corresponds to `CURLINFO_REDIRECT_URL` and may return an error if the /// url isn't valid utf-8 or an error happens. - pub fn redirect_url(&self) -> Result, Error> { + pub fn redirect_url(&mut self) -> Result, Error> { self.getopt_str(curl_sys::CURLINFO_REDIRECT_URL) } @@ -1917,7 +2072,7 @@ impl<'a> Easy<'a> { /// URL. /// /// Corresponds to `CURLINFO_REDIRECT_URL` and may return an error. - pub fn redirect_url_bytes(&self) -> Result, Error> { + pub fn redirect_url_bytes(&mut self) -> Result, Error> { self.getopt_bytes(curl_sys::CURLINFO_REDIRECT_URL) } @@ -1925,7 +2080,7 @@ impl<'a> Easy<'a> { /// /// Corresponds to `CURLINFO_HEADER_SIZE` and may return an error if the /// option isn't supported. - pub fn header_size(&self) -> Result { + pub fn header_size(&mut self) -> Result { self.getopt_long(curl_sys::CURLINFO_HEADER_SIZE).map(|c| c as u64) } @@ -1933,7 +2088,7 @@ impl<'a> Easy<'a> { /// /// Corresponds to `CURLINFO_REQUEST_SIZE` and may return an error if the /// option isn't supported. - pub fn request_size(&self) -> Result { + pub fn request_size(&mut self) -> Result { self.getopt_long(curl_sys::CURLINFO_REQUEST_SIZE).map(|c| c as u64) } @@ -1946,7 +2101,7 @@ impl<'a> Easy<'a> { /// /// Corresponds to `CURLINFO_CONTENT_TYPE` and may return an error if the /// option isn't supported. - pub fn content_type(&self) -> Result, Error> { + pub fn content_type(&mut self) -> Result, Error> { self.getopt_str(curl_sys::CURLINFO_CONTENT_TYPE) } @@ -1959,7 +2114,7 @@ impl<'a> Easy<'a> { /// /// Corresponds to `CURLINFO_CONTENT_TYPE` and may return an error if the /// option isn't supported. - pub fn content_type_bytes(&self) -> Result, Error> { + pub fn content_type_bytes(&mut self) -> Result, Error> { self.getopt_bytes(curl_sys::CURLINFO_CONTENT_TYPE) } @@ -1970,7 +2125,7 @@ impl<'a> Easy<'a> { /// /// Corresponds to `CURLINFO_OS_ERRNO` and may return an error if the /// option isn't supported. - pub fn os_errno(&self) -> Result { + pub fn os_errno(&mut self) -> Result { self.getopt_long(curl_sys::CURLINFO_OS_ERRNO).map(|c| c as i32) } @@ -1982,7 +2137,7 @@ impl<'a> Easy<'a> { /// /// Corresponds to `CURLINFO_PRIMARY_IP` and may return an error if the /// option isn't supported. - pub fn primary_ip(&self) -> Result, Error> { + pub fn primary_ip(&mut self) -> Result, Error> { self.getopt_str(curl_sys::CURLINFO_PRIMARY_IP) } @@ -1990,7 +2145,7 @@ impl<'a> Easy<'a> { /// /// Corresponds to `CURLINFO_PRIMARY_PORT` and may return an error if the /// option isn't supported. - pub fn primary_port(&self) -> Result { + pub fn primary_port(&mut self) -> Result { self.getopt_long(curl_sys::CURLINFO_PRIMARY_PORT).map(|c| c as u16) } @@ -2002,7 +2157,7 @@ impl<'a> Easy<'a> { /// /// Corresponds to `CURLINFO_LOCAL_IP` and may return an error if the /// option isn't supported. - pub fn local_ip(&self) -> Result, Error> { + pub fn local_ip(&mut self) -> Result, Error> { self.getopt_str(curl_sys::CURLINFO_LOCAL_IP) } @@ -2010,7 +2165,7 @@ impl<'a> Easy<'a> { /// /// Corresponds to `CURLINFO_LOCAL_PORT` and may return an error if the /// option isn't supported. - pub fn local_port(&self) -> Result { + pub fn local_port(&mut self) -> Result { self.getopt_long(curl_sys::CURLINFO_LOCAL_PORT).map(|c| c as u16) } @@ -2020,7 +2175,7 @@ impl<'a> Easy<'a> { /// /// Corresponds to the `CURLINFO_COOKIELIST` option and may return an error /// if the option isn't supported. - pub fn cookies(&self) -> Result { + pub fn cookies(&mut self) -> Result { unsafe { let mut list = 0 as *mut _; try!(cvt(curl_sys::curl_easy_getinfo(self.handle, @@ -2238,7 +2393,7 @@ impl<'a> Easy<'a> { } } - fn getopt_bytes(&self, opt: curl_sys::CURLINFO) + fn getopt_bytes(&mut self, opt: curl_sys::CURLINFO) -> Result, Error> { unsafe { let mut p = 0 as *const c_char; @@ -2251,7 +2406,7 @@ impl<'a> Easy<'a> { } } - fn getopt_str(&self, opt: curl_sys::CURLINFO) + fn getopt_str(&mut self, opt: curl_sys::CURLINFO) -> Result, Error> { match self.getopt_bytes(opt) { Ok(None) => Ok(None), @@ -2265,7 +2420,7 @@ impl<'a> Easy<'a> { } } - fn getopt_long(&self, opt: curl_sys::CURLINFO) -> Result { + fn getopt_long(&mut self, opt: curl_sys::CURLINFO) -> Result { unsafe { let mut p = 0; try!(cvt(curl_sys::curl_easy_getinfo(self.handle, opt, &mut p))); From 3695aa16da4b3777011a94331e237b97b49fb84f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 10 May 2016 16:08:52 -0700 Subject: [PATCH 55/71] Configure cainfo/capath by default If curl is linked statically then this'll always be needed --- Cargo.toml | 8 ++------ src/easy.rs | 25 +++++++++++++++++++++---- src/lib.rs | 3 +++ 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 963d3b3ba6..eb2971ec81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,9 +13,5 @@ libc = "0.2" curl-sys = { path = "curl-sys", version = "0.1.0" } # Unix platforms use OpenSSL for now to provide SSL functionality -# [target."cfg(all(unix, not(target_os = \"macos\")))".dependencies] -# openssl-sys = "0.7.0" -# [target.i686-unknown-linux-gnu.dependencies] -# openssl-sys = "0.7.0" -# [target.x86_64-unknown-linux-gnu.dependencies] -# openssl-sys = "0.7.0" +[target."cfg(all(unix, not(target_os = \"macos\")))".dependencies] +openssl-sys = "0.7.0" diff --git a/src/easy.rs b/src/easy.rs index 17cd01b2b1..22809c1041 100644 --- a/src/easy.rs +++ b/src/easy.rs @@ -253,9 +253,28 @@ impl<'a> Easy<'a> { handle: handle, _marker: marker::PhantomData, }; - let _ = ret.signal(false); - ret + configure(&mut ret); + return ret + } + + fn configure(handle: &mut Easy) { + let _ = handle.signal(false); + ssl_configure(handle); } + + #[cfg(all(unix, not(target_os = "macos")))] + fn ssl_configure(handle: &mut Easy) { + let probe = ::openssl_sys::probe::probe(); + if let Some(ref path) = probe.cert_file { + let _ = handle.cainfo(path); + } + if let Some(ref path) = probe.cert_dir { + let _ = handle.capath(path); + } + } + + #[cfg(not(all(unix, not(target_os = "macos"))))] + fn ssl_configure(_handle: &mut Easy) {} } // ========================================================================= @@ -642,14 +661,12 @@ impl<'a> Easy<'a> { /// # Examples /// /// ``` - /// use std::io::*; /// use std::str; /// /// use curl::easy::Easy; /// /// let mut headers = Vec::new(); /// { - /// let mut data_to_upload = b"foobar"; /// let mut callback = |header: &[u8]| { /// headers.push(str::from_utf8(header).unwrap().to_string()); /// true diff --git a/src/lib.rs b/src/lib.rs index d5dd8087f0..2c706b2ffa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,6 +46,9 @@ extern crate curl_sys; extern crate libc; +#[cfg(all(unix, not(target_os = "macos")))] +extern crate openssl_sys; + use std::ffi::CStr; use std::str; use std::sync::{Once, ONCE_INIT}; From f9fc980a2e2e368a240c0b18177f0c6aad8a6520 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 10 May 2016 17:36:01 -0700 Subject: [PATCH 56/71] Try using rustup on appveyor --- appveyor.yml | 14 +++++++++----- curl-sys/build.rs | 3 ++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index b4ec4e75bb..153192e81b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,15 +7,19 @@ environment: - TARGET: x86_64-pc-windows-msvc - TARGET: i686-pc-windows-msvc install: - - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:TARGET}.exe" - - rust-nightly-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust" - - set PATH=%PATH%;C:\Program Files (x86)\Rust\bin + - curl -sSf -o rustup-init.exe https://win.rustup.rs/ + - rustup-init.exe -y + - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin + - rustup update beta-x86_64-pc-windows-msvc + - rustup default beta-x86_64-pc-windows-msvc + - rustup target add %TARGET% - if defined MSYS_BITS set PATH=C:\msys64\mingw%MSYS_BITS%\bin;C:\msys64\usr\bin;%PATH% - rustc -V - cargo -V + - set CARGO_TARGET_DIR=target build: false test_script: - - cargo test - - cargo run --manifest-path systest/Cargo.toml + - cargo test --target %TARGET% + - cargo run --manifest-path systest/Cargo.toml --target %TARGET% diff --git a/curl-sys/build.rs b/curl-sys/build.rs index cfff02adde..13d380a504 100644 --- a/curl-sys/build.rs +++ b/curl-sys/build.rs @@ -106,7 +106,8 @@ fn main() { cmd.arg("--enable-optimize"); cmd.arg(format!("--prefix={}", msys_compatible(&dst))); - if target != host { + if target != host && + (!target.contains("windows") || !host.contains("windows")) { cmd.arg(format!("--host={}", host)); cmd.arg(format!("--target={}", target)); } From 5ffc8c680dd310cea0a78776f1404d4667faa8db Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 11 May 2016 08:55:38 -0700 Subject: [PATCH 57/71] Continue fighting with appveyor --- appveyor.yml | 2 +- systest/build.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 153192e81b..f4068a242f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,7 +12,7 @@ install: - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin - rustup update beta-x86_64-pc-windows-msvc - rustup default beta-x86_64-pc-windows-msvc - - rustup target add %TARGET% + - if NOT "%TARGET%" == "x86_64-pc-windows-msvc" rustup target add %TARGET% - if defined MSYS_BITS set PATH=C:\msys64\mingw%MSYS_BITS%\bin;C:\msys64\usr\bin;%PATH% - rustc -V - cargo -V diff --git a/systest/build.rs b/systest/build.rs index ee5928e548..fd2f432fe4 100644 --- a/systest/build.rs +++ b/systest/build.rs @@ -9,6 +9,7 @@ fn main() { cfg.include(&out); } cfg.header("curl/curl.h"); + cfg.define("CURL_STATICLIB", None); cfg.field_name(|s, field| { if s == "curl_fileinfo" { field.replace("strings_", "strings.") From 416a7d0c369b00c41753f0bac5889eaa46b82058 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 11 May 2016 09:02:38 -0700 Subject: [PATCH 58/71] Let's debug appveyor --- appveyor.yml | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index f4068a242f..f07e60d01c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,22 +1,38 @@ environment: matrix: + - TARGET: i686-pc-windows-gnu + MINGW_URL: https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/4.9.2/threads-win32/dwarf/i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z/download + MINGW_ARCHIVE: i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z + MINGW_DIR: mingw32 - TARGET: x86_64-pc-windows-gnu MSYS_BITS: 64 - - TARGET: i686-pc-windows-gnu - MSYS_BITS: 32 - - TARGET: x86_64-pc-windows-msvc - TARGET: i686-pc-windows-msvc + - TARGET: x86_64-pc-windows-msvc + install: + # Install rust, x86_64-pc-windows-msvc host - curl -sSf -o rustup-init.exe https://win.rustup.rs/ - rustup-init.exe -y - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin - rustup update beta-x86_64-pc-windows-msvc - rustup default beta-x86_64-pc-windows-msvc + + # Install the target we're compiling for - if NOT "%TARGET%" == "x86_64-pc-windows-msvc" rustup target add %TARGET% + + # Use the system msys if we can - if defined MSYS_BITS set PATH=C:\msys64\mingw%MSYS_BITS%\bin;C:\msys64\usr\bin;%PATH% - - rustc -V - - cargo -V - - set CARGO_TARGET_DIR=target + + # download a custom compiler otherwise + - if defined MINGW_ARCHIVE appveyor DownloadFile "%MINGW_URL%" -FileName "%MINGW_ARCHIVE%" + - if defined MINGW_ARCHIVE 7z x -y "%MINGW_ARCHIVE%" > nul + - if defined MINGW_ARCHIVE set PATH=%CD%\%MINGW_DIR%\bin;C:\msys64\usr\bin;%PATH% + + # let's see what we got + - where gcc rustc cargo + - rustc -vV + - cargo -vV + - set CARGO_TARGET_DIR=%CD%\target build: false From 551775419dc8d92372f85831d90013a65df822e3 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 11 May 2016 16:07:25 -0700 Subject: [PATCH 59/71] Update README examples --- .travis.yml | 1 + README.md | 91 ++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 71 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1e78fc6fc0..661fc6287b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,6 +47,7 @@ script: - cargo run --manifest-path systest/Cargo.toml --target $TARGET - cargo doc --no-deps --target $TARGET - cargo doc --no-deps -p curl-sys --target $TARGET + - rustdoc --test README.md -L target/$TARGET/debug -L target/$TARGET/debug/deps after_success: - travis-cargo --only nightly doc-upload notifications: diff --git a/README.md b/README.md index 65478e23e3..b4447f98af 100644 --- a/README.md +++ b/README.md @@ -11,40 +11,80 @@ could (will probably) break API compatibility at any time**. ```rust extern crate curl; -use curl::http; +use curl::easy::Easy; -pub fn main() { - let resp = http::handle() - .get("http://www.example.com") - .exec().unwrap(); +// Print a web page onto stdout +fn main() { + let mut easy = Easy::new(); + easy.url("https://www.rust-lang.org/").unwrap(); + easy.perform().unwrap(); - println!("code={}; headers={:?}; body={:?}", - resp.get_code(), resp.get_headers(), resp.get_body()); + println!("{}", easy.response_code().unwrap()); } ``` -Response header names are automatically lower cased. +```rust +extern crate curl; + +use curl::easy::Easy; + +// Capture output into a local `Vec`. +fn main() { + let mut dst = Vec::new(); + let mut write = |data: &[u8]| { + dst.extend_from_slice(data); + data.len() + }; + let mut easy = Easy::new(); + easy.url("https://www.rust-lang.org/").unwrap(); + easy.write_function(&mut write).unwrap(); + easy.perform().unwrap(); +} +``` ## Post / Put requests Both of these methods expect that a request body is provided. A request body can be a `&[u8]`, `&str`, or `&Reader`. For example: -```rust -let resp = http::handle() - .post("http://www.example.com/upload", "this is the body") - .exec().unwrap(); +```rust,no_run +extern crate curl; + +use std::io::Read; +use curl::easy::Easy; + +fn main() { + let mut data = "this is the body".as_bytes(); + let mut read = |slice: &mut [u8]| { + data.read(slice).unwrap_or(0) + }; + + let mut easy = Easy::new(); + easy.url("http://www.example.com/upload").unwrap(); + easy.read_function(&mut read).unwrap(); + easy.post(true).unwrap(); + easy.perform().unwrap(); +} ``` ## Custom headers Custom headers can be specified as part of the request: -```rust -http::handle() - .get("http://www.example.com") - .header("Authorization", "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==") - .exec(); +```rust,no_run +extern crate curl; + +use curl::easy::{Easy, List}; + +fn main() { + let mut list = List::new(); + list.append("Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==").unwrap(); + + let mut easy = Easy::new(); + easy.url("http://www.example.com").unwrap(); + easy.http_headers(&list).unwrap(); + easy.perform().unwrap(); +} ``` ## Keep alive @@ -52,11 +92,20 @@ http::handle() The handle can be re-used across multiple requests. Curl will attempt to keep the connections alive. -```rust -let handle = http::handle(); +```rust,no_run +extern crate curl; + +use curl::easy::Easy; + +fn main() { + let mut handle = Easy::new(); -let resp1 = handle.get("http://www.example.com/foo").exec().unwrap(); -let resp2 = handle.get("http://www.example.com/bar").exec().unwrap(); + handle.url("http://www.example.com/foo").unwrap(); + handle.perform().unwrap(); + + handle.url("http://www.example.com/bar").unwrap(); + handle.perform().unwrap(); +} ``` ## Version Support From 793d14aef3755154b86b023d27b8e8f08ea0fe43 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 11 May 2016 16:21:16 -0700 Subject: [PATCH 60/71] Pass --target to rustdoc --test --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 661fc6287b..dac4d267f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,7 +47,7 @@ script: - cargo run --manifest-path systest/Cargo.toml --target $TARGET - cargo doc --no-deps --target $TARGET - cargo doc --no-deps -p curl-sys --target $TARGET - - rustdoc --test README.md -L target/$TARGET/debug -L target/$TARGET/debug/deps + - rustdoc --target $TARGET --test README.md -L target/$TARGET/debug -L target/$TARGET/debug/deps after_success: - travis-cargo --only nightly doc-upload notifications: From 3ccfc62cfc1764efa1daa4b5dc75dd54c50ffe46 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 11 May 2016 16:33:26 -0700 Subject: [PATCH 61/71] Ok, don't test the README --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index dac4d267f2..1e78fc6fc0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,7 +47,6 @@ script: - cargo run --manifest-path systest/Cargo.toml --target $TARGET - cargo doc --no-deps --target $TARGET - cargo doc --no-deps -p curl-sys --target $TARGET - - rustdoc --target $TARGET --test README.md -L target/$TARGET/debug -L target/$TARGET/debug/deps after_success: - travis-cargo --only nightly doc-upload notifications: From c44f24ad076ce94b175ae166e3a89d81a16cedda Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 11 May 2016 21:51:01 -0700 Subject: [PATCH 62/71] Perform default configuration on reset() --- src/easy.rs | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/easy.rs b/src/easy.rs index 22809c1041..a232957916 100644 --- a/src/easy.rs +++ b/src/easy.rs @@ -253,28 +253,9 @@ impl<'a> Easy<'a> { handle: handle, _marker: marker::PhantomData, }; - configure(&mut ret); + default_configure(&mut ret); return ret } - - fn configure(handle: &mut Easy) { - let _ = handle.signal(false); - ssl_configure(handle); - } - - #[cfg(all(unix, not(target_os = "macos")))] - fn ssl_configure(handle: &mut Easy) { - let probe = ::openssl_sys::probe::probe(); - if let Some(ref path) = probe.cert_file { - let _ = handle.cainfo(path); - } - if let Some(ref path) = probe.cert_dir { - let _ = handle.capath(path); - } - } - - #[cfg(not(all(unix, not(target_os = "macos"))))] - fn ssl_configure(_handle: &mut Easy) {} } // ========================================================================= @@ -2307,6 +2288,7 @@ impl<'a> Easy<'a> { unsafe { curl_sys::curl_easy_reset(self.handle); } + default_configure(self); } /// Re-initializes this handle to all the default values *and* resets the @@ -2446,6 +2428,25 @@ impl<'a> Easy<'a> { } } +fn default_configure(handle: &mut Easy) { + let _ = handle.signal(false); + ssl_configure(handle); +} + +#[cfg(all(unix, not(target_os = "macos")))] +fn ssl_configure(handle: &mut Easy) { + let probe = ::openssl_sys::probe::probe(); + if let Some(ref path) = probe.cert_file { + let _ = handle.cainfo(path); + } + if let Some(ref path) = probe.cert_dir { + let _ = handle.capath(path); + } +} + +#[cfg(not(all(unix, not(target_os = "macos"))))] +fn ssl_configure(_handle: &mut Easy) {} + impl<'a> Drop for Easy<'a> { fn drop(&mut self) { unsafe { From 9f38b6d832c6bbf2d150c1acff6cdd0d61ba1fa4 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 11 May 2016 23:59:23 -0700 Subject: [PATCH 63/71] Add private pointer storage --- src/easy.rs | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/easy.rs b/src/easy.rs index a232957916..f0a9fe1995 100644 --- a/src/easy.rs +++ b/src/easy.rs @@ -1963,6 +1963,23 @@ impl<'a> Easy<'a> { enable as c_long) } + /// Stores a private pointer-sized piece of data. + /// + /// This can be retrieved through the `private` function and otherwise + /// libcurl does not tamper with this value. This corresponds to + /// `CURLOPT_PRIVATE` and defaults to 0. + pub fn set_private(&mut self, private: usize) -> Result<(), Error> { + self.setopt_ptr(curl_sys::CURLOPT_PRIVATE, private as *const _) + } + + /// Fetches this handle's private pointer-sized piece of data. + /// + /// This corresponds to + /// `CURLINFO_PRIVATE` and defaults to 0. + pub fn private(&mut self) -> Result { + self.getopt_ptr(curl_sys::CURLINFO_PRIVATE).map(|p| p as usize) + } + // ========================================================================= // getters @@ -2395,8 +2412,7 @@ impl<'a> Easy<'a> { fn getopt_bytes(&mut self, opt: curl_sys::CURLINFO) -> Result, Error> { unsafe { - let mut p = 0 as *const c_char; - try!(cvt(curl_sys::curl_easy_getinfo(self.handle, opt, &mut p))); + let p = try!(self.getopt_ptr(opt)); if p.is_null() { Ok(None) } else { @@ -2405,6 +2421,15 @@ impl<'a> Easy<'a> { } } + fn getopt_ptr(&mut self, opt: curl_sys::CURLINFO) + -> Result<*const c_char, Error> { + unsafe { + let mut p = 0 as *const c_char; + try!(cvt(curl_sys::curl_easy_getinfo(self.handle, opt, &mut p))); + Ok(p) + } + } + fn getopt_str(&mut self, opt: curl_sys::CURLINFO) -> Result, Error> { match self.getopt_bytes(opt) { From 9183f80525817103ad3a4c45c2c4d3a98d9c58dd Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 12 May 2016 10:36:11 -0700 Subject: [PATCH 64/71] Tweak the README again --- README.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b4447f98af..9dd6900097 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ -# Curl-Rust +# curl-rust -libcurl bindings for Rust. **This project is in active development and -could (will probably) break API compatibility at any time**. +libcurl bindings for Rust -[![Build Status](https://travis-ci.org/carllerche/curl-rust.svg?branch=master)](https://travis-ci.org/carllerche/curl-rust) -[![crates.io](http://meritbadge.herokuapp.com/mio)](https://crates.io/crates/curl) +[![Build Status](https://travis-ci.org/alexcrichton/curl-rust.svg?branch=master)](https://travis-ci.org/alexcrichton/curl-rust) +[![Build status](https://ci.appveyor.com/api/projects/status/lx98wtbxhhhajpr9?svg=true)](https://ci.appveyor.com/project/alexcrichton/curl-rust) + +[Documentation](http://alexcrichton.com/curl-rust) ## Quick Start @@ -113,3 +114,8 @@ fn main() { The bindings have been developed using curl version 7.24.0. They should work with any newer version of curl and possibly with older versions, but this has not been tested. + +## License + +The `curl-rust` crate is licensed under the MIT license, see `LICENSE` for more +details. From 03a417e259a391b05e799d7b3a58c1b3f936bb8c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 12 May 2016 10:46:02 -0700 Subject: [PATCH 65/71] Improve error message of failed programs Closes #71 --- curl-sys/build.rs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/curl-sys/build.rs b/curl-sys/build.rs index 13d380a504..34f3ae42f0 100644 --- a/curl-sys/build.rs +++ b/curl-sys/build.rs @@ -6,6 +6,7 @@ use std::ffi::OsString; use std::fs; use std::path::{PathBuf, Path}; use std::process::Command; +use std::io::ErrorKind; macro_rules! t { ($e:expr) => (match $e { @@ -131,16 +132,30 @@ fn main() { cmd.arg("--disable-smb"); cmd.arg("--disable-sspi"); - run(&mut cmd); + run(&mut cmd, "sh"); run(Command::new(make()) .arg(&format!("-j{}", env::var("NUM_JOBS").unwrap())) .arg("install") - .current_dir(&dst.join("build"))); + .current_dir(&dst.join("build")), "make"); } -fn run(cmd: &mut Command) { +fn run(cmd: &mut Command, program: &str) { println!("running: {:?}", cmd); - assert!(t!(cmd.status()).success()); + let status = match cmd.status() { + Ok(status) => status, + Err(ref e) if e.kind() == ErrorKind::NotFound => { + fail(&format!("failed to execute command: {}\nis `{}` not installed?", + e, program)); + } + Err(e) => fail(&format!("failed to execute command: {}", e)), + }; + if !status.success() { + fail(&format!("command did not execute successfully, got: {}", status)); + } +} + +fn fail(s: &str) -> ! { + panic!("\n{}\n\nbuild script failed, must exit now", s) } fn make() -> &'static str { @@ -202,7 +217,7 @@ fn build_msvc(target: &str) { let _ = fs::remove_file(&inc.join("lib/zlib_a.lib")); t!(fs::hard_link(inc.join("lib/zlib.lib"), inc.join("lib/zlib_a.lib"))); } - run(&mut cmd); + run(&mut cmd, "nmake"); let name = format!("libcurl-vc-{}-release-static-zlib-static-\ ipv6-sspi-winssl", machine); From a6d0480394e0e9370d92eb6f3de63be3b2853d78 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 12 May 2016 10:51:25 -0700 Subject: [PATCH 66/71] Clean up curl-sys Cargo.toml --- curl-sys/Cargo.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/curl-sys/Cargo.toml b/curl-sys/Cargo.toml index f08f9f03ce..40c56ef7f2 100644 --- a/curl-sys/Cargo.toml +++ b/curl-sys/Cargo.toml @@ -23,8 +23,3 @@ libc = "0.2" [target."cfg(all(unix, not(target_os = \"macos\")))".dependencies] openssl-sys = ">= 0" - -[target.i686-unknown-linux-gnu.dependencies] -openssl-sys = ">= 0" -[target.x86_64-unknown-linux-gnu.dependencies] -openssl-sys = ">= 0" From 2c1e524f8e528e0a1d4d6741cda0cf2aa7797ac0 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 12 May 2016 10:54:39 -0700 Subject: [PATCH 67/71] Bump versions --- Cargo.toml | 2 +- curl-sys/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5d8ca4c928..df30dae730 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "curl" -version = "0.2.19" +version = "0.3.0" authors = ["Carl Lerche ", "Alex Crichton "] license = "MIT" diff --git a/curl-sys/Cargo.toml b/curl-sys/Cargo.toml index 40c56ef7f2..8f2beab14d 100644 --- a/curl-sys/Cargo.toml +++ b/curl-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "curl-sys" -version = "0.1.34" +version = "0.2.0" authors = ["Carl Lerche ", "Alex Crichton "] links = "curl" From ae8ac072e0eeffc489f8883661d31a01834a95cf Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 12 May 2016 11:04:37 -0700 Subject: [PATCH 68/71] Update curl-sys requirement --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index df30dae730..13ee1a72f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ description = "Rust bindings to libcurl for making HTTP requests" [dependencies] libc = "0.2" -curl-sys = { path = "curl-sys", version = "0.1.0" } +curl-sys = { path = "curl-sys", version = "0.2.0" } # Unix platforms use OpenSSL for now to provide SSL functionality [target."cfg(all(unix, not(target_os = \"macos\")))".dependencies] From ccf35d3e4a41f1542fa025fe2da77d036f708092 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 12 May 2016 16:46:30 -0700 Subject: [PATCH 69/71] Don't expose private for now May want to do this in a more rustic fashion later on. --- src/easy.rs | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/easy.rs b/src/easy.rs index f0a9fe1995..0a3071da36 100644 --- a/src/easy.rs +++ b/src/easy.rs @@ -1963,22 +1963,22 @@ impl<'a> Easy<'a> { enable as c_long) } - /// Stores a private pointer-sized piece of data. - /// - /// This can be retrieved through the `private` function and otherwise - /// libcurl does not tamper with this value. This corresponds to - /// `CURLOPT_PRIVATE` and defaults to 0. - pub fn set_private(&mut self, private: usize) -> Result<(), Error> { - self.setopt_ptr(curl_sys::CURLOPT_PRIVATE, private as *const _) - } + // /// Stores a private pointer-sized piece of data. + // /// + // /// This can be retrieved through the `private` function and otherwise + // /// libcurl does not tamper with this value. This corresponds to + // /// `CURLOPT_PRIVATE` and defaults to 0. + // pub fn set_private(&mut self, private: usize) -> Result<(), Error> { + // self.setopt_ptr(curl_sys::CURLOPT_PRIVATE, private as *const _) + // } - /// Fetches this handle's private pointer-sized piece of data. - /// - /// This corresponds to - /// `CURLINFO_PRIVATE` and defaults to 0. - pub fn private(&mut self) -> Result { - self.getopt_ptr(curl_sys::CURLINFO_PRIVATE).map(|p| p as usize) - } + // /// Fetches this handle's private pointer-sized piece of data. + // /// + // /// This corresponds to + // /// `CURLINFO_PRIVATE` and defaults to 0. + // pub fn private(&mut self) -> Result { + // self.getopt_ptr(curl_sys::CURLINFO_PRIVATE).map(|p| p as usize) + // } // ========================================================================= // getters @@ -2308,8 +2308,8 @@ impl<'a> Easy<'a> { default_configure(self); } - /// Re-initializes this handle to all the default values *and* resets the - /// lifetime associated with this handle. + /// Re-initializes all lifetime-related configuration of this handle and + /// returns a handle with a new associated lifetime. /// /// The lifetime on this handle is currently used to prevent storing /// configuration which references data that lives shorter than this handle @@ -2317,8 +2317,9 @@ impl<'a> Easy<'a> { /// to reuse an `Easy` handle as the lifetime of callbacks may be limited to /// a smaller scope. /// - /// This will have the same effect as `reset`, plus the additional effect of - /// returning a handle with a new arbitrary lifetime. + /// This will have the same effect as `reset` for any configuration option + /// that is also bounded by this handle's lifetime, plus the additional + /// effect of returning a handle with a new arbitrary lifetime. pub fn reset_lifetime<'b>(mut self) -> Easy<'b> { self.reset(); let new = Easy { handle: self.handle, _marker: marker::PhantomData }; From 6106d34e3bb198e75033c4fb53c386a983b164f4 Mon Sep 17 00:00:00 2001 From: bar Date: Thu, 12 May 2016 18:07:23 -0700 Subject: [PATCH 70/71] Don't take functions by reference More ergonomic but requires an allocation. Deemed worth the tradeoff! --- README.md | 9 +- src/easy.rs | 244 ++++++++++++++++++++++++++++---------------------- tests/easy.rs | 43 +++++---- 3 files changed, 163 insertions(+), 133 deletions(-) diff --git a/README.md b/README.md index 9dd6900097..4ed1c318e4 100644 --- a/README.md +++ b/README.md @@ -56,13 +56,12 @@ use curl::easy::Easy; fn main() { let mut data = "this is the body".as_bytes(); - let mut read = |slice: &mut [u8]| { - data.read(slice).unwrap_or(0) - }; let mut easy = Easy::new(); easy.url("http://www.example.com/upload").unwrap(); - easy.read_function(&mut read).unwrap(); + easy.read_function(|buf| { + data.read(buf).unwrap_or(0) + }).unwrap(); easy.post(true).unwrap(); easy.perform().unwrap(); } @@ -83,7 +82,7 @@ fn main() { let mut easy = Easy::new(); easy.url("http://www.example.com").unwrap(); - easy.http_headers(&list).unwrap(); + easy.http_headers(list).unwrap(); easy.perform().unwrap(); } ``` diff --git a/src/easy.rs b/src/easy.rs index 0a3071da36..5fada45408 100644 --- a/src/easy.rs +++ b/src/easy.rs @@ -11,7 +11,6 @@ use std::cell::Cell; use std::ffi::{CString, CStr}; use std::io::SeekFrom; use std::marker; -use std::mem; use std::path::Path; use std::slice; use std::str; @@ -95,6 +94,17 @@ use panic; pub struct Easy<'a> { handle: *mut curl_sys::CURL, _marker: marker::PhantomData>, + data: Box>, +} + +struct EasyData<'a> { + write: Box usize + Send + 'a>, + read: Box usize + Send + 'a>, + seek: Box SeekResult + Send + 'a>, + debug: Box, + header: Box bool + Send + 'a>, + progress: Box bool + Send + 'a>, + header_list: List, } // libcurl guarantees that a CURL handle is fine to be transferred so long as @@ -226,7 +236,6 @@ pub struct Iter<'a> { } unsafe impl Send for List {} -unsafe impl Sync for List {} fn cvt(r: curl_sys::CURLcode) -> Result<(), Error> { if r == curl_sys::CURLE_OK { @@ -251,6 +260,7 @@ impl<'a> Easy<'a> { assert!(!handle.is_null()); let mut ret = Easy { handle: handle, + data: blank_data(), _marker: marker::PhantomData, }; default_configure(&mut ret); @@ -366,32 +376,36 @@ impl<'a> Easy<'a> { /// use std::io::*; /// use curl::easy::Easy; /// - /// let mut callback = |d: &[u8]| { - /// stdout().write(d).unwrap_or(0) - /// }; /// let mut handle = Easy::new(); /// handle.url("https://www.rust-lang.org/").unwrap(); - /// handle.write_function(&mut callback).unwrap(); + /// handle.write_function(|data| { + /// stdout().write(data).unwrap_or(0) + /// }).unwrap(); /// handle.perform().unwrap(); /// ``` - pub fn write_function(&mut self, f: &'a mut F) -> Result<(), Error> - where F: FnMut(&[u8]) -> usize + pub fn write_function(&mut self, f: F) -> Result<(), Error> + where F: FnMut(&[u8]) -> usize + Send + 'a { + self._write_function(Box::new(f)) + } + + fn _write_function(&mut self, f: Box usize + Send + 'a>) + -> Result<(), Error> { + self.data.write = f; try!(self.setopt_ptr(curl_sys::CURLOPT_WRITEFUNCTION, - cb:: as usize as *const c_char)); + cb as usize as *const c_char)); + let ptr = &*self.data as *const _; try!(self.setopt_ptr(curl_sys::CURLOPT_WRITEDATA, - f as *mut F as *const c_char)); + ptr as *const c_char)); return Ok(()); - unsafe extern fn cb(ptr: *mut c_char, - size: size_t, - nmemb: size_t, - data: *mut c_void) -> size_t - where F: FnMut(&[u8]) -> usize, - { + unsafe extern fn cb(ptr: *mut c_char, + size: size_t, + nmemb: size_t, + data: *mut c_void) -> size_t { let input = slice::from_raw_parts(ptr as *const u8, size * nmemb); panic::catch(|| { - (*(data as *mut F))(input) + ((*(data as *mut EasyData)).write)(input) }).unwrap_or(!0) } } @@ -428,33 +442,37 @@ impl<'a> Easy<'a> { /// use curl::easy::Easy; /// /// let mut data_to_upload = &b"foobar"[..]; - /// let mut callback = |into: &mut [u8]| { - /// data_to_upload.read(into).unwrap_or(0) - /// }; /// let mut handle = Easy::new(); /// handle.url("https://example.com/login").unwrap(); - /// handle.read_function(&mut callback).unwrap(); + /// handle.read_function(|into| { + /// data_to_upload.read(into).unwrap_or(0) + /// }).unwrap(); /// handle.post(true).unwrap(); /// handle.perform().unwrap(); /// ``` - pub fn read_function(&mut self, f: &'a mut F) -> Result<(), Error> - where F: FnMut(&mut [u8]) -> usize + pub fn read_function(&mut self, f: F) -> Result<(), Error> + where F: FnMut(&mut [u8]) -> usize + Send + 'a { + self._read_function(Box::new(f)) + } + + fn _read_function(&mut self, f: Box usize + Send + 'a>) + -> Result<(), Error> { + self.data.read = f; try!(self.setopt_ptr(curl_sys::CURLOPT_READFUNCTION, - cb:: as usize as *const c_char)); + cb as usize as *const c_char)); + let ptr = &*self.data as *const _; try!(self.setopt_ptr(curl_sys::CURLOPT_READDATA, - f as *mut F as *const c_char)); + ptr as *const c_char)); return Ok(()); - unsafe extern fn cb(ptr: *mut c_char, - size: size_t, - nmemb: size_t, - data: *mut c_void) -> size_t - where F: FnMut(&mut [u8]) -> usize, - { + unsafe extern fn cb(ptr: *mut c_char, + size: size_t, + nmemb: size_t, + data: *mut c_void) -> size_t { let input = slice::from_raw_parts_mut(ptr as *mut u8, size * nmemb); panic::catch(|| { - (*(data as *mut F))(input) + ((*(data as *mut EasyData)).read)(input) }).unwrap_or(!0) } } @@ -478,27 +496,32 @@ impl<'a> Easy<'a> { /// /// By default data this option is not set, and this corresponds to the /// `CURLOPT_SEEKFUNCTION` and `CURLOPT_SEEKDATA` options. - pub fn seek_function(&mut self, f: &'a mut F) -> Result<(), Error> - where F: FnMut(SeekFrom) -> SeekResult + pub fn seek_function(&mut self, f: F) -> Result<(), Error> + where F: FnMut(SeekFrom) -> SeekResult + Send + 'a { + self._seek_function(Box::new(f)) + } + + fn _seek_function(&mut self, f: Box SeekResult + Send + 'a>) + -> Result<(), Error> { + self.data.seek = f; try!(self.setopt_ptr(curl_sys::CURLOPT_SEEKFUNCTION, - cb:: as usize as *const c_char)); + cb as usize as *const c_char)); + let ptr = &*self.data as *const _; try!(self.setopt_ptr(curl_sys::CURLOPT_SEEKDATA, - f as *mut F as *const c_char)); + ptr as *const c_char)); return Ok(()); - unsafe extern fn cb(data: *mut c_void, - offset: curl_sys::curl_off_t, - origin: c_int) -> c_int - where F: FnMut(SeekFrom) -> SeekResult - { + unsafe extern fn cb(data: *mut c_void, + offset: curl_sys::curl_off_t, + origin: c_int) -> c_int { panic::catch(|| { let from = if origin == libc::SEEK_SET { SeekFrom::Start(offset as u64) } else { panic!("unknown origin from libcurl: {}", origin); }; - (*(data as *mut F))(from) as c_int + ((*(data as *mut EasyData)).seek)(from) as c_int }).unwrap_or(!0) } } @@ -534,25 +557,31 @@ impl<'a> Easy<'a> { /// /// By default this function calls an internal method and corresponds to /// `CURLOPT_XFERINFOFUNCTION` and `CURLOPT_XFERINFODATA`. - pub fn progress_function(&mut self, f: &'a mut F) -> Result<(), Error> - where F: FnMut(f64, f64, f64, f64) -> bool + pub fn progress_function(&mut self, f: F) -> Result<(), Error> + where F: FnMut(f64, f64, f64, f64) -> bool + Send + 'a { + self._progress_function(Box::new(f)) + } + + fn _progress_function(&mut self, + f: Box bool + Send + 'a>) + -> Result<(), Error> { + self.data.progress = f; try!(self.setopt_ptr(curl_sys::CURLOPT_PROGRESSFUNCTION, - cb:: as usize as *const c_char)); + cb as usize as *const c_char)); + let ptr = &*self.data as *const _; try!(self.setopt_ptr(curl_sys::CURLOPT_PROGRESSDATA, - f as *mut F as *const c_char)); + ptr as *const c_char)); return Ok(()); - unsafe extern fn cb(data: *mut c_void, - dltotal: c_double, - dlnow: c_double, - ultotal: c_double, - ulnow: c_double) -> c_int - where F: FnMut(f64, f64, f64, f64) -> bool - { + unsafe extern fn cb(data: *mut c_void, + dltotal: c_double, + dlnow: c_double, + ultotal: c_double, + ulnow: c_double) -> c_int { let keep_going = panic::catch(|| { - let fnptr = &mut *(data as *mut F); - fnptr(dltotal, dlnow, ultotal, ulnow) + let data = &mut *(data as *mut EasyData); + (data.progress)(dltotal, dlnow, ultotal, ulnow) }).unwrap_or(false); if keep_going { 0 @@ -570,23 +599,28 @@ impl<'a> Easy<'a> { /// /// By default this option is not set and corresponds to the /// `CURLOPT_DEBUGFUNCTION` and `CURLOPT_DEBUGDATA` options. - pub fn debug_function(&mut self, f: &'a mut F) -> Result<(), Error> - where F: FnMut(InfoType, &[u8]) + pub fn debug_function(&mut self, f: F) -> Result<(), Error> + where F: FnMut(InfoType, &[u8]) + Send + 'a { + self._debug_function(Box::new(f)) + } + + fn _debug_function(&mut self, f: Box) + -> Result<(), Error> { + self.data.debug = f; try!(self.setopt_ptr(curl_sys::CURLOPT_DEBUGFUNCTION, - cb:: as usize as *const c_char)); + cb as usize as *const c_char)); + let ptr = &*self.data as *const _; try!(self.setopt_ptr(curl_sys::CURLOPT_DEBUGDATA, - f as *mut F as *const c_char)); + ptr as *const c_char)); return Ok(()); // TODO: expose `handle`? is that safe? - unsafe extern fn cb(_handle: *mut curl_sys::CURL, - kind: curl_sys::curl_infotype, - data: *mut c_char, - size: size_t, - userptr: *mut c_void) -> c_int - where F: FnMut(InfoType, &[u8]) - { + unsafe extern fn cb(_handle: *mut curl_sys::CURL, + kind: curl_sys::curl_infotype, + data: *mut c_char, + size: size_t, + userptr: *mut c_void) -> c_int { panic::catch(|| { let data = slice::from_raw_parts(data as *const u8, size); let kind = match kind { @@ -599,7 +633,7 @@ impl<'a> Easy<'a> { curl_sys::CURLINFO_SSL_DATA_OUT => InfoType::SslDataOut, _ => return, }; - (*(userptr as *mut F))(kind, data) + ((*(userptr as *mut EasyData)).debug)(kind, data) }); return 0 } @@ -648,37 +682,41 @@ impl<'a> Easy<'a> { /// /// let mut headers = Vec::new(); /// { - /// let mut callback = |header: &[u8]| { - /// headers.push(str::from_utf8(header).unwrap().to_string()); - /// true - /// }; /// let mut handle = Easy::new(); /// handle.url("https://www.rust-lang.org/").unwrap(); - /// handle.header_function(&mut callback).unwrap(); + /// handle.header_function(|header| { + /// headers.push(str::from_utf8(header).unwrap().to_string()); + /// true + /// }).unwrap(); /// handle.perform().unwrap(); /// } /// /// println!("{:?}", headers); /// ``` - pub fn header_function(&mut self, f: &'a mut F) -> Result<(), Error> - where F: FnMut(&[u8]) -> bool + pub fn header_function(&mut self, f: F) -> Result<(), Error> + where F: FnMut(&[u8]) -> bool + Send + 'a { + self._header_function(Box::new(f)) + } + + fn _header_function(&mut self, f: Box bool + Send + 'a>) + -> Result<(), Error> { + self.data.header = f; try!(self.setopt_ptr(curl_sys::CURLOPT_HEADERFUNCTION, - cb:: as usize as *const c_char)); + cb as usize as *const c_char)); + let ptr = &*self.data as *const _; try!(self.setopt_ptr(curl_sys::CURLOPT_HEADERDATA, - f as *mut F as *const c_char)); + ptr as *const c_char)); return Ok(()); - unsafe extern fn cb(buffer: *mut c_char, - size: size_t, - nitems: size_t, - userptr: *mut c_void) -> size_t - where F: FnMut(&[u8]) -> bool - { + unsafe extern fn cb(buffer: *mut c_char, + size: size_t, + nitems: size_t, + userptr: *mut c_void) -> size_t { let keep_going = panic::catch(|| { let data = slice::from_raw_parts(buffer as *const u8, size * nitems); - (*(userptr as *mut F))(data) + ((*(userptr as *mut EasyData)).header)(data) }).unwrap_or(false); if keep_going { size * nitems @@ -1110,11 +1148,13 @@ impl<'a> Easy<'a> { /// /// let mut handle = Easy::new(); /// handle.url("https://www.rust-lang.org/").unwrap(); - /// handle.http_headers(&list).unwrap(); + /// handle.http_headers(list).unwrap(); /// handle.perform().unwrap(); /// ``` - pub fn http_headers(&mut self, list: &'a List) -> Result<(), Error> { - self.setopt_ptr(curl_sys::CURLOPT_HTTPHEADER, list.raw as *const _) + pub fn http_headers(&mut self, list: List) -> Result<(), Error> { + self.data.header_list = list; + let ptr = self.data.header_list.raw; + self.setopt_ptr(curl_sys::CURLOPT_HTTPHEADER, ptr as *const _) } // /// Add some headers to send to the HTTP proxy. @@ -2290,6 +2330,7 @@ impl<'a> Easy<'a> { } else { Some(Easy { handle: handle, + data: blank_data(), _marker: marker::PhantomData, }) } @@ -2308,25 +2349,6 @@ impl<'a> Easy<'a> { default_configure(self); } - /// Re-initializes all lifetime-related configuration of this handle and - /// returns a handle with a new associated lifetime. - /// - /// The lifetime on this handle is currently used to prevent storing - /// configuration which references data that lives shorter than this handle - /// (e.g. preventing use-after-free). This can make it difficult, however, - /// to reuse an `Easy` handle as the lifetime of callbacks may be limited to - /// a smaller scope. - /// - /// This will have the same effect as `reset` for any configuration option - /// that is also bounded by this handle's lifetime, plus the additional - /// effect of returning a handle with a new arbitrary lifetime. - pub fn reset_lifetime<'b>(mut self) -> Easy<'b> { - self.reset(); - let new = Easy { handle: self.handle, _marker: marker::PhantomData }; - mem::forget(self); - new - } - /// Receives data from a connected socket. /// /// Only useful after a successful `perform` with the `connect_only` option @@ -2454,6 +2476,18 @@ impl<'a> Easy<'a> { } } +fn blank_data<'a>() -> Box> { + Box::new(EasyData { + write: Box::new(|_| panic!()), + read: Box::new(|_| panic!()), + seek: Box::new(|_| panic!()), + debug: Box::new(|_, _| panic!()), + header: Box::new(|_| panic!()), + progress: Box::new(|_, _, _, _| panic!()), + header_list: List::new(), + }) +} + fn default_configure(handle: &mut Easy) { let _ = handle.signal(false); ssl_configure(handle); diff --git a/tests/easy.rs b/tests/easy.rs index 3ec6e93ee0..4e48c20ee6 100644 --- a/tests/easy.rs +++ b/tests/easy.rs @@ -68,13 +68,12 @@ Accept: */*\r\n\ let mut all = Vec::::new(); { - let mut write = |data: &[u8]| { - all.extend(data); - data.len() - }; let mut handle = handle(); t!(handle.url(&s.url("/"))); - t!(handle.write_function(&mut write)); + t!(handle.write_function(|data| { + all.extend(data); + data.len() + })); t!(handle.perform()); } assert_eq!(all, b"hello!"); @@ -98,12 +97,11 @@ Accept: */*\r\n\ dl = a; true }; - let mut write = sink; let mut handle = handle(); t!(handle.url(&s.url("/foo"))); t!(handle.progress(true)); t!(handle.progress_function(&mut cb)); - t!(handle.write_function(&mut write)); + t!(handle.write_function(sink)); t!(handle.perform()); } assert!(hits > 0); @@ -127,15 +125,13 @@ Hello!"); let mut headers = Vec::new(); { - let mut header = |h: &[u8]| { - headers.push(str::from_utf8(h).unwrap().to_string()); - true - }; - let mut write = sink; let mut handle = handle(); t!(handle.url(&s.url("/"))); - t!(handle.header_function(&mut header)); - t!(handle.write_function(&mut write)); + t!(handle.header_function(|h| { + headers.push(str::from_utf8(h).unwrap().to_string()); + true + })); + t!(handle.write_function(sink)); t!(handle.perform()); } assert_eq!(headers, vec![ @@ -216,7 +212,7 @@ HTTP/1.1 200 OK\r\n\ let mut h = handle(); t!(h.url("http://example.com/")); t!(h.proxy(&s.url("/"))); - t!(h.http_headers(&header)); + t!(h.http_headers(header)); t!(h.perform()); } @@ -333,16 +329,17 @@ HTTP/1.1 200 OK\r\n\ \r\n"); let mut data = "data\n".as_bytes(); - let mut read = |buf: &mut [u8]| data.read(buf).unwrap(); let mut list = List::new(); t!(list.append("Expect:")); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.put(true)); - t!(h.read_function(&mut read)); + t!(h.read_function(|buf| { + data.read(buf).unwrap() + })); t!(h.in_filesize(5)); t!(h.upload(true)); - t!(h.http_headers(&list)); + t!(h.http_headers(list)); t!(h.perform()); } @@ -406,12 +403,13 @@ HTTP/1.1 200 OK\r\n\ \r\n"); let mut data = "data\n".as_bytes(); - let mut read = |buf: &mut [u8]| data.read(buf).unwrap(); let mut h = handle(); t!(h.url(&s.url("/"))); t!(h.post(true)); t!(h.post_field_size(5)); - t!(h.read_function(&mut read)); + t!(h.read_function(|buf| { + data.read(buf).unwrap() + })); t!(h.perform()); } @@ -470,7 +468,7 @@ HTTP/1.1 200 OK\r\n\ t!(custom.append("Accept:")); let mut h = handle(); t!(h.url(&s.url("/"))); - t!(h.http_headers(&custom)); + t!(h.http_headers(custom)); t!(h.perform()); } @@ -552,9 +550,8 @@ Accept: */*\r\n\ HTTP/1.1 200 OK\r\n\ \r\n"); - let mut header = |_: &[u8]| panic!(); let mut h = handle(); t!(h.url(&s.url("/"))); - t!(h.header_function(&mut header)); + t!(h.header_function(|_| panic!())); t!(h.perform()); } From 72f5a882cdbbcb4644f55572a7ce211ad9e8cb37 Mon Sep 17 00:00:00 2001 From: bar Date: Thu, 12 May 2016 23:24:37 -0700 Subject: [PATCH 71/71] Don't expose duphandle, doesn't seem safe for now --- src/easy.rs | 51 +++++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/easy.rs b/src/easy.rs index 5fada45408..8ce8f36275 100644 --- a/src/easy.rs +++ b/src/easy.rs @@ -2312,30 +2312,33 @@ impl<'a> Easy<'a> { } } - /// Attempts to clone this handle, returning a new session handle with the - /// same options set for this handle. - /// - /// Internal state info and things like persistent connections ccannot be - /// transferred. - /// - /// # Errors - /// - /// If a new handle could not be allocated or another error happens, `None` - /// is returned. - pub fn try_clone<'b>(&mut self) -> Option> { - unsafe { - let handle = curl_sys::curl_easy_duphandle(self.handle); - if handle.is_null() { - None - } else { - Some(Easy { - handle: handle, - data: blank_data(), - _marker: marker::PhantomData, - }) - } - } - } + // TODO: I don't think this is safe, you can drop this which has all the + // callback data and then the next is use-after-free + // + // /// Attempts to clone this handle, returning a new session handle with the + // /// same options set for this handle. + // /// + // /// Internal state info and things like persistent connections ccannot be + // /// transferred. + // /// + // /// # Errors + // /// + // /// If a new handle could not be allocated or another error happens, `None` + // /// is returned. + // pub fn try_clone<'b>(&mut self) -> Option> { + // unsafe { + // let handle = curl_sys::curl_easy_duphandle(self.handle); + // if handle.is_null() { + // None + // } else { + // Some(Easy { + // handle: handle, + // data: blank_data(), + // _marker: marker::PhantomData, + // }) + // } + // } + // } /// Re-initializes this handle to the default values. ///