Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,18 @@ 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"
rand = "0.3.12"
byteorder = "0.5.1"
net2 = "0.2.17"
sha1 = "0.2.0"

[features]
default = ["ssl"]
nightly = ["hyper/nightly"]
ssl = ["openssl", "hyper/ssl"]
4 changes: 2 additions & 2 deletions examples/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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();

Expand Down
4 changes: 2 additions & 2 deletions examples/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
22 changes: 20 additions & 2 deletions src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -68,8 +70,13 @@ impl Client<DataFrame, Sender<WebSocketStream>, Receiver<WebSocketStream>> {
/// A connection is established, however the request is not sent to
/// the server until a call to ```send()```.
pub fn connect<T: ToWebSocketUrlComponents>(components: T) -> WebSocketResult<Request<WebSocketStream, WebSocketStream>> {
#[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.
///
Expand All @@ -80,18 +87,29 @@ impl Client<DataFrame, Sender<WebSocketStream>, Receiver<WebSocketStream>> {
/// the server until a call to ```send()```.
pub fn connect_ssl_context<T: ToWebSocketUrlComponents>(components: T, context: &SslContext) -> WebSocketResult<Request<WebSocketStream, WebSocketStream>> {
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)
}
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)
}
Expand Down
11 changes: 4 additions & 7 deletions src/header/accept.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ 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;
extern crate sha1;

#[macro_use]
extern crate bitflags;
Expand All @@ -68,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;
}}
12 changes: 12 additions & 0 deletions src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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),
}
Expand All @@ -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",
}
Expand All @@ -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,
Expand Down Expand Up @@ -98,6 +109,7 @@ impl From<ParseError> for WebSocketError {
}
}

#[cfg(feature="ssl")]
impl From<SslError> for WebSocketError {
fn from(err: SslError) -> WebSocketError {
WebSocketError::SslError(err)
Expand Down
20 changes: 15 additions & 5 deletions src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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,
Expand Down Expand Up @@ -113,6 +120,7 @@ impl<'a> Server<'a> {
pub fn accept(&mut self) -> io::Result<Connection<WebSocketStream, WebSocketStream>> {
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,
Expand All @@ -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())))
Expand Down
12 changes: 12 additions & 0 deletions src/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -12,13 +13,15 @@ pub enum WebSocketStream {
/// A TCP stream.
Tcp(TcpStream),
/// An SSL-backed TCP Stream
#[cfg(feature="ssl")]
Ssl(SslStream<TcpStream>)
}

impl Read for WebSocketStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match *self {
WebSocketStream::Tcp(ref mut inner) => inner.read(buf),
#[cfg(feature="ssl")]
WebSocketStream::Ssl(ref mut inner) => inner.read(buf),
}
}
Expand All @@ -28,13 +31,15 @@ impl Write for WebSocketStream {
fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
match *self {
WebSocketStream::Tcp(ref mut inner) => inner.write(msg),
#[cfg(feature="ssl")]
WebSocketStream::Ssl(ref mut inner) => inner.write(msg),
}
}

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(),
}
}
Expand All @@ -45,41 +50,47 @@ impl WebSocketStream {
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
match *self {
WebSocketStream::Tcp(ref inner) => inner.peer_addr(),
#[cfg(feature="ssl")]
WebSocketStream::Ssl(ref inner) => inner.get_ref().peer_addr(),
}
}
/// See `TcpStream.local_addr()`.
pub fn local_addr(&self) -> io::Result<SocketAddr> {
match *self {
WebSocketStream::Tcp(ref inner) => inner.local_addr(),
#[cfg(feature="ssl")]
WebSocketStream::Ssl(ref inner) => inner.get_ref().local_addr(),
}
}
/// See `TcpStream.set_nodelay()`.
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),
}
}
/// See `TcpStream.set_keepalive()`.
pub fn set_keepalive(&mut self, delay_in_ms: Option<u32>) -> 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),
}
}
/// See `TcpStream.shutdown()`.
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),
}
}
/// See `TcpStream.try_clone()`.
pub fn try_clone(&self) -> io::Result<WebSocketStream> {
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())),
})
}
Expand All @@ -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),
}
}
Expand Down