Skip to content

Commit

Permalink
feat(lib): remove SSL dependencies
Browse files Browse the repository at this point in the history
BREAKING CHANGE: hyper will no longer provide OpenSSL support out of the
  box. The `hyper::net::Openssl` and related types are gone. The `Client`
  now uses an `HttpConnector` by default, which will error trying to
  access HTTPS URLs.

  TLS support should be added in from other crates, such as
  hyper-openssl, or similar using different TLS implementations.
  • Loading branch information
seanmonstar committed Jan 4, 2017
1 parent 14a4f1c commit 2f48612
Show file tree
Hide file tree
Showing 4 changed files with 1 addition and 337 deletions.
16 changes: 1 addition & 15 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,9 @@ unicase = "1.0"
url = "1.0"

[dependencies.cookie]
version = "0.2"
version = "0.5"
default-features = false

[dependencies.openssl]
version = "0.7"
optional = true

[dependencies.openssl-verify]
version = "0.1"
optional = true

[dependencies.security-framework]
version = "0.1.4"
optional = true

[dependencies.solicit]
version = "0.4"
default-features = false
Expand All @@ -52,7 +40,5 @@ optional = true
env_logger = "0.3"

[features]
default = ["ssl"]
ssl = ["openssl", "openssl-verify", "cookie/secure"]
serde-serialization = ["serde", "mime/serde"]
nightly = []
19 changes: 0 additions & 19 deletions src/client/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,6 @@ use client::scheme::Scheme;
use method::Method;
use net::{NetworkConnector, HttpConnector, NetworkStream, SslClient};

