1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
//! A `Future` interface on top of libcurl //! //! This crate provides a futures-based interface to the libcurl HTTP library. //! Building on top of the `curl` crate on crates.io, this allows using a //! battle-tested C library for sending HTTP requests in an asynchronous //! fashion. //! //! # Examples //! //! ```rust //! extern crate curl; //! extern crate futures; //! extern crate tokio_core; //! extern crate tokio_curl; //! //! use std::io::{self, Write}; //! //! use curl::easy::Easy; //! use futures::Future; //! use tokio_core::reactor::Core; //! use tokio_curl::Session; //! //! fn main() { //! // Create an event loop that we'll run on, as well as an HTTP `Session` //! // which we'll be routing all requests through. //! let mut lp = Core::new().unwrap(); //! let session = Session::new(lp.handle()); //! //! // Prepare the HTTP request to be sent. //! let mut req = Easy::new(); //! req.get(true).unwrap(); //! req.url("https://www.rust-lang.org").unwrap(); //! req.write_function(|data| { //! io::stdout().write_all(data).unwrap(); //! Ok(data.len()) //! }).unwrap(); //! //! // Once we've got our session, issue an HTTP request to download the //! // rust-lang home page //! let request = session.perform(req); //! //! // Execute the request, and print the response code as well as the error //! // that happened (if any). //! let mut req = lp.run(request).unwrap(); //! println!("{:?}", req.response_code()); //! } //! ``` //! //! # Platform support //! //! This crate works on both Unix and Windows, but note that it will not scale //! well on Windows. Unfortunately the implementation (seemingly from libcurl) //! relies on `select`, which does not scale very far on Windows. #![deny(missing_docs)] // TODO: handle level a bit better by turning the event loop every so often #[macro_use] extern crate log; #[macro_use] extern crate futures; extern crate tokio_core; extern crate curl; #[macro_use] #[cfg(unix)] extern crate scoped_tls; #[cfg(windows)] #[path = "windows.rs"] mod imp; #[cfg(unix)] #[path = "unix.rs"] mod imp; mod stack; use std::error; use std::fmt; use std::io; use futures::{Future, Poll, Async}; use curl::easy::Easy; use tokio_core::reactor::Handle; /// A shared cache for HTTP requests to pool data such as TCP connections /// between. /// /// All HTTP requests in this crate are performed through a `Session` type. A /// `Session` can be cloned to acquire multiple references to the same session. /// /// Sessions are created through the `Session::new` method, which returns a /// future that will resolve to a session once it's been initialized. #[derive(Clone)] pub struct Session { inner: imp::Session, } /// A future returned from the `Session::perform` method. /// /// This future represents the execution of an entire HTTP request. This future /// will resolve to the original `Easy` handle provided once the HTTP request is /// complete so metadata about the request can be inspected. pub struct Perform { inner: imp::Perform, } /// Error returned by the future returned from `perform`. /// /// This error can be converted to an `io::Error` or the underlying `Easy` /// handle may also be extracted. pub struct PerformError { error: io::Error, handle: Option<Easy>, } impl Session { /// Creates a new HTTP session object which will be bound to the given event /// loop. /// /// When using libcurl it will provide us with file descriptors to listen /// for events on, so we'll need raw access to an actual event loop in order /// to hook up all the pieces together. The event loop will also be the I/O /// home for this HTTP session. All HTTP I/O will occur on the event loop /// thread. /// /// This function returns a future which will resolve to a `Session` once /// it's been initialized. pub fn new(handle: Handle) -> Session { Session { inner: imp::Session::new(handle) } } /// Execute and HTTP request asynchronously, returning a future representing /// the request's completion. /// /// This method will consume the provided `Easy` handle, which should be /// configured appropriately to send off an HTTP request. The returned /// future will resolve back to the handle once the request is performed, /// along with any error that happened along the way. /// /// The `Item` of the returned future is `(Easy, Option<Error>)` so you can /// always get the `Easy` handle back, and the `Error` part of the future is /// `io::Error` which represents errors communicating with the event loop or /// otherwise fatal errors for the `Easy` provided. /// /// Note that if the `Perform` future is dropped it will cancel the /// outstanding HTTP request, cleaning up all resources associated with it. pub fn perform(&self, handle: Easy) -> Perform { Perform { inner: self.inner.perform(handle) } } } impl Future for Perform { type Item = Easy; type Error = PerformError; fn poll(&mut self) -> Poll<Easy, PerformError> { match self.inner.poll() { Err(e) => Err(PerformError { error: e, handle: None }), Ok(Async::Ready((handle, None))) => Ok(Async::Ready(handle)), Ok(Async::Ready((handle, Some(err)))) => { Err(PerformError { error: err.into(), handle: Some(handle) }) } Ok(Async::NotReady) => Ok(Async::NotReady), } } } impl PerformError { /// Attempts to extract the underlying `Easy` handle, if one is available. /// /// For some HTTP requests that fail the `Easy` handle is still available to /// recycle for another request. Additionally, the handle may contain /// information about the failed request. If this is needed, then this /// method can be called to extract the easy handle. /// /// Note that not all failed HTTP requests will have an easy handle /// available to return. Some requests may end up destroying the original /// easy handle as it couldn't be reclaimed. pub fn take_easy(&mut self) -> Option<Easy> { self.handle.take() } /// Returns the underlying I/O error that caused this error. /// /// All `PerformError` structures will have an associated I/O error with /// them. This error indicates why the HTTP request failed, and is likely /// going to be backed by a `curl::Error` or a `curl::MultiError`. /// /// It's also likely if it is available the `Easy` handle recovered from /// `take_handle` will have even more detailed information about the error. pub fn into_error(self) -> io::Error { self.error } } impl From<PerformError> for io::Error { fn from(p: PerformError) -> io::Error { p.into_error() } } impl fmt::Display for PerformError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { "failed to execute request".fmt(f) } } impl fmt::Debug for PerformError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("PerformError") .field("error", &self.error) .finish() } } impl error::Error for PerformError { fn description(&self) -> &str { "failed to execute request" } fn cause(&self) -> Option<&error::Error> { Some(&self.error) } }