| //! 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 std::io::{stdout, Write}; |
| //! |
| //! 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.write_function(|data| { |
| //! stdout().write_all(data).unwrap(); |
| //! Ok(data.len()) |
| //! }).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. Initial bindings of this interface can be found in the `multi` |
| //! module, but feedback is welcome! |
| //! |
| //! # 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. |
| //! |
| //! # Initialization |
| //! |
| //! The underlying libcurl library must be initialized before use and has |
| //! certain requirements on how this is done. Check the documentation for |
| //! [`init`] for more details. |
| |
| #![deny(missing_docs, missing_debug_implementations)] |
| #![doc(html_root_url = "https://docs.rs/curl/0.4")] |
| |
| use std::ffi::CStr; |
| use std::str; |
| use std::sync::Once; |
| |
| pub use crate::error::{Error, FormError, MultiError, ShareError}; |
| mod error; |
| |
| pub use crate::version::{Protocols, Version}; |
| mod version; |
| |
| pub mod easy; |
| pub mod multi; |
| mod panic; |
| |
| #[cfg(test)] |
| static INITIALIZED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); |
| |
| /// Initializes the underlying libcurl library. |
| /// |
| /// The underlying libcurl library must be initialized before use, and must be |
| /// done so on the main thread before any other threads are created by the |
| /// program. This crate will do this for you automatically in the following |
| /// scenarios: |
| /// |
| /// - Creating a new [`Easy`][easy::Easy] or [`Multi`][multi::Multi] handle |
| /// - At program startup on Windows, macOS, Linux, Android, or FreeBSD systems |
| /// |
| /// This should be sufficient for most applications and scenarios, but in any |
| /// other case, it is strongly recommended that you call this function manually |
| /// as soon as your program starts. |
| /// |
| /// Calling this function more than once is harmless and has no effect. |
| #[inline] |
| pub fn init() { |
| /// Used to prevent concurrent or duplicate initialization. |
| static INIT: Once = Once::new(); |
| |
| INIT.call_once(|| { |
| #[cfg(need_openssl_init)] |
| openssl_probe::init_ssl_cert_env_vars(); |
| #[cfg(need_openssl_init)] |
| openssl_sys::init(); |
| |
| unsafe { |
| assert_eq!(curl_sys::curl_global_init(curl_sys::CURL_GLOBAL_ALL), 0); |
| } |
| |
| #[cfg(test)] |
| { |
| INITIALIZED.store(true, std::sync::atomic::Ordering::SeqCst); |
| } |
| |
| // Note that we explicitly don't schedule a call to |
| // `curl_global_cleanup`. The documentation for that function says |
| // |
| // > You must not call it when any other thread in the program (i.e. a |
| // > thread sharing the same memory) is running. This doesn't just mean |
| // > no other thread that is using libcurl. |
| // |
| // We can't ever be sure of that, so unfortunately we can't call the |
| // function. |
| }); |
| } |
| |
| /// An exported constructor function. On supported platforms, this will be |
| /// invoked automatically before the program's `main` is called. This is done |
| /// for the convenience of library users since otherwise the thread-safety rules |
| /// around initialization can be difficult to fulfill. |
| /// |
| /// This is a hidden public item to ensure the symbol isn't optimized away by a |
| /// rustc/LLVM bug: https://github.com/rust-lang/rust/issues/47384. As long as |
| /// any item in this module is used by the final binary (which `init` will be) |
| /// then this symbol should be preserved. |
| #[used] |
| #[doc(hidden)] |
| #[cfg_attr( |
| any(target_os = "linux", target_os = "freebsd", target_os = "android"), |
| link_section = ".init_array" |
| )] |
| #[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")] |
| #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")] |
| pub static INIT_CTOR: extern "C" fn() = { |
| /// This is the body of our constructor function. |
| #[cfg_attr( |
| any(target_os = "linux", target_os = "android"), |
| link_section = ".text.startup" |
| )] |
| extern "C" fn init_ctor() { |
| init(); |
| } |
| |
| init_ctor |
| }; |
| |
| 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()) |
| } |
| } |
| |
| fn cvt(r: curl_sys::CURLcode) -> Result<(), Error> { |
| if r == curl_sys::CURLE_OK { |
| Ok(()) |
| } else { |
| Err(Error::new(r)) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| #[test] |
| #[cfg(any( |
| target_os = "linux", |
| target_os = "macos", |
| target_os = "windows", |
| target_os = "freebsd", |
| target_os = "android" |
| ))] |
| fn is_initialized_before_main() { |
| assert!(INITIALIZED.load(std::sync::atomic::Ordering::SeqCst)); |
| } |
| } |