#[cfg(all(feature = "openssl", not(feature = "security-framework")))]
pub fn tunnel(proxy: (Scheme, Cow<'static, str>, u16)) -> Proxy<HttpConnector, ::net::Openssl> {
Proxy {
connector: HttpConnector,
proxy: proxy,
ssl: Default::default()
}
}

#[cfg(feature = "security-framework")]
pub fn tunnel(proxy: (Scheme, Cow<'static, str>, u16)) -> Proxy<HttpConnector, ::net::ClientWrapper> {
Proxy {
connector: HttpConnector,
proxy: proxy,
ssl: Default::default()
}
}

#[cfg(not(any(feature = "openssl", feature = "security-framework")))]
pub fn tunnel(proxy: (Scheme, Cow<'static, str>, u16)) -> Proxy<HttpConnector, self::no_ssl::Plaintext> {
Proxy {
connector: HttpConnector,
Expand Down
6 changes: 0 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,6 @@
extern crate rustc_serialize as serialize;
extern crate time;
#[macro_use] extern crate url;
#[cfg(feature = "openssl")]
extern crate openssl;
#[cfg(feature = "openssl-verify")]
extern crate openssl_verify;
#[cfg(feature = "security-framework")]
extern crate security_framework;
#[cfg(feature = "serde-serialization")]
extern crate serde;
extern crate cookie;
Expand Down
297 changes: 0 additions & 297 deletions src/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@ use std::io::{self, ErrorKind, Read, Write};
use std::net::{SocketAddr, ToSocketAddrs, TcpStream, TcpListener, Shutdown};
use std::mem;

#[cfg(feature = "openssl")]
pub use self::openssl::{Openssl, OpensslClient};

#[cfg(feature = "security-framework")]
pub use self::security_framework::ClientWrapper;

use std::time::Duration;

use typeable::Typeable;
Expand Down Expand Up @@ -413,18 +407,6 @@ impl<F> NetworkConnector for F where F: Fn(&str, u16, &str) -> io::Result<TcpStr
}
}

/// Deprecated
///
/// Use `SslClient` and `SslServer` instead.
pub trait Ssl {
/// The protected stream.
type Stream: NetworkStream + Send + Clone;
/// Wrap a client stream with SSL.
fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Self::Stream>;
/// Wrap a server stream with SSL.
fn wrap_server(&self, stream: HttpStream) -> ::Result<Self::Stream>;
}

/// An abstraction to allow any SSL implementation to be used with client-side HttpsStreams.
pub trait SslClient<T: NetworkStream + Send + Clone = HttpStream> {
/// The protected stream.
Expand All @@ -441,22 +423,6 @@ pub trait SslServer<T: NetworkStream + Send + Clone = HttpStream> {
fn wrap_server(&self, stream: T) -> ::Result<Self::Stream>;
}

impl<S: Ssl> SslClient<HttpStream> for S {
type Stream = <S as Ssl>::Stream;

fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Self::Stream> {
Ssl::wrap_client(self, stream, host)
}
}

impl<S: Ssl> SslServer<HttpStream> for S {
type Stream = <S as Ssl>::Stream;

fn wrap_server(&self, stream: HttpStream) -> ::Result<Self::Stream> {
Ssl::wrap_server(self, stream)
}
}

/// A stream over the HTTP protocol, possibly protected by SSL.
#[derive(Debug, Clone)]
pub enum HttpsStream<S: NetworkStream> {
Expand Down Expand Up @@ -603,272 +569,9 @@ impl<S: SslClient, C: NetworkConnector<Stream=HttpStream>> NetworkConnector for
}


#[cfg(all(not(feature = "openssl"), not(feature = "security-framework")))]
#[doc(hidden)]
pub type DefaultConnector = HttpConnector;

#[cfg(feature = "openssl")]
#[doc(hidden)]
pub type DefaultConnector = HttpsConnector<self::openssl::OpensslClient>;

#[cfg(all(feature = "security-framework", not(feature = "openssl")))]
pub type DefaultConnector = HttpsConnector<self::security_framework::ClientWrapper>;

#[cfg(feature = "openssl")]
mod openssl {
use std::io;
use std::net::{SocketAddr, Shutdown};
use std::path::Path;
use std::sync::Arc;
use std::time::Duration;

use openssl::ssl::{Ssl, SslContext, SslStream, SslMethod, SSL_VERIFY_NONE, SSL_VERIFY_PEER, SSL_OP_NO_SSLV2, SSL_OP_NO_SSLV3, SSL_OP_NO_COMPRESSION};
use openssl::ssl::error::StreamError as SslIoError;
use openssl::ssl::error::SslError;
use openssl::x509::X509FileType;
use super::{NetworkStream, HttpStream};

/// An implementation of `Ssl` for OpenSSL.
///
/// # Example
///
/// ```no_run
/// use hyper::Server;
/// use hyper::net::Openssl;
///
/// let ssl = Openssl::with_cert_and_key("/home/foo/cert", "/home/foo/key").unwrap();
/// Server::https("0.0.0.0:443", ssl).unwrap();
/// ```
///
/// For complete control, create a `SslContext` with the options you desire
/// and then create `Openssl { context: ctx }
#[derive(Debug, Clone)]
pub struct Openssl {
/// The `SslContext` from openssl crate.
pub context: Arc<SslContext>
}

/// A client-specific implementation of OpenSSL.
#[derive(Debug, Clone)]
pub struct OpensslClient(SslContext);

impl Default for OpensslClient {
fn default() -> OpensslClient {
let mut ctx = SslContext::new(SslMethod::Sslv23).unwrap();
ctx.set_default_verify_paths().unwrap();
ctx.set_options(SSL_OP_NO_SSLV2 | SSL_OP_NO_SSLV3 | SSL_OP_NO_COMPRESSION);
// cipher list taken from curl:
// https://github.com/curl/curl/blob/5bf5f6ebfcede78ef7c2b16daa41c4b7ba266087/lib/vtls/openssl.h#L120
ctx.set_cipher_list("ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4@STRENGTH").unwrap();
OpensslClient::new(ctx)
}
}

impl OpensslClient {
/// Creates a new OpensslClient with a custom SslContext
pub fn new(ctx: SslContext) -> OpensslClient {
OpensslClient(ctx)
}
}

impl<T: NetworkStream + Send + Clone> super::SslClient<T> for OpensslClient {
type Stream = SslStream<T>;

#[cfg(not(windows))]
fn wrap_client(&self, stream: T, host: &str) -> ::Result<Self::Stream> {
let mut ssl = try!(Ssl::new(&self.0));
try!(ssl.set_hostname(host));
let host = host.to_owned();
ssl.set_verify_callback(SSL_VERIFY_PEER, move |p, x| ::openssl_verify::verify_callback(&host, p, x));
SslStream::connect(ssl, stream).map_err(From::from)
}


#[cfg(windows)]
fn wrap_client(&self, stream: T, host: &str) -> ::Result<Self::Stream> {
let mut ssl = try!(Ssl::new(&self.0));
try!(ssl.set_hostname(host));
SslStream::connect(ssl, stream).map_err(From::from)
}
}

impl Default for Openssl {
fn default() -> Openssl {
Openssl {
context: Arc::new(SslContext::new(SslMethod::Sslv23).unwrap_or_else(|e| {
// if we cannot create a SslContext, that's because of a
// serious problem. just crash.
panic!("{}", e)
}))
}
}
}

impl Openssl {
/// Ease creating an `Openssl` with a certificate and key.
pub fn with_cert_and_key<C, K>(cert: C, key: K) -> Result<Openssl, SslError>
where C: AsRef<Path>, K: AsRef<Path> {
let mut ctx = try!(SslContext::new(SslMethod::Sslv23));
try!(ctx.set_cipher_list("DEFAULT"));
try!(ctx.set_certificate_chain_file(cert.as_ref(), X509FileType::PEM));
try!(ctx.set_private_key_file(key.as_ref(), X509FileType::PEM));
ctx.set_verify(SSL_VERIFY_NONE, None);
Ok(Openssl { context: Arc::new(ctx) })
}
}

impl super::Ssl for Openssl {
type Stream = SslStream<HttpStream>;

fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Self::Stream> {
let ssl = try!(Ssl::new(&self.context));
try!(ssl.set_hostname(host));
SslStream::connect(ssl, stream).map_err(From::from)
}

fn wrap_server(&self, stream: HttpStream) -> ::Result<Self::Stream> {
match SslStream::accept(&*self.context, stream) {
Ok(ssl_stream) => Ok(ssl_stream),
Err(SslIoError(e)) => {
Err(io::Error::new(io::ErrorKind::ConnectionAborted, e).into())
},
Err(e) => Err(e.into())
}
}
}

impl<S: NetworkStream> NetworkStream for SslStream<S> {
#[inline]
fn peer_addr(&mut self) -> io::Result<SocketAddr> {
self.get_mut().peer_addr()
}

#[inline]
fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.get_ref().set_read_timeout(dur)
}

#[inline]
fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.get_ref().set_write_timeout(dur)
}

fn close(&mut self, how: Shutdown) -> io::Result<()> {
self.get_mut().close(how)
}
}
}

#[cfg(feature = "security-framework")]
pub mod security_framework {
use std::io;
use std::fmt;
use std::sync::{Arc, Mutex};
use std::net::{Shutdown, SocketAddr};
use std::time::Duration;
use security_framework::secure_transport::{SslStream, ClientBuilder, ServerBuilder};

use error::Error;
use net::{SslClient, SslServer, HttpStream, NetworkStream};

#[derive(Default)]
pub struct ClientWrapper(ClientBuilder);

impl ClientWrapper {
pub fn new(builder: ClientBuilder) -> ClientWrapper {
ClientWrapper(builder)
}
}

impl SslClient for ClientWrapper {
type Stream = Stream;

fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Stream> {
match self.0.handshake(host, stream) {
Ok(s) => Ok(Stream(Arc::new(Mutex::new(s)))),
Err(e) => Err(Error::Ssl(e.into())),
}
}
}

#[derive(Clone)]
pub struct ServerWrapper(Arc<ServerBuilder>);

impl ServerWrapper {
pub fn new(builder: ServerBuilder) -> ServerWrapper {
ServerWrapper(Arc::new(builder))
}
}

impl SslServer for ServerWrapper {
type Stream = Stream;

fn wrap_server(&self, stream: HttpStream) -> ::Result<Stream> {
match self.0.handshake(stream) {
Ok(s) => Ok(Stream(Arc::new(Mutex::new(s)))),
Err(e) => Err(Error::Ssl(e.into())),
}
}
}

#[derive(Clone)]
pub struct Stream(Arc<Mutex<SslStream<HttpStream>>>);

impl io::Read for Stream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.lock().unwrap_or_else(|e| e.into_inner()).read(buf)
}

fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
self.0.lock().unwrap_or_else(|e| e.into_inner()).read_to_end(buf)
}

fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
self.0.lock().unwrap_or_else(|e| e.into_inner()).read_to_string(buf)
}

fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
self.0.lock().unwrap_or_else(|e| e.into_inner()).read_exact(buf)
}
}

impl io::Write for Stream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.lock().unwrap_or_else(|e| e.into_inner()).write(buf)
}

fn flush(&mut self) -> io::Result<()> {
self.0.lock().unwrap_or_else(|e| e.into_inner()).flush()
}

fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.0.lock().unwrap_or_else(|e| e.into_inner()).write_all(buf)
}

fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> {
self.0.lock().unwrap_or_else(|e| e.into_inner()).write_fmt(fmt)
}
}

impl NetworkStream for Stream {
fn peer_addr(&mut self) -> io::Result<SocketAddr> {
self.0.lock().unwrap_or_else(|e| e.into_inner()).get_mut().peer_addr()
}

fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.0.lock().unwrap_or_else(|e| e.into_inner()).get_mut().set_read_timeout(dur)
}

fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.0.lock().unwrap_or_else(|e| e.into_inner()).get_mut().set_write_timeout(dur)
}

fn close(&mut self, how: Shutdown) -> io::Result<()> {
self.0.lock().unwrap_or_else(|e| e.into_inner()).get_mut().close(how)
}
}
}

#[cfg(test)]
mod tests {
use mock::MockStream;
Expand Down

0 comments on commit 2f48612

Please sign in to comment.