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