| 1 | //! Rust bindings to the libcurl C library |
| 2 | //! |
| 3 | //! This crate contains bindings for an HTTP/HTTPS client which is powered by |
| 4 | //! [libcurl], the same library behind the `curl` command line tool. The API |
| 5 | //! currently closely matches that of libcurl itself, except that a Rustic layer |
| 6 | //! of safety is applied on top. |
| 7 | //! |
| 8 | //! [libcurl]: https://curl.haxx.se/libcurl/ |
| 9 | //! |
| 10 | //! # The "Easy" API |
| 11 | //! |
| 12 | //! The easiest way to send a request is to use the `Easy` api which corresponds |
| 13 | //! to `CURL` in libcurl. This handle supports a wide variety of options and can |
| 14 | //! be used to make a single blocking request in a thread. Callbacks can be |
| 15 | //! specified to deal with data as it arrives and a handle can be reused to |
| 16 | //! cache connections and such. |
| 17 | //! |
| 18 | //! ```rust,no_run |
| 19 | //! use std::io::{stdout, Write}; |
| 20 | //! |
| 21 | //! use curl::easy::Easy; |
| 22 | //! |
| 23 | //! // Write the contents of rust-lang.org to stdout |
| 24 | //! let mut easy = Easy::new(); |
| 25 | //! easy.url("https://www.rust-lang.org/" ).unwrap(); |
| 26 | //! easy.write_function(|data| { |
| 27 | //! stdout().write_all(data).unwrap(); |
| 28 | //! Ok(data.len()) |
| 29 | //! }).unwrap(); |
| 30 | //! easy.perform().unwrap(); |
| 31 | //! ``` |
| 32 | //! |
| 33 | //! # What about multiple concurrent HTTP requests? |
| 34 | //! |
| 35 | //! One option you have currently is to send multiple requests in multiple |
| 36 | //! threads, but otherwise libcurl has a "multi" interface for doing this |
| 37 | //! operation. Initial bindings of this interface can be found in the `multi` |
| 38 | //! module, but feedback is welcome! |
| 39 | //! |
| 40 | //! # Where does libcurl come from? |
| 41 | //! |
| 42 | //! This crate links to the `curl-sys` crate which is in turn responsible for |
| 43 | //! acquiring and linking to the libcurl library. Currently this crate will |
| 44 | //! build libcurl from source if one is not already detected on the system. |
| 45 | //! |
| 46 | //! There is a large number of releases for libcurl, all with different sets of |
| 47 | //! capabilities. Robust programs may wish to inspect `Version::get()` to test |
| 48 | //! what features are implemented in the linked build of libcurl at runtime. |
| 49 | //! |
| 50 | //! # Initialization |
| 51 | //! |
| 52 | //! The underlying libcurl library must be initialized before use and has |
| 53 | //! certain requirements on how this is done. Check the documentation for |
| 54 | //! [`init`] for more details. |
| 55 | |
| 56 | #![deny (missing_docs, missing_debug_implementations)] |
| 57 | #![doc (html_root_url = "https://docs.rs/curl/0.4" )] |
| 58 | |
| 59 | use std::ffi::CStr; |
| 60 | use std::str; |
| 61 | use std::sync::Once; |
| 62 | |
| 63 | pub use crate::error::{Error, FormError, MultiError, ShareError}; |
| 64 | mod error; |
| 65 | |
| 66 | pub use crate::version::{Protocols, Version}; |
| 67 | mod version; |
| 68 | |
| 69 | pub mod easy; |
| 70 | pub mod multi; |
| 71 | mod panic; |
| 72 | |
| 73 | #[cfg (test)] |
| 74 | static INITIALIZED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); |
| 75 | |
| 76 | /// Initializes the underlying libcurl library. |
| 77 | /// |
| 78 | /// The underlying libcurl library must be initialized before use, and must be |
| 79 | /// done so on the main thread before any other threads are created by the |
| 80 | /// program. This crate will do this for you automatically in the following |
| 81 | /// scenarios: |
| 82 | /// |
| 83 | /// - Creating a new [`Easy`][easy::Easy] or [`Multi`][multi::Multi] handle |
| 84 | /// - At program startup on Windows, macOS, Linux, Android, or FreeBSD systems |
| 85 | /// |
| 86 | /// This should be sufficient for most applications and scenarios, but in any |
| 87 | /// other case, it is strongly recommended that you call this function manually |
| 88 | /// as soon as your program starts. |
| 89 | /// |
| 90 | /// Calling this function more than once is harmless and has no effect. |
| 91 | #[inline ] |
| 92 | pub fn init() { |
| 93 | /// Used to prevent concurrent or duplicate initialization. |
| 94 | static INIT: Once = Once::new(); |
| 95 | |
| 96 | INIT.call_once(|| { |
| 97 | #[cfg (need_openssl_init)] |
| 98 | openssl_probe::init_ssl_cert_env_vars(); |
| 99 | #[cfg (need_openssl_init)] |
| 100 | openssl_sys::init(); |
| 101 | |
| 102 | unsafe { |
| 103 | assert_eq!(curl_sys::curl_global_init(curl_sys::CURL_GLOBAL_ALL), 0); |
| 104 | } |
| 105 | |
| 106 | #[cfg (test)] |
| 107 | { |
| 108 | INITIALIZED.store(true, std::sync::atomic::Ordering::SeqCst); |
| 109 | } |
| 110 | |
| 111 | // Note that we explicitly don't schedule a call to |
| 112 | // `curl_global_cleanup`. The documentation for that function says |
| 113 | // |
| 114 | // > You must not call it when any other thread in the program (i.e. a |
| 115 | // > thread sharing the same memory) is running. This doesn't just mean |
| 116 | // > no other thread that is using libcurl. |
| 117 | // |
| 118 | // We can't ever be sure of that, so unfortunately we can't call the |
| 119 | // function. |
| 120 | }); |
| 121 | } |
| 122 | |
| 123 | /// An exported constructor function. On supported platforms, this will be |
| 124 | /// invoked automatically before the program's `main` is called. This is done |
| 125 | /// for the convenience of library users since otherwise the thread-safety rules |
| 126 | /// around initialization can be difficult to fulfill. |
| 127 | /// |
| 128 | /// This is a hidden public item to ensure the symbol isn't optimized away by a |
| 129 | /// rustc/LLVM bug: https://github.com/rust-lang/rust/issues/47384. As long as |
| 130 | /// any item in this module is used by the final binary (which `init` will be) |
| 131 | /// then this symbol should be preserved. |
| 132 | #[used ] |
| 133 | #[doc (hidden)] |
| 134 | #[cfg_attr ( |
| 135 | any(target_os = "linux" , target_os = "freebsd" , target_os = "android" ), |
| 136 | link_section = ".init_array" |
| 137 | )] |
| 138 | #[cfg_attr (target_os = "macos" , link_section = "__DATA,__mod_init_func" )] |
| 139 | #[cfg_attr (target_os = "windows" , link_section = ".CRT$XCU" )] |
| 140 | pub static INIT_CTOR: extern "C" fn() = { |
| 141 | /// This is the body of our constructor function. |
| 142 | #[cfg_attr ( |
| 143 | any(target_os = "linux" , target_os = "android" ), |
| 144 | link_section = ".text.startup" |
| 145 | )] |
| 146 | extern "C" fn init_ctor() { |
| 147 | init(); |
| 148 | } |
| 149 | |
| 150 | init_ctor |
| 151 | }; |
| 152 | |
| 153 | unsafe fn opt_str<'a>(ptr: *const libc::c_char) -> Option<&'a str> { |
| 154 | if ptr.is_null() { |
| 155 | None |
| 156 | } else { |
| 157 | Some(str::from_utf8(CStr::from_ptr(ptr).to_bytes()).unwrap()) |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | fn cvt(r: curl_sys::CURLcode) -> Result<(), Error> { |
| 162 | if r == curl_sys::CURLE_OK { |
| 163 | Ok(()) |
| 164 | } else { |
| 165 | Err(Error::new(code:r)) |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | #[cfg (test)] |
| 170 | mod tests { |
| 171 | use super::*; |
| 172 | |
| 173 | #[test ] |
| 174 | #[cfg (any( |
| 175 | target_os = "linux" , |
| 176 | target_os = "macos" , |
| 177 | target_os = "windows" , |
| 178 | target_os = "freebsd" , |
| 179 | target_os = "android" |
| 180 | ))] |
| 181 | fn is_initialized_before_main() { |
| 182 | assert!(INITIALIZED.load(std::sync::atomic::Ordering::SeqCst)); |
| 183 | } |
| 184 | } |
| 185 | |