From 5b098dc22bfdb7a8d6ace6091c017a85c586f062 Mon Sep 17 00:00:00 2001 From: Vitaly _Vi Shukela Date: Thu, 24 Nov 2016 02:32:16 +0300 Subject: [PATCH 1/2] Use sha1 from separate crate instead of from openssl --- Cargo.toml | 1 + src/header/accept.rs | 11 ++++------- src/lib.rs | 1 + 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9be299b9c6..6ea41b5827 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ bitflags = "0.7" rand = "0.3.12" byteorder = "0.5.1" net2 = "0.2.17" +sha1 = "0.2.0" [features] nightly = ["hyper/nightly"] diff --git a/src/header/accept.rs b/src/header/accept.rs index 8bc0ddab15..6b556c3342 100644 --- a/src/header/accept.rs +++ b/src/header/accept.rs @@ -5,7 +5,7 @@ use std::fmt::{self, Debug}; use std::str::FromStr; use serialize::base64::{ToBase64, FromBase64, STANDARD}; use header::WebSocketKey; -use openssl::crypto::hash::{self, hash}; +use sha1::Sha1; use result::{WebSocketResult, WebSocketError}; static MAGIC_GUID: &'static str = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; @@ -54,12 +54,9 @@ impl WebSocketAccept { let mut concat_key = String::with_capacity(serialized.len() + 36); concat_key.push_str(&serialized[..]); concat_key.push_str(MAGIC_GUID); - let output = hash(hash::Type::SHA1, concat_key.as_bytes()); - let mut iter = output.into_iter(); - let mut bytes = [0u8; 20]; - for i in bytes.iter_mut() { - *i = iter.next().unwrap(); - } + let mut sha1 = Sha1::new(); + sha1.update(concat_key.as_bytes()); + let bytes = sha1.digest().bytes(); WebSocketAccept(bytes) } /// Return the Base64 encoding of this WebSocketAccept diff --git a/src/lib.rs b/src/lib.rs index e90c72ee95..f47ed0a2bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,6 +43,7 @@ extern crate rustc_serialize as serialize; extern crate openssl; extern crate rand; extern crate byteorder; +extern crate sha1; #[macro_use] extern crate bitflags; From b0888901dc4803bf4220c1c24c4ccb21f4e59611 Mon Sep 17 00:00:00 2001 From: Vitaly _Vi Shukela Date: Thu, 24 Nov 2016 03:19:55 +0300 Subject: [PATCH 2/2] Make openssl optional dependency. Hacks: * Dummy SslContext * Trickery in secure server doctest * Sha1 used from other crate instead of openssl (even openssl enabled) --- Cargo.toml | 6 ++++-- examples/client.rs | 4 ++-- examples/server.rs | 4 ++-- src/client/mod.rs | 22 ++++++++++++++++++++-- src/lib.rs | 6 ++++++ src/result.rs | 12 ++++++++++++ src/server/mod.rs | 20 +++++++++++++++----- src/stream.rs | 12 ++++++++++++ 8 files changed, 73 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6ea41b5827..8372001ace 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,9 +17,9 @@ keywords = ["websocket", "websockets", "rfc6455"] license = "MIT" [dependencies] -hyper = ">=0.7, <0.10" +hyper = { version = ">=0.7, <0.10", default-features=false } unicase = "1.0.1" -openssl = "0.7.6" +openssl = { version = "0.7.6", optional=true } url = "1.0" rustc-serialize = "0.3.16" bitflags = "0.7" @@ -29,4 +29,6 @@ net2 = "0.2.17" sha1 = "0.2.0" [features] +default = ["ssl"] nightly = ["hyper/nightly"] +ssl = ["openssl", "hyper/ssl"] diff --git a/examples/client.rs b/examples/client.rs index 95e63552d3..8643fae5c0 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -2,7 +2,7 @@ extern crate websocket; fn main() { use std::thread; - use std::sync::mpsc::channel; + use std::sync::mpsc::sync_channel; use std::io::stdin; use websocket::{Message, Sender, Receiver}; @@ -26,7 +26,7 @@ fn main() { let (mut sender, mut receiver) = response.begin().split(); - let (tx, rx) = channel(); + let (tx, rx) = sync_channel(10); let tx_1 = tx.clone(); diff --git a/examples/server.rs b/examples/server.rs index 1bfef5c090..28b444c58c 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -34,8 +34,8 @@ fn main() { println!("Connection from {}", ip); - let message: Message = Message::text("Hello".to_string()); - client.send_message(&message).unwrap(); + //let message: Message = Message::text("Hello".to_string()); + //client.send_message(&message).unwrap(); let (mut sender, mut receiver) = client.split(); diff --git a/src/client/mod.rs b/src/client/mod.rs index b62302d0bf..a200a1b399 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -12,7 +12,9 @@ use stream::WebSocketStream; use dataframe::DataFrame; use ws::dataframe::DataFrame as DataFrameable; -use openssl::ssl::{SslContext, SslMethod, SslStream}; +use openssl::ssl::SslContext; +#[cfg(feature="ssl")] +use openssl::ssl::{SslMethod, SslStream}; pub use self::request::Request; pub use self::response::Response; @@ -68,8 +70,13 @@ impl Client, Receiver> { /// A connection is established, however the request is not sent to /// the server until a call to ```send()```. pub fn connect(components: T) -> WebSocketResult> { + #[cfg(feature="ssl")] let context = try!(SslContext::new(SslMethod::Tlsv1)); - Client::connect_ssl_context(components, &context) + #[cfg(feature="ssl")] + {Client::connect_ssl_context(components, &context)} + #[cfg(not(feature="ssl"))] + {Client::connect_ssl_context(components, &SslContext)} + } /// Connects to the specified wss:// URL using the given SSL context. /// @@ -80,11 +87,17 @@ impl Client, Receiver> { /// the server until a call to ```send()```. pub fn connect_ssl_context(components: T, context: &SslContext) -> WebSocketResult> { let (host, resource_name, secure) = try!(components.to_components()); + + #[cfg(not(feature="ssl"))] + {if secure { + return Err(::result::WebSocketError::SslFeatureNotEnabled); + }} let connection = try!(TcpStream::connect( (&host.hostname[..], host.port.unwrap_or(if secure { 443 } else { 80 })) )); + #[cfg(feature="ssl")] let stream = if secure { let sslstream = try!(SslStream::connect(context, connection)); WebSocketStream::Ssl(sslstream) @@ -92,6 +105,11 @@ impl Client, Receiver> { else { WebSocketStream::Tcp(connection) }; + + #[cfg(not(feature="ssl"))] + let stream = WebSocketStream::Tcp(connection); + #[cfg(not(feature="ssl"))] + let _ = context; Request::new((host, resource_name, secure), try!(stream.try_clone()), stream) } diff --git a/src/lib.rs b/src/lib.rs index f47ed0a2bf..d685100463 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,7 @@ extern crate hyper; extern crate unicase; extern crate url; extern crate rustc_serialize as serialize; +#[cfg(feature="ssl")] extern crate openssl; extern crate rand; extern crate byteorder; @@ -69,3 +70,8 @@ pub mod stream; pub mod header; pub mod receiver; pub mod sender; + +#[cfg(not(feature="ssl"))] +mod openssl { pub mod ssl { + pub struct SslContext; +}} diff --git a/src/result.rs b/src/result.rs index f6b9b25fa8..f664d31ac7 100644 --- a/src/result.rs +++ b/src/result.rs @@ -5,6 +5,7 @@ use std::str::Utf8Error; use std::error::Error; use std::convert::From; use std::fmt; +#[cfg(feature="ssl")] use openssl::ssl::error::SslError; use hyper::Error as HttpError; use url::ParseError; @@ -34,7 +35,11 @@ pub enum WebSocketError { /// A WebSocket URL error WebSocketUrlError(WSUrlErrorKind), /// An SSL error + #[cfg(feature="ssl")] SslError(SslError), + /// An error when user tries wss:// when SSL feature is not enabled + #[cfg(not(feature="ssl"))] + SslFeatureNotEnabled, /// A UTF-8 error Utf8Error(Utf8Error), } @@ -58,7 +63,10 @@ impl Error for WebSocketError { WebSocketError::IoError(_) => "I/O failure", WebSocketError::HttpError(_) => "HTTP failure", WebSocketError::UrlError(_) => "URL failure", + #[cfg(feature="ssl")] WebSocketError::SslError(_) => "SSL failure", + #[cfg(not(feature="ssl"))] + WebSocketError::SslFeatureNotEnabled => "SSL feature not enabled", WebSocketError::Utf8Error(_) => "UTF-8 failure", WebSocketError::WebSocketUrlError(_) => "WebSocket URL failure", } @@ -69,7 +77,10 @@ impl Error for WebSocketError { WebSocketError::IoError(ref error) => Some(error), WebSocketError::HttpError(ref error) => Some(error), WebSocketError::UrlError(ref error) => Some(error), + #[cfg(feature="ssl")] WebSocketError::SslError(ref error) => Some(error), + #[cfg(not(feature="ssl"))] + WebSocketError::SslFeatureNotEnabled => None, WebSocketError::Utf8Error(ref error) => Some(error), WebSocketError::WebSocketUrlError(ref error) => Some(error), _ => None, @@ -98,6 +109,7 @@ impl From for WebSocketError { } } +#[cfg(feature="ssl")] impl From for WebSocketError { fn from(err: SslError) -> WebSocketError { WebSocketError::SslError(err) diff --git a/src/server/mod.rs b/src/server/mod.rs index 8236b1c089..03c8b1af5a 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -9,7 +9,8 @@ pub use self::response::Response; use stream::WebSocketStream; use openssl::ssl::SslContext; -use openssl::ssl::SslStream; +#[cfg(feature="ssl")] +use openssl::ssl::{SslStream}; pub mod request; pub mod response; @@ -46,14 +47,17 @@ pub mod response; /// ///#Secure Servers /// ```no_run +///# #[cfg(feature="ssl")] pub mod ssltest { ///extern crate websocket; ///extern crate openssl; -///# fn main() { +///# pub fn main() { +///# use self::websocket; +///# use self::openssl; ///use std::thread; ///use std::path::Path; -///use websocket::{Server, Message}; -///use openssl::ssl::{SslContext, SslMethod}; -///use openssl::x509::X509FileType; +///use self::websocket::{Server, Message}; +///use self::openssl::ssl::{SslContext, SslMethod}; +///use self::openssl::x509::X509FileType; /// ///let mut context = SslContext::new(SslMethod::Tlsv1).unwrap(); ///let _ = context.set_certificate_file(&(Path::new("cert.pem")), X509FileType::PEM); @@ -74,6 +78,9 @@ pub mod response; /// }); ///} /// # } +/// # } +/// # #[cfg(feature="ssl")] fn main() { ssltest::main() } +/// # #[cfg(not(feature="ssl"))] fn main(){ println!("SSL server test ignored"); } /// ``` pub struct Server<'a> { inner: TcpListener, @@ -113,6 +120,7 @@ impl<'a> Server<'a> { pub fn accept(&mut self) -> io::Result> { let stream = try!(self.inner.accept()).0; let wsstream = match self.context { + #[cfg(feature="ssl")] Some(context) => { let sslstream = match SslStream::accept(context, stream) { Ok(s) => s, @@ -122,6 +130,8 @@ impl<'a> Server<'a> { }; WebSocketStream::Ssl(sslstream) } + #[cfg(not(feature="ssl"))] + Some(_) => panic!("Assertion failed. SSL feature is not enabled"), None => { WebSocketStream::Tcp(stream) } }; Ok(Connection(try!(wsstream.try_clone()), try!(wsstream.try_clone()))) diff --git a/src/stream.rs b/src/stream.rs index 623b280bbd..fb6b193d5b 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -3,6 +3,7 @@ extern crate net2; use std::io::{self, Read, Write}; use self::net2::TcpStreamExt; +#[cfg(feature="ssl")] use openssl::ssl::SslStream; pub use std::net::{SocketAddr, Shutdown, TcpStream}; @@ -12,6 +13,7 @@ pub enum WebSocketStream { /// A TCP stream. Tcp(TcpStream), /// An SSL-backed TCP Stream + #[cfg(feature="ssl")] Ssl(SslStream) } @@ -19,6 +21,7 @@ impl Read for WebSocketStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { match *self { WebSocketStream::Tcp(ref mut inner) => inner.read(buf), + #[cfg(feature="ssl")] WebSocketStream::Ssl(ref mut inner) => inner.read(buf), } } @@ -28,6 +31,7 @@ impl Write for WebSocketStream { fn write(&mut self, msg: &[u8]) -> io::Result { match *self { WebSocketStream::Tcp(ref mut inner) => inner.write(msg), + #[cfg(feature="ssl")] WebSocketStream::Ssl(ref mut inner) => inner.write(msg), } } @@ -35,6 +39,7 @@ impl Write for WebSocketStream { fn flush(&mut self) -> io::Result<()> { match *self { WebSocketStream::Tcp(ref mut inner) => inner.flush(), + #[cfg(feature="ssl")] WebSocketStream::Ssl(ref mut inner) => inner.flush(), } } @@ -45,6 +50,7 @@ impl WebSocketStream { pub fn peer_addr(&self) -> io::Result { match *self { WebSocketStream::Tcp(ref inner) => inner.peer_addr(), + #[cfg(feature="ssl")] WebSocketStream::Ssl(ref inner) => inner.get_ref().peer_addr(), } } @@ -52,6 +58,7 @@ impl WebSocketStream { pub fn local_addr(&self) -> io::Result { match *self { WebSocketStream::Tcp(ref inner) => inner.local_addr(), + #[cfg(feature="ssl")] WebSocketStream::Ssl(ref inner) => inner.get_ref().local_addr(), } } @@ -59,6 +66,7 @@ impl WebSocketStream { pub fn set_nodelay(&mut self, nodelay: bool) -> io::Result<()> { match *self { WebSocketStream::Tcp(ref mut inner) => TcpStreamExt::set_nodelay(inner, nodelay), + #[cfg(feature="ssl")] WebSocketStream::Ssl(ref mut inner) => TcpStreamExt::set_nodelay(inner.get_mut(), nodelay), } } @@ -66,6 +74,7 @@ impl WebSocketStream { pub fn set_keepalive(&mut self, delay_in_ms: Option) -> io::Result<()> { match *self { WebSocketStream::Tcp(ref mut inner) => TcpStreamExt::set_keepalive_ms(inner, delay_in_ms), + #[cfg(feature="ssl")] WebSocketStream::Ssl(ref mut inner) => TcpStreamExt::set_keepalive_ms(inner.get_mut(), delay_in_ms), } } @@ -73,6 +82,7 @@ impl WebSocketStream { pub fn shutdown(&mut self, shutdown: Shutdown) -> io::Result<()> { match *self { WebSocketStream::Tcp(ref mut inner) => inner.shutdown(shutdown), + #[cfg(feature="ssl")] WebSocketStream::Ssl(ref mut inner) => inner.get_mut().shutdown(shutdown), } } @@ -80,6 +90,7 @@ impl WebSocketStream { pub fn try_clone(&self) -> io::Result { Ok(match *self { WebSocketStream::Tcp(ref inner) => WebSocketStream::Tcp(try!(inner.try_clone())), + #[cfg(feature="ssl")] WebSocketStream::Ssl(ref inner) => WebSocketStream::Ssl(try!(inner.try_clone())), }) } @@ -88,6 +99,7 @@ impl WebSocketStream { pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { match *self { WebSocketStream::Tcp(ref inner) => inner.set_nonblocking(nonblocking), + #[cfg(feature="ssl")] WebSocketStream::Ssl(ref inner) => inner.get_ref().set_nonblocking(nonblocking), } }