curl/
lib.rs

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
59use std::ffi::CStr;
60use std::str;
61use std::sync::Once;
62
63pub use crate::error::{Error, FormError, MultiError, ShareError};
64mod error;
65
66pub use crate::version::{Protocols, Version};
67mod version;
68
69pub mod easy;
70pub mod multi;
71mod panic;
72
73#[cfg(test)]
74static 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]
92pub 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")]
140pub 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
153unsafe 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
161fn cvt(r: curl_sys::CURLcode) -> Result<(), Error> {
162    if r == curl_sys::CURLE_OK {
163        Ok(())
164    } else {
165        Err(Error::new(r))
166    }
167}
168
169#[cfg(test)]
170mod 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}