diff --git a/Cargo.toml b/Cargo.toml index 84e3d8d37a..e4949242f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,12 +12,10 @@ authors = ["Sean McArthur ", keywords = ["http", "hyper", "hyperium"] [dependencies] -cookie = "0.1" httparse = "0.1" log = "0.3" mime = "0.0.11" num_cpus = "0.2" -openssl = "0.6" rustc-serialize = "0.3" time = "0.1" unicase = "0.1" @@ -29,4 +27,17 @@ typeable = "0.1" env_logger = "*" [features] +default = ["ssl"] +# These are grouped together b/c it doesn't really make sense to +# enable one w/o the other +ssl = ["openssl", "cookie"] nightly = [] + + +[dependencies.cookie] +version = "0.1" +optional = true + +[dependencies.openssl] +version = "0.6" +optional = true diff --git a/src/client/mod.rs b/src/client/mod.rs index 386c9249e8..84bd1857dd 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -41,7 +41,9 @@ use url::ParseError as UrlError; use header::{Headers, Header, HeaderFormat}; use header::{ContentLength, Location}; use method::Method; -use net::{NetworkConnector, NetworkStream, ContextVerifier}; +use net::{NetworkConnector, NetworkStream}; +#[cfg(feature = "openssl")] +use net::ContextVerifier; use status::StatusClass::Redirection; use {Url}; use Error; @@ -84,6 +86,7 @@ impl Client { } /// Set the SSL verifier callback for use with OpenSSL. + #[cfg(feature = "openssl")] pub fn set_ssl_verifier(&mut self, verifier: ContextVerifier) { self.connector.set_ssl_verifier(verifier); } @@ -149,6 +152,7 @@ impl + Send, S: NetworkStream + Send> NetworkConne Ok(try!(self.0.connect(host, port, scheme)).into()) } #[inline] + #[cfg(feature = "openssl")] fn set_ssl_verifier(&mut self, verifier: ContextVerifier) { self.0.set_ssl_verifier(verifier); } @@ -164,6 +168,7 @@ impl NetworkConnector for Connector { Ok(try!(self.0.connect(host, port, scheme)).into()) } #[inline] + #[cfg(feature = "openssl")] fn set_ssl_verifier(&mut self, verifier: ContextVerifier) { self.0.set_ssl_verifier(verifier); } diff --git a/src/client/pool.rs b/src/client/pool.rs index 45b3bb1a42..37d372692a 100644 --- a/src/client/pool.rs +++ b/src/client/pool.rs @@ -5,7 +5,9 @@ use std::io::{self, Read, Write}; use std::net::{SocketAddr, Shutdown}; use std::sync::{Arc, Mutex}; -use net::{NetworkConnector, NetworkStream, HttpConnector, ContextVerifier}; +use net::{NetworkConnector, NetworkStream, HttpConnector}; +#[cfg(feature = "openssl")] +use net::ContextVerifier; /// The `NetworkConnector` that behaves as a connection pool used by hyper's `Client`. pub struct Pool { @@ -121,6 +123,7 @@ impl, S: NetworkStream + Send> NetworkConnector fo }) } #[inline] + #[cfg(feature = "openssl")] fn set_ssl_verifier(&mut self, verifier: ContextVerifier) { self.connector.set_ssl_verifier(verifier); } diff --git a/src/error.rs b/src/error.rs index a85eff20e0..80b0c41cc7 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,19 +4,11 @@ use std::fmt; use std::io::Error as IoError; use httparse; +#[cfg(feature = "openssl")] use openssl::ssl::error::SslError; use url; -use self::Error::{ - Method, - Uri, - Version, - Header, - Status, - Io, - Ssl, - TooLarge -}; +use self::Error::*; /// Result type often returned from methods that can have hyper `Error`s. @@ -40,6 +32,7 @@ pub enum Error { /// An `io::Error` that occurred while trying to read or write to a network stream. Io(IoError), /// An error from the `openssl` library. + #[cfg(feature = "openssl")] Ssl(SslError) } @@ -59,6 +52,7 @@ impl StdError for Error { Status => "Invalid Status provided", Uri(ref e) => e.description(), Io(ref e) => e.description(), + #[cfg(feature = "openssl")] Ssl(ref e) => e.description(), } } @@ -66,6 +60,7 @@ impl StdError for Error { fn cause(&self) -> Option<&StdError> { match *self { Io(ref error) => Some(error), + #[cfg(feature = "openssl")] Ssl(ref error) => Some(error), Uri(ref error) => Some(error), _ => None, @@ -85,6 +80,7 @@ impl From for Error { } } +#[cfg(feature = "openssl")] impl From for Error { fn from(err: SslError) -> Error { match err { diff --git a/src/header/common/mod.rs b/src/header/common/mod.rs index 5f93b854dc..813683c6e3 100644 --- a/src/header/common/mod.rs +++ b/src/header/common/mod.rs @@ -25,6 +25,7 @@ pub use self::content_length::ContentLength; pub use self::content_encoding::ContentEncoding; pub use self::content_language::ContentLanguage; pub use self::content_type::ContentType; +#[cfg(feature = "cookie")] pub use self::cookie::Cookie; pub use self::date::Date; pub use self::etag::ETag; @@ -42,6 +43,7 @@ pub use self::location::Location; pub use self::pragma::Pragma; pub use self::referer::Referer; pub use self::server::Server; +#[cfg(feature = "cookie")] pub use self::set_cookie::SetCookie; pub use self::transfer_encoding::TransferEncoding; pub use self::upgrade::{Upgrade, Protocol, ProtocolName}; @@ -329,6 +331,7 @@ mod accept_ranges; mod allow; mod authorization; mod cache_control; +#[cfg(feature = "cookie")] mod cookie; mod connection; mod content_encoding; @@ -351,6 +354,7 @@ mod location; mod pragma; mod referer; mod server; +#[cfg(feature = "cookie")] mod set_cookie; mod transfer_encoding; mod upgrade; diff --git a/src/lib.rs b/src/lib.rs index 595a33afd7..9d16b100d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,7 +131,9 @@ extern crate rustc_serialize as serialize; extern crate time; extern crate url; +#[cfg(feature = "openssl")] extern crate openssl; +#[cfg(feature = "cookie")] extern crate cookie; extern crate unicase; extern crate httparse; diff --git a/src/net.rs b/src/net.rs index 0e3580d0bb..087754dead 100644 --- a/src/net.rs +++ b/src/net.rs @@ -4,12 +4,18 @@ use std::fmt; use std::io::{self, ErrorKind, Read, Write}; use std::net::{SocketAddr, ToSocketAddrs, TcpStream, TcpListener, Shutdown}; use std::mem; +#[cfg(feature = "openssl")] use std::path::Path; +#[cfg(feature = "openssl")] use std::sync::Arc; +#[cfg(feature = "openssl")] use openssl::ssl::{Ssl, SslStream, SslContext, SSL_VERIFY_NONE}; +#[cfg(feature = "openssl")] use openssl::ssl::SslMethod::Sslv23; +#[cfg(feature = "openssl")] use openssl::ssl::error::StreamError as SslIoError; +#[cfg(feature = "openssl")] use openssl::x509::X509FileType; use typeable::Typeable; @@ -72,6 +78,7 @@ pub trait NetworkConnector { fn connect(&self, host: &str, port: u16, scheme: &str) -> ::Result; /// Sets the given `ContextVerifier` to be used when verifying the SSL context /// on the establishment of a new connection. + #[cfg(feature = "openssl")] fn set_ssl_verifier(&mut self, verifier: ContextVerifier); } @@ -147,6 +154,7 @@ pub enum HttpListener { /// Http variant. Http(TcpListener), /// Https variant. The two paths point to the certificate and key PEM files, in that order. + #[cfg(feature = "openssl")] Https(TcpListener, Arc) } @@ -154,6 +162,7 @@ impl Clone for HttpListener { fn clone(&self) -> HttpListener { match *self { HttpListener::Http(ref tcp) => HttpListener::Http(tcp.try_clone().unwrap()), + #[cfg(feature = "openssl")] HttpListener::Https(ref tcp, ref ssl) => HttpListener::Https(tcp.try_clone().unwrap(), ssl.clone()), } } @@ -167,6 +176,7 @@ impl HttpListener { } /// Start listening to an address over HTTPS. + #[cfg(feature = "openssl")] pub fn https(addr: To, cert: &Path, key: &Path) -> ::Result { let mut ssl_context = try!(SslContext::new(Sslv23)); try!(ssl_context.set_cipher_list("DEFAULT")); @@ -177,6 +187,7 @@ impl HttpListener { } /// Start listening to an address of HTTPS using the given SslContext + #[cfg(feature = "openssl")] pub fn https_with_context(addr: To, ssl_context: SslContext) -> ::Result { Ok(HttpListener::Https(try!(TcpListener::bind(addr)), Arc::new(ssl_context))) } @@ -189,6 +200,7 @@ impl NetworkListener for HttpListener { fn accept(&mut self) -> ::Result { match *self { HttpListener::Http(ref mut tcp) => Ok(HttpStream::Http(CloneTcpStream(try!(tcp.accept()).0))), + #[cfg(feature = "openssl")] HttpListener::Https(ref mut tcp, ref ssl_context) => { let stream = CloneTcpStream(try!(tcp.accept()).0); match SslStream::new_server(&**ssl_context, stream) { @@ -206,6 +218,7 @@ impl NetworkListener for HttpListener { fn local_addr(&mut self) -> io::Result { match *self { HttpListener::Http(ref mut tcp) => tcp.local_addr(), + #[cfg(feature = "openssl")] HttpListener::Https(ref mut tcp, _) => tcp.local_addr(), } } @@ -245,6 +258,7 @@ pub enum HttpStream { /// A stream over the HTTP protocol. Http(CloneTcpStream), /// A stream over the HTTP protocol, protected by SSL. + #[cfg(feature = "openssl")] Https(SslStream), } @@ -252,6 +266,7 @@ impl fmt::Debug for HttpStream { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { HttpStream::Http(_) => write!(fmt, "Http HttpStream"), + #[cfg(feature = "openssl")] HttpStream::Https(_) => write!(fmt, "Https HttpStream"), } } @@ -262,6 +277,7 @@ impl Read for HttpStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { match *self { HttpStream::Http(ref mut inner) => inner.read(buf), + #[cfg(feature = "openssl")] HttpStream::Https(ref mut inner) => inner.read(buf) } } @@ -272,6 +288,7 @@ impl Write for HttpStream { fn write(&mut self, msg: &[u8]) -> io::Result { match *self { HttpStream::Http(ref mut inner) => inner.write(msg), + #[cfg(feature = "openssl")] HttpStream::Https(ref mut inner) => inner.write(msg) } } @@ -279,6 +296,7 @@ impl Write for HttpStream { fn flush(&mut self) -> io::Result<()> { match *self { HttpStream::Http(ref mut inner) => inner.flush(), + #[cfg(feature = "openssl")] HttpStream::Https(ref mut inner) => inner.flush(), } } @@ -289,6 +307,7 @@ impl NetworkStream for HttpStream { fn peer_addr(&mut self) -> io::Result { match *self { HttpStream::Http(ref mut inner) => inner.0.peer_addr(), + #[cfg(feature = "openssl")] HttpStream::Https(ref mut inner) => inner.get_mut().0.peer_addr() } } @@ -307,16 +326,22 @@ impl NetworkStream for HttpStream { match *self { HttpStream::Http(ref mut inner) => shutdown(&mut inner.0, how), + #[cfg(feature = "openssl")] HttpStream::Https(ref mut inner) => shutdown(&mut inner.get_mut().0, how) } } } +#[cfg(feature = "openssl")] /// A connector that will produce HttpStreams. pub struct HttpConnector(pub Option); +#[cfg(not(feature = "openssl"))] +/// A connector that will produce HttpStreams. +pub struct HttpConnector(pub Option<()>); /// A method that can set verification methods on an SSL context +#[cfg(feature = "openssl")] pub type ContextVerifier = Box () + Send>; impl NetworkConnector for HttpConnector { @@ -329,6 +354,7 @@ impl NetworkConnector for HttpConnector { debug!("http scheme"); Ok(HttpStream::Http(CloneTcpStream(try!(TcpStream::connect(addr))))) }, + #[cfg(feature = "openssl")] "https" => { debug!("https scheme"); let stream = CloneTcpStream(try!(TcpStream::connect(addr))); @@ -341,12 +367,13 @@ impl NetworkConnector for HttpConnector { let stream = try!(SslStream::new(&context, stream)); Ok(HttpStream::Https(stream)) }, - _ => { + x => { Err(io::Error::new(io::ErrorKind::InvalidInput, - "Invalid scheme for Http")) + format!("Invalid scheme for Http: {}", x))) } })) } + #[cfg(feature = "openssl")] fn set_ssl_verifier(&mut self, verifier: ContextVerifier) { self.0 = Some(verifier); } diff --git a/src/server/mod.rs b/src/server/mod.rs index 08b92e3ef1..bfb8c7edaf 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -20,10 +20,12 @@ use std::fmt; use std::io::{ErrorKind, BufWriter, Write}; use std::marker::PhantomData; use std::net::{SocketAddr, ToSocketAddrs}; +#[cfg(feature = "openssl")] use std::path::Path; use std::thread::{self, JoinHandle}; use num_cpus; +#[cfg(feature = "openssl")] use openssl::ssl::SslContext; pub use self::request::Request; @@ -49,10 +51,13 @@ pub mod response; mod listener; #[derive(Debug)] +#[cfg(feature = "openssl")] enum SslConfig<'a> { CertAndKey(&'a Path, &'a Path), Context(SslContext), } +#[cfg(not(feature = "openssl"))] +type SslConfig<'a> = PhantomData<&'a ()>; /// A server can listen on a TCP socket. /// @@ -91,6 +96,7 @@ impl<'a, H: Handler + 'static> Server<'a, H, HttpListener> { Server::new(handler) } /// Creates a new server that will handler `HttpStreams`s using a TLS connection. + #[cfg(feature = "openssl")] pub fn https(handler: H, cert: &'a Path, key: &'a Path) -> Server<'a, H, HttpListener> { Server { handler: handler, @@ -99,6 +105,7 @@ impl<'a, H: Handler + 'static> Server<'a, H, HttpListener> { } } /// Creates a new server that will handler `HttpStreams`s using a TLS connection defined by an SslContext. + #[cfg(feature = "openssl")] pub fn https_with_context(handler: H, ssl_context: SslContext) -> Server<'a, H, HttpListener> { Server { handler: handler, @@ -112,8 +119,12 @@ impl<'a, H: Handler + 'static> Server<'a, H, HttpListener> { /// Binds to a socket, and starts handling connections using a task pool. pub fn listen_threads(self, addr: T, threads: usize) -> ::Result { let listener = try!(match self.ssl { + #[cfg(feature = "openssl")] Some(SslConfig::CertAndKey(cert, key)) => HttpListener::https(addr, cert, key), + #[cfg(feature = "openssl")] Some(SslConfig::Context(ssl_context)) => HttpListener::https_with_context(addr, ssl_context), + #[cfg(not(feature = "openssl"))] + Some(_) => panic!("Internal error - we have an SSL config even though SSL is disabled"), None => HttpListener::http(addr) }); with_listener(self.handler, listener, threads)