Repository: seanmonstar/reqwest (language: rust)

Issue:
Add [features] to the Cargo.toml file for disabling TLS
Hello all, I am currently using reqwest in a simple project of mine that was meant to be more portable than the original shell scripts. Using this library, and working in Rust has been a huge step in the right direction! 

Unfortunately, because of reqwest's reliance on rust-native-tls, my program isn't portable between different openssl versions. I am not using any tls in my program, and am dealing solely with plain http requests, so I could certainly do without tls support.

I am proposing, and offering to help implement, that we add a feature to the crate that will enable the switching of tls support on or off at compile time.  

In the end, I would like to be able to import reqwest in the following way:
```
reqwest = { version = "*", features = ["no-tls"] }
```
And be able to compile my program without any openssl dependancies.

Let me know what this change would entail, and how I can get started.

Thanks,
Brooks J Rady


Discussion / hints:
I have heard a few users tell me they wish they could use reqwest with a different TLS implementation. While I don't intend for the main `reqwest` crate, as part of the appeal of reqwest is that it Just Works, I have considered some sort of `reqwest-core` crate or something that is all of reqwest without the TLS.

Though, that may be much more than what you're suggesting. I'm not certain yet which would be the easiest or best way forward, but interested in discussing!
>I have heard a few users tell me they wish they could use reqwest with a different TLS implementation. While I don't intend for the main reqwest crate, as part of the appeal of reqwest is that it Just Works

