| 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 |  | 
|---|