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(code: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}
185