Allowing other TLS implementations doesn't make `reqwest` stop "just working." The most flexible case would be making some sort of interface like `hyper`'s [connectors](https://docs.rs/hyper/0.11.10/hyper/client/struct.HttpConnector.html). A less flexible case would just be using feature gates and implementing other TLS solutions (or having them PR'd in).

In the latter case, using default features, reqwest would function exactly the same. You'd have to opt in to using other TLS implementations (where the API would remain the same). It Just Works™.

The only thing stopping me from using `reqwest` is that I can't use `rustls` with it, but I'd really like to use it instead of my hack crate that wraps `hyper` back into blocking mode.
  
Right. We just had an instance where `reqwest` did not "just work" because it couldn't find the appropriate openssl binding. Using a default of `rustls` would mean that `reqwest` wouldn't be dependent on the system to provide the SSL functionality, making it more "just works". At least making the TLS component plugable allows consumers to choose an alternative, particularly if/when `openssl` is not wanted.
I'd love to find some way to offer reqwest with rustls. Here's some random thoughts:

- reqwest currently defaults to native-tls because it uses mature, reviewed TLS libraries, **and** it works with more of the web (rustls doesn't support older TLS or SSL versions).
- They can't really be cargo features, as features are additive: if one dependency enables the `native-tls` feature, and another enables `rustls`, and they both tried to define what the default TLS was, they would conflict and cause compilation errors.
- They could perhaps become cargo features if they enabled methods on the `ClientBuilder` to be able to pick a TLS backend explicitly. Like, with a `rustls` feature enabled, then `Client::builder().use_rustls().build()` or something. Hm, this might work out OK...

What do you all think of the 3rd point?
I would prefer a `with_backend` function or a `reqwest::backend::rustls::Client` and `reqwest::backend::native_tls::Client`, similar to how `diesel` [handles](https://docs.rs/diesel/1.1.1/diesel/pg/index.html) [different](https://docs.rs/diesel/1.1.1/diesel/sqlite/index.html) [SQL](https://docs.rs/diesel/1.1.1/diesel/mysql/index.html) [backends](https://docs.rs/diesel/1.1.1/diesel/backend/trait.Backend.html).

In addition to being more flexible and extensible (I can make my own implementation of a `Backend` trait for my own custom TLS, etc.), it's more of what I would expect from an API like this, like how `hyper` has connectors.

Also, while supporting more of the web is *good* in a sense, supporting outdated, insecure software is a loss in my book. [Most of the web uses up-to-date protocols at this point, anyway.](https://www.ssllabs.com/ssl-pulse/)
Just out of curiosity, why would you prefer to have strictly typed clients over the just having a `reqwest::Client` that has a boxed TLS implementation inside it?

We could provide a TLS trait (and perhaps typed `Client`s for some backends built-in). Maybe worth starting inside `reqwest::unstable` first?
Really, doing it exactly the same as `diesel` isn't great. Extensible is good, but including TLS options in this crate will fall into the same trap that `hyper` did, where there's code bloat from TLS implementations. Including rustls support in *this* crate is a bad idea, but having a crate like `reqwest_rustls` which provides a backend that can be tied into `reqwest` makes sense.
This feature would be much appreciated, I'm working on a project which requires cross compilation and I'm finding myself trying to decide whether it is worth downloading and compiling OpenSSL from source to get this working. I don't even need TLS for this project as it will be communicating with a local web server only, even an option to disable TLS entirely would be awesome!
I have an amusingly similar use case to @TheLostLambda (porting a mess of shell scripts to rust), and as a rust newbie I'd dearly love to be able to use reqwest for this
I'm currently muddling through with [hyper-rustls](https://crates.io/crates/hyper-rustls) and I do have it working, but it was quite tricky for me to get right
This is what I have so far to download a file to disk, and I haven't even got following redirects working yet: https://github.com/jokeyrhyme/dotfiles-rs/blob/0.6.1/src/utils/http.rs#L12
Specifying TLS backends per-client, combined with making `native-tls` an optional feature, sounds like the best solution to me.

It would be good if attempting to send a TLS request without a provider was a compile-time error. Perhaps by making the provider a type parameter on the client, and only implementing e.g. a `TlsSend` trait on the client if its provider implements e.g. `TlsProvider`? Then a `Client<NoTls>` (or `Client<()>`) can be used when we don't want to include any TLS-providing libraries.
It would be nice to have the ability to disable TLS entirely, even if it's too much design work to make the provider modular. Most applications have no need to directly support TLS (this is what reverse proxies are for), so having the extra code is purely a liability.

Fix patch:
diff --git a/.travis.yml b/.travis.yml
index ef199b999..41f4de53a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,17 +8,14 @@ matrix:
           rust: stable
 
         - rust: stable
-          env: FEATURES=""
         - rust: beta
-          env: FEATURES=""
         - rust: nightly
-          env: FEATURES=""
+
+        # Disable default-tls
+        - rust: stable
+          env: FEATURES="--no-default-features"
 
         - rust: stable
-          env: FEATURES="--features hyper-011"
-        - rust: beta
-          env: FEATURES="--features hyper-011"
-        - rust: nightly
           env: FEATURES="--features hyper-011"
 
         # minimum version
diff --git a/Cargo.toml b/Cargo.toml
index 1f5ffba2e..d7f7eaf74 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,12 +17,12 @@ futures = "0.1.23"
 http = "0.1.10"
 hyper = "0.12.13"
 hyper-old-types = { version = "0.11", optional = true, features = ["compat"] }
-hyper-tls = "0.3"
+hyper-tls = { version = "0.3", optional = true }
 libflate = "0.1.18"
 log = "0.4"
 mime = "0.3.7"
 mime_guess = "2.0.0-alpha.6"
-native-tls = "0.2"
+native-tls = { version = "0.2", optional = true }
 serde = "1.0"
 serde_json = "1.0"
 serde_urlencoded = "0.5"
@@ -36,8 +36,9 @@ env_logger = "0.5"
 serde_derive = "1.0"
 
 [features]
-default = []
+default = ["default-tls"]
 hyper-011 = ["hyper-old-types"]
+default-tls = ["hyper-tls", "native-tls"]
 
 [package.metadata.docs.rs]
 rustdoc-args = ["--cfg", "docs_rs_workaround"]
diff --git a/src/async_impl/client.rs b/src/async_impl/client.rs
index 56ec98ea3..673abd58b 100644
--- a/src/async_impl/client.rs
+++ b/src/async_impl/client.rs
@@ -8,6 +8,7 @@ use hyper::client::ResponseFuture;
 use header::{HeaderMap, HeaderValue, LOCATION, USER_AGENT, REFERER, ACCEPT,
              ACCEPT_ENCODING, RANGE, TRANSFER_ENCODING, CONTENT_TYPE, CONTENT_LENGTH, CONTENT_ENCODING};
 use mime::{self};
+#[cfg(feature = "default-tls")]
 use native_tls::{TlsConnector, TlsConnectorBuilder};
 
 
@@ -16,7 +17,9 @@ use super::response::Response;
 use connect::Connector;
 use into_url::to_uri;
 use redirect::{self, RedirectPolicy, remove_sensitive_headers};
-use {Certificate, Identity, IntoUrl, Method, Proxy, StatusCode, Url};
+use {IntoUrl, Method, Proxy, StatusCode, Url};
+#[cfg(feature = "default-tls")]
+use {Certificate, Identity};
 
 static DEFAULT_USER_AGENT: &'static str =
     concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
@@ -41,12 +44,15 @@ pub struct ClientBuilder {
 struct Config {
     gzip: bool,
     headers: HeaderMap,
+    #[cfg(feature = "default-tls")]
     hostname_verification: bool,
+    #[cfg(feature = "default-tls")]
     certs_verification: bool,
     proxies: Vec<Proxy>,
     redirect_policy: RedirectPolicy,
     referer: bool,
     timeout: Option<Duration>,
+    #[cfg(feature = "default-tls")]
     tls: TlsConnectorBuilder,
     dns_threads: usize,
 }
@@ -62,12 +68,15 @@ impl ClientBuilder {
             config: Config {
                 gzip: true,
                 headers: headers,
+                #[cfg(feature = "default-tls")]
                 hostname_verification: true,
+                #[cfg(feature = "default-tls")]
                 certs_verification: true,
                 proxies: Vec::new(),
                 redirect_policy: RedirectPolicy::default(),
                 referer: true,
                 timeout: None,
+                #[cfg(feature = "default-tls")]
                 tls: TlsConnector::builder(),
                 dns_threads: 4,
             },
@@ -80,16 +89,31 @@ impl ClientBuilder {
     ///
     /// This method fails if native TLS backend cannot be initialized.
     pub fn build(self) -> ::Result<Client> {
-        let mut config = self.config;
+        let config = self.config;
 
-        config.tls.danger_accept_invalid_hostnames(!config.hostname_verification);
-        config.tls.danger_accept_invalid_certs(!config.certs_verification);
 
-        let tls = try_!(config.tls.build());
+        let connector = {
+            #[cfg(feature = "default-tls")]
+            {
+            let mut tls = config.tls;
+            tls.danger_accept_invalid_hostnames(!config.hostname_verification);
+            tls.danger_accept_invalid_certs(!config.certs_verification);
 
-        let proxies = Arc::new(config.proxies);
+            let tls = try_!(tls.build());
 
-        let connector = Connector::new(config.dns_threads, tls, proxies.clone());
+            let proxies = Arc::new(config.proxies);
+
+            Connector::new(config.dns_threads, tls, proxies.clone())
+            }
+
+
+            #[cfg(not(feature = "default-tls"))]
+            {
+            let proxies = Arc::new(config.proxies);
+
+            Connector::new(config.dns_threads, proxies.clone())
+            }
+        };
 
         let hyper_client = ::hyper::Client::builder()
             .build(connector);
@@ -109,12 +133,14 @@ impl ClientBuilder {
     ///
     /// This can be used to connect to a server that has a self-signed
     /// certificate for example.
+    #[cfg(feature = "default-tls")]
     pub fn add_root_certificate(mut self, cert: Certificate) -> ClientBuilder {
         self.config.tls.add_root_certificate(cert.cert());
         self
     }
 
     /// Sets the identity to be used for client certificate authentication.
+    #[cfg(feature = "default-tls")]
     pub fn identity(mut self, identity: Identity) -> ClientBuilder {
         self.config.tls.identity(identity.pkcs12());
         self
@@ -130,6 +156,7 @@ impl ClientBuilder {
     /// 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.
+    #[cfg(feature = "default-tls")]
     pub fn danger_accept_invalid_hostnames(mut self, accept_invalid_hostname: bool) -> ClientBuilder {
         self.config.hostname_verification = !accept_invalid_hostname;
         self
@@ -147,6 +174,7 @@ impl ClientBuilder {
     /// will be trusted for use. This includes expired certificates. This
     /// introduces significant vulnerabilities, and should only be used
     /// as a last resort.
+    #[cfg(feature = "default-tls")]
     pub fn danger_accept_invalid_certs(mut self, accept_invalid_certs: bool) -> ClientBuilder {
         self.config.certs_verification = !accept_invalid_certs;
         self
diff --git a/src/client.rs b/src/client.rs
index 31497c077..02d722b31 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -9,7 +9,9 @@ use futures::sync::{mpsc, oneshot};
 
 use request::{Request, RequestBuilder};
 use response::Response;
-use {async_impl, header, Certificate, Identity, Method, IntoUrl, Proxy, RedirectPolicy, wait};
+use {async_impl, header, Method, IntoUrl, Proxy, RedirectPolicy, wait};
+#[cfg(feature = "default-tls")]
+use {Certificate, Identity};
 
 /// A `Client` to make Requests with.
 ///
@@ -106,6 +108,7 @@ impl ClientBuilder {
     /// # Errors
     ///
     /// This method fails if adding root certificate was unsuccessful.
+    #[cfg(feature = "default-tls")]
     pub fn add_root_certificate(self, cert: Certificate) -> ClientBuilder {
         self.with_inner(move |inner| inner.add_root_certificate(cert))
     }
@@ -133,6 +136,7 @@ impl ClientBuilder {
     /// # Ok(())
     /// # }
     /// ```
+    #[cfg(feature = "default-tls")]
     pub fn identity(self, identity: Identity) -> ClientBuilder {
         self.with_inner(move |inner| inner.identity(identity))
     }
@@ -148,6 +152,7 @@ impl ClientBuilder {
     /// 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.
+    #[cfg(feature = "default-tls")]
     pub fn danger_accept_invalid_hostnames(self, accept_invalid_hostname: bool) -> ClientBuilder {
         self.with_inner(|inner| inner.danger_accept_invalid_hostnames(accept_invalid_hostname))
     }
@@ -164,6 +169,7 @@ impl ClientBuilder {
     /// will be trusted for use. This includes expired certificates. This
     /// introduces significant vulnerabilities, and should only be used
     /// as a last resort.
+    #[cfg(feature = "default-tls")]
     pub fn danger_accept_invalid_certs(self, accept_invalid_certs: bool) -> ClientBuilder {
         self.with_inner(|inner| inner.danger_accept_invalid_certs(accept_invalid_certs))
     }
diff --git a/src/connect.rs b/src/connect.rs
index 4474b36e3..cc9ff093a 100644
--- a/src/connect.rs
+++ b/src/connect.rs
@@ -1,34 +1,50 @@
-use bytes::{Buf, BufMut, IntoBuf};
-use futures::{Async, Future, Poll};
+use bytes::{Buf, BufMut};
+use futures::{Future, Poll};
 use http::uri::Scheme;
 use hyper::client::{HttpConnector};
 use hyper::client::connect::{Connect, Connected, Destination};
+#[cfg(feature = "default-tls")]
 use hyper_tls::{HttpsConnector, MaybeHttpsStream};
+#[cfg(feature = "default-tls")]
 use native_tls::TlsConnector;
 use tokio_io::{AsyncRead, AsyncWrite};
+#[cfg(feature = "default-tls")]
 use connect_async::{TlsConnectorExt, TlsStream};
 
-use std::io::{self, Cursor, Read, Write};
+use std::io::{self, Read, Write};
 use std::sync::Arc;
 
 use Proxy;
 
 pub(crate) struct Connector {
-    https: HttpsConnector<HttpConnector>,
+    #[cfg(feature = "default-tls")]
+    http: HttpsConnector<HttpConnector>,
+    #[cfg(not(feature = "default-tls"))]
+    http: HttpConnector,
     proxies: Arc<Vec<Proxy>>,
+    #[cfg(feature = "default-tls")]
     tls: TlsConnector,
 }
 
 impl Connector {
+    #[cfg(not(feature = "default-tls"))]
+    pub(crate) fn new(threads: usize, proxies: Arc<Vec<Proxy>>) -> Connector {
+        let http = HttpConnector::new(threads);
+        Connector {
+            http,
+            proxies,
+        }
+    }
+    #[cfg(feature = "default-tls")]
     pub(crate) fn new(threads: usize, tls: TlsConnector, proxies: Arc<Vec<Proxy>>) -> Connector {
         let mut http = HttpConnector::new(threads);
         http.enforce_http(false);
-        let https = HttpsConnector::from((http, tls.clone()));
+        let http = HttpsConnector::from((http, tls.clone()));
 
         Connector {
-            https: https,
-            proxies: proxies,
-            tls: tls,
+            http,
+            proxies,
+            tls,
         }
     }
 }
@@ -55,11 +71,13 @@ impl Connect for Connector {
 
                 ndst.set_port(puri.port());
 
+                #[cfg(feature = "default-tls")]
+                {
                 if dst.scheme() == "https" {
                     let host = dst.host().to_owned();
                     let port = dst.port().unwrap_or(443);
                     let tls = self.tls.clone();
-                    return Box::new(self.https.connect(ndst).and_then(move |(conn, connected)| {
+                    return Box::new(self.http.connect(ndst).and_then(move |(conn, connected)| {
                         trace!("tunneling HTTPS over proxy");
                         tunnel(conn, host.clone(), port)
                             .and_then(move |tunneled| {
@@ -69,20 +87,27 @@ impl Connect for Connector {
                             .map(|io| (Conn::Proxied(io), connected.proxy(true)))
                     }));
                 }
-                return Box::new(self.https.connect(ndst).map(|(io, connected)| (Conn::Normal(io), connected.proxy(true))));
+                }
+                return Box::new(self.http.connect(ndst).map(|(io, connected)| (Conn::Normal(io), connected.proxy(true))));
             }
         }
-        Box::new(self.https.connect(dst).map(|(io, connected)| (Conn::Normal(io), connected)))
+        Box::new(self.http.connect(dst).map(|(io, connected)| (Conn::Normal(io), connected)))
     }
 }
 
 type HttpStream = <HttpConnector as Connect>::Transport;
+#[cfg(feature = "default-tls")]
 type HttpsStream = MaybeHttpsStream<HttpStream>;
 
+
 pub(crate) type Connecting = Box<Future<Item=(Conn, Connected), Error=io::Error> + Send>;
 
 pub(crate) enum Conn {
+    #[cfg(feature = "default-tls")]
     Normal(HttpsStream),
+    #[cfg(not(feature = "default-tls"))]
+    Normal(HttpStream),
+    #[cfg(feature = "default-tls")]
     Proxied(TlsStream<MaybeHttpsStream<HttpStream>>),
 }
 
@@ -91,6 +116,7 @@ impl Read for Conn {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         match *self {
             Conn::Normal(ref mut s) => s.read(buf),
+            #[cfg(feature = "default-tls")]
             Conn::Proxied(ref mut s) => s.read(buf),
         }
     }
@@ -101,6 +127,7 @@ impl Write for Conn {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
         match *self {
             Conn::Normal(ref mut s) => s.write(buf),
+            #[cfg(feature = "default-tls")]
             Conn::Proxied(ref mut s) => s.write(buf),
         }
     }
@@ -109,6 +136,7 @@ impl Write for Conn {
     fn flush(&mut self) -> io::Result<()> {
         match *self {
             Conn::Normal(ref mut s) => s.flush(),
+            #[cfg(feature = "default-tls")]
             Conn::Proxied(ref mut s) => s.flush(),
         }
     }
@@ -118,6 +146,7 @@ impl AsyncRead for Conn {
     unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
         match *self {
             Conn::Normal(ref s) => s.prepare_uninitialized_buffer(buf),
+            #[cfg(feature = "default-tls")]
             Conn::Proxied(ref s) => s.prepare_uninitialized_buffer(buf),
         }
     }
@@ -125,6 +154,7 @@ impl AsyncRead for Conn {
     fn read_buf<B: BufMut>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
         match *self {
             Conn::Normal(ref mut s) => s.read_buf(buf),
+            #[cfg(feature = "default-tls")]
             Conn::Proxied(ref mut s) => s.read_buf(buf),
         }
     }
@@ -134,6 +164,7 @@ impl AsyncWrite for Conn {
     fn shutdown(&mut self) -> Poll<(), io::Error> {
         match *self {
             Conn::Normal(ref mut s) => s.shutdown(),
+            #[cfg(feature = "default-tls")]
             Conn::Proxied(ref mut s) => s.shutdown(),
         }
     }
@@ -141,11 +172,13 @@ impl AsyncWrite for Conn {
     fn write_buf<B: Buf>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
         match *self {
             Conn::Normal(ref mut s) => s.write_buf(buf),
+            #[cfg(feature = "default-tls")]
             Conn::Proxied(ref mut s) => s.write_buf(buf),
         }
     }
 }
 
+#[cfg(feature = "default-tls")]
 fn tunnel<T>(conn: T, host: String, port: u16) -> Tunnel<T> {
      let buf = format!("\
         CONNECT {0}:{1} HTTP/1.1\r\n\
@@ -154,23 +187,26 @@ fn tunnel<T>(conn: T, host: String, port: u16) -> Tunnel<T> {
     ", host, port).into_bytes();
 
      Tunnel {
-        buf: buf.into_buf(),
+        buf: io::Cursor::new(buf),
         conn: Some(conn),
         state: TunnelState::Writing,
      }
 }
 
+#[cfg(feature = "default-tls")]
 struct Tunnel<T> {
-    buf: Cursor<Vec<u8>>,
+    buf: io::Cursor<Vec<u8>>,
     conn: Option<T>,
     state: TunnelState,
 }
 
+#[cfg(feature = "default-tls")]
 enum TunnelState {
     Writing,
     Reading
 }
 
+#[cfg(feature = "default-tls")]
 impl<T> Future for Tunnel<T>
 where T: AsyncRead + AsyncWrite {
     type Item = T;
@@ -194,7 +230,7 @@ where T: AsyncRead + AsyncWrite {
                 } else if read.len() > 12 {
                     if read.starts_with(b"HTTP/1.1 200") || read.starts_with(b"HTTP/1.0 200") {
                         if read.ends_with(b"\r\n\r\n") {
-                            return Ok(Async::Ready(self.conn.take().unwrap()));
+                            return Ok(self.conn.take().unwrap().into());
                         }
                         // else read more
                     } else {
@@ -206,6 +242,7 @@ where T: AsyncRead + AsyncWrite {
     }
 }
 
+#[cfg(feature = "default-tls")]
 #[inline]
 fn tunnel_eof() -> io::Error {
     io::Error::new(
@@ -214,6 +251,7 @@ fn tunnel_eof() -> io::Error {
     )
 }
 
+#[cfg(feature = "default-tls")]
 #[cfg(test)]
 mod tests {
     use std::io::{Read, Write};
diff --git a/src/connect_async.rs b/src/connect_async.rs
index f870fcf95..ee9c7697b 100644
--- a/src/connect_async.rs
+++ b/src/connect_async.rs
@@ -1,8 +1,7 @@
 use std::io::{self, Read, Write};
 
 use futures::{Poll, Future, Async};
-use native_tls;
-use native_tls::{HandshakeError, Error, TlsConnector};
+use native_tls::{self, HandshakeError, Error, TlsConnector};
 use tokio_io::{AsyncRead, AsyncWrite};
 
 /// A wrapper around an underlying raw stream which implements the TLS or SSL
@@ -128,4 +127,4 @@ impl<S: Read + Write> Future for MidHandshake<S> {
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/error.rs b/src/error.rs
index 807c42fe9..cf2edb2cd 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -135,6 +135,7 @@ impl Error {
             Kind::Hyper(ref e) => Some(e),
             Kind::Mime(ref e) => Some(e),
             Kind::Url(ref e) => Some(e),
+            #[cfg(feature = "default-tls")]
             Kind::Tls(ref e) => Some(e),
             Kind::Io(ref e) => Some(e),
             Kind::UrlEncoded(ref e) => Some(e),
@@ -224,6 +225,7 @@ impl fmt::Display for Error {
             Kind::Mime(ref e) => fmt::Display::fmt(e, f),
             Kind::Url(ref e) => fmt::Display::fmt(e, f),
             Kind::UrlBadScheme => f.write_str("URL scheme is not allowed"),
+            #[cfg(feature = "default-tls")]
             Kind::Tls(ref e) => fmt::Display::fmt(e, f),
             Kind::Io(ref e) => fmt::Display::fmt(e, f),
             Kind::UrlEncoded(ref e) => fmt::Display::fmt(e, f),
@@ -250,6 +252,7 @@ impl StdError for Error {
             Kind::Mime(ref e) => e.description(),
             Kind::Url(ref e) => e.description(),
             Kind::UrlBadScheme => "URL scheme is not allowed",
+            #[cfg(feature = "default-tls")]
             Kind::Tls(ref e) => e.description(),
             Kind::Io(ref e) => e.description(),
             Kind::UrlEncoded(ref e) => e.description(),
@@ -267,6 +270,7 @@ impl StdError for Error {
             Kind::Hyper(ref e) => e.cause(),
             Kind::Mime(ref e) => e.cause(),
             Kind::Url(ref e) => e.cause(),
+            #[cfg(feature = "default-tls")]
             Kind::Tls(ref e) => e.cause(),
             Kind::Io(ref e) => e.cause(),
             Kind::UrlEncoded(ref e) => e.cause(),
@@ -287,6 +291,7 @@ pub(crate) enum Kind {
     Mime(::mime::FromStrError),
     Url(::url::ParseError),
     UrlBadScheme,
+    #[cfg(feature = "default-tls")]
     Tls(::native_tls::Error),
     Io(io::Error),
     UrlEncoded(::serde_urlencoded::ser::Error),
@@ -347,6 +352,7 @@ impl From<::serde_json::Error> for Kind {
     }
 }
 
+#[cfg(feature = "default-tls")]
 impl From<::native_tls::Error> for Kind {
     fn from(err: ::native_tls::Error) -> Kind {
         Kind::Tls(err)
diff --git a/src/lib.rs b/src/lib.rs
index 0df1dace5..2b40871a3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -117,6 +117,16 @@
 //! # }
 //! ```
 //!
+//! ## Optional Features
+//!
+//! The following are a list of [Cargo features][cargo-features] that can be
+//! enabled or disabled:
+//!
+//! - **default-tls** *(enabled by default)*: Provides TLS support via the
+//!   `native-tls` library to connect over HTTPS.
+//! - **hyper-011**: Provides support for hyper's old typed headers.
+//!
+//!
 //! [hyper]: http://hyper.rs
 //! [client]: ./struct.Client.html
 //! [response]: ./struct.Response.html
@@ -124,6 +134,7 @@
 //! [builder]: ./struct.RequestBuilder.html
 //! [serde]: http://serde.rs
 //! [cookiejar_issue]: https://github.com/seanmonstar/reqwest/issues/14
+//! [cargo-features]: https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-features-section
 
 extern crate base64;
 extern crate bytes;
@@ -134,12 +145,14 @@ extern crate http;
 extern crate hyper;
 #[cfg(feature = "hyper-011")]
 pub extern crate hyper_old_types as hyper_011;
+#[cfg(feature = "default-tls")]
 extern crate hyper_tls;
 #[macro_use]
 extern crate log;
 extern crate libflate;
 extern crate mime;
 extern crate mime_guess;
+#[cfg(feature = "default-tls")]
 extern crate native_tls;
 extern crate serde;
 #[cfg(test)]
@@ -148,7 +161,7 @@ extern crate serde_derive;
 extern crate serde_json;
 extern crate serde_urlencoded;
 extern crate tokio;
-#[macro_use]
+#[cfg_attr(feature = "default-tls", macro_use)]
 extern crate tokio_io;
 extern crate url;
 extern crate uuid;
@@ -167,6 +180,7 @@ pub use self::proxy::Proxy;
 pub use self::redirect::{RedirectAction, RedirectAttempt, RedirectPolicy};
 pub use self::request::{Request, RequestBuilder};
 pub use self::response::Response;
+#[cfg(feature = "default-tls")]
 pub use self::tls::{Certificate, Identity};
 
 
@@ -176,6 +190,7 @@ mod error;
 
 mod async_impl;
 mod connect;
+#[cfg(feature = "default-tls")]
 mod connect_async;
 mod body;
 mod client;
@@ -184,6 +199,7 @@ mod proxy;
 mod redirect;
 mod request;
 mod response;
+#[cfg(feature = "default-tls")]
 mod tls;
 mod wait;
 


Test patch: