use std::fmt; use std::sync::Arc; use std::time::Duration; use futures::{Future, Stream}; use futures::sync::{mpsc, oneshot}; use request::{self, Request, RequestBuilder}; use response::{self, Response}; use {async_impl, Certificate, Method, IntoUrl, Proxy, RedirectPolicy, wait}; /// A `Client` to make Requests with. /// /// The Client has various configuration values to tweak, but the defaults /// are set to what is usually the most commonly desired value. /// /// The `Client` holds a connection pool internally, so it is advised that /// you create one and reuse it. /// /// # Examples /// /// ```rust /// # use reqwest::{Error, Client}; /// # /// # fn run() -> Result<(), Error> { /// let client = Client::new()?; /// let resp = client.get("http://httpbin.org/")?.send()?; /// # drop(resp); /// # Ok(()) /// # } /// /// ``` #[derive(Clone)] pub struct Client { inner: ClientHandle, } /// A `ClientBuilder` can be used to create a `Client` with custom configuration. /// /// # Example /// /// ``` /// # fn run() -> Result<(), Error> { /// use std::time::Duration; /// /// let client = reqwest::Client::builder()? /// .gzip(true) /// .timeout(Duration::from_secs(10)) /// .build()?; /// # } /// ``` pub struct ClientBuilder { gzip: bool, inner: async_impl::ClientBuilder, timeout: Option, } impl ClientBuilder { /// Constructs a new `ClientBuilder` /// /// # Errors /// /// This method fails if native TLS backend cannot be created. pub fn new() -> ::Result { async_impl::ClientBuilder::new().map(|builder| ClientBuilder { inner: builder, gzip: true, timeout: None, }) } /// Returns a `Client` that uses this `ClientBuilder` configuration. /// /// # Errors /// /// This method fails if native TLS backend cannot be initialized. /// /// # Panics /// /// This method consumes the internal state of the builder. /// Trying to use this builder again after calling `build` will panic. pub fn build(&mut self) -> ::Result { ClientHandle::new(self).map(|handle| Client { inner: handle, }) } /// Add a custom root certificate. /// /// This can be used to connect to a server that has a self-signed /// certificate for example. /// /// # Example /// ``` /// # use std::fs::File; /// # use std::io::Read; /// # fn build_client() -> Result<(), Box> { /// // read a local binary DER encoded certificate /// let mut buf = Vec::new(); /// File::open("my-cert.der")?.read_to_end(&mut buf)?; /// /// // create a certificate /// let cert = reqwest::Certificate::from_der(&buf)?; /// /// // get a client builder /// let client = reqwest::ClientBuilder::new()? /// .add_root_certificate(cert)? /// .build()?; /// # drop(client); /// # Ok(()) /// # } /// ``` /// /// # Errors /// /// This method fails if adding root certificate was unsuccessful. pub fn add_root_certificate(&mut self, cert: Certificate) -> ::Result<&mut ClientBuilder> { self.inner.add_root_certificate(cert)?; Ok(self) } /// Disable hostname verification. /// /// # Warning /// /// You should think very carefully before you use this method. If /// hostname verification is not used, any valid certificate for any /// site will be trusted for use from any other. This introduces a /// significant vulnerability to man-in-the-middle attacks. #[inline] pub fn danger_disable_hostname_verification(&mut self) -> &mut ClientBuilder { self.inner.danger_disable_hostname_verification(); self } /// Enable hostname verification. /// /// Default is enabled. #[inline] pub fn enable_hostname_verification(&mut self) -> &mut ClientBuilder { self.inner.enable_hostname_verification(); self } /// Enable auto gzip decompression by checking the ContentEncoding response header. /// /// Default is enabled. #[inline] pub fn gzip(&mut self, enable: bool) -> &mut ClientBuilder { self.inner.gzip(enable); self.gzip = enable; self } /// Add a `Proxy` to the list of proxies the `Client` will use. #[inline] pub fn proxy(&mut self, proxy: Proxy) -> &mut ClientBuilder { self.inner.proxy(proxy); self } /// Set a `RedirectPolicy` for this client. /// /// Default will follow redirects up to a maximum of 10. #[inline] pub fn redirect(&mut self, policy: RedirectPolicy) -> &mut ClientBuilder { self.inner.redirect(policy); self } /// Enable or disable automatic setting of the `Referer` header. /// /// Default is `true`. #[inline] pub fn referer(&mut self, enable: bool) -> &mut ClientBuilder { self.inner.referer(enable); self } /// Set a timeout for connect, read and write operations of a `Client`. #[inline] pub fn timeout(&mut self, timeout: Duration) -> &mut ClientBuilder { self.timeout = Some(timeout); self } } impl Client { /// Constructs a new `Client`. /// /// # Errors /// /// This method fails if native TLS backend cannot be created or initialized. #[inline] pub fn new() -> ::Result { ClientBuilder::new()?.build() } /// Creates a `ClientBuilder` to configure a `Client`. /// /// # Errors /// /// This method fails if native TLS backend cannot be created. #[inline] pub fn builder() -> ::Result { ClientBuilder::new() } /// Convenience method to make a `GET` request to a URL. /// /// # Errors /// /// This method fails whenever supplied `Url` cannot be parsed. pub fn get(&self, url: U) -> ::Result { self.request(Method::Get, url) } /// Convenience method to make a `POST` request to a URL. /// /// # Errors /// /// This method fails whenever supplied `Url` cannot be parsed. pub fn post(&self, url: U) -> ::Result { self.request(Method::Post, url) } /// Convenience method to make a `PUT` request to a URL. /// /// # Errors /// /// This method fails whenever supplied `Url` cannot be parsed. pub fn put(&self, url: U) -> ::Result { self.request(Method::Put, url) } /// Convenience method to make a `PATCH` request to a URL. /// /// # Errors /// /// This method fails whenever supplied `Url` cannot be parsed. pub fn patch(&self, url: U) -> ::Result { self.request(Method::Patch, url) } /// Convenience method to make a `DELETE` request to a URL. /// /// # Errors /// /// This method fails whenever supplied `Url` cannot be parsed. pub fn delete(&self, url: U) -> ::Result { self.request(Method::Delete, url) } /// Convenience method to make a `HEAD` request to a URL. /// /// # Errors /// /// This method fails whenever supplied `Url` cannot be parsed. pub fn head(&self, url: U) -> ::Result { self.request(Method::Head, url) } /// Start building a `Request` with the `Method` and `Url`. /// /// Returns a `RequestBuilder`, which will allow setting headers and /// request body before sending. /// /// # Errors /// /// This method fails whenever supplied `Url` cannot be parsed. pub fn request(&self, method: Method, url: U) -> ::Result { let url = try_!(url.into_url()); Ok(request::builder(self.clone(), Request::new(method, url))) } /// Executes a `Request`. /// /// A `Request` can be built manually with `Request::new()` or obtained /// from a RequestBuilder with `RequestBuilder::build()`. /// /// You should prefer to use the `RequestBuilder` and /// `RequestBuilder::send()`. /// /// # Errors /// /// This method fails if there was an error while sending request, /// redirect loop was detected or redirect limit was exhausted. pub fn execute(&self, request: Request) -> ::Result { self.inner.execute_request(request) } } impl fmt::Debug for Client { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Client") //.field("gzip", &self.inner.gzip) //.field("redirect_policy", &self.inner.redirect_policy) //.field("referer", &self.inner.referer) .finish() } } impl fmt::Debug for ClientBuilder { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("ClientBuilder") .finish() } } #[derive(Clone)] struct ClientHandle { gzip: bool, timeout: Option, tx: Arc } type ThreadSender = mpsc::UnboundedSender<(async_impl::Request, oneshot::Sender<::Result>)>; impl ClientHandle { fn new(builder: &mut ClientBuilder) -> ::Result { use std::thread; let gzip = builder.gzip; let timeout = builder.timeout; let mut builder = async_impl::client::take_builder(&mut builder.inner); let (tx, rx) = mpsc::unbounded(); let (spawn_tx, spawn_rx) = oneshot::channel::<::Result<()>>(); try_!(thread::Builder::new().name("reqwest-internal-sync-core".into()).spawn(move || { use tokio_core::reactor::Core; let built = (|| { let core = try_!(Core::new()); let handle = core.handle(); let client = builder.build(&handle)?; Ok((core, handle, client)) })(); let (mut core, handle, client) = match built { Ok((a, b, c)) => { if let Err(_) = spawn_tx.send(Ok(())) { return; } (a, b, c) }, Err(e) => { let _ = spawn_tx.send(Err(e)); return; } }; let work = rx.for_each(|(req, tx)| { let tx: oneshot::Sender<::Result> = tx; let task = client.execute(req) .then(move |x| tx.send(x).map_err(|_| ())); handle.spawn(task); Ok(()) }); // work is Future<(), ()>, and our closure will never return Err let _ = core.run(work); })); wait::timeout(spawn_rx, timeout).expect("core thread cancelled")?; Ok(ClientHandle { gzip: gzip, timeout: timeout, tx: Arc::new(tx), }) } fn execute_request(&self, req: Request) -> ::Result { let (tx, rx) = oneshot::channel(); let (req, body) = request::async(req); let url = req.url().clone(); self.tx.send((req, tx)).expect("core thread panicked"); if let Some(body) = body { try_!(body.send(), &url); } let res = match wait::timeout(rx, self.timeout) { Ok(res) => res, Err(wait::Waited::TimedOut) => return Err(::error::timedout(Some(url))), Err(wait::Waited::Err(_canceled)) => { // The only possible reason there would be a Cancelled error // is if the thread running the Core panicked. We could return // an Err here, like a BrokenPipe, but the Client is not // recoverable. Additionally, the panic in the other thread // is not normal, and should likely be propagated. panic!("core thread panicked"); } }; res.map(|res| { response::new(res, self.gzip, self.timeout, KeepCoreThreadAlive(self.tx.clone())) }) } } // pub(crate) pub struct KeepCoreThreadAlive(Arc);