Repository: websockets-rs/rust-websocket (language: rust)

Issue:
Convert messages into semantic types on receive.
Currently you need to convert a text message into a string when you receive it (you can only receive messages in an `Cow<'a, [u8]>` right now). This makes some autobahn tests fail (i.e. if it is an incorrect UTF-8 message the connection should close).

Also, it's nicer for users if messages of a certain type are parsed as that type.
But is closing the connection when an invalid frame arrives too strong?


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index bfe5de37db..2c4fdf652d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,9 @@
 /target
 /Cargo.lock
 
+# emacs
+*.#*.rs
+
 # Windows image file caches
 Thumbs.db
 ehthumbs.db
diff --git a/.travis.yml b/.travis.yml
index 74c38f865d..fb83404629 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,7 +7,7 @@ before_script:
 
 script:
     - cargo fmt -- --write-mode=diff
-    - cargo build --features nightly
+    - ./scripts/build-all.sh
     - cargo test --features nightly
     - cargo bench --features nightly
 
diff --git a/Cargo.toml b/Cargo.toml
index 3199a39d91..cd555323a3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,19 +18,24 @@ license = "MIT"
 
 [dependencies]
 hyper = "^0.10.6"
-unicase = "^1.0"
-url = "^1.0"
-bitflags = "^0.8"
-rand = "^0.3"
-byteorder = "^1.0"
-sha1 = "^0.2"
-openssl = { version = "^0.9.10", optional = true }
-base64 = "^0.5"
-
-[dev-dependencies]
-serde_json = "^1.0"
+unicase = "1.0"
+url = "1.0"
+bitflags = "0.8"
+rand = "0.3"
+byteorder = "1.0"
+sha1 = "0.2"
+base64 = "0.5"
+futures = { version = "0.1", optional = true }
+tokio-core = { version = "0.1", optional = true }
+tokio-io = { version = "^0.1.2", optional = true }
+tokio-tls = { version = "0.1", optional = true }
+bytes = { version = "0.4", optional = true }
+native-tls = { version = "^0.1.2", optional = true }
 
 [features]
-default = ["ssl"]
-ssl = ["openssl"]
+default = ["sync", "sync-ssl", "async", "async-ssl"]
+sync = []
+sync-ssl = ["native-tls", "sync"]
+async = ["tokio-core", "tokio-io", "bytes", "futures"]
+async-ssl = ["native-tls", "tokio-tls", "async"]
 nightly = ["hyper/nightly"]
diff --git a/autobahn/client-results.json b/autobahn/client-results.json
index 987591f7be..0ac6c056f8 100644
--- a/autobahn/client-results.json
+++ b/autobahn/client-results.json
@@ -1723,7 +1723,7 @@
          "reportfile": "rust_websocket_case_3_2.json"
       },
       "3.3": {
-         "behavior": "NON-STRICT",
+         "behavior": "OK",
          "behaviorClose": "OK",
          "duration": 2,
          "remoteCloseCode": null,
@@ -2773,28 +2773,28 @@
          "reportfile": "rust_websocket_case_6_3_2.json"
       },
       "6.4.1": {
-         "behavior": "NON-STRICT",
+         "behavior": "OK",
          "behaviorClose": "OK",
          "duration": 2002,
          "remoteCloseCode": null,
          "reportfile": "rust_websocket_case_6_4_1.json"
       },
       "6.4.2": {
-         "behavior": "NON-STRICT",
+         "behavior": "OK",
          "behaviorClose": "OK",
          "duration": 2002,
          "remoteCloseCode": null,
          "reportfile": "rust_websocket_case_6_4_2.json"
       },
       "6.4.3": {
-         "behavior": "NON-STRICT",
+         "behavior": "OK",
          "behaviorClose": "OK",
          "duration": 2003,
          "remoteCloseCode": null,
          "reportfile": "rust_websocket_case_6_4_3.json"
       },
       "6.4.4": {
-         "behavior": "NON-STRICT",
+         "behavior": "OK",
          "behaviorClose": "OK",
          "duration": 2002,
          "remoteCloseCode": null,
@@ -3634,4 +3634,4 @@
          "reportfile": "rust_websocket_case_9_8_6.json"
       }
    }
-}
\ No newline at end of file
+}
diff --git a/autobahn/server-results.json b/autobahn/server-results.json
index 2ec851ec70..4b1ea8df0a 100644
--- a/autobahn/server-results.json
+++ b/autobahn/server-results.json
@@ -2773,28 +2773,28 @@
          "reportfile": "rust_websocket_case_6_3_2.json"
       },
       "6.4.1": {
-         "behavior": "NON-STRICT",
+         "behavior": "OK",
          "behaviorClose": "OK",
          "duration": 2002,
          "remoteCloseCode": null,
          "reportfile": "rust_websocket_case_6_4_1.json"
       },
       "6.4.2": {
-         "behavior": "NON-STRICT",
+         "behavior": "OK",
          "behaviorClose": "OK",
          "duration": 2001,
          "remoteCloseCode": null,
          "reportfile": "rust_websocket_case_6_4_2.json"
       },
       "6.4.3": {
-         "behavior": "NON-STRICT",
+         "behavior": "OK",
          "behaviorClose": "OK",
          "duration": 2003,
          "remoteCloseCode": null,
          "reportfile": "rust_websocket_case_6_4_3.json"
       },
       "6.4.4": {
-         "behavior": "NON-STRICT",
+         "behavior": "OK",
          "behaviorClose": "OK",
          "duration": 2003,
          "remoteCloseCode": null,
@@ -3634,4 +3634,4 @@
          "reportfile": "rust_websocket_case_9_8_6.json"
       }
    }
-}
\ No newline at end of file
+}
diff --git a/examples/async-autobahn-client.rs b/examples/async-autobahn-client.rs
new file mode 100644
index 0000000000..703509c123
--- /dev/null
+++ b/examples/async-autobahn-client.rs
@@ -0,0 +1,109 @@
+extern crate websocket;
+extern crate tokio_core;
+extern crate futures;
+
+use websocket::{ClientBuilder, OwnedMessage};
+use websocket::result::WebSocketError;
+use tokio_core::reactor::Core;
+use futures::sink::Sink;
+use futures::stream::Stream;
+use futures::Future;
+use futures::future::{self, Loop};
+
+fn main() {
+	let addr = "ws://127.0.0.1:9001".to_string();
+	let agent = "rust-websocket";
+	let mut core = Core::new().unwrap();
+	let handle = core.handle();
+
+	println!("Using fuzzingserver {}", addr);
+	println!("Using agent {}", agent);
+
+	let case_count = get_case_count(addr.clone(), &mut core);
+	println!("We will be running {} test cases!", case_count);
+
+	println!("Running test suite...");
+	for case_id in 1..(case_count + 1) {
+		let url = addr.clone() + "/runCase?case=" + &case_id.to_string()[..] + "&agent=" + agent;
+
+		let test_case = ClientBuilder::new(&url)
+			.unwrap()
+			.async_connect_insecure(&handle)
+			.and_then(move |(duplex, _)| {
+				println!("Executing test case: {}/{}", case_id, case_count);
+				future::loop_fn(duplex, |stream| {
+					stream.into_future()
+					      .or_else(|(err, stream)| {
+						               println!("Could not receive message: {:?}", err);
+						               stream.send(OwnedMessage::Close(None)).map(|s| (None, s))
+						              })
+					      .and_then(|(msg, stream)| match msg {
+					                    Some(OwnedMessage::Text(txt)) => {
+						                    stream.send(OwnedMessage::Text(txt))
+					                              .map(|s| Loop::Continue(s))
+					                              .boxed()
+					                    }
+					                    Some(OwnedMessage::Binary(bin)) => {
+						                    stream.send(OwnedMessage::Binary(bin))
+					                              .map(|s| Loop::Continue(s))
+					                              .boxed()
+					                    }
+					                    Some(OwnedMessage::Ping(data)) => {
+						                    stream.send(OwnedMessage::Pong(data))
+					                              .map(|s| Loop::Continue(s))
+					                              .boxed()
+					                    }
+					                    Some(OwnedMessage::Close(_)) => {
+						                    stream.send(OwnedMessage::Close(None))
+					                              .map(|_| Loop::Break(()))
+					                              .boxed()
+					                    }
+					                    Some(OwnedMessage::Pong(_)) => {
+						                    future::ok(Loop::Continue(stream)).boxed()
+					                    }
+					                    None => future::ok(Loop::Break(())).boxed(),
+					                })
+				})
+			})
+			.map(move |_| {
+				     println!("Test case {} is finished!", case_id);
+				    })
+			.or_else(move |err| {
+				         println!("Test case {} ended with an error: {:?}", case_id, err);
+				         Ok(()) as Result<(), ()>
+				        });
+
+		core.run(test_case).ok();
+	}
+
+	update_reports(addr.clone(), agent, &mut core);
+	println!("Test suite finished!");
+}
+
+fn get_case_count(addr: String, core: &mut Core) -> usize {
+	let url = addr + "/getCaseCount";
+	let err = "Unsupported message in /getCaseCount";
+
+	let counter = ClientBuilder::new(&url)
+		.unwrap()
+		.async_connect_insecure(&core.handle())
+		.and_then(|(s, _)| s.into_future().map_err(|e| e.0))
+		.and_then(|(msg, _)| match msg {
+		              Some(OwnedMessage::Text(txt)) => Ok(txt.parse().unwrap()),
+		              _ => Err(WebSocketError::ProtocolError(err)),
+		          });
+	core.run(counter).unwrap()
+}
+
+fn update_reports(addr: String, agent: &str, core: &mut Core) {
+	println!("Updating reports...");
+	let url = addr + "/updateReports?agent=" + agent;
+
+	let updater = ClientBuilder::new(&url)
+		.unwrap()
+		.async_connect_insecure(&core.handle())
+		.and_then(|(sink, _)| sink.send(OwnedMessage::Close(None)));
+	core.run(updater).unwrap();
+
+	println!("Reports updated.");
+}
diff --git a/examples/async-autobahn-server.rs b/examples/async-autobahn-server.rs
new file mode 100644
index 0000000000..e22d58717a
--- /dev/null
+++ b/examples/async-autobahn-server.rs
@@ -0,0 +1,54 @@
+extern crate websocket;
+extern crate futures;
+extern crate tokio_core;
+
+use websocket::message::OwnedMessage;
+use websocket::server::InvalidConnection;
+use websocket::async::Server;
+
+use tokio_core::reactor::Core;
+use futures::{Future, Sink, Stream};
+
+fn main() {
+	let mut core = Core::new().unwrap();
+	let handle = core.handle();
+	// bind to the server
+	let server = Server::bind("127.0.0.1:9002", &handle).unwrap();
+
+	// time to build the server's future
+	// this will be a struct containing everything the server is going to do
+
+	// a stream of incoming connections
+	let f = server.incoming()
+        // we don't wanna save the stream if it drops
+        .map_err(|InvalidConnection { error, .. }| error)
+        .for_each(|(upgrade, addr)| {
+            // accept the request to be a ws connection
+            println!("Got a connection from: {}", addr);
+            let f = upgrade
+                .accept()
+                .and_then(|(s, _)| {
+                    // simple echo server impl
+                    let (sink, stream) = s.split();
+                    stream
+                    .take_while(|m| Ok(!m.is_close()))
+                    .filter_map(|m| {
+                        match m {
+                            OwnedMessage::Ping(p) => Some(OwnedMessage::Pong(p)),
+                            OwnedMessage::Pong(_) => None,
+                            _ => Some(m),
+                        }
+                    })
+                    .forward(sink)
+                    .and_then(|(_, sink)| {
+                        sink.send(OwnedMessage::Close(None))
+                    })
+                });
+
+	          handle.spawn(f.map_err(move |e| println!("{}: '{:?}'", addr, e))
+	                       .map(move |_| println!("{} closed.", addr)));
+            Ok(())
+        });
+
+	core.run(f).unwrap();
+}
diff --git a/examples/async-client.rs b/examples/async-client.rs
new file mode 100644
index 0000000000..999f5906f3
--- /dev/null
+++ b/examples/async-client.rs
@@ -0,0 +1,65 @@
+extern crate websocket;
+extern crate futures;
+extern crate tokio_core;
+
+use std::thread;
+use std::io::stdin;
+use tokio_core::reactor::Core;
+use futures::future::Future;
+use futures::sink::Sink;
+use futures::stream::Stream;
+use futures::sync::mpsc;
+use websocket::result::WebSocketError;
+use websocket::{ClientBuilder, OwnedMessage};
+
+const CONNECTION: &'static str = "ws://127.0.0.1:2794";
+
+fn main() {
+	println!("Connecting to {}", CONNECTION);
+	let mut core = Core::new().unwrap();
+
+	// standard in isn't supported in mio yet, so we use a thread
+	// see https://github.com/carllerche/mio/issues/321
+	let (usr_msg, stdin_ch) = mpsc::channel(0);
+	thread::spawn(move || {
+		let mut input = String::new();
+		let mut stdin_sink = usr_msg.wait();
+		loop {
+			input.clear();
+			stdin().read_line(&mut input).unwrap();
+			let trimmed = input.trim();
+
+			let (close, msg) = match trimmed {
+				"/close" => (true, OwnedMessage::Close(None)),
+				"/ping" => (false, OwnedMessage::Ping(b"PING".to_vec())),
+				_ => (false, OwnedMessage::Text(trimmed.to_string())),
+			};
+
+			stdin_sink.send(msg)
+			          .expect("Sending message across stdin channel.");
+
+			if close {
+				break;
+			}
+		}
+	});
+
+	let runner = ClientBuilder::new(CONNECTION)
+		.unwrap()
+		.add_protocol("rust-websocket")
+		.async_connect_insecure(&core.handle())
+		.and_then(|(duplex, _)| {
+			let (sink, stream) = duplex.split();
+			stream.filter_map(|message| {
+				                  println!("Received Message: {:?}", message);
+				                  match message {
+				                      OwnedMessage::Close(e) => Some(OwnedMessage::Close(e)),
+				                      OwnedMessage::Ping(d) => Some(OwnedMessage::Pong(d)),
+				                      _ => None,
+				                  }
+				                 })
+			      .select(stdin_ch.map_err(|_| WebSocketError::NoDataAvailable))
+			      .forward(sink)
+		});
+	core.run(runner).unwrap();
+}
diff --git a/examples/async-server.rs b/examples/async-server.rs
new file mode 100644
index 0000000000..34714bc93e
--- /dev/null
+++ b/examples/async-server.rs
@@ -0,0 +1,74 @@
+extern crate websocket;
+extern crate futures;
+extern crate tokio_core;
+
+use std::fmt::Debug;
+
+use websocket::message::{Message, OwnedMessage};
+use websocket::server::InvalidConnection;
+use websocket::async::Server;
+
+use tokio_core::reactor::{Handle, Core};
+use futures::{Future, Sink, Stream};
+
+fn main() {
+	let mut core = Core::new().unwrap();
+	let handle = core.handle();
+	// bind to the server
+	let server = Server::bind("127.0.0.1:2794", &handle).unwrap();
+
+	// time to build the server's future
+	// this will be a struct containing everything the server is going to do
+
+	// a stream of incoming connections
+	let f = server.incoming()
+        // we don't wanna save the stream if it drops
+        .map_err(|InvalidConnection { error, .. }| error)
+        .for_each(|(upgrade, addr)| {
+            println!("Got a connection from: {}", addr);
+            // check if it has the protocol we want
+            if !upgrade.protocols().iter().any(|s| s == "rust-websocket") {
+                // reject it if it doesn't
+                spawn_future(upgrade.reject(), "Upgrade Rejection", &handle);
+                return Ok(());
+            }
+
+            // accept the request to be a ws connection if it does
+            let f = upgrade
+                .use_protocol("rust-websocket")
+                .accept()
+                // send a greeting!
+                .and_then(|(s, _)| s.send(Message::text("Hello World!").into()))
+                // simple echo server impl
+                .and_then(|s| {
+                    let (sink, stream) = s.split();
+                    stream
+                    .take_while(|m| Ok(!m.is_close()))
+                    .filter_map(|m| {
+                        println!("Message from Client: {:?}", m);
+                        match m {
+                            OwnedMessage::Ping(p) => Some(OwnedMessage::Pong(p)),
+                            OwnedMessage::Pong(_) => None,
+                            _ => Some(m),
+                        }
+                    })
+                    .forward(sink)
+                    .and_then(|(_, sink)| {
+                        sink.send(OwnedMessage::Close(None))
+                    })
+                });
+
+            spawn_future(f, "Client Status", &handle);
+            Ok(())
+        });
+
+	core.run(f).unwrap();
+}
+
+fn spawn_future<F, I, E>(f: F, desc: &'static str, handle: &Handle)
+	where F: Future<Item = I, Error = E> + 'static,
+	      E: Debug
+{
+	handle.spawn(f.map_err(move |e| println!("{}: '{:?}'", desc, e))
+	              .map(move |_| println!("{}: Finished.", desc)));
+}
diff --git a/examples/autobahn-client.rs b/examples/autobahn-client.rs
index e56c49ae07..97fe567a8c 100644
--- a/examples/autobahn-client.rs
+++ b/examples/autobahn-client.rs
@@ -1,10 +1,8 @@
 extern crate websocket;
-extern crate serde_json;
 
-use std::str::from_utf8;
 use websocket::ClientBuilder;
+use websocket::OwnedMessage;
 use websocket::Message;
-use websocket::message::Type;
 
 fn main() {
 	let addr = "ws://127.0.0.1:9001".to_string();
@@ -33,7 +31,7 @@ fn main() {
 		println!("Executing test case: {}/{}", case_id, case_count);
 
 		for message in receiver.incoming_messages() {
-			let message: Message = match message {
+			let message = match message {
 				Ok(message) => message,
 				Err(e) => {
 					println!("Error: {:?}", e);
@@ -42,20 +40,19 @@ fn main() {
 				}
 			};
 
-			match message.opcode {
-				Type::Text => {
-					let response = Message::text(from_utf8(&*message.payload).unwrap());
-					sender.send_message(&response).unwrap();
+			match message {
+				OwnedMessage::Text(txt) => {
+					sender.send_message(&OwnedMessage::Text(txt)).unwrap();
 				}
-				Type::Binary => {
-					sender.send_message(&Message::binary(message.payload)).unwrap();
+				OwnedMessage::Binary(bin) => {
+					sender.send_message(&OwnedMessage::Binary(bin)).unwrap();
 				}
-				Type::Close => {
-					let _ = sender.send_message(&Message::close());
+				OwnedMessage::Close(_) => {
+					let _ = sender.send_message(&OwnedMessage::Close(None));
 					break;
 				}
-				Type::Ping => {
-					sender.send_message(&Message::pong(message.payload)).unwrap();
+				OwnedMessage::Ping(data) => {
+					sender.send_message(&OwnedMessage::Pong(data)).unwrap();
 				}
 				_ => (),
 			}
@@ -81,7 +78,7 @@ fn get_case_count(addr: String) -> usize {
 	let mut count = 0;
 
 	for message in receiver.incoming_messages() {
-		let message: Message = match message {
+		let message = match message {
 			Ok(message) => message,
 			Err(e) => {
 				println!("Error: {:?}", e);
@@ -90,17 +87,17 @@ fn get_case_count(addr: String) -> usize {
 				break;
 			}
 		};
-		match message.opcode {
-			Type::Text => {
-				count = serde_json::from_str(from_utf8(&*message.payload).unwrap()).unwrap();
+		match message {
+			OwnedMessage::Text(txt) => {
+				count = txt.parse().unwrap();
 				println!("Will run {} cases...", count);
 			}
-			Type::Close => {
+			OwnedMessage::Close(_) => {
 				let _ = sender.send_message(&Message::close());
 				break;
 			}
-			Type::Ping => {
-				sender.send_message(&Message::pong(message.payload)).unwrap();
+			OwnedMessage::Ping(data) => {
+				sender.send_message(&OwnedMessage::Pong(data)).unwrap();
 			}
 			_ => (),
 		}
@@ -125,7 +122,7 @@ fn update_reports(addr: String, agent: &str) {
 	println!("Updating reports...");
 
 	for message in receiver.incoming_messages() {
-		let message: Message = match message {
+		let message = match message {
 			Ok(message) => message,
 			Err(e) => {
 				println!("Error: {:?}", e);
@@ -133,15 +130,15 @@ fn update_reports(addr: String, agent: &str) {
 				return;
 			}
 		};
-		match message.opcode {
-			Type::Close => {
+		match message {
+			OwnedMessage::Close(_) => {
 				let _ = sender.send_message(&Message::close());
 				println!("Reports updated.");
 				println!("Test suite finished!");
 				return;
 			}
-			Type::Ping => {
-				sender.send_message(&Message::pong(message.payload)).unwrap();
+			OwnedMessage::Ping(data) => {
+				sender.send_message(&OwnedMessage::Pong(data)).unwrap();
 			}
 			_ => (),
 		}
diff --git a/examples/autobahn-server.rs b/examples/autobahn-server.rs
index 3d27f1e647..5b7f14229e 100644
--- a/examples/autobahn-server.rs
+++ b/examples/autobahn-server.rs
@@ -1,9 +1,9 @@
 extern crate websocket;
 
 use std::thread;
-use std::str::from_utf8;
-use websocket::{Server, Message};
-use websocket::message::Type;
+use websocket::{Message, OwnedMessage};
+use websocket::sync::Server;
+
 
 fn main() {
 	let server = Server::bind("127.0.0.1:9002").unwrap();
@@ -16,7 +16,7 @@ fn main() {
 			let (mut receiver, mut sender) = client.split().unwrap();
 
 			for message in receiver.incoming_messages() {
-				let message: Message = match message {
+				let message = match message {
 					Ok(message) => message,
 					Err(e) => {
 						println!("{:?}", e);
@@ -25,21 +25,19 @@ fn main() {
 					}
 				};
 
-				match message.opcode {
-					Type::Text => {
-						let response = Message::text(from_utf8(&*message.payload).unwrap());
-						sender.send_message(&response).unwrap()
+				match message {
+					OwnedMessage::Text(txt) => {
+						sender.send_message(&OwnedMessage::Text(txt)).unwrap()
 					}
-					Type::Binary => {
-						sender.send_message(&Message::binary(message.payload)).unwrap()
+					OwnedMessage::Binary(bin) => {
+						sender.send_message(&OwnedMessage::Binary(bin)).unwrap()
 					}
-					Type::Close => {
-						let _ = sender.send_message(&Message::close());
+					OwnedMessage::Close(_) => {
+						sender.send_message(&OwnedMessage::Close(None)).ok();
 						return;
 					}
-					Type::Ping => {
-						let message = Message::pong(message.payload);
-						sender.send_message(&message).unwrap();
+					OwnedMessage::Ping(data) => {
+						sender.send_message(&OwnedMessage::Pong(data)).unwrap();
 					}
 					_ => (),
 				}
diff --git a/examples/client.rs b/examples/client.rs
index bc4c1cb40a..c1c3d2028a 100644
--- a/examples/client.rs
+++ b/examples/client.rs
@@ -1,15 +1,15 @@
 extern crate websocket;
 
+use std::thread;
+use std::sync::mpsc::channel;
+use std::io::stdin;
+
+use websocket::{Message, OwnedMessage};
+use websocket::client::ClientBuilder;
+
 const CONNECTION: &'static str = "ws://127.0.0.1:2794";
 
 fn main() {
-	use std::thread;
-	use std::sync::mpsc::channel;
-	use std::io::stdin;
-
-	use websocket::Message;
-	use websocket::message::Type;
-	use websocket::client::ClientBuilder;
 
 	println!("Connecting to {}", CONNECTION);
 
@@ -30,17 +30,20 @@ fn main() {
 	let send_loop = thread::spawn(move || {
 		loop {
 			// Send loop
-			let message: Message = match rx.recv() {
+			let message = match rx.recv() {
 				Ok(m) => m,
 				Err(e) => {
 					println!("Send Loop: {:?}", e);
 					return;
 				}
 			};
-			if Type::Close == message.opcode {
-				let _ = sender.send_message(&message);
-				// If it's a close message, just send it and then return.
-				return;
+			match message {
+				OwnedMessage::Close(_) => {
+					let _ = sender.send_message(&message);
+					// If it's a close message, just send it and then return.
+					return;
+				}
+				_ => (),
 			}
 			// Send the message
 			match sender.send_message(&message) {
@@ -57,22 +60,22 @@ fn main() {
 	let receive_loop = thread::spawn(move || {
 		// Receive loop
 		for message in receiver.incoming_messages() {
-			let message: Message = match message {
+			let message = match message {
 				Ok(m) => m,
 				Err(e) => {
 					println!("Receive Loop: {:?}", e);
-					let _ = tx_1.send(Message::close());
+					let _ = tx_1.send(OwnedMessage::Close(None));
 					return;
 				}
 			};
-			match message.opcode {
-				Type::Close => {
+			match message {
+				OwnedMessage::Close(_) => {
 					// Got a close message, so send a close message and return
-					let _ = tx_1.send(Message::close());
+					let _ = tx_1.send(OwnedMessage::Close(None));
 					return;
 				}
-				Type::Ping => {
-					match tx_1.send(Message::pong(message.payload)) {
+				OwnedMessage::Ping(data) => {
+					match tx_1.send(OwnedMessage::Pong(data)) {
 						// Send a pong in response
 						Ok(()) => (),
 						Err(e) => {
@@ -97,13 +100,13 @@ fn main() {
 		let message = match trimmed {
 			"/close" => {
 				// Close the connection
-				let _ = tx.send(Message::close());
+				let _ = tx.send(OwnedMessage::Close(None));
 				break;
 			}
 			// Send a ping
-			"/ping" => Message::ping(b"PING".to_vec()),
+			"/ping" => OwnedMessage::Ping(b"PING".to_vec()),
 			// Otherwise, just send text
-			_ => Message::text(trimmed.to_string()),
+			_ => OwnedMessage::Text(trimmed.to_string()),
 		};
 
 		match tx.send(message) {
diff --git a/examples/hyper.rs b/examples/hyper.rs
index de1880a024..a8fa0a8307 100644
--- a/examples/hyper.rs
+++ b/examples/hyper.rs
@@ -3,8 +3,8 @@ extern crate hyper;
 
 use std::thread;
 use std::io::Write;
-use websocket::{Server, Message};
-use websocket::message::Type;
+use websocket::{Message, OwnedMessage};
+use websocket::sync::Server;
 use hyper::Server as HttpServer;
 use hyper::net::Fresh;
 use hyper::server::request::Request;
@@ -44,23 +44,23 @@ fn main() {
 
 			println!("Connection from {}", ip);
 
-			let message = Message::text("Hello".to_string());
+			let message = Message::text("Hello");
 			client.send_message(&message).unwrap();
 
 			let (mut receiver, mut sender) = client.split().unwrap();
 
 			for message in receiver.incoming_messages() {
-				let message: Message = message.unwrap();
+				let message = message.unwrap();
 
-				match message.opcode {
-					Type::Close => {
+				match message {
+					OwnedMessage::Close(_) => {
 						let message = Message::close();
 						sender.send_message(&message).unwrap();
 						println!("Client {} disconnected", ip);
 						return;
 					}
-					Type::Ping => {
-						let message = Message::pong(message.payload);
+					OwnedMessage::Ping(data) => {
+						let message = Message::pong(data);
 						sender.send_message(&message).unwrap();
 					}
 					_ => sender.send_message(&message).unwrap(),
diff --git a/examples/server.rs b/examples/server.rs
index 3dd9799979..324efdfa41 100644
--- a/examples/server.rs
+++ b/examples/server.rs
@@ -1,8 +1,8 @@
 extern crate websocket;
 
 use std::thread;
-use websocket::{Server, Message};
-use websocket::message::Type;
+use websocket::OwnedMessage;
+use websocket::sync::Server;
 
 fn main() {
 	let server = Server::bind("127.0.0.1:2794").unwrap();
@@ -21,23 +21,23 @@ fn main() {
 
 			println!("Connection from {}", ip);
 
-			let message: Message = Message::text("Hello".to_string());
+			let message = OwnedMessage::Text("Hello".to_string());
 			client.send_message(&message).unwrap();
 
 			let (mut receiver, mut sender) = client.split().unwrap();
 
 			for message in receiver.incoming_messages() {
-				let message: Message = message.unwrap();
+				let message = message.unwrap();
 
-				match message.opcode {
-					Type::Close => {
-						let message = Message::close();
+				match message {
+					OwnedMessage::Close(_) => {
+						let message = OwnedMessage::Close(None);
 						sender.send_message(&message).unwrap();
 						println!("Client {} disconnected", ip);
 						return;
 					}
-					Type::Ping => {
-						let message = Message::pong(message.payload);
+					OwnedMessage::Ping(ping) => {
+						let message = OwnedMessage::Pong(ping);
 						sender.send_message(&message).unwrap();
 					}
 					_ => sender.send_message(&message).unwrap(),
diff --git a/examples/ssl-client.rs b/examples/ssl-client.rs
new file mode 100644
index 0000000000..fa43551ea6
--- /dev/null
+++ b/examples/ssl-client.rs
@@ -0,0 +1,64 @@
+extern crate websocket;
+extern crate futures;
+extern crate tokio_core;
+
+use std::thread;
+use std::io::stdin;
+use tokio_core::reactor::Core;
+use futures::future::Future;
+use futures::sink::Sink;
+use futures::stream::Stream;
+use futures::sync::mpsc;
+use websocket::result::WebSocketError;
+use websocket::{ClientBuilder, OwnedMessage};
+
+const CONNECTION: &'static str = "wss://echo.websocket.org";
+
+fn main() {
+	println!("Connecting to {}", CONNECTION);
+	let mut core = Core::new().unwrap();
+
+	// standard in isn't supported in mio yet, so we use a thread
+	// see https://github.com/carllerche/mio/issues/321
+	let (usr_msg, stdin_ch) = mpsc::channel(0);
+	thread::spawn(move || {
+		let mut input = String::new();
+		let mut stdin_sink = usr_msg.wait();
+		loop {
+			input.clear();
+			stdin().read_line(&mut input).unwrap();
+			let trimmed = input.trim();
+
+			let (close, msg) = match trimmed {
+				"/close" => (true, OwnedMessage::Close(None)),
+				"/ping" => (false, OwnedMessage::Ping(b"PING".to_vec())),
+				_ => (false, OwnedMessage::Text(trimmed.to_string())),
+			};
+
+			stdin_sink.send(msg)
+			          .expect("Sending message across stdin channel.");
+
+			if close {
+				break;
+			}
+		}
+	});
+
+	let runner = ClientBuilder::new(CONNECTION)
+		.unwrap()
+		.async_connect_secure(None, &core.handle())
+		.and_then(|(duplex, _)| {
+			let (sink, stream) = duplex.split();
+			stream.filter_map(|message| {
+				                  println!("Received Message: {:?}", message);
+				                  match message {
+				                      OwnedMessage::Close(e) => Some(OwnedMessage::Close(e)),
+				                      OwnedMessage::Ping(d) => Some(OwnedMessage::Pong(d)),
+				                      _ => None,
+				                  }
+				                 })
+			      .select(stdin_ch.map_err(|_| WebSocketError::NoDataAvailable))
+			      .forward(sink)
+		});
+	core.run(runner).unwrap();
+}
diff --git a/scripts/autobahn-client.sh b/scripts/autobahn-client.sh
index 0c05ac9e08..a9ec1d011f 100755
--- a/scripts/autobahn-client.sh
+++ b/scripts/autobahn-client.sh
@@ -15,18 +15,26 @@ wstest -m fuzzingserver -s 'autobahn/fuzzingserver.json' & \
     FUZZINGSERVER_PID=$!
 sleep 10
 
+function test_diff() {
+    SAVED_RESULTS=$(sed 's/NON-STRICT/OK/g' autobahn/client-results.json)
+    DIFF=$(diff \
+        <(jq -S 'del(."rust-websocket" | .. | .duration?)' "$SAVED_RESULTS") \
+        <(jq -S 'del(."rust-websocket" | .. | .duration?)' 'autobahn/client/index.json') )
+
+    if [[ $DIFF ]]; then
+        echo 'Difference in results, either this is a regression or' \
+             'one should update autobahn/client-results.json with the new results.' \
+             'The results are:'
+        echo $DIFF
+        exit 64
+    fi
+}
+
 cargo build --example autobahn-client
 cargo run --example autobahn-client
+test_diff
 
-DIFF=$(diff \
-    <(jq -S 'del(."rust-websocket" | .. | .duration?)' 'autobahn/client-results.json') \
-    <(jq -S 'del(."rust-websocket" | .. | .duration?)' 'autobahn/client/index.json') )
-
-if [[ $DIFF ]]; then
-    echo Difference in results, either this is a regression or \
-         one should update autobahn/client-results.json with the new results. \
-         The results are:
-    echo $DIFF
-    exit 64
-fi
+cargo build --example async-autobahn-client
+cargo run --example async-autobahn-client
+test_diff
 
diff --git a/scripts/autobahn-server.sh b/scripts/autobahn-server.sh
index af05e8fbf7..8a85bc98fa 100755
--- a/scripts/autobahn-server.sh
+++ b/scripts/autobahn-server.sh
@@ -12,23 +12,36 @@ function cleanup() {
 }
 trap cleanup TERM EXIT
 
+function test_diff() {
+    SAVED_RESULTS=$(sed 's/NON-STRICT/OK/g' autobahn/server-results.json)
+    DIFF=$(diff \
+        <(jq -S 'del(."rust-websocket" | .. | .duration?)' "$SAVED_RESULTS") \
+        <(jq -S 'del(."rust-websocket" | .. | .duration?)' 'autobahn/server/index.json') )
+
+    if [[ $DIFF ]]; then
+        echo Difference in results, either this is a regression or \
+             one should update autobahn/server-results.json with the new results. \
+             The results are:
+        echo $DIFF
+        exit 64
+    fi
+}
+
+# Test synchronous version
 cargo build --example autobahn-server
-./target/debug/examples/autobahn-server & \
-    WSSERVER_PID=$!
+./target/debug/examples/autobahn-server & WSSERVER_PID=$!
 echo "Server PID: ${WSSERVER_PID}"
 sleep 10
-
 wstest -m fuzzingclient -s 'autobahn/fuzzingclient.json'
+kill -9 ${WSSERVER_PID}
+test_diff
 
-DIFF=$(diff \
-    <(jq -S 'del(."rust-websocket" | .. | .duration?)' 'autobahn/server-results.json') \
-    <(jq -S 'del(."rust-websocket" | .. | .duration?)' 'autobahn/server/index.json') )
-
-if [[ $DIFF ]]; then
-    echo Difference in results, either this is a regression or \
-         one should update autobahn/server-results.json with the new results. \
-         The results are:
-    echo $DIFF
-    exit 64
-fi
+# Test asynchronous version
+cargo build --example async-autobahn-server
+./target/debug/examples/async-autobahn-server & WSSERVER_PID=$!
+echo "Server PID: ${WSSERVER_PID}"
+sleep 10
+wstest -m fuzzingclient -s 'autobahn/fuzzingclient.json'
+kill -9 ${WSSERVER_PID}
+test_diff
 
diff --git a/scripts/build-all.sh b/scripts/build-all.sh
new file mode 100755
index 0000000000..798601932c
--- /dev/null
+++ b/scripts/build-all.sh
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+set -euo pipefail
+SOURCE_DIR=$(readlink -f "${BASH_SOURCE[0]}")
+SOURCE_DIR=$(dirname "$SOURCE_DIR")
+cd "${SOURCE_DIR}/.."
+
+if [[ ${1:-} = "-i" ]]; then
+    INTERACTIVE=true
+fi
+
+ALL_FEATURES="
+async
+sync
+sync-ssl
+async-ssl
+async sync
+async sync-ssl
+sync async-ssl
+sync-ssl async-ssl"
+
+while read FEATS; do
+    if [[ ${INTERACTIVE:-} ]]; then
+        cargo build --no-default-features --features "$FEATS" \
+              --color always 2>&1 | less -r
+    else
+        set -x
+        cargo build --no-default-features --features "$FEATS"
+        set +x
+    fi
+done < <(echo "$ALL_FEATURES")
+
+## all combs of features (lol)
+# async
+# sync
+# sync-ssl
+# async-ssl
+# async sync
+# async sync-ssl
+# async async-ssl
+# sync sync-ssl
+# sync async-ssl
+# sync-ssl async-ssl
+# async sync sync-ssl
+# async sync async-ssl
+# async sync-ssl async-ssl
+# sync sync-ssl async-ssl
+# async sync sync-ssl async-ssl
diff --git a/src/client/async.rs b/src/client/async.rs
new file mode 100644
index 0000000000..c497a51fd4
--- /dev/null
+++ b/src/client/async.rs
@@ -0,0 +1,75 @@
+//! Contains the asynchronous websocket client.
+//!
+//! The async client is simply a `Stream + Sink` of `OwnedMessage` structs.
+//! This definition means that you don't have to learn any new APIs other than
+//! futures-rs.
+//! The client simply wraps around an `AsyncRead + AsyncWrite` stream and uses
+//! a `MessageCodec` to chop up the bytes into websocket messages.
+//! See the `codec` module for all the cool codecs this crate has.
+//!
+//! Since this is all asynchronous, you will not create a client from `ClientBuilder`
+//! but instead you will create a `ClientNew` struct, which is a Future that
+//! will eventually evaluate to a `Client`.
+//!
+//! # Example with Type Annotations
+//!
+//! ```rust,no_run
+//! # extern crate tokio_core;
+//! # extern crate futures;
+//! # extern crate websocket;
+//! use websocket::ClientBuilder;
+//! use websocket::async::client::{Client, ClientNew};
+//! use websocket::async::TcpStream;
+//! use websocket::futures::{Future, Stream, Sink};
+//! use websocket::Message;
+//! use tokio_core::reactor::Core;
+//! # fn main() {
+//!
+//! let mut core = Core::new().unwrap();
+//!
+//! // create a Future of a client
+//! let client_future: ClientNew<TcpStream> =
+//!     ClientBuilder::new("ws://echo.websocket.org").unwrap()
+//!         .async_connect_insecure(&core.handle());
+//!
+//! // send a message
+//! let send_future = client_future
+//!     .and_then(|(client, headers)| {
+//!         // just to make it clear what type this is
+//!         let client: Client<TcpStream> = client;
+//!         client.send(Message::text("hallo").into())
+//!     });
+//!
+//! core.run(send_future).unwrap();
+//! # }
+//! ```
+
+pub use tokio_core::reactor::Handle;
+pub use tokio_io::codec::Framed;
+pub use tokio_core::net::TcpStream;
+pub use futures::Future;
+use hyper::header::Headers;
+
+use result::WebSocketError;
+use codec::ws::MessageCodec;
+use message::OwnedMessage;
+
+#[cfg(feature="async-ssl")]
+pub use tokio_tls::TlsStream;
+
+/// An asynchronous websocket client.
+///
+/// This is simply a `Stream` and `Sink` of `OwnedMessage`s.
+/// See the docs for `Stream` and `Sink` to learn more about how to use
+/// these futures.
+pub type Client<S> = Framed<S, MessageCodec<OwnedMessage>>;
+
+/// A future which will evaluate to a `Client` and a set of hyper `Headers`.
+///
+/// The `Client` can send and receive websocket messages, and the Headers are
+/// the headers that came back from the server handshake.
+/// If the user used a protocol or attached some other headers check these response
+/// headers to see if the server accepted the protocol or other custom header.
+/// This crate will not automatically close the connection if the server refused
+/// to use the user protocols given to it, you must check that the server accepted.
+pub type ClientNew<S> = Box<Future<Item = (Client<S>, Headers), Error = WebSocketError>>;
diff --git a/src/client/builder.rs b/src/client/builder.rs
index 3807ae8217..708b63e2fc 100644
--- a/src/client/builder.rs
+++ b/src/client/builder.rs
@@ -1,26 +1,62 @@
 //! Everything you need to create a client connection to a websocket.
 
 use std::borrow::Cow;
-use std::net::TcpStream;
 pub use url::{Url, ParseError};
-use url::Position;
-use hyper::version::HttpVersion;
-use hyper::status::StatusCode;
-use hyper::buffer::BufReader;
-use hyper::http::h1::parse_response;
-use hyper::header::{Headers, Header, HeaderFormat, Host, Connection, ConnectionOption, Upgrade,
-                    Protocol, ProtocolName};
-use unicase::UniCase;
-#[cfg(feature="ssl")]
-use openssl::ssl::{SslMethod, SslStream, SslConnector, SslConnectorBuilder};
 use header::extensions::Extension;
-use header::{WebSocketAccept, WebSocketKey, WebSocketVersion, WebSocketProtocol,
-             WebSocketExtensions, Origin};
-use result::{WSUrlErrorKind, WebSocketResult, WebSocketError};
-#[cfg(feature="ssl")]
-use stream::NetworkStream;
-use stream::Stream;
-use super::Client;
+use header::{WebSocketKey, WebSocketVersion, WebSocketProtocol, WebSocketExtensions, Origin};
+use hyper::header::{Headers, Header, HeaderFormat};
+use hyper::version::HttpVersion;
+
+#[cfg(any(feature="sync", feature="async"))]
+mod common_imports {
+	pub use std::net::TcpStream;
+	pub use std::net::ToSocketAddrs;
+	pub use url::Position;
+	pub use hyper::http::h1::Incoming;
+	pub use hyper::http::RawStatus;
+	pub use hyper::status::StatusCode;
+	pub use hyper::buffer::BufReader;
+	pub use hyper::method::Method;
+	pub use hyper::uri::RequestUri;
+	pub use hyper::http::h1::parse_response;
+	pub use hyper::header::{Host, Connection, ConnectionOption, Upgrade, Protocol, ProtocolName};
+	pub use unicase::UniCase;
+	pub use header::WebSocketAccept;
+	pub use result::{WSUrlErrorKind, WebSocketResult, WebSocketError};
+	pub use stream::{self, Stream};
+}
+#[cfg(any(feature="sync", feature="async"))]
+use self::common_imports::*;
+
+#[cfg(feature="sync")]
+use super::sync::Client;
+
+#[cfg(feature="sync-ssl")]
+use stream::sync::NetworkStream;
+
+#[cfg(any(feature="sync-ssl", feature="async-ssl"))]
+use native_tls::TlsConnector;
+#[cfg(feature="sync-ssl")]
+use native_tls::TlsStream;
+
+#[cfg(feature="async")]
+mod async_imports {
+	pub use super::super::async;
+	pub use tokio_io::codec::Framed;
+	pub use tokio_core::net::TcpStreamNew;
+	pub use tokio_core::reactor::Handle;
+	pub use futures::{Future, Sink};
+	pub use futures::future;
+	pub use futures::Stream as FutureStream;
+	pub use codec::ws::{MessageCodec, Context};
+	#[cfg(feature="async-ssl")]
+	pub use tokio_tls::TlsConnectorExt;
+}
+#[cfg(feature="async")]
+use self::async_imports::*;
+
+// TODO: add extra funcs for future stuff, like auto ping and auto close
+
 
 /// Build clients with a builder-style API
 /// This makes it easy to create and configure a websocket
@@ -333,42 +369,6 @@ impl<'u> ClientBuilder<'u> {
 		self.headers.get::<H>()
 	}
 
-	fn establish_tcp(&mut self, secure: Option<bool>) -> WebSocketResult<TcpStream> {
-		let port = match (self.url.port(), secure) {
-			(Some(port), _) => port,
-			(None, None) if self.url.scheme() == "wss" => 443,
-			(None, None) => 80,
-			(None, Some(true)) => 443,
-			(None, Some(false)) => 80,
-		};
-		let host = match self.url.host_str() {
-			Some(h) => h,
-			None => return Err(WebSocketError::WebSocketUrlError(WSUrlErrorKind::NoHostName)),
-		};
-
-		let tcp_stream = try!(TcpStream::connect((host, port)));
-		Ok(tcp_stream)
-	}
-
-	#[cfg(feature="ssl")]
-	fn wrap_ssl(
-		&self,
-		tcp_stream: TcpStream,
-		connector: Option<SslConnector>,
-	) -> WebSocketResult<SslStream<TcpStream>> {
-		let host = match self.url.host_str() {
-			Some(h) => h,
-			None => return Err(WebSocketError::WebSocketUrlError(WSUrlErrorKind::NoHostName)),
-		};
-		let connector = match connector {
-			Some(c) => c,
-			None => try!(SslConnectorBuilder::new(SslMethod::tls())).build(),
-		};
-
-		let ssl_stream = try!(connector.connect(host, tcp_stream));
-		Ok(ssl_stream)
-	}
-
 	/// Connect to a server (finally)!
 	/// This will use a `Box<NetworkStream>` to represent either an SSL
 	/// connection or a normal TCP connection, what to use will be decided
@@ -388,10 +388,10 @@ impl<'u> ClientBuilder<'u> {
 	/// let message = Message::text("m337 47 7pm");
 	/// client.send_message(&message).unwrap();
 	/// ```
-	#[cfg(feature="ssl")]
+	#[cfg(feature="sync-ssl")]
 	pub fn connect(
 		&mut self,
-		ssl_config: Option<SslConnector>,
+		ssl_config: Option<TlsConnector>,
 	) -> WebSocketResult<Client<Box<NetworkStream + Send>>> {
 		let tcp_stream = try!(self.establish_tcp(None));
 
@@ -406,7 +406,7 @@ impl<'u> ClientBuilder<'u> {
 	}
 
 	/// Create an insecure (plain TCP) connection to the client.
-	/// In this case no `Box` will be used you will just get a TcpStream,
+	/// In this case no `Box` will be used, you will just get a TcpStream,
 	/// giving you the ability to split the stream into a reader and writer
 	/// (since SSL streams cannot be cloned).
 	///
@@ -419,6 +419,7 @@ impl<'u> ClientBuilder<'u> {
 	/// // split into two (for some reason)!
 	/// let (receiver, sender) = client.split().unwrap();
 	/// ```
+	#[cfg(feature="sync")]
 	pub fn connect_insecure(&mut self) -> WebSocketResult<Client<TcpStream>> {
 		let tcp_stream = try!(self.establish_tcp(Some(false)));
 
@@ -426,14 +427,14 @@ impl<'u> ClientBuilder<'u> {
 	}
 
 	/// Create an SSL connection to the sever.
-	/// This will only use an `SslStream`, this is useful
+	/// This will only use an `TlsStream`, this is useful
 	/// when you want to be sure to connect over SSL or when you want access
-	/// to the `SslStream` functions (without having to go through a `Box`).
-	#[cfg(feature="ssl")]
+	/// to the `TlsStream` functions (without having to go through a `Box`).
+	#[cfg(feature="sync-ssl")]
 	pub fn connect_secure(
 		&mut self,
-		ssl_config: Option<SslConnector>,
-	) -> WebSocketResult<Client<SslStream<TcpStream>>> {
+		ssl_config: Option<TlsConnector>,
+	) -> WebSocketResult<Client<TlsStream<TcpStream>>> {
 		let tcp_stream = try!(self.establish_tcp(Some(true)));
 
 		let ssl_stream = try!(self.wrap_ssl(tcp_stream, ssl_config));
@@ -441,7 +442,6 @@ impl<'u> ClientBuilder<'u> {
 		self.connect_on(ssl_stream)
 	}
 
-	// TODO: similar ability for server?
 	/// Connects to a websocket server on any stream you would like.
 	/// Possible streams:
 	///  - Unix Sockets
@@ -450,7 +450,7 @@ impl<'u> ClientBuilder<'u> {
 	///
 	/// ```rust
 	/// # use websocket::ClientBuilder;
-	/// use websocket::stream::ReadWritePair;
+	/// use websocket::sync::stream::ReadWritePair;
 	/// use std::io::Cursor;
 	///
 	/// let accept = b"HTTP/1.1 101 Switching Protocols\r
@@ -471,11 +471,364 @@ impl<'u> ClientBuilder<'u> {
 	/// let text = String::from_utf8(text).unwrap();
 	/// assert!(text.contains("dGhlIHNhbXBsZSBub25jZQ=="), "{}", text);
 	/// ```
+	#[cfg(feature="sync")]
 	pub fn connect_on<S>(&mut self, mut stream: S) -> WebSocketResult<Client<S>>
 		where S: Stream
 	{
-		let resource = self.url[Position::BeforePath..Position::AfterQuery].to_owned();
+		// send request
+		let resource = self.build_request();
+		try!(write!(stream, "GET {} {}\r\n", resource, self.version));
+		try!(write!(stream, "{}\r\n", self.headers));
+
+		// wait for a response
+		let mut reader = BufReader::new(stream);
+		let response = try!(parse_response(&mut reader));
+
+		// validate
+		self.validate(&response)?;
+
+		Ok(Client::unchecked(reader, response.headers, true, false))
+	}
+
+	/// Connect to a websocket server asynchronously.
+	///
+	/// This will use a `Box<AsyncRead + AsyncWrite + Send>` to represent either
+	/// an SSL connection or a normal TCP connection, what to use will be decided
+	/// using the protocol of the URL passed in (e.g. `ws://` or `wss://`)
+	///
+	/// If you have non-default SSL circumstances, you can use the `ssl_config`
+	/// parameter to configure those.
+	///
+	///# Example
+	///
+	/// ```rust,no_run
+	/// # extern crate rand;
+	/// # extern crate tokio_core;
+	/// # extern crate futures;
+	/// # extern crate websocket;
+	/// use websocket::ClientBuilder;
+	/// use websocket::futures::{Future, Stream, Sink};
+	/// use websocket::Message;
+	/// use tokio_core::reactor::Core;
+	/// # use rand::Rng;
+	///
+	/// # fn main() {
+	/// let mut core = Core::new().unwrap();
+	///
+	/// // let's randomly do either SSL or plaintext
+	/// let url = if rand::thread_rng().gen() {
+	///     "ws://echo.websocket.org"
+	/// } else {
+	///     "wss://echo.websocket.org"
+	/// };
+	///
+	/// // send a message and hear it come back
+	/// let echo_future = ClientBuilder::new(url).unwrap()
+	///     .async_connect(None, &core.handle())
+	///     .and_then(|(s, _)| s.send(Message::text("hallo").into()))
+	///     .and_then(|s| s.into_future().map_err(|e| e.0))
+	///     .map(|(m, _)| {
+	///         assert_eq!(m, Some(Message::text("hallo").into()))
+	///     });
+	///
+	/// core.run(echo_future).unwrap();
+	/// # }
+	/// ```
+	#[cfg(feature="async-ssl")]
+	pub fn async_connect(
+		self,
+		ssl_config: Option<TlsConnector>,
+		handle: &Handle,
+	) -> async::ClientNew<Box<stream::async::Stream + Send>> {
+		// connect to the tcp stream
+		let tcp_stream = match self.async_tcpstream(None, handle) {
+			Ok(t) => t,
+			Err(e) => return future::err(e).boxed(),
+		};
+
+		let builder = ClientBuilder {
+			url: Cow::Owned(self.url.into_owned()),
+			version: self.version,
+			headers: self.headers,
+			version_set: self.version_set,
+			key_set: self.key_set,
+		};
+
+		// check if we should connect over ssl or not
+		if builder.url.scheme() == "wss" {
+			// configure the tls connection
+			let (host, connector) = {
+				match builder.extract_host_ssl_conn(ssl_config) {
+					Ok((h, conn)) => (h.to_string(), conn),
+					Err(e) => return future::err(e).boxed(),
+				}
+			};
+			// secure connection, wrap with ssl
+			let future =
+				tcp_stream.map_err(|e| e.into())
+				          .and_then(move |s| {
+					                    connector.connect_async(&host, s)
+					                             .map_err(|e| e.into())
+					                   })
+				          .and_then(move |stream| {
+					                    let stream: Box<stream::async::Stream + Send> = Box::new(stream);
+					                    builder.async_connect_on(stream)
+					                   });
+			Box::new(future)
+		} else {
+			// insecure connection, connect normally
+			let future =
+				tcp_stream.map_err(|e| e.into())
+				          .and_then(move |stream| {
+					                    let stream: Box<stream::async::Stream + Send> = Box::new(stream);
+					                    builder.async_connect_on(stream)
+					                   });
+			Box::new(future)
+		}
+	}
+
+	/// Asynchronously create an SSL connection to a websocket sever.
+	///
+	/// This method will only try to connect over SSL and fail otherwise, useful
+	/// when you want to be sure to connect over SSL or when you want access
+	/// to the `TlsStream` functions (without having to go through a `Box`).
+	///
+	/// If you have non-default SSL circumstances, you can use the `ssl_config`
+	/// parameter to configure those.
+	///
+	///# Example
+	///
+	/// ```rust
+	/// # extern crate tokio_core;
+	/// # extern crate futures;
+	/// # extern crate websocket;
+	/// use websocket::ClientBuilder;
+	/// use websocket::futures::{Future, Stream, Sink};
+	/// use websocket::Message;
+	/// use tokio_core::reactor::Core;
+	/// # fn main() {
+	///
+	/// let mut core = Core::new().unwrap();
+	///
+	/// // send a message and hear it come back
+	/// let echo_future = ClientBuilder::new("wss://echo.websocket.org").unwrap()
+	///     .async_connect_secure(None, &core.handle())
+	///     .and_then(|(s, _)| s.send(Message::text("hallo").into()))
+	///     .and_then(|s| s.into_future().map_err(|e| e.0))
+	///     .map(|(m, _)| {
+	///         assert_eq!(m, Some(Message::text("hallo").into()))
+	///     });
+	///
+	/// core.run(echo_future).unwrap();
+	/// # }
+	/// ```
+	#[cfg(feature="async-ssl")]
+	pub fn async_connect_secure(
+		self,
+		ssl_config: Option<TlsConnector>,
+		handle: &Handle,
+	) -> async::ClientNew<async::TlsStream<async::TcpStream>> {
+		// connect to the tcp stream
+		let tcp_stream = match self.async_tcpstream(Some(true), handle) {
+			Ok(t) => t,
+			Err(e) => return future::err(e).boxed(),
+		};
+
+		// configure the tls connection
+		let (host, connector) = {
+			match self.extract_host_ssl_conn(ssl_config) {
+				Ok((h, conn)) => (h.to_string(), conn),
+				Err(e) => return future::err(e).boxed(),
+			}
+		};
+
+		let builder = ClientBuilder {
+			url: Cow::Owned(self.url.into_owned()),
+			version: self.version,
+			headers: self.headers,
+			version_set: self.version_set,
+			key_set: self.key_set,
+		};
 
+		// put it all together
+		let future = tcp_stream.map_err(|e| e.into())
+		                       .and_then(move |s| {
+			                                 connector.connect_async(&host, s)
+			                                          .map_err(|e| e.into())
+			                                })
+		                       .and_then(move |stream| {
+			                                 builder.async_connect_on(stream)
+			                                });
+		Box::new(future)
+	}
+
+	// TODO: add timeout option for connecting
+	// TODO: add conveniences like .response_to_pings, .send_close, etc.
+	/// Asynchronously create an insecure (plain TCP) connection to the client.
+	///
+	/// In this case no `Box` will be used, you will just get a `TcpStream`,
+	/// giving you less allocations on the heap and direct access to `TcpStream`
+	/// functions.
+	///
+	///# Example
+	///
+	/// ```rust,no_run
+	/// # extern crate tokio_core;
+	/// # extern crate futures;
+	/// # extern crate websocket;
+	/// use websocket::ClientBuilder;
+	/// use websocket::futures::{Future, Stream, Sink};
+	/// use websocket::Message;
+	/// use tokio_core::reactor::Core;
+	/// # fn main() {
+	///
+	/// let mut core = Core::new().unwrap();
+	///
+	/// // send a message and hear it come back
+	/// let echo_future = ClientBuilder::new("ws://echo.websocket.org").unwrap()
+	///     .async_connect_insecure(&core.handle())
+	///     .and_then(|(s, _)| s.send(Message::text("hallo").into()))
+	///     .and_then(|s| s.into_future().map_err(|e| e.0))
+	///     .map(|(m, _)| {
+	///         assert_eq!(m, Some(Message::text("hallo").into()))
+	///     });
+	///
+	/// core.run(echo_future).unwrap();
+	/// # }
+	/// ```
+	#[cfg(feature="async")]
+	pub fn async_connect_insecure(self, handle: &Handle) -> async::ClientNew<async::TcpStream> {
+		let tcp_stream = match self.async_tcpstream(Some(false), handle) {
+			Ok(t) => t,
+			Err(e) => return future::err(e).boxed(),
+		};
+
+		let builder = ClientBuilder {
+			url: Cow::Owned(self.url.into_owned()),
+			version: self.version,
+			headers: self.headers,
+			version_set: self.version_set,
+			key_set: self.key_set,
+		};
+
+		let future = tcp_stream.map_err(|e| e.into())
+		                       .and_then(move |stream| {
+			                                 builder.async_connect_on(stream)
+			                                });
+		Box::new(future)
+	}
+
+	/// Asynchronously connects to a websocket server on any stream you would like.
+	/// Possible streams:
+	///  - Unix Sockets
+	///  - Bluetooth
+	///  - Logging Middle-ware
+	///  - SSH
+	///
+	/// The stream must be `AsyncRead + AsyncWrite + Send + 'static`.
+	///
+	/// # Example
+	///
+	/// ```rust
+	/// use websocket::header::WebSocketProtocol;
+	/// use websocket::ClientBuilder;
+	/// use websocket::sync::stream::ReadWritePair;
+	/// use websocket::futures::Future;
+	/// use websocket::async::Core;
+	/// # use std::io::Cursor;
+	///
+	/// let mut core = Core::new().unwrap();
+	///
+	/// let accept = b"\
+	/// HTTP/1.1 101 Switching Protocols\r\n\
+	/// Upgrade: websocket\r\n\
+	/// Sec-WebSocket-Protocol: proto-metheus\r\n\
+	/// Connection: Upgrade\r\n\
+	/// Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\
+	/// \r\n";
+	///
+	/// let input = Cursor::new(&accept[..]);
+	/// let output = Cursor::new(Vec::new());
+	///
+	/// let client = ClientBuilder::new("wss://test.ws").unwrap()
+	///     .key(b"the sample nonce".clone())
+	///     .async_connect_on(ReadWritePair(input, output))
+	///     .map(|(_, headers)| {
+	///         let proto: &WebSocketProtocol = headers.get().unwrap();
+	///         assert_eq!(proto.0.first().unwrap(), "proto-metheus")
+	///     });
+	///
+	/// core.run(client).unwrap();
+	/// ```
+	#[cfg(feature="async")]
+	pub fn async_connect_on<S>(self, stream: S) -> async::ClientNew<S>
+		where S: stream::async::Stream + Send + 'static
+	{
+		let mut builder = ClientBuilder {
+			url: Cow::Owned(self.url.into_owned()),
+			version: self.version,
+			headers: self.headers,
+			version_set: self.version_set,
+			key_set: self.key_set,
+		};
+		let resource = builder.build_request();
+		let framed = stream.framed(::codec::http::HttpClientCodec);
+		let request = Incoming {
+			version: builder.version,
+			headers: builder.headers.clone(),
+			subject: (Method::Get, RequestUri::AbsolutePath(resource)),
+		};
+
+		let future = framed
+		      // send request
+          .send(request).map_err(::std::convert::Into::into)
+
+          // wait for a response
+		      .and_then(|stream| stream.into_future().map_err(|e| e.0.into()))
+
+          // validate
+		      .and_then(move |(message, stream)| {
+              message
+                  .ok_or(WebSocketError::ProtocolError(
+                      "Connection closed before handshake could complete."))
+                  .and_then(|message| builder.validate(&message).map(|()| (message, stream)))
+          })
+
+          // output the final client and metadata
+          .map(|(message, stream)| {
+              let codec = MessageCodec::default(Context::Client);
+              let client = Framed::from_parts(stream.into_parts(), codec);
+              (client, message.headers)
+          });
+
+		Box::new(future)
+	}
+
+	#[cfg(feature="async")]
+	fn async_tcpstream(
+		&self,
+		secure: Option<bool>,
+		handle: &Handle,
+	) -> WebSocketResult<TcpStreamNew> {
+		// get the address to connect to, return an error future if ther's a problem
+		let address = match self.extract_host_port(secure).and_then(|p| Ok(p.to_socket_addrs()?)) {
+			Ok(mut s) => {
+				match s.next() {
+					Some(a) => a,
+					None => {
+						return Err(WebSocketError::WebSocketUrlError(WSUrlErrorKind::NoHostName));
+					}
+				}
+			}
+			Err(e) => return Err(e.into()),
+		};
+
+		// connect a tcp stream
+		Ok(async::TcpStream::connect(&address, handle))
+	}
+
+	#[cfg(any(feature="sync", feature="async"))]
+	fn build_request(&mut self) -> String {
 		// enter host if available (unix sockets don't have hosts)
 		if let Some(host) = self.url.host_str() {
 			self.headers
@@ -487,8 +840,8 @@ impl<'u> ClientBuilder<'u> {
 
 		self.headers
 		    .set(Connection(vec![
-            ConnectionOption::ConnectionHeader(UniCase("Upgrade".to_string()))
-        ]));
+                ConnectionOption::ConnectionHeader(UniCase("Upgrade".to_string()))
+            ]));
 
 		self.headers
 		    .set(Upgrade(vec![
@@ -507,15 +860,14 @@ impl<'u> ClientBuilder<'u> {
 		}
 
 		// send request
-		try!(write!(stream, "GET {} {}\r\n", resource, self.version));
-		try!(write!(stream, "{}\r\n", self.headers));
+		let resource = self.url[Position::BeforePath..Position::AfterQuery].to_owned();
+		resource
+	}
 
-		// wait for a response
-		let mut reader = BufReader::new(stream);
-		let response = try!(parse_response(&mut reader));
+	#[cfg(any(feature="sync", feature="async"))]
+	fn validate(&self, response: &Incoming<RawStatus>) -> WebSocketResult<()> {
 		let status = StatusCode::from_u16(response.subject.0);
 
-		// validate
 		if status != StatusCode::SwitchingProtocols {
 			return Err(WebSocketError::ResponseError("Status code must be Switching Protocols"));
 		}
@@ -540,12 +892,61 @@ impl<'u> ClientBuilder<'u> {
 
 		if self.headers.get() !=
 		   Some(&(Connection(vec![
-            ConnectionOption::ConnectionHeader(UniCase("Upgrade".to_string())),
-        ]))) {
+                ConnectionOption::ConnectionHeader(UniCase("Upgrade".to_string())),
+            ]))) {
 			return Err(WebSocketError::ResponseError("Connection field must be 'Upgrade'"));
 		}
 
-		Ok(Client::unchecked(reader, response.headers, true, false))
+		Ok(())
+	}
+
+	#[cfg(any(feature="sync", feature="async"))]
+	fn extract_host_port(&self, secure: Option<bool>) -> WebSocketResult<(&str, u16)> {
+		let port = match (self.url.port(), secure) {
+			(Some(port), _) => port,
+			(None, None) if self.url.scheme() == "wss" => 443,
+			(None, None) => 80,
+			(None, Some(true)) => 443,
+			(None, Some(false)) => 80,
+		};
+		let host = match self.url.host_str() {
+			Some(h) => h,
+			None => return Err(WebSocketError::WebSocketUrlError(WSUrlErrorKind::NoHostName)),
+		};
+
+		Ok((host, port))
+	}
+
+	#[cfg(feature="sync")]
+	fn establish_tcp(&mut self, secure: Option<bool>) -> WebSocketResult<TcpStream> {
+		Ok(TcpStream::connect(self.extract_host_port(secure)?)?)
+	}
+
+	#[cfg(any(feature="sync-ssl", feature="async-ssl"))]
+	fn extract_host_ssl_conn(
+		&self,
+		connector: Option<TlsConnector>,
+	) -> WebSocketResult<(&str, TlsConnector)> {
+		let host = match self.url.host_str() {
+			Some(h) => h,
+			None => return Err(WebSocketError::WebSocketUrlError(WSUrlErrorKind::NoHostName)),
+		};
+		let connector = match connector {
+			Some(c) => c,
+			None => TlsConnector::builder()?.build()?,
+		};
+		Ok((host, connector))
+	}
+
+	#[cfg(feature="sync-ssl")]
+	fn wrap_ssl(
+		&self,
+		tcp_stream: TcpStream,
+		connector: Option<TlsConnector>,
+	) -> WebSocketResult<TlsStream<TcpStream>> {
+		let (host, connector) = self.extract_host_ssl_conn(connector)?;
+		let ssl_stream = try!(connector.connect(host, tcp_stream));
+		Ok(ssl_stream)
 	}
 }
 
diff --git a/src/client/mod.rs b/src/client/mod.rs
index 158e52bf95..8a4749b1ce 100644
--- a/src/client/mod.rs
+++ b/src/client/mod.rs
@@ -1,396 +1,23 @@
-//! Contains the WebSocket client.
-extern crate url;
-
-use std::net::TcpStream;
-use std::net::SocketAddr;
-use std::io::Result as IoResult;
-use std::io::{Read, Write};
-use hyper::header::Headers;
-use hyper::buffer::BufReader;
-
-use ws;
-use ws::sender::Sender as SenderTrait;
-use ws::receiver::{DataFrameIterator, MessageIterator};
-use ws::receiver::Receiver as ReceiverTrait;
-use result::WebSocketResult;
-use stream::{AsTcpStream, Stream, Splittable, Shutdown};
-use dataframe::DataFrame;
-use header::{WebSocketProtocol, WebSocketExtensions};
-use header::extensions::Extension;
-
-use ws::dataframe::DataFrame as DataFrameable;
-use sender::Sender;
-use receiver::Receiver;
-pub use sender::Writer;
-pub use receiver::Reader;
+//! Build and use asynchronously or synchronous websocket clients.
+//!
+//! This crate is split up into a synchronous and asynchronous half.
+//! These can be turned on and off by switching the `sync` and `async` features
+//! on and off (plus `sync-ssl` and `async-ssl` for SSL connections).
+//!
+//! In general pick a style you would like to write in and use `ClientBuilder`
+//! to create your websocket connections. Use the `.async_connect` functions to create
+//! async connections, and the normal `.connect` functions for synchronous clients.
+//! The `ClientBuilder` creates both async and sync connections, the actual sync and
+//! async clients live in the `client::sync` and `client::async` modules, respectively.
+//!
+//! Many of the useful things from this module will be hoisted and re-exported under the
+//! `websocket::{sync, async}::client` module which will have all sync or all async things.
 
 pub mod builder;
 pub use self::builder::{ClientBuilder, Url, ParseError};
 
-/// Represents a WebSocket client, which can send and receive messages/data frames.
-///
-/// The client just wraps around a `Stream` (which is something that can be read from
-/// and written to) and handles the websocket protocol. TCP or SSL over TCP is common,
-/// but any stream can be used.
-///
-/// A `Client` can also be split into a `Reader` and a `Writer` which can then be moved
-/// to different threads, often using a send loop and receiver loop concurrently,
-/// as shown in the client example in `examples/client.rs`.
-/// This is only possible for streams that implement the `Splittable` trait, which
-/// currently is only TCP streams. (it is unsafe to duplicate an SSL stream)
-///
-///# Connecting to a Server
-///
-///```no_run
-///extern crate websocket;
-///# fn main() {
-///
-///use websocket::{ClientBuilder, Message};
-///
-///let mut client = ClientBuilder::new("ws://127.0.0.1:1234")
-///    .unwrap()
-///    .connect_insecure()
-///    .unwrap();
-///
-///let message = Message::text("Hello, World!");
-///client.send_message(&message).unwrap(); // Send message
-///# }
-///```
-pub struct Client<S>
-	where S: Stream
-{
-	stream: BufReader<S>,
-	headers: Headers,
-	sender: Sender,
-	receiver: Receiver,
-}
-
-impl Client<TcpStream> {
-	/// Shuts down the sending half of the client connection, will cause all pending
-	/// and future IO to return immediately with an appropriate value.
-	pub fn shutdown_sender(&self) -> IoResult<()> {
-		self.stream.get_ref().as_tcp().shutdown(Shutdown::Write)
-	}
-
-	/// Shuts down the receiving half of the client connection, will cause all pending
-	/// and future IO to return immediately with an appropriate value.
-	pub fn shutdown_receiver(&self) -> IoResult<()> {
-		self.stream.get_ref().as_tcp().shutdown(Shutdown::Read)
-	}
-}
-
-impl<S> Client<S>
-    where S: AsTcpStream + Stream
-{
-	/// Shuts down the client connection, will cause all pending and future IO to
-	/// return immediately with an appropriate value.
-	pub fn shutdown(&self) -> IoResult<()> {
-		self.stream.get_ref().as_tcp().shutdown(Shutdown::Both)
-	}
-
-	/// See [`TcpStream::peer_addr`]
-	/// (https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.peer_addr).
-	pub fn peer_addr(&self) -> IoResult<SocketAddr> {
-		self.stream.get_ref().as_tcp().peer_addr()
-	}
-
-	/// See [`TcpStream::local_addr`]
-	/// (https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.local_addr).
-	pub fn local_addr(&self) -> IoResult<SocketAddr> {
-		self.stream.get_ref().as_tcp().local_addr()
-	}
-
-	/// See [`TcpStream::set_nodelay`]
-	/// (https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.set_nodelay).
-	pub fn set_nodelay(&mut self, nodelay: bool) -> IoResult<()> {
-		self.stream.get_ref().as_tcp().set_nodelay(nodelay)
-	}
-
-	/// Changes whether the stream is in nonblocking mode.
-	pub fn set_nonblocking(&self, nonblocking: bool) -> IoResult<()> {
-		self.stream.get_ref().as_tcp().set_nonblocking(nonblocking)
-	}
-}
-
-impl<S> Client<S>
-    where S: Stream
-{
-	/// Creates a Client from a given stream
-	/// **without sending any handshake** this is meant to only be used with
-	/// a stream that has a websocket connection already set up.
-	/// If in doubt, don't use this!
-	#[doc(hidden)]
-	pub fn unchecked(
-		stream: BufReader<S>,
-		headers: Headers,
-		out_mask: bool,
-		in_mask: bool,
-	) -> Self {
-		Client {
-			headers: headers,
-			stream: stream,
-			// NOTE: these are always true & false, see
-			// https://tools.ietf.org/html/rfc6455#section-5
-			sender: Sender::new(out_mask),
-			receiver: Receiver::new(in_mask),
-		}
-	}
-
-	/// Sends a single data frame to the remote endpoint.
-	pub fn send_dataframe<D>(&mut self, dataframe: &D) -> WebSocketResult<()>
-		where D: DataFrameable
-	{
-		self.sender.send_dataframe(self.stream.get_mut(), dataframe)
-	}
-
-	/// Sends a single message to the remote endpoint.
-	pub fn send_message<'m, M, D>(&mut self, message: &'m M) -> WebSocketResult<()>
-		where M: ws::Message<'m, D>,
-		      D: DataFrameable
-	{
-		self.sender.send_message(self.stream.get_mut(), message)
-	}
-
-	/// Reads a single data frame from the remote endpoint.
-	pub fn recv_dataframe(&mut self) -> WebSocketResult<DataFrame> {
-		self.receiver.recv_dataframe(&mut self.stream)
-	}
-
-	/// Returns an iterator over incoming data frames.
-	pub fn incoming_dataframes(&mut self) -> DataFrameIterator<Receiver, BufReader<S>> {
-		self.receiver.incoming_dataframes(&mut self.stream)
-	}
-
-	/// Reads a single message from this receiver.
-	///
-	/// ```rust,no_run
-	/// use websocket::{ClientBuilder, Message};
-	/// let mut client = ClientBuilder::new("ws://localhost:3000")
-	///     .unwrap()
-	///     .connect_insecure()
-	///     .unwrap();
-	///
-	/// client.send_message(&Message::text("Hello world!")).unwrap();
-	///
-	/// let message: Message = client.recv_message().unwrap();
-	/// ```
-	pub fn recv_message<'m, M, I, D>(&mut self) -> WebSocketResult<M>
-		where M: ws::Message<'m, D, DataFrameIterator = I>,
-		      I: Iterator<Item = D>,
-		      D: DataFrameable
-	{
-		self.receiver.recv_message(&mut self.stream)
-	}
-
-	/// Access the headers that were sent in the server's handshake response.
-	/// This is a catch all for headers other than protocols and extensions.
-	pub fn headers(&self) -> &Headers {
-		&self.headers
-	}
-
-	/// **If you supplied a protocol, you must check that it was accepted by
-	/// the server** using this function.
-	/// This is not done automatically because the terms of accepting a protocol
-	/// can get complicated, especially if some protocols depend on others, etc.
-	///
-	/// ```rust,no_run
-	/// # use websocket::ClientBuilder;
-	/// let mut client = ClientBuilder::new("wss://test.fysh.in").unwrap()
-	///     .add_protocol("xmpp")
-	///     .connect_insecure()
-	///     .unwrap();
-	///
-	/// // be sure to check the protocol is there!
-	/// assert!(client.protocols().iter().any(|p| p as &str == "xmpp"));
-	/// ```
-	pub fn protocols(&self) -> &[String] {
-		self.headers
-		    .get::<WebSocketProtocol>()
-		    .map(|p| p.0.as_slice())
-		    .unwrap_or(&[])
-	}
-
-	/// If you supplied a protocol, be sure to check if it was accepted by the
-	/// server here. Since no extensions are implemented out of the box yet, using
-	/// one will require its own implementation.
-	pub fn extensions(&self) -> &[Extension] {
-		self.headers
-		    .get::<WebSocketExtensions>()
-		    .map(|e| e.0.as_slice())
-		    .unwrap_or(&[])
-	}
-
-	/// Get a reference to the stream.
-	/// Useful to be able to set options on the stream.
-	///
-	/// ```rust,no_run
-	/// # use websocket::ClientBuilder;
-	/// let mut client = ClientBuilder::new("ws://double.down").unwrap()
-	///     .connect_insecure()
-	///     .unwrap();
-	///
-	/// client.stream_ref().set_ttl(60).unwrap();
-	/// ```
-	pub fn stream_ref(&self) -> &S {
-		self.stream.get_ref()
-	}
-
-	/// Get a handle to the writable portion of this stream.
-	/// This can be used to write custom extensions.
-	///
-	/// ```rust,no_run
-	/// # use websocket::ClientBuilder;
-	/// use websocket::Message;
-	/// use websocket::ws::sender::Sender as SenderTrait;
-	/// use websocket::sender::Sender;
-	///
-	/// let mut client = ClientBuilder::new("ws://the.room").unwrap()
-	///     .connect_insecure()
-	///     .unwrap();
-	///
-	/// let message = Message::text("Oh hi, Mark.");
-	/// let mut sender = Sender::new(true);
-	/// let mut buf = Vec::new();
-	///
-	/// sender.send_message(&mut buf, &message);
-	///
-	/// /* transform buf somehow */
-	///
-	/// client.writer_mut().write_all(&buf);
-	/// ```
-	pub fn writer_mut(&mut self) -> &mut Write {
-		self.stream.get_mut()
-	}
-
-	/// Get a handle to the readable portion of this stream.
-	/// This can be used to transform raw bytes before they
-	/// are read in.
-	///
-	/// ```rust,no_run
-	/// # use websocket::ClientBuilder;
-	/// use std::io::Cursor;
-	/// use websocket::Message;
-	/// use websocket::ws::receiver::Receiver as ReceiverTrait;
-	/// use websocket::receiver::Receiver;
-	///
-	/// let mut client = ClientBuilder::new("ws://the.room").unwrap()
-	///     .connect_insecure()
-	///     .unwrap();
-	///
-	/// let mut receiver = Receiver::new(false);
-	/// let mut buf = Vec::new();
-	///
-	/// client.reader_mut().read_to_end(&mut buf);
-	///
-	/// /* transform buf somehow */
-	///
-	/// let mut buf_reader = Cursor::new(&mut buf);
-	/// let message: Message = receiver.recv_message(&mut buf_reader).unwrap();
-	/// ```
-	pub fn reader_mut(&mut self) -> &mut Read {
-		&mut self.stream
-	}
-
-	/// Deconstruct the client into its underlying stream and
-	/// maybe some of the buffer that was already read from the stream.
-	/// The client uses a buffered reader to read in messages, so some
-	/// bytes might already be read from the stream when this is called,
-	/// these buffered bytes are returned in the form
-	///
-	/// `(byte_buffer: Vec<u8>, buffer_capacity: usize, buffer_position: usize)`
-	pub fn into_stream(self) -> (S, Option<(Vec<u8>, usize, usize)>) {
-		let (stream, buf, pos, cap) = self.stream.into_parts();
-		(stream, Some((buf, pos, cap)))
-	}
-
-	/// Returns an iterator over incoming messages.
-	///
-	///```no_run
-	///# extern crate websocket;
-	///# fn main() {
-	///use websocket::{ClientBuilder, Message};
-	///
-	///let mut client = ClientBuilder::new("ws://127.0.0.1:1234").unwrap()
-	///                     .connect(None).unwrap();
-	///
-	///for message in client.incoming_messages() {
-	///    let message: Message = message.unwrap();
-	///    println!("Recv: {:?}", message);
-	///}
-	///# }
-	///```
-	///
-	/// Note that since this method mutably borrows the `Client`, it may be necessary to
-	/// first `split()` the `Client` and call `incoming_messages()` on the returned
-	/// `Receiver` to be able to send messages within an iteration.
-	///
-	///```no_run
-	///# extern crate websocket;
-	///# fn main() {
-	///use websocket::{ClientBuilder, Message};
-	///
-	///let mut client = ClientBuilder::new("ws://127.0.0.1:1234").unwrap()
-	///                     .connect_insecure().unwrap();
-	///
-	///let (mut receiver, mut sender) = client.split().unwrap();
-	///
-	///for message in receiver.incoming_messages() {
-	///    let message: Message = message.unwrap();
-	///    // Echo the message back
-	///    sender.send_message(&message).unwrap();
-	///}
-	///# }
-	///```
-	pub fn incoming_messages<'a, M, D>(&'a mut self,)
-		-> MessageIterator<'a, Receiver, D, M, BufReader<S>>
-		where M: ws::Message<'a, D>,
-		      D: DataFrameable
-	{
-		self.receiver.incoming_messages(&mut self.stream)
-	}
-}
+#[cfg(feature="async")]
+pub mod async;
 
-impl<S> Client<S>
-    where S: Splittable + Stream
-{
-	/// Split this client into its constituent Sender and Receiver pair.
-	///
-	/// This allows the Sender and Receiver to be sent to different threads.
-	///
-	///```no_run
-	///# extern crate websocket;
-	///# fn main() {
-	///use std::thread;
-	///use websocket::{ClientBuilder, Message};
-	///
-	///let mut client = ClientBuilder::new("ws://127.0.0.1:1234").unwrap()
-	///                     .connect_insecure().unwrap();
-	///
-	///let (mut receiver, mut sender) = client.split().unwrap();
-	///
-	///thread::spawn(move || {
-	///    for message in receiver.incoming_messages() {
-	///        let message: Message = message.unwrap();
-	///        println!("Recv: {:?}", message);
-	///    }
-	///});
-	///
-	///let message = Message::text("Hello, World!");
-	///sender.send_message(&message).unwrap();
-	///# }
-	///```
-	pub fn split
-		(self,)
-		 -> IoResult<(Reader<<S as Splittable>::Reader>, Writer<<S as Splittable>::Writer>)> {
-		let (stream, buf, pos, cap) = self.stream.into_parts();
-		let (read, write) = try!(stream.split());
-		Ok((Reader {
-		        stream: BufReader::from_parts(read, buf, pos, cap),
-		        receiver: self.receiver,
-		    },
-		    Writer {
-		        stream: write,
-		        sender: self.sender,
-		    }))
-	}
-}
+#[cfg(feature="sync")]
+pub mod sync;
diff --git a/src/client/sync.rs b/src/client/sync.rs
new file mode 100644
index 0000000000..ff5ca980c7
--- /dev/null
+++ b/src/client/sync.rs
@@ -0,0 +1,377 @@
+//! Contains the WebSocket client.
+use std::net::TcpStream;
+use std::net::SocketAddr;
+use std::io::Result as IoResult;
+use std::io::{Read, Write};
+use hyper::header::Headers;
+use hyper::buffer::BufReader;
+
+use ws;
+use ws::sender::Sender as SenderTrait;
+use ws::receiver::{DataFrameIterator, MessageIterator};
+use ws::receiver::Receiver as ReceiverTrait;
+use message::OwnedMessage;
+use result::WebSocketResult;
+use stream::sync::{AsTcpStream, Stream, Splittable, Shutdown};
+use dataframe::DataFrame;
+use header::{WebSocketProtocol, WebSocketExtensions};
+use header::extensions::Extension;
+
+use ws::dataframe::DataFrame as DataFrameable;
+use sender::Sender;
+use receiver::Receiver;
+pub use sender::Writer;
+pub use receiver::Reader;
+
+/// Represents a WebSocket client, which can send and receive messages/data frames.
+///
+/// The client just wraps around a `Stream` (which is something that can be read from
+/// and written to) and handles the websocket protocol. TCP or SSL over TCP is common,
+/// but any stream can be used.
+///
+/// A `Client` can also be split into a `Reader` and a `Writer` which can then be moved
+/// to different threads, often using a send loop and receiver loop concurrently,
+/// as shown in the client example in `examples/client.rs`.
+/// This is only possible for streams that implement the `Splittable` trait, which
+/// currently is only TCP streams. (it is unsafe to duplicate an SSL stream)
+///
+///# Connecting to a Server
+///
+///```no_run
+///extern crate websocket;
+///# fn main() {
+///
+///use websocket::{ClientBuilder, Message};
+///
+///let mut client = ClientBuilder::new("ws://127.0.0.1:1234")
+///    .unwrap()
+///    .connect_insecure()
+///    .unwrap();
+///
+///let message = Message::text("Hello, World!");
+///client.send_message(&message).unwrap(); // Send message
+///# }
+///```
+pub struct Client<S>
+	where S: Stream
+{
+	stream: BufReader<S>,
+	headers: Headers,
+	sender: Sender,
+	receiver: Receiver,
+}
+
+impl Client<TcpStream> {
+	/// Shuts down the sending half of the client connection, will cause all pending
+	/// and future IO to return immediately with an appropriate value.
+	pub fn shutdown_sender(&self) -> IoResult<()> {
+		self.stream.get_ref().as_tcp().shutdown(Shutdown::Write)
+	}
+
+	/// Shuts down the receiving half of the client connection, will cause all pending
+	/// and future IO to return immediately with an appropriate value.
+	pub fn shutdown_receiver(&self) -> IoResult<()> {
+		self.stream.get_ref().as_tcp().shutdown(Shutdown::Read)
+	}
+}
+
+impl<S> Client<S>
+    where S: AsTcpStream + Stream
+{
+	/// Shuts down the client connection, will cause all pending and future IO to
+	/// return immediately with an appropriate value.
+	pub fn shutdown(&self) -> IoResult<()> {
+		self.stream.get_ref().as_tcp().shutdown(Shutdown::Both)
+	}
+
+	/// See [`TcpStream::peer_addr`]
+	/// (https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.peer_addr).
+	pub fn peer_addr(&self) -> IoResult<SocketAddr> {
+		self.stream.get_ref().as_tcp().peer_addr()
+	}
+
+	/// See [`TcpStream::local_addr`]
+	/// (https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.local_addr).
+	pub fn local_addr(&self) -> IoResult<SocketAddr> {
+		self.stream.get_ref().as_tcp().local_addr()
+	}
+
+	/// See [`TcpStream::set_nodelay`]
+	/// (https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.set_nodelay).
+	pub fn set_nodelay(&mut self, nodelay: bool) -> IoResult<()> {
+		self.stream.get_ref().as_tcp().set_nodelay(nodelay)
+	}
+
+	/// Changes whether the stream is in nonblocking mode.
+	pub fn set_nonblocking(&self, nonblocking: bool) -> IoResult<()> {
+		self.stream.get_ref().as_tcp().set_nonblocking(nonblocking)
+	}
+}
+
+impl<S> Client<S>
+    where S: Stream
+{
+	/// Creates a Client from a given stream
+	/// **without sending any handshake** this is meant to only be used with
+	/// a stream that has a websocket connection already set up.
+	/// If in doubt, don't use this!
+	#[doc(hidden)]
+	pub fn unchecked(
+		stream: BufReader<S>,
+		headers: Headers,
+		out_mask: bool,
+		in_mask: bool,
+	) -> Self {
+		Client {
+			headers: headers,
+			stream: stream,
+			sender: Sender::new(out_mask), // true
+			receiver: Receiver::new(in_mask), // false
+		}
+	}
+
+	/// Sends a single data frame to the remote endpoint.
+	pub fn send_dataframe<D>(&mut self, dataframe: &D) -> WebSocketResult<()>
+		where D: DataFrameable
+	{
+		self.sender.send_dataframe(self.stream.get_mut(), dataframe)
+	}
+
+	/// Sends a single message to the remote endpoint.
+	pub fn send_message<M>(&mut self, message: &M) -> WebSocketResult<()>
+		where M: ws::Message
+	{
+		self.sender.send_message(self.stream.get_mut(), message)
+	}
+
+	/// Reads a single data frame from the remote endpoint.
+	pub fn recv_dataframe(&mut self) -> WebSocketResult<DataFrame> {
+		self.receiver.recv_dataframe(&mut self.stream)
+	}
+
+	/// Returns an iterator over incoming data frames.
+	pub fn incoming_dataframes(&mut self) -> DataFrameIterator<Receiver, BufReader<S>> {
+		self.receiver.incoming_dataframes(&mut self.stream)
+	}
+
+	/// Reads a single message from this receiver.
+	///
+	/// ```rust,no_run
+	/// use websocket::{ClientBuilder, Message};
+	/// let mut client = ClientBuilder::new("ws://localhost:3000")
+	///     .unwrap()
+	///     .connect_insecure()
+	///     .unwrap();
+	///
+	/// client.send_message(&Message::text("Hello world!")).unwrap();
+	///
+	/// let response = client.recv_message().unwrap();
+	/// ```
+	pub fn recv_message(&mut self) -> WebSocketResult<OwnedMessage> {
+		self.receiver.recv_message(&mut self.stream)
+	}
+
+	/// Access the headers that were sent in the server's handshake response.
+	/// This is a catch all for headers other than protocols and extensions.
+	pub fn headers(&self) -> &Headers {
+		&self.headers
+	}
+
+	/// **If you supplied a protocol, you must check that it was accepted by
+	/// the server** using this function.
+	/// This is not done automatically because the terms of accepting a protocol
+	/// can get complicated, especially if some protocols depend on others, etc.
+	///
+	/// ```rust,no_run
+	/// # use websocket::ClientBuilder;
+	/// let mut client = ClientBuilder::new("wss://test.fysh.in").unwrap()
+	///     .add_protocol("xmpp")
+	///     .connect_insecure()
+	///     .unwrap();
+	///
+	/// // be sure to check the protocol is there!
+	/// assert!(client.protocols().iter().any(|p| p as &str == "xmpp"));
+	/// ```
+	pub fn protocols(&self) -> &[String] {
+		self.headers
+		    .get::<WebSocketProtocol>()
+		    .map(|p| p.0.as_slice())
+		    .unwrap_or(&[])
+	}
+
+	/// If you supplied a protocol, be sure to check if it was accepted by the
+	/// server here. Since no extensions are implemented out of the box yet, using
+	/// one will require its own implementation.
+	pub fn extensions(&self) -> &[Extension] {
+		self.headers
+		    .get::<WebSocketExtensions>()
+		    .map(|e| e.0.as_slice())
+		    .unwrap_or(&[])
+	}
+
+	/// Get a reference to the stream.
+	/// Useful to be able to set options on the stream.
+	///
+	/// ```rust,no_run
+	/// # use websocket::ClientBuilder;
+	/// let mut client = ClientBuilder::new("ws://double.down").unwrap()
+	///     .connect_insecure()
+	///     .unwrap();
+	///
+	/// client.stream_ref().set_ttl(60).unwrap();
+	/// ```
+	pub fn stream_ref(&self) -> &S {
+		self.stream.get_ref()
+	}
+
+	/// Get a handle to the writable portion of this stream.
+	/// This can be used to write custom extensions.
+	///
+	/// ```rust,no_run
+	/// # use websocket::ClientBuilder;
+	/// use websocket::Message;
+	/// use websocket::ws::sender::Sender as SenderTrait;
+	/// use websocket::sender::Sender;
+	///
+	/// let mut client = ClientBuilder::new("ws://the.room").unwrap()
+	///     .connect_insecure()
+	///     .unwrap();
+	///
+	/// let message = Message::text("Oh hi, Mark.");
+	/// let mut sender = Sender::new(true);
+	/// let mut buf = Vec::new();
+	///
+	/// sender.send_message(&mut buf, &message);
+	///
+	/// /* transform buf somehow */
+	///
+	/// client.writer_mut().write_all(&buf);
+	/// ```
+	pub fn writer_mut(&mut self) -> &mut Write {
+		self.stream.get_mut()
+	}
+
+	/// Get a handle to the readable portion of this stream.
+	/// This can be used to transform raw bytes before they
+	/// are read in.
+	///
+	/// ```rust,no_run
+	/// # use websocket::ClientBuilder;
+	/// use std::io::Cursor;
+	/// use websocket::ws::receiver::Receiver as ReceiverTrait;
+	/// use websocket::receiver::Receiver;
+	///
+	/// let mut client = ClientBuilder::new("ws://the.room").unwrap()
+	///     .connect_insecure()
+	///     .unwrap();
+	///
+	/// let mut receiver = Receiver::new(false);
+	/// let mut buf = Vec::new();
+	///
+	/// client.reader_mut().read_to_end(&mut buf);
+	///
+	/// /* transform buf somehow */
+	///
+	/// let mut buf_reader = Cursor::new(&mut buf);
+	/// let message = receiver.recv_message(&mut buf_reader).unwrap();
+	/// ```
+	pub fn reader_mut(&mut self) -> &mut Read {
+		&mut self.stream
+	}
+
+	/// Deconstruct the client into its underlying stream and
+	/// maybe some of the buffer that was already read from the stream.
+	/// The client uses a buffered reader to read in messages, so some
+	/// bytes might already be read from the stream when this is called,
+	/// these buffered bytes are returned in the form
+	///
+	/// `(byte_buffer: Vec<u8>, buffer_capacity: usize, buffer_position: usize)`
+	pub fn into_stream(self) -> (S, Option<(Vec<u8>, usize, usize)>) {
+		let (stream, buf, pos, cap) = self.stream.into_parts();
+		(stream, Some((buf, pos, cap)))
+	}
+
+	/// Returns an iterator over incoming messages.
+	///
+	///```no_run
+	///# extern crate websocket;
+	///# fn main() {
+	///use websocket::ClientBuilder;
+	///
+	///let mut client = ClientBuilder::new("ws://127.0.0.1:1234").unwrap()
+	///                     .connect(None).unwrap();
+	///
+	///for message in client.incoming_messages() {
+	///    println!("Recv: {:?}", message.unwrap());
+	///}
+	///# }
+	///```
+	///
+	/// Note that since this method mutably borrows the `Client`, it may be necessary to
+	/// first `split()` the `Client` and call `incoming_messages()` on the returned
+	/// `Receiver` to be able to send messages within an iteration.
+	///
+	///```no_run
+	///# extern crate websocket;
+	///# fn main() {
+	///use websocket::ClientBuilder;
+	///
+	///let mut client = ClientBuilder::new("ws://127.0.0.1:1234").unwrap()
+	///                     .connect_insecure().unwrap();
+	///
+	///let (mut receiver, mut sender) = client.split().unwrap();
+	///
+	///for message in receiver.incoming_messages() {
+	///    // Echo the message back
+	///    sender.send_message(&message.unwrap()).unwrap();
+	///}
+	///# }
+	///```
+	pub fn incoming_messages<'a>(&'a mut self) -> MessageIterator<'a, Receiver, BufReader<S>> {
+		self.receiver.incoming_messages(&mut self.stream)
+	}
+}
+
+impl<S> Client<S>
+    where S: Splittable + Stream
+{
+	/// Split this client into its constituent Sender and Receiver pair.
+	///
+	/// This allows the Sender and Receiver to be sent to different threads.
+	///
+	///```no_run
+	///# extern crate websocket;
+	///# fn main() {
+	///use std::thread;
+	///use websocket::{ClientBuilder, Message};
+	///
+	///let mut client = ClientBuilder::new("ws://127.0.0.1:1234").unwrap()
+	///                     .connect_insecure().unwrap();
+	///
+	///let (mut receiver, mut sender) = client.split().unwrap();
+	///
+	///thread::spawn(move || {
+	///    for message in receiver.incoming_messages() {
+	///        println!("Recv: {:?}", message.unwrap());
+	///    }
+	///});
+	///
+	///let message = Message::text("Hello, World!");
+	///sender.send_message(&message).unwrap();
+	///# }
+	///```
+	pub fn split
+		(self,)
+		 -> IoResult<(Reader<<S as Splittable>::Reader>, Writer<<S as Splittable>::Writer>)> {
+		let (stream, buf, pos, cap) = self.stream.into_parts();
+		let (read, write) = try!(stream.split());
+		Ok((Reader {
+		        stream: BufReader::from_parts(read, buf, pos, cap),
+		        receiver: self.receiver,
+		    },
+		    Writer {
+		        stream: write,
+		        sender: self.sender,
+		    }))
+	}
+}
diff --git a/src/codec/http.rs b/src/codec/http.rs
new file mode 100644
index 0000000000..b801ee0705
--- /dev/null
+++ b/src/codec/http.rs
@@ -0,0 +1,325 @@
+//! Send HTTP requests and responses asynchronously.
+//!
+//! This module has both an `HttpClientCodec` for an async HTTP client and an
+//! `HttpServerCodec` for an async HTTP server.
+use std::io::{self, Write};
+use std::error::Error;
+use std::fmt::{self, Formatter, Display};
+use hyper;
+use hyper::http::h1::Incoming;
+use hyper::http::h1::parse_response;
+use hyper::http::h1::parse_request;
+use hyper::http::RawStatus;
+use hyper::status::StatusCode;
+use hyper::method::Method;
+use hyper::uri::RequestUri;
+use hyper::buffer::BufReader;
+use tokio_io::codec::{Decoder, Encoder};
+use bytes::BytesMut;
+use bytes::BufMut;
+
+#[derive(Copy, Clone, Debug)]
+///A codec to be used with `tokio` codecs that can serialize HTTP requests and
+///deserialize HTTP responses. One can use this on it's own without websockets to
+///make a very bare async HTTP server.
+///
+///# Example
+///```rust,no_run
+///# extern crate tokio_core;
+///# extern crate tokio_io;
+///# extern crate websocket;
+///# extern crate hyper;
+///use websocket::async::HttpClientCodec;
+///# use websocket::async::futures::{Future, Sink, Stream};
+///# use tokio_core::net::TcpStream;
+///# use tokio_core::reactor::Core;
+///# use tokio_io::AsyncRead;
+///# use hyper::http::h1::Incoming;
+///# use hyper::version::HttpVersion;
+///# use hyper::header::Headers;
+///# use hyper::method::Method;
+///# use hyper::uri::RequestUri;
+///
+///# fn main() {
+///let mut core = Core::new().unwrap();
+///let addr = "crouton.net".parse().unwrap();
+///
+///let f = TcpStream::connect(&addr, &core.handle())
+///    .and_then(|s| {
+///        Ok(s.framed(HttpClientCodec))
+///    })
+///    .and_then(|s| {
+///        s.send(Incoming {
+///            version: HttpVersion::Http11,
+///            subject: (Method::Get, RequestUri::AbsolutePath("/".to_string())),
+///            headers: Headers::new(),
+///        })
+///    })
+///    .map_err(|e| e.into())
+///    .and_then(|s| s.into_future().map_err(|(e, _)| e))
+///    .map(|(m, _)| println!("You got a crouton: {:?}", m));
+///
+///core.run(f).unwrap();
+///# }
+///```
+pub struct HttpClientCodec;
+
+fn split_off_http(src: &mut BytesMut) -> Option<BytesMut> {
+	match src.windows(4).position(|i| i == b"\r\n\r\n") {
+		Some(p) => Some(src.split_to(p + 4)),
+		None => None,
+	}
+}
+
+impl Encoder for HttpClientCodec {
+	type Item = Incoming<(Method, RequestUri)>;
+	type Error = io::Error;
+
+	fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
+		// TODO: optomize this!
+		let request = format!("{} {} {}\r\n{}\r\n",
+		                      item.subject.0,
+		                      item.subject.1,
+		                      item.version,
+		                      item.headers);
+		let byte_len = request.as_bytes().len();
+		if byte_len > dst.remaining_mut() {
+			dst.reserve(byte_len);
+		}
+		dst.writer().write(request.as_bytes()).map(|_| ())
+	}
+}
+
+impl Decoder for HttpClientCodec {
+	type Item = Incoming<RawStatus>;
+	type Error = HttpCodecError;
+
+	fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
+		// check if we get a request from hyper
+		// TODO: this is ineffecient, but hyper does not give us a better way to parse
+		match split_off_http(src) {
+			Some(buf) => {
+				let mut reader = BufReader::with_capacity(&*buf as &[u8], buf.len());
+				let res = match parse_response(&mut reader) {
+					Err(hyper::Error::Io(ref e)) if e.kind() == io::ErrorKind::UnexpectedEof => {
+						return Ok(None)
+					}
+					Err(hyper::Error::TooLarge) => return Ok(None),
+					Err(e) => return Err(e.into()),
+					Ok(r) => r,
+				};
+				Ok(Some(res))
+			}
+			None => Ok(None),
+		}
+	}
+}
+
+///A codec that can be used with streams implementing `AsyncRead + AsyncWrite`
+///that can serialize HTTP responses and deserialize HTTP requests. Using this
+///with an async `TcpStream` will give you a very bare async HTTP server.
+///
+///This crate sends out one HTTP request / response in order to perform the websocket
+///handshake then never talks HTTP again. Because of this an async HTTP implementation
+///is needed.
+///
+///# Example
+///
+///```rust,no_run
+///# extern crate tokio_core;
+///# extern crate tokio_io;
+///# extern crate websocket;
+///# extern crate hyper;
+///# use std::io;
+///use websocket::async::HttpServerCodec;
+///# use websocket::async::futures::{Future, Sink, Stream};
+///# use tokio_core::net::TcpStream;
+///# use tokio_core::reactor::Core;
+///# use tokio_io::AsyncRead;
+///# use hyper::http::h1::Incoming;
+///# use hyper::version::HttpVersion;
+///# use hyper::header::Headers;
+///# use hyper::method::Method;
+///# use hyper::uri::RequestUri;
+///# use hyper::status::StatusCode;
+///# fn main() {
+///
+///let mut core = Core::new().unwrap();
+///let addr = "nothing-to-see-here.com".parse().unwrap();
+///
+///let f = TcpStream::connect(&addr, &core.handle())
+///   .map(|s| s.framed(HttpServerCodec))
+///   .map_err(|e| e.into())
+///   .and_then(|s| s.into_future().map_err(|(e, _)| e))
+///   .and_then(|(m, s)| match m {
+///       Some(ref m) if m.subject.0 == Method::Get => Ok(s),
+///       _ => panic!(),
+///   })
+///   .and_then(|stream| {
+///       stream
+///          .send(Incoming {
+///               version: HttpVersion::Http11,
+///               subject: StatusCode::NotFound,
+///               headers: Headers::new(),
+///           })
+///           .map_err(|e| e.into())
+///   });
+///
+///core.run(f).unwrap();
+///# }
+///```
+#[derive(Copy, Clone, Debug)]
+pub struct HttpServerCodec;
+
+impl Encoder for HttpServerCodec {
+	type Item = Incoming<StatusCode>;
+	type Error = io::Error;
+
+	fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
+		// TODO: optomize this!
+		let response = format!("{} {}\r\n{}\r\n", item.version, item.subject, item.headers);
+		let byte_len = response.as_bytes().len();
+		if byte_len > dst.remaining_mut() {
+			dst.reserve(byte_len);
+		}
+		dst.writer().write(response.as_bytes()).map(|_| ())
+	}
+}
+
+impl Decoder for HttpServerCodec {
+	type Item = Incoming<(Method, RequestUri)>;
+	type Error = HttpCodecError;
+
+	fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
+		// check if we get a request from hyper
+		// TODO: this is ineffecient, but hyper does not give us a better way to parse
+		match split_off_http(src) {
+			Some(buf) => {
+				let mut reader = BufReader::with_capacity(&*buf as &[u8], buf.len());
+				let res = match parse_request(&mut reader) {
+					Err(hyper::Error::Io(ref e)) if e.kind() == io::ErrorKind::UnexpectedEof => {
+						return Ok(None);
+					}
+					Err(hyper::Error::TooLarge) => return Ok(None),
+					Err(e) => return Err(e.into()),
+					Ok(r) => r,
+				};
+				Ok(Some(res))
+			}
+			None => Ok(None),
+		}
+	}
+}
+
+/// Any error that can happen during the writing or parsing of HTTP requests
+/// and responses. This consists of HTTP parsing errors (the `Http` variant) and
+/// errors that can occur when writing to IO (the `Io` variant).
+#[derive(Debug)]
+pub enum HttpCodecError {
+	/// An error that occurs during the writing or reading of HTTP data
+	/// from a socket.
+	Io(io::Error),
+	/// An error that occurs during the parsing of an HTTP request or response.
+	Http(hyper::Error),
+}
+
+impl Display for HttpCodecError {
+	fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
+		fmt.write_str(self.description())
+	}
+}
+
+impl Error for HttpCodecError {
+	fn description(&self) -> &str {
+		match *self {
+			HttpCodecError::Io(ref e) => e.description(),
+			HttpCodecError::Http(ref e) => e.description(),
+		}
+	}
+
+	fn cause(&self) -> Option<&Error> {
+		match *self {
+			HttpCodecError::Io(ref error) => Some(error),
+			HttpCodecError::Http(ref error) => Some(error),
+		}
+	}
+}
+
+impl From<io::Error> for HttpCodecError {
+	fn from(err: io::Error) -> HttpCodecError {
+		HttpCodecError::Io(err)
+	}
+}
+
+impl From<hyper::Error> for HttpCodecError {
+	fn from(err: hyper::Error) -> HttpCodecError {
+		HttpCodecError::Http(err)
+	}
+}
+
+#[cfg(test)]
+mod tests {
+	use super::*;
+	use std::io::Cursor;
+	use stream::ReadWritePair;
+	use tokio_core::reactor::Core;
+	use futures::{Stream, Sink, Future};
+	use tokio_io::AsyncRead;
+	use hyper::version::HttpVersion;
+	use hyper::header::Headers;
+
+	#[test]
+	fn test_client_http_codec() {
+		let mut core = Core::new().unwrap();
+		let response = "HTTP/1.1 404 Not Found\r\n\r\npssst extra data here";
+		let input = Cursor::new(response.as_bytes());
+		let output = Cursor::new(Vec::new());
+
+		let f = ReadWritePair(input, output)
+			.framed(HttpClientCodec)
+			.send(Incoming {
+			          version: HttpVersion::Http11,
+			          subject: (Method::Get, RequestUri::AbsolutePath("/".to_string())),
+			          headers: Headers::new(),
+			      })
+			.map_err(|e| e.into())
+			.and_then(|s| s.into_future().map_err(|(e, _)| e))
+			.and_then(|(m, _)| match m {
+			              Some(ref m) if StatusCode::from_u16(m.subject.0) ==
+			                             StatusCode::NotFound => Ok(()),
+			              _ => Err(io::Error::new(io::ErrorKind::Other, "test failed").into()),
+			          });
+		core.run(f).unwrap();
+	}
+
+	#[test]
+	fn test_server_http_codec() {
+		let mut core = Core::new().unwrap();
+		let request = "\
+		    GET / HTTP/1.0\r\n\
+		    Host: www.rust-lang.org\r\n\
+		    \r\n\
+		    "
+			.as_bytes();
+		let input = Cursor::new(request);
+		let output = Cursor::new(Vec::new());
+
+		let f = ReadWritePair(input, output)
+			.framed(HttpServerCodec)
+			.into_future()
+			.map_err(|(e, _)| e)
+			.and_then(|(m, s)| match m {
+			              Some(ref m) if m.subject.0 == Method::Get => Ok(s),
+			              _ => Err(io::Error::new(io::ErrorKind::Other, "test failed").into()),
+			          })
+			.and_then(|s| {
+				          s.send(Incoming {
+				                     version: HttpVersion::Http11,
+				                     subject: StatusCode::NotFound,
+				                     headers: Headers::new(),
+				                 })
+				           .map_err(|e| e.into())
+				         });
+		core.run(f).unwrap();
+	}
+}
diff --git a/src/codec/mod.rs b/src/codec/mod.rs
new file mode 100644
index 0000000000..ca6db82f99
--- /dev/null
+++ b/src/codec/mod.rs
@@ -0,0 +1,14 @@
+//! Useful `Codec` types for asynchronously encoding and decoding messages.
+//!
+//! This is intended to be used with the `Framed` type in the `tokio-io` crate.
+//! This module contains an `http` codec which can be used to send and receive
+//! hyper HTTP requests and responses asynchronously.
+//! See it's module level documentation for more info.
+//!
+//! Second but most importantly this module contains a codec for asynchronously
+//! encoding and decoding websocket messages (and dataframes if you want to go
+//! more low level) in the `ws` module.
+//! See it's module level documentation for more info.
+
+pub mod http;
+pub mod ws;
diff --git a/src/codec/ws.rs b/src/codec/ws.rs
new file mode 100644
index 0000000000..5124b42be2
--- /dev/null
+++ b/src/codec/ws.rs
@@ -0,0 +1,417 @@
+//! Send websocket messages and dataframes asynchronously.
+//!
+//! This module provides codecs that can be be used with `tokio` to create
+//! asynchronous streams that can serialize/deserialize websocket messages
+//! (and dataframes for users that want low level control).
+//!
+//! For websocket messages, see the documentation for `MessageCodec`, for
+//! dataframes see the documentation for `DataFrameCodec`
+
+use std::borrow::Borrow;
+use std::marker::PhantomData;
+use std::io::Cursor;
+use std::mem;
+
+use tokio_io::codec::Decoder;
+use tokio_io::codec::Encoder;
+use bytes::BytesMut;
+use bytes::BufMut;
+
+use dataframe::DataFrame;
+use message::OwnedMessage;
+use ws::dataframe::DataFrame as DataFrameTrait;
+use ws::message::Message as MessageTrait;
+use ws::util::header::read_header;
+use result::WebSocketError;
+
+// TODO: IMPORTANT: check if frame_size is correct,
+// do not call .reserve with the entire size
+
+/// Even though a websocket connection may look perfectly symmetrical
+/// in reality there are small differences between clients and servers.
+/// This type is passed to the codecs to inform them of what role they are in
+/// (i.e. that of a Client or Server).
+///
+/// For those familiar with the protocol, this decides wether the data should be
+/// masked or not.
+#[derive(Clone,PartialEq,Eq,Debug)]
+pub enum Context {
+	/// Set the codec to act in `Server` mode, used when
+	/// implementing a websocket server.
+	Server,
+	/// Set the codec to act in `Client` mode, used when
+	/// implementing a websocket client.
+	Client,
+}
+
+/**************
+ * Dataframes *
+ **************/
+
+/// A codec for deconding and encoding websocket dataframes.
+///
+/// This codec decodes dataframes into the crates default implementation
+/// of `Dataframe` but can encode and send any struct that implements the
+/// `ws::Dataframe` trait. The type of struct to encode is given by the `D`
+/// type parameter in the struct.
+///
+/// Using dataframes directly is meant for users who want low-level access to the
+/// connection. If you don't want to do anything low-level please use the
+/// `MessageCodec` codec instead, or better yet use the `ClientBuilder` to make
+/// clients and the `Server` to make servers.
+pub struct DataFrameCodec<D> {
+	is_server: bool,
+	frame_type: PhantomData<D>,
+}
+
+impl DataFrameCodec<DataFrame> {
+	/// Create a new `DataFrameCodec` struct using the crate's implementation
+	/// of dataframes for reading and writing dataframes.
+	///
+	/// Use this method if you don't want to provide a custom implementation
+	/// for your dataframes.
+	pub fn default(context: Context) -> Self {
+		DataFrameCodec::new(context)
+	}
+}
+
+impl<D> DataFrameCodec<D> {
+	/// Create a new `DataFrameCodec` struct using any implementation of
+	/// `ws::Dataframe` you want. This is useful if you want to manipulate
+	/// the websocket layer very specifically.
+	///
+	/// If you only want to be able to send and receive the crate's
+	/// `DataFrame` struct use `.default(Context)` instead.
+	pub fn new(context: Context) -> DataFrameCodec<D> {
+		DataFrameCodec {
+			is_server: context == Context::Server,
+			frame_type: PhantomData,
+		}
+	}
+}
+
+impl<D> Decoder for DataFrameCodec<D> {
+	type Item = DataFrame;
+	type Error = WebSocketError;
+
+	// TODO: do not retry to read the header on each new data (keep a buffer)
+	fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
+		let (header, bytes_read) = {
+			// we'll make a fake reader and keep track of the bytes read
+			let mut reader = Cursor::new(src.as_ref());
+
+			// read header to get the size, bail if not enough
+			let header = match read_header(&mut reader) {
+				Ok(head) => head,
+				Err(WebSocketError::NoDataAvailable) => return Ok(None),
+				Err(e) => return Err(e),
+			};
+
+			(header, reader.position())
+		};
+
+		// check if we have enough bytes to continue
+		if header.len + bytes_read > src.len() as u64 {
+			return Ok(None);
+		}
+
+		// TODO: using usize is not the right thing here (can be larger)
+		let _ = src.split_to(bytes_read as usize);
+		let body = src.split_to(header.len as usize).to_vec();
+
+		// construct a dataframe
+		Ok(Some(DataFrame::read_dataframe_body(header, body, self.is_server)?))
+	}
+}
+
+impl<D> Encoder for DataFrameCodec<D>
+    where D: Borrow<DataFrameTrait>
+{
+	type Item = D;
+	type Error = WebSocketError;
+
+	fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
+		let masked = !self.is_server;
+		let frame_size = item.borrow().frame_size(masked);
+		if frame_size > dst.remaining_mut() {
+			dst.reserve(frame_size);
+		}
+		item.borrow().write_to(&mut dst.writer(), masked)
+	}
+}
+
+/************
+ * Messages *
+ ************/
+
+/// A codec for asynchronously decoding and encoding websocket messages.
+///
+/// This codec decodes messages into the `OwnedMessage` struct, so using this
+/// the user will receive messages as `OwnedMessage`s. However it can encode
+/// any type of message that implements the `ws::Message` trait (that type is
+/// decided by the `M` type parameter) like `OwnedMessage` and `Message`.
+///
+/// Warning: if you don't know what your doing or want a simple websocket connection
+/// please use the `ClientBuilder` or the `Server` structs. You should only use this
+/// after a websocket handshake has already been completed on the stream you are
+/// using.
+///
+///# Example
+///
+///```rust
+///# extern crate tokio_core;
+///# extern crate tokio_io;
+///# extern crate websocket;
+///# extern crate hyper;
+///# use std::io::{self, Cursor};
+///use websocket::async::{MessageCodec, MsgCodecCtx};
+///# use websocket::{Message, OwnedMessage};
+///# use websocket::ws::Message as MessageTrait;
+///# use websocket::stream::ReadWritePair;
+///# use websocket::async::futures::{Future, Sink, Stream};
+///# use tokio_core::net::TcpStream;
+///# use tokio_core::reactor::Core;
+///# use tokio_io::AsyncRead;
+///# use hyper::http::h1::Incoming;
+///# use hyper::version::HttpVersion;
+///# use hyper::header::Headers;
+///# use hyper::method::Method;
+///# use hyper::uri::RequestUri;
+///# use hyper::status::StatusCode;
+///# fn main() {
+///
+///let mut core = Core::new().unwrap();
+///let mut input = Vec::new();
+///Message::text("50 schmeckels").serialize(&mut input, false);
+///
+///let f = ReadWritePair(Cursor::new(input), Cursor::new(vec![]))
+///    .framed(MessageCodec::default(MsgCodecCtx::Client))
+///    .into_future()
+///    .map_err(|e| e.0)
+///    .map(|(m, _)| {
+///        assert_eq!(m, Some(OwnedMessage::Text("50 schmeckels".to_string())));
+///    });
+///
+///core.run(f).unwrap();
+///# }
+pub struct MessageCodec<M>
+	where M: MessageTrait
+{
+	buffer: Vec<DataFrame>,
+	dataframe_codec: DataFrameCodec<DataFrame>,
+	message_type: PhantomData<fn(M)>,
+}
+
+impl MessageCodec<OwnedMessage> {
+	/// Create a new `MessageCodec` with a role of `context` (either `Client`
+	/// or `Server`) to read and write messages asynchronously.
+	///
+	/// This will create the crate's default codec which sends and receives
+	/// `OwnedMessage` structs. The message data has to be sent to an intermediate
+	/// buffer anyway so sending owned data is preferable.
+	///
+	/// If you have your own implementation of websocket messages, you can
+	/// use the `new` method to create a codec for that implementation.
+	pub fn default(context: Context) -> Self {
+		Self::new(context)
+	}
+}
+
+impl<M> MessageCodec<M>
+    where M: MessageTrait
+{
+	/// Creates a codec that can encode a custom implementation of a websocket
+	/// message.
+	///
+	/// If you just want to use a normal codec without a specific implementation
+	/// of a websocket message, take a look at `MessageCodec::default`.
+	pub fn new(context: Context) -> MessageCodec<M> {
+		MessageCodec {
+			buffer: Vec::new(),
+			dataframe_codec: DataFrameCodec::new(context),
+			message_type: PhantomData,
+		}
+	}
+}
+
+impl<M> Decoder for MessageCodec<M>
+    where M: MessageTrait
+{
+	type Item = OwnedMessage;
+	type Error = WebSocketError;
+
+	fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
+		while let Some(frame) = self.dataframe_codec.decode(src)? {
+			let is_first = self.buffer.is_empty();
+			let finished = frame.finished;
+
+			match frame.opcode as u8 {
+				// continuation code
+				0 if is_first => {
+					return Err(WebSocketError::ProtocolError("Unexpected continuation data frame opcode"));
+				}
+				// control frame
+				8...15 => {
+					return Ok(Some(OwnedMessage::from_dataframes(vec![frame])?));
+				}
+				// data frame
+				1...7 if !is_first => {
+					return Err(WebSocketError::ProtocolError("Unexpected data frame opcode"));
+				}
+				// its good
+				_ => {
+					self.buffer.push(frame);
+				}
+			};
+
+			if finished {
+				let buffer = mem::replace(&mut self.buffer, Vec::new());
+				return Ok(Some(OwnedMessage::from_dataframes(buffer)?));
+			}
+		}
+
+		Ok(None)
+	}
+}
+
+impl<M> Encoder for MessageCodec<M>
+    where M: MessageTrait
+{
+	type Item = M;
+	type Error = WebSocketError;
+
+	fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
+		let masked = !self.dataframe_codec.is_server;
+		let frame_size = item.message_size(masked);
+		if frame_size > dst.remaining_mut() {
+			dst.reserve(frame_size);
+		}
+		item.serialize(&mut dst.writer(), masked)
+	}
+}
+
+// TODO: add tests to check boundary cases for reading dataframes
+
+#[cfg(test)]
+mod tests {
+	use super::*;
+	use tokio_io::AsyncRead;
+	use tokio_core::reactor::Core;
+	use futures::{Stream, Sink, Future};
+	use std::io::Cursor;
+	use stream::ReadWritePair;
+	use message::CloseData;
+	use message::Message;
+
+	#[test]
+	fn owned_message_predicts_size() {
+		let messages = vec![
+			OwnedMessage::Text("nilbog".to_string()),
+			OwnedMessage::Binary(vec![1, 2, 3, 4]),
+			OwnedMessage::Binary(vec![42; 256]),
+			OwnedMessage::Binary(vec![42; 65535]),
+			OwnedMessage::Binary(vec![42; 65555]),
+			OwnedMessage::Ping("beep".to_string().into_bytes()),
+			OwnedMessage::Pong("boop".to_string().into_bytes()),
+			OwnedMessage::Close(None),
+			OwnedMessage::Close(Some(CloseData {
+			                             status_code: 64,
+			                             reason: "because".to_string(),
+			                         })),
+		];
+
+		for message in messages.into_iter() {
+			let masked_predicted = message.message_size(true);
+			let mut masked_buf = Vec::new();
+			message.serialize(&mut masked_buf, true).unwrap();
+			assert_eq!(masked_buf.len(), masked_predicted);
+
+			let unmasked_predicted = message.message_size(false);
+			let mut unmasked_buf = Vec::new();
+			message.serialize(&mut unmasked_buf, false).unwrap();
+			assert_eq!(unmasked_buf.len(), unmasked_predicted);
+		}
+	}
+
+	#[test]
+	fn cow_message_predicts_size() {
+		let messages = vec![
+			Message::binary(vec![1, 2, 3, 4]),
+			Message::binary(vec![42; 256]),
+			Message::binary(vec![42; 65535]),
+			Message::binary(vec![42; 65555]),
+			Message::text("nilbog".to_string()),
+			Message::ping("beep".to_string().into_bytes()),
+			Message::pong("boop".to_string().into_bytes()),
+			Message::close(),
+			Message::close_because(64, "because"),
+		];
+
+		for message in messages.iter() {
+			let masked_predicted = message.message_size(true);
+			let mut masked_buf = Vec::new();
+			message.serialize(&mut masked_buf, true).unwrap();
+			assert_eq!(masked_buf.len(), masked_predicted);
+
+			let unmasked_predicted = message.message_size(false);
+			let mut unmasked_buf = Vec::new();
+			message.serialize(&mut unmasked_buf, false).unwrap();
+			assert_eq!(unmasked_buf.len(), unmasked_predicted);
+		}
+	}
+
+	#[test]
+	fn message_codec_client_send_receive() {
+		let mut core = Core::new().unwrap();
+		let mut input = Vec::new();
+		Message::text("50 schmeckels").serialize(&mut input, false).unwrap();
+
+		let f = ReadWritePair(Cursor::new(input), Cursor::new(vec![]))
+			.framed(MessageCodec::new(Context::Client))
+			.into_future()
+			.map_err(|e| e.0)
+			.map(|(m, s)| {
+				     assert_eq!(m, Some(OwnedMessage::Text("50 schmeckels".to_string())));
+				     s
+				    })
+			.and_then(|s| s.send(Message::text("ethan bradberry")))
+			.and_then(|s| {
+				let mut stream = s.into_parts().inner;
+				stream.1.set_position(0);
+				println!("buffer: {:?}", stream.1);
+				ReadWritePair(stream.1, stream.0)
+					.framed(MessageCodec::default(Context::Server))
+					.into_future()
+					.map_err(|e| e.0)
+					.map(|(message, _)| {
+						     assert_eq!(message, Some(Message::text("ethan bradberry").into()))
+						    })
+			});
+
+		core.run(f).unwrap();
+	}
+
+	#[test]
+	fn message_codec_server_send_receive() {
+		let mut core = Core::new().unwrap();
+		let mut input = Vec::new();
+		Message::text("50 schmeckels").serialize(&mut input, true).unwrap();
+
+		let f = ReadWritePair(Cursor::new(input.as_slice()), Cursor::new(vec![]))
+			.framed(MessageCodec::new(Context::Server))
+			.into_future()
+			.map_err(|e| e.0)
+			.map(|(m, s)| {
+				     assert_eq!(m, Some(OwnedMessage::Text("50 schmeckels".to_string())));
+				     s
+				    })
+			.and_then(|s| s.send(Message::text("ethan bradberry")))
+			.map(|s| {
+				     let mut written = vec![];
+				     Message::text("ethan bradberry").serialize(&mut written, false).unwrap();
+				     assert_eq!(written, s.into_parts().inner.1.into_inner());
+				    });
+
+		core.run(f).unwrap();
+	}
+}
diff --git a/src/dataframe.rs b/src/dataframe.rs
index d7e32a4c73..9def50bcaa 100644
--- a/src/dataframe.rs
+++ b/src/dataframe.rs
@@ -1,8 +1,8 @@
 //! Module containing the default implementation of data frames.
-use std::io::Read;
-use std::borrow::Cow;
+use std::io::{Read, Write};
 use result::{WebSocketResult, WebSocketError};
 use ws::dataframe::DataFrame as DataFrameable;
+use ws::util::header::DataFrameHeader;
 use ws::util::header as dfh;
 use ws::util::mask;
 
@@ -37,40 +37,60 @@ impl DataFrame {
 		}
 	}
 
-	/// Reads a DataFrame from a Reader.
-	pub fn read_dataframe<R>(reader: &mut R, should_be_masked: bool) -> WebSocketResult<Self>
-		where R: Read
-	{
-		let header = try!(dfh::read_header(reader));
+	/// Take the body and header of a dataframe and combine it into a single
+	/// Dataframe struct. A websocket message can be made up of many individual
+	/// dataframes, use the methods from the Message or OwnedMessage structs to
+	/// take many of these and create a websocket message.
+	pub fn read_dataframe_body(
+		header: DataFrameHeader,
+		body: Vec<u8>,
+		should_be_masked: bool,
+	) -> WebSocketResult<Self> {
+		let finished = header.flags.contains(dfh::FIN);
 
-		Ok(DataFrame {
-		       finished: header.flags.contains(dfh::FIN),
-		       reserved: [
+		let reserved = [
 			header.flags.contains(dfh::RSV1),
 			header.flags.contains(dfh::RSV2),
 			header.flags.contains(dfh::RSV3),
-		],
-		       opcode: Opcode::new(header.opcode).expect("Invalid header opcode!"),
-		       data: match header.mask {
-		           Some(mask) => {
-			if !should_be_masked {
-				return Err(WebSocketError::DataFrameError("Expected unmasked data frame"));
+		];
+
+		let opcode = Opcode::new(header.opcode).expect("Invalid header opcode!");
+
+		let data = match header.mask {
+			Some(mask) => {
+				if !should_be_masked {
+					return Err(WebSocketError::DataFrameError("Expected unmasked data frame"));
+				}
+				// TODO: move data to avoid copy?
+				mask::mask_data(mask, &body)
 			}
-			let mut data: Vec<u8> = Vec::with_capacity(header.len as usize);
-			try!(reader.take(header.len).read_to_end(&mut data));
-			mask::mask_data(mask, &data)
-		}
-		           None => {
-			if should_be_masked {
-				return Err(WebSocketError::DataFrameError("Expected masked data frame"));
+			None => {
+				if should_be_masked {
+					return Err(WebSocketError::DataFrameError("Expected masked data frame"));
+				}
+				body
 			}
-			let mut data: Vec<u8> = Vec::with_capacity(header.len as usize);
-			try!(reader.take(header.len).read_to_end(&mut data));
-			data
-		}
-		       },
+		};
+
+		Ok(DataFrame {
+		       finished: finished,
+		       reserved: reserved,
+		       opcode: opcode,
+		       data: data,
 		   })
 	}
+
+	/// Reads a DataFrame from a Reader.
+	pub fn read_dataframe<R>(reader: &mut R, should_be_masked: bool) -> WebSocketResult<Self>
+		where R: Read
+	{
+		let header = try!(dfh::read_header(reader));
+
+		let mut data: Vec<u8> = Vec::with_capacity(header.len as usize);
+		reader.take(header.len).read_to_end(&mut data)?;
+
+		DataFrame::read_dataframe_body(header, data, should_be_masked)
+	}
 }
 
 impl DataFrameable for DataFrame {
@@ -90,8 +110,19 @@ impl DataFrameable for DataFrame {
 	}
 
 	#[inline(always)]
-	fn payload(&self) -> Cow<[u8]> {
-		Cow::Borrowed(&self.data)
+	fn size(&self) -> usize {
+		self.data.len()
+	}
+
+	#[inline(always)]
+	fn write_payload(&self, socket: &mut Write) -> WebSocketResult<()> {
+		socket.write_all(self.data.as_slice())?;
+		Ok(())
+	}
+
+	#[inline(always)]
+	fn take_payload(self) -> Vec<u8> {
+		self.data
 	}
 }
 
diff --git a/src/lib.rs b/src/lib.rs
index cbea7fb91d..64b7685410 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,33 +5,25 @@
 
 //! Rust-WebSocket is a WebSocket (RFC6455) library written in Rust.
 //!
-//! # Clients
-//! WebSocket clients make use of the `Client` object, which features two connection
-//! functions: `connect()` and `connect_ssl_context()`. See the `Client` struct
-//! documentation for more information. These both return a client-side `Request`
-//! object which is sent to the server with the `send()` method. The `Request` can
-//! be altered, typically using `Request.headers.set()` to add additional headers
-//! or change existing ones before calling `send()`.
+//! # Synchronous and Asynchronous
+//! This crate has both async and sync implementations of websockets, you are free to
+//! choose which one you would like to use by switching on the `async` or `sync` features
+//! for this crate. By default both are switched on since they do not conflict with each
+//! other.
+//!
+//! You'll find many modules with `::sync` and `::async` submodules that separate these
+//! behaviours. Since it get's tedious to add these on when appropriate a top-level
+//! convenience module called `websocket::sync` and `websocket::async` has been added that
+//! groups all the sync and async stuff, respectively.
 //!
-//! Calling `send()` on a `Request` will obtain a `Response`, which can be checked
-//! with the `validate()` method, which will return `Ok(())` if the response is a
-//! valid one. Data frames and messages can then be sent by obtaining a `Client`
-//! object with `begin()`.
+//! # Clients
+//! To make a client use the `ClientBuilder` struct, this builder has methods
+//! for creating both synchronous and asynchronous clients.
 //!
 //! # Servers
 //! WebSocket servers act similarly to the `TcpListener`, and listen for connections.
 //! See the `Server` struct documentation for more information. The `bind()` and
 //! `bind_secure()` functions will bind the server to the given `SocketAddr`.
-//! `Server` implements Iterator and can be used to iterate over incoming `Request`
-//! items.
-//!
-//! Requests can be validated using `validate()`, and other parts of the request may
-//! be examined (e.g. the Host header and/or the Origin header). A call to `accept()`
-//! or `fail()` will generate a `Response` which either accepts the connection, or
-//! denies it respectively.
-//!
-//! A `Response` can then be altered if necessary, and is sent with the 'send()`
-//! method, returning a `Client` ready to send and receive data frames or messages.
 //!
 //! # Extending Rust-WebSocket
 //! The `ws` module contains the traits and functions used by Rust-WebSockt at a lower
@@ -42,9 +34,19 @@ pub extern crate url;
 extern crate rand;
 extern crate byteorder;
 extern crate sha1;
-#[cfg(feature="ssl")]
-extern crate openssl;
 extern crate base64;
+#[cfg(any(feature="sync-ssl", feature="async-ssl"))]
+extern crate native_tls;
+#[cfg(feature="async")]
+extern crate tokio_core;
+#[cfg(feature="async")]
+extern crate tokio_io;
+#[cfg(feature="async")]
+extern crate bytes;
+#[cfg(feature="async")]
+pub extern crate futures;
+#[cfg(feature="async-ssl")]
+extern crate tokio_tls;
 
 #[macro_use]
 extern crate bitflags;
@@ -52,14 +54,6 @@ extern crate bitflags;
 #[cfg(all(feature = "nightly", test))]
 extern crate test;
 
-pub use self::client::{Client, ClientBuilder};
-pub use self::server::Server;
-pub use self::dataframe::DataFrame;
-pub use self::message::Message;
-pub use self::stream::Stream;
-pub use self::ws::Sender;
-pub use self::ws::Receiver;
-
 macro_rules! upsert_header {
     ($headers:expr; $header:ty; {
         Some($pat:pat) => $some_match:expr,
@@ -75,14 +69,94 @@ macro_rules! upsert_header {
     }}
 }
 
-
 pub mod ws;
-pub mod client;
-pub mod server;
 pub mod dataframe;
 pub mod message;
 pub mod result;
-pub mod stream;
 pub mod header;
+
+#[cfg(feature="async")]
+pub mod codec;
+
+#[cfg(feature="sync")]
 pub mod receiver;
+#[cfg(feature="sync")]
 pub mod sender;
+
+pub mod client;
+pub mod server;
+pub mod stream;
+
+/// A collection of handy synchronous-only parts of the crate.
+#[cfg(feature="sync")]
+pub mod sync {
+	pub use sender;
+	pub use sender::Writer;
+
+	pub use receiver;
+	pub use receiver::Reader;
+
+	pub use stream::sync::Stream;
+	pub use stream::sync as stream;
+
+	/// A collection of handy synchronous-only parts of the `server` module.
+	pub mod server {
+		pub use server::sync::*;
+		pub use server::upgrade::sync::Upgrade;
+		pub use server::upgrade::sync::IntoWs;
+		pub use server::upgrade::sync as upgrade;
+	}
+	pub use server::sync::Server;
+
+	/// A collection of handy synchronous-only parts of the `client` module.
+	pub mod client {
+		pub use client::sync::*;
+		pub use client::builder::ClientBuilder;
+	}
+	pub use client::sync::Client;
+}
+
+/// A collection of handy asynchronous-only parts of the crate.
+#[cfg(feature="async")]
+pub mod async {
+	pub use codec;
+	pub use codec::ws::MessageCodec;
+	pub use codec::ws::Context as MsgCodecCtx;
+	pub use codec::http::HttpClientCodec;
+	pub use codec::http::HttpServerCodec;
+
+	pub use stream::async::Stream;
+	pub use stream::async as stream;
+
+	/// A collection of handy asynchronous-only parts of the `server` module.
+	pub mod server {
+		pub use server::async::*;
+		pub use server::upgrade::async::Upgrade;
+		pub use server::upgrade::async::IntoWs;
+		pub use server::upgrade::async as upgrade;
+	}
+	pub use server::async::Server;
+
+	/// A collection of handy asynchronous-only parts of the `client` module.
+	pub mod client {
+		pub use client::async::*;
+		pub use client::builder::ClientBuilder;
+	}
+	pub use client::async::Client;
+
+	pub use result::async::WebSocketFuture;
+
+	pub use futures;
+	pub use tokio_core::net::TcpStream;
+	pub use tokio_core::net::TcpListener;
+	pub use tokio_core::reactor::Core;
+	pub use tokio_core::reactor::Handle;
+}
+
+pub use self::message::Message;
+pub use self::message::CloseData;
+pub use self::message::OwnedMessage;
+pub use self::client::builder::ClientBuilder;
+
+pub use self::result::WebSocketError;
+pub use self::result::WebSocketResult;
diff --git a/src/message.rs b/src/message.rs
index 58276dea5b..d9b6b1dadd 100644
--- a/src/message.rs
+++ b/src/message.rs
@@ -1,9 +1,11 @@
 //! Module containing the default implementation for messages.
+use std::str::from_utf8;
+use std::io;
 use std::io::Write;
 use std::borrow::Cow;
-use std::iter::{Take, Repeat, repeat};
 use result::{WebSocketResult, WebSocketError};
 use dataframe::Opcode;
+use ws::dataframe::DataFrame as DataFrameTrait;
 use byteorder::{WriteBytesExt, ReadBytesExt, BigEndian};
 use ws::util::bytes_to_string;
 use ws;
@@ -11,7 +13,7 @@ use ws;
 const FALSE_RESERVED_BITS: &'static [bool; 3] = &[false; 3];
 
 /// Valid types of messages (in the default implementation)
-#[derive(Debug, PartialEq, Clone, Copy)]
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
 pub enum Type {
 	/// Message with UTF8 test
 	Text = 1,
@@ -33,7 +35,7 @@ pub enum Type {
 ///
 /// Incidentally this (the default implementation of Message) implements the DataFrame trait
 /// because this message just gets sent as one single DataFrame.
-#[derive(PartialEq, Clone, Debug)]
+#[derive(Eq, PartialEq, Clone, Debug)]
 pub struct Message<'a> {
 	/// Type of WebSocket message
 	pub opcode: Type,
@@ -136,70 +138,367 @@ impl<'a> ws::dataframe::DataFrame for Message<'a> {
 		FALSE_RESERVED_BITS
 	}
 
-	fn payload(&self) -> Cow<[u8]> {
-		let mut buf = Vec::with_capacity(self.size());
-		self.write_payload(&mut buf).ok();
-		Cow::Owned(buf)
-	}
-
 	fn size(&self) -> usize {
 		self.payload.len() + if self.cd_status_code.is_some() { 2 } else { 0 }
 	}
 
-	fn write_payload<W>(&self, socket: &mut W) -> WebSocketResult<()>
-		where W: Write
-	{
+	fn write_payload(&self, socket: &mut Write) -> WebSocketResult<()> {
 		if let Some(reason) = self.cd_status_code {
 			try!(socket.write_u16::<BigEndian>(reason));
 		}
 		try!(socket.write_all(&*self.payload));
 		Ok(())
 	}
+
+	fn take_payload(self) -> Vec<u8> {
+		if let Some(reason) = self.cd_status_code {
+			let mut buf = Vec::with_capacity(2 + self.payload.len());
+			buf.write_u16::<BigEndian>(reason)
+			   .expect("failed to write close code in take_payload");
+			buf.append(&mut self.payload.into_owned());
+			buf
+		} else {
+			self.payload.into_owned()
+		}
+	}
 }
 
-impl<'a, 'b> ws::Message<'b, &'b Message<'a>> for Message<'a> {
-	type DataFrameIterator = Take<Repeat<&'b Message<'a>>>;
+impl<'a> ws::Message for Message<'a> {
+	/// Attempt to form a message from a series of data frames
+	fn serialize(&self, writer: &mut Write, masked: bool) -> WebSocketResult<()> {
+		self.write_to(writer, masked)
+	}
 
-	fn dataframes(&'b self) -> Self::DataFrameIterator {
-		repeat(self).take(1)
+	/// Returns how many bytes this message will take up
+	fn message_size(&self, masked: bool) -> usize {
+		self.frame_size(masked)
 	}
 
 	/// Attempt to form a message from a series of data frames
 	fn from_dataframes<D>(frames: Vec<D>) -> WebSocketResult<Self>
-		where D: ws::dataframe::DataFrame
+		where D: DataFrameTrait
 	{
 		let opcode = try!(frames.first()
 		                        .ok_or(WebSocketError::ProtocolError("No dataframes provided"))
 		                        .map(|d| d.opcode()));
+		let opcode = Opcode::new(opcode);
 
-		let mut data = Vec::new();
+		let payload_size = frames.iter().map(|d| d.size()).sum();
 
-		for (i, dataframe) in frames.iter().enumerate() {
+		let mut data = Vec::with_capacity(payload_size);
+
+		for (i, dataframe) in frames.into_iter().enumerate() {
 			if i > 0 && dataframe.opcode() != Opcode::Continuation as u8 {
 				return Err(WebSocketError::ProtocolError("Unexpected non-continuation data frame"));
 			}
 			if *dataframe.reserved() != [false; 3] {
 				return Err(WebSocketError::ProtocolError("Unsupported reserved bits received"));
 			}
-			data.extend(dataframe.payload().iter().cloned());
-		}
-
-		Ok(match Opcode::new(opcode) {
-		       Some(Opcode::Text) => Message::text(try!(bytes_to_string(&data[..]))),
-		       Some(Opcode::Binary) => Message::binary(data),
-		       Some(Opcode::Close) => {
-			       if !data.is_empty() {
-			           let status_code = try!((&data[..]).read_u16::<BigEndian>());
-			           let reason = try!(bytes_to_string(&data[2..]));
-			           Message::close_because(status_code, reason)
-			          } else {
-			           Message::close()
-			          }
-		       }
-		       Some(Opcode::Ping) => Message::ping(data),
-		       Some(Opcode::Pong) => Message::pong(data),
-		       _ => return Err(WebSocketError::ProtocolError("Unsupported opcode received")),
-		   })
+			data.append(&mut dataframe.take_payload());
+		}
+
+		if opcode == Some(Opcode::Text) {
+			if let Err(e) = from_utf8(data.as_slice()) {
+				return Err(e.into());
+			}
+		}
+
+		let msg = match opcode {
+			Some(Opcode::Text) => {
+				Message {
+					opcode: Type::Text,
+					cd_status_code: None,
+					payload: Cow::Owned(data),
+				}
+			}
+			Some(Opcode::Binary) => Message::binary(data),
+			Some(Opcode::Close) => {
+				if data.len() > 0 {
+					let status_code = try!((&data[..]).read_u16::<BigEndian>());
+					let reason = try!(bytes_to_string(&data[2..]));
+					Message::close_because(status_code, reason)
+				} else {
+					Message::close()
+				}
+			}
+			Some(Opcode::Ping) => Message::ping(data),
+			Some(Opcode::Pong) => Message::pong(data),
+			_ => return Err(WebSocketError::ProtocolError("Unsupported opcode received")),
+		};
+		Ok(msg)
+	}
+}
+
+/// Represents an owned WebSocket message.
+///
+/// `OwnedMessage`s are generated when the user receives a message (since the data
+/// has to be copied out of the network buffer anyway).
+/// If you would like to create a message out of borrowed data to use for sending
+/// please use the `Message` struct (which contains a `Cow`).
+///
+/// Note that `OwnedMessage` can `Message` can be converted into each other.
+#[derive(Eq, PartialEq, Clone, Debug)]
+pub enum OwnedMessage {
+	/// A message containing UTF-8 text data
+	Text(String),
+	/// A message containing binary data
+	Binary(Vec<u8>),
+	/// A message which indicates closure of the WebSocket connection.
+	/// This message may or may not contain data.
+	Close(Option<CloseData>),
+	/// A ping message - should be responded to with a pong message.
+	/// Usually the pong message will be sent with the same data as the
+	/// received ping message.
+	Ping(Vec<u8>),
+	/// A pong message, sent in response to a Ping message, usually
+	/// containing the same data as the received ping message.
+	Pong(Vec<u8>),
+}
+
+impl OwnedMessage {
+	/// Checks if this message is a close message.
+	///
+	///```rust
+	///# use websocket::OwnedMessage;
+	///assert!(OwnedMessage::Close(None).is_close());
+	///```
+	pub fn is_close(&self) -> bool {
+		match *self {
+			OwnedMessage::Close(_) => true,
+			_ => false,
+		}
+	}
+
+	/// Checks if this message is a control message.
+	/// Control messages are either `Close`, `Ping`, or `Pong`.
+	///
+	///```rust
+	///# use websocket::OwnedMessage;
+	///assert!(OwnedMessage::Ping(vec![]).is_control());
+	///assert!(OwnedMessage::Pong(vec![]).is_control());
+	///assert!(OwnedMessage::Close(None).is_control());
+	///```
+	pub fn is_control(&self) -> bool {
+		match *self {
+			OwnedMessage::Close(_) => true,
+			OwnedMessage::Ping(_) => true,
+			OwnedMessage::Pong(_) => true,
+			_ => false,
+		}
+	}
+
+	/// Checks if this message is a data message.
+	/// Data messages are either `Text` or `Binary`.
+	///
+	///```rust
+	///# use websocket::OwnedMessage;
+	///assert!(OwnedMessage::Text("1337".to_string()).is_data());
+	///assert!(OwnedMessage::Binary(vec![]).is_data());
+	///```
+	pub fn is_data(&self) -> bool {
+		!self.is_control()
+	}
+
+	/// Checks if this message is a ping message.
+	/// `Ping` messages can come at any time and usually generate a `Pong` message
+	/// response.
+	///
+	///```rust
+	///# use websocket::OwnedMessage;
+	///assert!(OwnedMessage::Ping("ping".to_string().into_bytes()).is_ping());
+	///```
+	pub fn is_ping(&self) -> bool {
+		match *self {
+			OwnedMessage::Ping(_) => true,
+			_ => false,
+		}
+	}
+
+	/// Checks if this message is a pong message.
+	/// `Pong` messages are usually sent only in response to `Ping` messages.
+	///
+	///```rust
+	///# use websocket::OwnedMessage;
+	///assert!(OwnedMessage::Pong("pong".to_string().into_bytes()).is_pong());
+	///```
+	pub fn is_pong(&self) -> bool {
+		match *self {
+			OwnedMessage::Pong(_) => true,
+			_ => false,
+		}
+	}
+}
+
+impl ws::Message for OwnedMessage {
+	/// Attempt to form a message from a series of data frames
+	fn serialize(&self, writer: &mut Write, masked: bool) -> WebSocketResult<()> {
+		self.write_to(writer, masked)
+	}
+
+	/// Returns how many bytes this message will take up
+	fn message_size(&self, masked: bool) -> usize {
+		self.frame_size(masked)
+	}
+
+	/// Attempt to form a message from a series of data frames
+	fn from_dataframes<D>(frames: Vec<D>) -> WebSocketResult<Self>
+		where D: DataFrameTrait
+	{
+		Ok(Message::from_dataframes(frames)?.into())
+	}
+}
+
+impl ws::dataframe::DataFrame for OwnedMessage {
+	#[inline(always)]
+	fn is_last(&self) -> bool {
+		true
+	}
+
+	#[inline(always)]
+	fn opcode(&self) -> u8 {
+		(match *self {
+		     OwnedMessage::Text(_) => Type::Text,
+		     OwnedMessage::Binary(_) => Type::Binary,
+		     OwnedMessage::Close(_) => Type::Close,
+		     OwnedMessage::Ping(_) => Type::Ping,
+		     OwnedMessage::Pong(_) => Type::Pong,
+		 }) as u8
+	}
+
+	#[inline(always)]
+	fn reserved(&self) -> &[bool; 3] {
+		FALSE_RESERVED_BITS
+	}
+
+	fn size(&self) -> usize {
+		match *self {
+			OwnedMessage::Text(ref txt) => txt.len(),
+			OwnedMessage::Binary(ref bin) => bin.len(),
+			OwnedMessage::Ping(ref data) => data.len(),
+			OwnedMessage::Pong(ref data) => data.len(),
+			OwnedMessage::Close(ref data) => {
+				match data {
+					&Some(ref c) => c.reason.len() + 2,
+					&None => 0,
+				}
+			}
+		}
+	}
+
+	fn write_payload(&self, socket: &mut Write) -> WebSocketResult<()> {
+		match *self {
+			OwnedMessage::Text(ref txt) => {
+				socket.write_all(txt.as_bytes())?
+			}
+			OwnedMessage::Binary(ref bin) => {
+				socket.write_all(bin.as_slice())?
+			}
+			OwnedMessage::Ping(ref data) => {
+				socket.write_all(data.as_slice())?
+			}
+			OwnedMessage::Pong(ref data) => {
+				socket.write_all(data.as_slice())?
+			}
+			OwnedMessage::Close(ref data) => {
+				match data {
+					&Some(ref c) => {
+						socket.write_u16::<BigEndian>(c.status_code)?;
+						socket.write_all(c.reason.as_bytes())?
+					}
+					&None => (),
+				}
+			}
+		};
+		Ok(())
+	}
+
+	fn take_payload(self) -> Vec<u8> {
+		match self {
+			OwnedMessage::Text(txt) => txt.into_bytes(),
+			OwnedMessage::Binary(bin) => bin,
+			OwnedMessage::Ping(data) => data,
+			OwnedMessage::Pong(data) => data,
+			OwnedMessage::Close(data) => {
+				match data {
+					Some(c) => {
+						let mut buf = Vec::with_capacity(2 + c.reason.len());
+						buf.write_u16::<BigEndian>(c.status_code)
+						   .expect("failed to write close code in take_payload");
+						buf.append(&mut c.reason.into_bytes());
+						buf
+					}
+					None => vec![],
+				}
+			}
+		}
+	}
+}
+
+impl<'m> From<Message<'m>> for OwnedMessage {
+	fn from(message: Message<'m>) -> Self {
+		match message.opcode {
+			Type::Text => {
+				let convert = String::from_utf8_lossy(&message.payload).into_owned();
+				OwnedMessage::Text(convert)
+			}
+			Type::Close => {
+				match message.cd_status_code {
+					Some(code) => OwnedMessage::Close(Some(CloseData {
+                status_code: code,
+                reason: String::from_utf8_lossy(&message.payload).into_owned(),
+            })),
+					None => OwnedMessage::Close(None),
+				}
+			}
+			Type::Binary => OwnedMessage::Binary(message.payload.into_owned()),
+			Type::Ping => OwnedMessage::Ping(message.payload.into_owned()),
+			Type::Pong => OwnedMessage::Pong(message.payload.into_owned()),
+		}
+	}
+}
+
+impl<'m> From<OwnedMessage> for Message<'m> {
+	fn from(message: OwnedMessage) -> Self {
+		match message {
+			OwnedMessage::Text(txt) => Message::text(txt),
+			OwnedMessage::Binary(bin) => Message::binary(bin),
+			OwnedMessage::Close(because) => {
+				match because {
+					Some(c) => Message::close_because(c.status_code, c.reason),
+					None => Message::close(),
+				}
+			}
+			OwnedMessage::Ping(data) => Message::ping(data),
+			OwnedMessage::Pong(data) => Message::pong(data),
+		}
+	}
+}
+
+/// Represents data contained in a Close message
+#[derive(Eq, PartialEq, Clone, Debug)]
+pub struct CloseData {
+	/// The status-code of the CloseData
+	pub status_code: u16,
+	/// The reason-phrase of the CloseData
+	pub reason: String,
+}
+
+impl CloseData {
+	/// Create a new CloseData object
+	pub fn new(status_code: u16, reason: String) -> CloseData {
+		CloseData {
+			status_code: status_code,
+			reason: reason,
+		}
+	}
+	/// Convert this into a vector of bytes
+	pub fn into_bytes(self) -> io::Result<Vec<u8>> {
+		let mut buf = Vec::new();
+		try!(buf.write_u16::<BigEndian>(self.status_code));
+		for i in self.reason.as_bytes().iter() {
+			buf.push(*i);
+		}
+		Ok(buf)
 	}
 }
 
diff --git a/src/receiver.rs b/src/receiver.rs
index 91aac7f381..dc75589c6b 100644
--- a/src/receiver.rs
+++ b/src/receiver.rs
@@ -8,11 +8,11 @@ use hyper::buffer::BufReader;
 use dataframe::{DataFrame, Opcode};
 use result::{WebSocketResult, WebSocketError};
 use ws;
-use ws::dataframe::DataFrame as DataFrameable;
 use ws::receiver::Receiver as ReceiverTrait;
 use ws::receiver::{MessageIterator, DataFrameIterator};
-use stream::{AsTcpStream, Stream};
-pub use stream::Shutdown;
+use message::OwnedMessage;
+use stream::sync::{AsTcpStream, Stream};
+pub use stream::sync::Shutdown;
 
 /// This reader bundles an existing stream with a parsing algorithm.
 /// It is used by the client in its `.split()` function as the reading component.
@@ -39,21 +39,15 @@ impl<R> Reader<R>
 	}
 
 	/// Reads a single message from this receiver.
-	pub fn recv_message<'m, M, D, I>(&mut self) -> WebSocketResult<M>
-		where M: ws::Message<'m, D, DataFrameIterator = I>,
-		      I: Iterator<Item = D>,
-		      D: DataFrameable
+	pub fn recv_message<I>(&mut self) -> WebSocketResult<OwnedMessage>
+		where I: Iterator<Item = DataFrame>
 	{
 		self.receiver.recv_message(&mut self.stream)
 	}
 
 	/// An iterator over incoming messsages.
 	/// This iterator will block until new messages arrive and will never halt.
-	pub fn incoming_messages<'a, M, D>(&'a mut self,)
-		-> MessageIterator<'a, Receiver, D, M, BufReader<R>>
-		where M: ws::Message<'a, D>,
-		      D: DataFrameable
-	{
+	pub fn incoming_messages<'a>(&'a mut self) -> MessageIterator<'a, Receiver, BufReader<R>> {
 		self.receiver.incoming_messages(&mut self.stream)
 	}
 }
@@ -95,6 +89,8 @@ impl Receiver {
 impl ws::Receiver for Receiver {
 	type F = DataFrame;
 
+	type M = OwnedMessage;
+
 	/// Reads a single data frame from the remote endpoint.
 	fn recv_dataframe<R>(&mut self, reader: &mut R) -> WebSocketResult<DataFrame>
 		where R: Read
@@ -136,6 +132,7 @@ impl ws::Receiver for Receiver {
 			}
 		}
 
+		// TODO: this is slow, easy fix
 		let buffer = self.buffer.clone();
 		self.buffer.clear();
 
diff --git a/src/result.rs b/src/result.rs
index f87b2ce5cc..723bb537d1 100644
--- a/src/result.rs
+++ b/src/result.rs
@@ -7,15 +7,28 @@ use std::convert::From;
 use std::fmt;
 use hyper::Error as HttpError;
 use url::ParseError;
+use server::upgrade::HyperIntoWsError;
 
-#[cfg(feature="ssl")]
-use openssl::error::ErrorStack as SslError;
-#[cfg(feature="ssl")]
-use openssl::ssl::HandshakeError as SslHandshakeError;
+#[cfg(any(feature="sync-ssl", feature="async-ssl"))]
+use native_tls::Error as TlsError;
+#[cfg(any(feature="sync-ssl", feature="async-ssl"))]
+use native_tls::HandshakeError as TlsHandshakeError;
 
 /// The type used for WebSocket results
 pub type WebSocketResult<T> = Result<T, WebSocketError>;
 
+/// This module contains convenience types to make working with Futures and
+/// websocket results easier.
+#[cfg(feature="async")]
+pub mod async {
+	use futures::Future;
+	use super::WebSocketError;
+
+	/// The most common Future in this library, it is simply some result `I` or
+	/// a `WebSocketError`. This is analogous to the `WebSocketResult` type.
+	pub type WebSocketFuture<I> = Box<Future<Item = I, Error = WebSocketError>>;
+}
+
 /// Represents a WebSocket error
 #[derive(Debug)]
 pub enum WebSocketError {
@@ -38,14 +51,14 @@ pub enum WebSocketError {
 	/// A WebSocket URL error
 	WebSocketUrlError(WSUrlErrorKind),
 	/// An SSL error
-	#[cfg(feature="ssl")]
-	SslError(SslError),
+	#[cfg(any(feature="sync-ssl", feature="async-ssl"))]
+	TlsError(TlsError),
 	/// an ssl handshake failure
-	#[cfg(feature="ssl")]
-	SslHandshakeFailure,
+	#[cfg(any(feature="sync-ssl", feature="async-ssl"))]
+	TlsHandshakeFailure,
 	/// an ssl handshake interruption
-	#[cfg(feature="ssl")]
-	SslHandshakeInterruption,
+	#[cfg(any(feature="sync-ssl", feature="async-ssl"))]
+	TlsHandshakeInterruption,
 	/// A UTF-8 error
 	Utf8Error(Utf8Error),
 }
@@ -69,12 +82,12 @@ 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(feature="ssl")]
-            WebSocketError::SslHandshakeFailure => "SSL Handshake failure",
-			#[cfg(feature="ssl")]
-            WebSocketError::SslHandshakeInterruption => "SSL Handshake interrupted",
+			#[cfg(any(feature="sync-ssl", feature="async-ssl"))]
+			      WebSocketError::TlsError(_) => "TLS failure",
+			#[cfg(any(feature="sync-ssl", feature="async-ssl"))]
+            WebSocketError::TlsHandshakeFailure => "TLS Handshake failure",
+			#[cfg(any(feature="sync-ssl", feature="async-ssl"))]
+            WebSocketError::TlsHandshakeInterruption => "TLS Handshake interrupted",
 			WebSocketError::Utf8Error(_) => "UTF-8 failure",
 			WebSocketError::WebSocketUrlError(_) => "WebSocket URL failure",
 		}
@@ -85,8 +98,8 @@ 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(any(feature="sync-ssl", feature="async-ssl"))]
+			      WebSocketError::TlsError(ref error) => Some(error),
 			WebSocketError::Utf8Error(ref error) => Some(error),
 			WebSocketError::WebSocketUrlError(ref error) => Some(error),
 			_ => None,
@@ -115,20 +128,19 @@ impl From<ParseError> for WebSocketError {
 	}
 }
 
-#[cfg(feature="ssl")]
-impl From<SslError> for WebSocketError {
-	fn from(err: SslError) -> WebSocketError {
-		WebSocketError::SslError(err)
+#[cfg(any(feature="sync-ssl", feature="async-ssl"))]
+impl From<TlsError> for WebSocketError {
+	fn from(err: TlsError) -> WebSocketError {
+		WebSocketError::TlsError(err)
 	}
 }
 
-#[cfg(feature="ssl")]
-impl<T> From<SslHandshakeError<T>> for WebSocketError {
-	fn from(err: SslHandshakeError<T>) -> WebSocketError {
+#[cfg(any(feature="sync-ssl", feature="async-ssl"))]
+impl<T> From<TlsHandshakeError<T>> for WebSocketError {
+	fn from(err: TlsHandshakeError<T>) -> WebSocketError {
 		match err {
-			SslHandshakeError::SetupFailure(err) => WebSocketError::SslError(err),
-			SslHandshakeError::Failure(_) => WebSocketError::SslHandshakeFailure,
-			SslHandshakeError::Interrupted(_) => WebSocketError::SslHandshakeInterruption,
+			TlsHandshakeError::Failure(_) => WebSocketError::TlsHandshakeFailure,
+			TlsHandshakeError::Interrupted(_) => WebSocketError::TlsHandshakeInterruption,
 		}
 	}
 }
@@ -139,12 +151,41 @@ impl From<Utf8Error> for WebSocketError {
 	}
 }
 
+#[cfg(feature="async")]
+impl From<::codec::http::HttpCodecError> for WebSocketError {
+	fn from(src: ::codec::http::HttpCodecError) -> Self {
+		match src {
+			::codec::http::HttpCodecError::Io(e) => WebSocketError::IoError(e),
+			::codec::http::HttpCodecError::Http(e) => WebSocketError::HttpError(e),
+		}
+	}
+}
+
 impl From<WSUrlErrorKind> for WebSocketError {
 	fn from(err: WSUrlErrorKind) -> WebSocketError {
 		WebSocketError::WebSocketUrlError(err)
 	}
 }
 
+impl From<HyperIntoWsError> for WebSocketError {
+	fn from(err: HyperIntoWsError) -> WebSocketError {
+		use self::HyperIntoWsError::*;
+		use WebSocketError::*;
+		match err {
+			Io(io) => IoError(io),
+			Parsing(err) => HttpError(err),
+			MethodNotGet => ProtocolError("Request method must be GET"),
+			UnsupportedHttpVersion => ProtocolError("Unsupported request HTTP version"),
+			UnsupportedWebsocketVersion => ProtocolError("Unsupported WebSocket version"),
+			NoSecWsKeyHeader => ProtocolError("Missing Sec-WebSocket-Key header"),
+			NoWsUpgradeHeader => ProtocolError("Invalid Upgrade WebSocket header"),
+			NoUpgradeHeader => ProtocolError("Missing Upgrade WebSocket header"),
+			NoWsConnectionHeader => ProtocolError("Invalid Connection WebSocket header"),
+			NoConnectionHeader => ProtocolError("Missing Connection WebSocket header"),
+		}
+	}
+}
+
 /// Represents a WebSocket URL error
 #[derive(Debug)]
 pub enum WSUrlErrorKind {
diff --git a/src/sender.rs b/src/sender.rs
index 398520f18d..3c6f514d55 100644
--- a/src/sender.rs
+++ b/src/sender.rs
@@ -4,10 +4,10 @@ use std::io::Write;
 use std::io::Result as IoResult;
 use result::WebSocketResult;
 use ws::dataframe::DataFrame;
-use stream::AsTcpStream;
+use stream::sync::AsTcpStream;
 use ws;
 use ws::sender::Sender as SenderTrait;
-pub use stream::Shutdown;
+pub use stream::sync::Shutdown;
 
 /// A writer that bundles a stream with a serializer to send the messages.
 /// This is used in the client's `.split()` function as the writing component.
@@ -32,9 +32,8 @@ impl<W> Writer<W>
 	}
 
 	/// Sends a single message to the remote endpoint.
-	pub fn send_message<'m, M, D>(&mut self, message: &'m M) -> WebSocketResult<()>
-		where M: ws::Message<'m, D>,
-		      D: DataFrame
+	pub fn send_message<M>(&mut self, message: &M) -> WebSocketResult<()>
+		where M: ws::Message
 	{
 		self.sender.send_message(&mut self.stream, message)
 	}
@@ -70,11 +69,7 @@ impl Sender {
 }
 
 impl ws::Sender for Sender {
-	/// Sends a single data frame to the remote endpoint.
-	fn send_dataframe<D, W>(&mut self, writer: &mut W, dataframe: &D) -> WebSocketResult<()>
-		where D: DataFrame,
-		      W: Write
-	{
-		dataframe.write_to(writer, self.mask)
+	fn is_masked(&self) -> bool {
+		self.mask
 	}
 }
diff --git a/src/server/async.rs b/src/server/async.rs
new file mode 100644
index 0000000000..832461b6b8
--- /dev/null
+++ b/src/server/async.rs
@@ -0,0 +1,150 @@
+//! The asynchronous implementation of a websocket server.
+use std::io;
+use std::net::ToSocketAddrs;
+use std::net::SocketAddr;
+use server::{WsServer, NoTlsAcceptor};
+use tokio_core::net::{TcpListener, TcpStream};
+use futures::{Stream, Future};
+use server::upgrade::async::{IntoWs, Upgrade};
+use server::InvalidConnection;
+use bytes::BytesMut;
+pub use tokio_core::reactor::Handle;
+
+#[cfg(any(feature="async-ssl"))]
+use native_tls::TlsAcceptor;
+#[cfg(any(feature="async-ssl"))]
+use tokio_tls::{TlsAcceptorExt, TlsStream};
+
+/// The asynchronous specialization of a websocket server.
+/// Use this struct to create asynchronous servers.
+pub type Server<S> = WsServer<S, TcpListener>;
+
+/// A stream of websocket connections and addresses the server generates.
+///
+/// Each item of the stream is the address of the incoming connection and an `Upgrade`
+/// struct which lets the user decide wether to turn the connection into a websocket
+/// connection or reject it.
+pub type Incoming<S> = Box<Stream<Item = (Upgrade<S>, SocketAddr),
+                                  Error = InvalidConnection<S, BytesMut>>>;
+
+/// Asynchronous methods for creating an async server and accepting incoming connections.
+impl WsServer<NoTlsAcceptor, TcpListener> {
+	/// Bind a websocket server to an address.
+	/// Creating a websocket server can be done immediately so this does not
+	/// return a `Future` but a simple `Result`.
+	pub fn bind<A: ToSocketAddrs>(addr: A, handle: &Handle) -> io::Result<Self> {
+		let tcp = ::std::net::TcpListener::bind(addr)?;
+		let address = tcp.local_addr()?;
+		Ok(Server {
+		       listener: TcpListener::from_listener(tcp, &address, handle)?,
+		       ssl_acceptor: NoTlsAcceptor,
+		   })
+	}
+
+	/// Turns the server into a stream of connection objects.
+	///
+	/// Each item of the stream is the address of the incoming connection and an `Upgrade`
+	/// struct which lets the user decide wether to turn the connection into a websocket
+	/// connection or reject it.
+	///
+	/// See the [`examples/async-server.rs`]
+	/// (https://github.com/cyderize/rust-websocket/blob/master/examples/async-server.rs)
+	/// example for a good echo server example.
+	pub fn incoming(self) -> Incoming<TcpStream> {
+		let future = self.listener
+		                 .incoming()
+		                 .map_err(|e| {
+			                          InvalidConnection {
+			                              stream: None,
+			                              parsed: None,
+			                              buffer: None,
+			                              error: e.into(),
+			                          }
+			                         })
+		                 .and_then(|(stream, a)| {
+			stream.into_ws()
+			      .map_err(|(stream, req, buf, err)| {
+				               InvalidConnection {
+				                   stream: Some(stream),
+				                   parsed: req,
+				                   buffer: Some(buf),
+				                   error: err,
+				               }
+				              })
+			      .map(move |u| (u, a))
+		});
+		Box::new(future)
+	}
+}
+
+/// Asynchronous methods for creating an async SSL server and accepting incoming connections.
+#[cfg(any(feature="async-ssl"))]
+impl WsServer<TlsAcceptor, TcpListener> {
+	/// Bind an SSL websocket server to an address.
+	/// Creating a websocket server can be done immediately so this does not
+	/// return a `Future` but a simple `Result`.
+	///
+	/// Since this is an SSL server one needs to provide a `TlsAcceptor` that contains
+	/// the server's SSL information.
+	pub fn bind_secure<A: ToSocketAddrs>(
+		addr: A,
+		acceptor: TlsAcceptor,
+		handle: &Handle,
+	) -> io::Result<Self> {
+		let tcp = ::std::net::TcpListener::bind(addr)?;
+		let address = tcp.local_addr()?;
+		Ok(Server {
+		       listener: TcpListener::from_listener(tcp, &address, handle)?,
+		       ssl_acceptor: acceptor,
+		   })
+	}
+
+	/// Turns the server into a stream of connection objects.
+	///
+	/// Each item of the stream is the address of the incoming connection and an `Upgrade`
+	/// struct which lets the user decide wether to turn the connection into a websocket
+	/// connection or reject it.
+	///
+	/// See the [`examples/async-server.rs`]
+	/// (https://github.com/cyderize/rust-websocket/blob/master/examples/async-server.rs)
+	/// example for a good echo server example.
+	pub fn incoming(self) -> Incoming<TlsStream<TcpStream>> {
+		let acceptor = self.ssl_acceptor;
+		let future = self.listener
+		                 .incoming()
+		                 .map_err(|e| {
+			                          InvalidConnection {
+			                              stream: None,
+			                              parsed: None,
+			                              buffer: None,
+			                              error: e.into(),
+			                          }
+			                         })
+		                 .and_then(move |(stream, a)| {
+			acceptor.accept_async(stream)
+			        .map_err(|e| {
+				InvalidConnection {
+					stream: None,
+					parsed: None,
+					buffer: None,
+					// TODO: better error types
+					error: io::Error::new(io::ErrorKind::Other, e).into(),
+				}
+			})
+			        .map(move |s| (s, a))
+		})
+		                 .and_then(|(stream, a)| {
+			stream.into_ws()
+			      .map_err(|(stream, req, buf, err)| {
+				               InvalidConnection {
+				                   stream: Some(stream),
+				                   parsed: req,
+				                   buffer: Some(buf),
+				                   error: err,
+				               }
+				              })
+			      .map(move |u| (u, a))
+		});
+		Box::new(future)
+	}
+}
diff --git a/src/server/mod.rs b/src/server/mod.rs
index 801ae6b502..6db12ef484 100644
--- a/src/server/mod.rs
+++ b/src/server/mod.rs
@@ -1,21 +1,36 @@
 //! Provides an implementation of a WebSocket server
-use std::net::{SocketAddr, ToSocketAddrs, TcpListener, TcpStream};
-use std::io;
-use std::convert::Into;
-#[cfg(feature="ssl")]
-use openssl::ssl::{SslStream, SslAcceptor};
+#[cfg(any(feature="sync-ssl", feature="async-ssl"))]
+use native_tls::TlsAcceptor;
+
 use stream::Stream;
-use self::upgrade::{WsUpgrade, IntoWs, Buffer};
-pub use self::upgrade::{Request, HyperIntoWsError};
+use self::upgrade::{Request, HyperIntoWsError};
 
 pub mod upgrade;
 
+#[cfg(feature="async")]
+pub mod async;
+
+#[cfg(feature="sync")]
+pub mod sync;
+
+/// Marker struct for a struct not being secure
+#[derive(Clone)]
+pub struct NoTlsAcceptor;
+/// Trait that is implemented over NoSslAcceptor and SslAcceptor that
+/// serves as a generic bound to make a struct with.
+/// Used in the Server to specify impls based on wether the server
+/// is running over SSL or not.
+pub trait OptionalTlsAcceptor {}
+impl OptionalTlsAcceptor for NoTlsAcceptor {}
+#[cfg(any(feature="sync-ssl", feature="async-ssl"))]
+impl OptionalTlsAcceptor for TlsAcceptor {}
+
 /// When a sever tries to accept a connection many things can go wrong.
 ///
 /// This struct is all the information that is recovered from a failed
 /// websocket handshake, in case one wants to use the connection for something
 /// else (such as HTTP).
-pub struct InvalidConnection<S>
+pub struct InvalidConnection<S, B>
 	where S: Stream
 {
 	/// if the stream was successfully setup it will be included here
@@ -27,313 +42,53 @@ pub struct InvalidConnection<S>
 	/// `server::upgrade` module to integrate this crate with your server.
 	pub parsed: Option<Request>,
 	/// the buffered data that was already taken from the stream
-	pub buffer: Option<Buffer>,
+	pub buffer: Option<B>,
 	/// the cause of the failed websocket connection setup
 	pub error: HyperIntoWsError,
 }
 
-/// Either the stream was established and it sent a websocket handshake
-/// which represents the `Ok` variant, or there was an error (this is the
-/// `Err` variant).
-pub type AcceptResult<S> = Result<WsUpgrade<S>, InvalidConnection<S>>;
-
-/// Marker struct for a struct not being secure
-#[derive(Clone)]
-pub struct NoSslAcceptor;
-/// Trait that is implemented over NoSslAcceptor and SslAcceptor that
-/// serves as a generic bound to make a struct with.
-/// Used in the Server to specify impls based on wether the server
-/// is running over SSL or not.
-pub trait OptionalSslAcceptor: Clone {}
-impl OptionalSslAcceptor for NoSslAcceptor {}
-#[cfg(feature="ssl")]
-impl OptionalSslAcceptor for SslAcceptor {}
-
 /// Represents a WebSocket server which can work with either normal
 /// (non-secure) connections, or secure WebSocket connections.
 ///
 /// This is a convenient way to implement WebSocket servers, however
 /// it is possible to use any sendable Reader and Writer to obtain
 /// a WebSocketClient, so if needed, an alternative server implementation can be used.
-///# Non-secure Servers
-///
-/// ```no_run
-///extern crate websocket;
-///# fn main() {
-///use std::thread;
-///use websocket::{Server, Message};
-///
-///let server = Server::bind("127.0.0.1:1234").unwrap();
-///
-///for connection in server.filter_map(Result::ok) {
-///    // Spawn a new thread for each connection.
-///    thread::spawn(move || {
-///		   let mut client = connection.accept().unwrap();
 ///
-///		   let message = Message::text("Hello, client!");
-///		   let _ = client.send_message(&message);
+/// # Synchronous Servers
+/// Synchronous implementations of a websocket server are available below, each method is
+/// documented so the reader knows wether is it synchronous or asynchronous.
 ///
-///		   // ...
-///    });
-///}
-/// # }
-/// ```
+/// To use the synchronous implementation, you must have the `sync` feature enabled
+/// (it is enabled by default).
+/// To use the synchronous SSL implementation, you must have the `sync-ssl` feature enabled
+/// (it is enabled by default).
 ///
-///# Secure Servers
-/// ```no_run
-///extern crate websocket;
-///extern crate openssl;
-///# fn main() {
-///use std::thread;
-///use std::io::Read;
-///use std::fs::File;
-///use websocket::{Server, Message};
-///use openssl::pkcs12::Pkcs12;
-///use openssl::ssl::{SslMethod, SslAcceptorBuilder, SslStream};
+/// # Asynchronous Servers
+/// Asynchronous implementations of a websocket server are available below, each method is
+/// documented so the reader knows wether is it synchronous or asynchronous.
+/// Simply look out for the implementation of `Server` whose methods only return `Future`s
+/// (it is also written in the docs if the method is async).
 ///
-///// In this example we retrieve our keypair and certificate chain from a PKCS #12 archive,
-///// but but they can also be retrieved from, for example, individual PEM- or DER-formatted
-///// files. See the documentation for the `PKey` and `X509` types for more details.
-///let mut file = File::open("identity.pfx").unwrap();
-///let mut pkcs12 = vec![];
-///file.read_to_end(&mut pkcs12).unwrap();
-///let pkcs12 = Pkcs12::from_der(&pkcs12).unwrap();
-///let identity = pkcs12.parse("password123").unwrap();
-///
-///let acceptor = SslAcceptorBuilder::mozilla_intermediate(SslMethod::tls(),
-///                                                        &identity.pkey,
-///                                                        &identity.cert,
-///                                                        &identity.chain)
-///                   .unwrap()
-///                   .build();
-///
-///let server = Server::bind_secure("127.0.0.1:1234", acceptor).unwrap();
-///
-///for connection in server.filter_map(Result::ok) {
-///    // Spawn a new thread for each connection.
-///    thread::spawn(move || {
-///		   let mut client = connection.accept().unwrap();
-///
-///		   let message = Message::text("Hello, client!");
-///		   let _ = client.send_message(&message);
-///
-///		   // ...
-///    });
-///}
-/// # }
-/// ```
+/// To use the asynchronous implementation, you must have the `async` feature enabled
+/// (it is enabled by default).
+/// To use the asynchronous SSL implementation, you must have the `async-ssl` feature enabled
+/// (it is enabled by default).
 ///
 /// # A Hyper Server
 /// This crates comes with hyper integration out of the box, you can create a hyper
 /// server and serve websocket and HTTP **on the same port!**
-/// check out the docs over at `websocket::server::upgrade::from_hyper` for an example.
+/// check out the docs over at `websocket::server::upgrade::sync::HyperRequest` for an example.
 ///
 /// # A Custom Server
 /// So you don't want to use any of our server implementations? That's O.K.
 /// All it takes is implementing the `IntoWs` trait for your server's streams,
 /// then calling `.into_ws()` on them.
-/// check out the docs over at `websocket::server::upgrade` for more.
-pub struct Server<S>
-	where S: OptionalSslAcceptor
-{
-	listener: TcpListener,
-	ssl_acceptor: S,
-}
-
-impl<S> Server<S>
-    where S: OptionalSslAcceptor
+/// check out the docs over at `websocket::server::upgrade::sync` for more.
+#[cfg(any(feature="sync", feature="async"))]
+pub struct WsServer<S, L>
+	where S: OptionalTlsAcceptor
 {
-	/// Get the socket address of this server
-	pub fn local_addr(&self) -> io::Result<SocketAddr> {
-		self.listener.local_addr()
-	}
-
-	/// Create a new independently owned handle to the underlying socket.
-	pub fn try_clone(&self) -> io::Result<Server<S>> {
-		let inner = try!(self.listener.try_clone());
-		Ok(Server {
-		       listener: inner,
-		       ssl_acceptor: self.ssl_acceptor.clone(),
-		   })
-	}
-
-	/// Changes whether the Server is in nonblocking mode.
-	///
-	/// If it is in nonblocking mode, accept() will return an error instead of blocking when there
-	/// are no incoming connections.
-	///
-	///# Examples
-	///```no_run
-	/// # extern crate websocket;
-	/// # use websocket::Server;
-	/// # fn main() {
-	/// // Suppose we have to work in a single thread, but want to
-	/// // accomplish two unrelated things:
-	/// // (1) Once in a while we want to check if anybody tried to connect to
-	/// // our websocket server, and if so, handle the TcpStream.
-	/// // (2) In between we need to something else, possibly unrelated to networking.
-	///
-	/// let mut server = Server::bind("127.0.0.1:0").unwrap();
-	///
-	/// // Set the server to non-blocking.
-	/// server.set_nonblocking(true);
-	///
-	/// for i in 1..3 {
-	/// 	let result = match server.accept() {
-	/// 		Ok(wsupgrade) => {
-	/// 			// Do something with the established TcpStream.
-	/// 		}
-	/// 		_ => {
-	/// 			// Nobody tried to connect, move on.
-	/// 		}
-	/// 	};
-	/// 	// Perform another task. Because we have a non-blocking server,
-	/// 	// this will execute independent of whether someone tried to
-	/// 	// establish a connection.
-	/// 	let two = 1+1;
-	/// }
-	/// # }
-	///```
-	pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
-		self.listener.set_nonblocking(nonblocking)
-	}
-}
-
-#[cfg(feature="ssl")]
-impl Server<SslAcceptor> {
-	/// Bind this Server to this socket, utilising the given SslContext
-	pub fn bind_secure<A>(addr: A, acceptor: SslAcceptor) -> io::Result<Self>
-		where A: ToSocketAddrs
-	{
-		Ok(Server {
-		       listener: try!(TcpListener::bind(&addr)),
-		       ssl_acceptor: acceptor,
-		   })
-	}
-
-	/// Wait for and accept an incoming WebSocket connection, returning a WebSocketRequest
-	pub fn accept(&mut self) -> AcceptResult<SslStream<TcpStream>> {
-		let stream = match self.listener.accept() {
-			Ok(s) => s.0,
-			Err(e) => {
-				return Err(InvalidConnection {
-				               stream: None,
-				               parsed: None,
-				               buffer: None,
-				               error: e.into(),
-				           })
-			}
-		};
-
-		let stream = match self.ssl_acceptor.accept(stream) {
-			Ok(s) => s,
-			Err(err) => {
-				return Err(InvalidConnection {
-				               stream: None,
-				               parsed: None,
-				               buffer: None,
-				               error: io::Error::new(io::ErrorKind::Other, err).into(),
-				           })
-			}
-		};
-
-		match stream.into_ws() {
-			Ok(u) => Ok(u),
-			Err((s, r, b, e)) => {
-				Err(InvalidConnection {
-				        stream: Some(s),
-				        parsed: r,
-				        buffer: b,
-				        error: e.into(),
-				    })
-			}
-		}
-	}
-}
-
-#[cfg(feature="ssl")]
-impl Iterator for Server<SslAcceptor> {
-	type Item = AcceptResult<SslStream<TcpStream>>;
-
-	fn next(&mut self) -> Option<<Self as Iterator>::Item> {
-		Some(self.accept())
-	}
-}
-
-impl Server<NoSslAcceptor> {
-	/// Bind this Server to this socket
-	pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<Self> {
-		Ok(Server {
-		       listener: try!(TcpListener::bind(&addr)),
-		       ssl_acceptor: NoSslAcceptor,
-		   })
-	}
-
-	/// Wait for and accept an incoming WebSocket connection, returning a WebSocketRequest
-	pub fn accept(&mut self) -> AcceptResult<TcpStream> {
-		let stream = match self.listener.accept() {
-			Ok(s) => s.0,
-			Err(e) => {
-				return Err(InvalidConnection {
-				               stream: None,
-				               parsed: None,
-				               buffer: None,
-				               error: e.into(),
-				           })
-			}
-		};
-
-		match stream.into_ws() {
-			Ok(u) => Ok(u),
-			Err((s, r, b, e)) => {
-				Err(InvalidConnection {
-				        stream: Some(s),
-				        parsed: r,
-				        buffer: b,
-				        error: e.into(),
-				    })
-			}
-		}
-	}
-}
-
-impl Iterator for Server<NoSslAcceptor> {
-	type Item = AcceptResult<TcpStream>;
-
-	fn next(&mut self) -> Option<<Self as Iterator>::Item> {
-		Some(self.accept())
-	}
-}
-
-mod tests {
-	#[test]
-	// test the set_nonblocking() method for Server<NoSslAcceptor>.
-	// Some of this is copied from
-	// https://doc.rust-lang.org/src/std/net/tcp.rs.html#1413
-	fn set_nonblocking() {
-
-		use super::*;
-
-		// Test unsecure server
-
-		let mut server = Server::bind("127.0.0.1:0").unwrap();
-
-		// Note that if set_nonblocking() doesn't work, but the following
-		// fails to panic for some reason, then the .accept() method below
-		// will block indefinitely.
-		server.set_nonblocking(true).unwrap();
-
-		let result = server.accept();
-		match result {
-			// nobody tried to establish a connection, so we expect an error
-			Ok(_) => panic!("expected error"),
-			Err(e) => {
-				match e.error {
-					HyperIntoWsError::Io(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
-					_ => panic!("unexpected error {}"),
-				}
-			}
-		}
-
-	}
+	listener: L,
+	/// The SSL acceptor given to the server
+	pub ssl_acceptor: S,
 }
diff --git a/src/server/sync.rs b/src/server/sync.rs
new file mode 100644
index 0000000000..3630af2f2a
--- /dev/null
+++ b/src/server/sync.rs
@@ -0,0 +1,306 @@
+//! Provides an implementation of a WebSocket server
+use std::net::{SocketAddr, ToSocketAddrs, TcpListener, TcpStream};
+use std::io;
+use std::convert::Into;
+#[cfg(feature="sync-ssl")]
+use native_tls::{TlsStream, TlsAcceptor};
+use server::{WsServer, OptionalTlsAcceptor, NoTlsAcceptor, InvalidConnection};
+use server::upgrade::sync::{Upgrade, IntoWs, Buffer};
+pub use server::upgrade::{Request, HyperIntoWsError};
+
+#[cfg(feature="async")]
+use tokio_core::reactor::Handle;
+#[cfg(feature="async")]
+use tokio_core::net::TcpListener as AsyncTcpListener;
+#[cfg(feature="async")]
+use server::async;
+
+/// Either the stream was established and it sent a websocket handshake
+/// which represents the `Ok` variant, or there was an error (this is the
+/// `Err` variant).
+pub type AcceptResult<S> = Result<Upgrade<S>, InvalidConnection<S, Buffer>>;
+
+/// Represents a WebSocket server which can work with either normal
+/// (non-secure) connections, or secure WebSocket connections.
+///
+/// This is a convenient way to implement WebSocket servers, however
+/// it is possible to use any sendable Reader and Writer to obtain
+/// a WebSocketClient, so if needed, an alternative server implementation can be used.
+pub type Server<S> = WsServer<S, TcpListener>;
+
+/// Synchronous methods for creating a server and accepting incoming connections.
+impl<S> WsServer<S, TcpListener>
+    where S: OptionalTlsAcceptor
+{
+	/// Get the socket address of this server
+	pub fn local_addr(&self) -> io::Result<SocketAddr> {
+		self.listener.local_addr()
+	}
+
+	/// Changes whether the Server is in nonblocking mode.
+	/// NOTE: It is strongly encouraged to use the `websocket::async` module instead
+	/// of this. It provides high level APIs for creating asynchronous servers.
+	///
+	/// If it is in nonblocking mode, accept() will return an error instead of
+	/// blocking when there are no incoming connections.
+	///
+	///```no_run
+	/// # extern crate websocket;
+	/// # use websocket::sync::Server;
+	/// # fn main() {
+	/// // Suppose we have to work in a single thread, but want to
+	/// // accomplish two unrelated things:
+	/// // (1) Once in a while we want to check if anybody tried to connect to
+	/// // our websocket server, and if so, handle the TcpStream.
+	/// // (2) In between we need to something else, possibly unrelated to networking.
+	///
+	/// let mut server = Server::bind("127.0.0.1:0").unwrap();
+	///
+	/// // Set the server to non-blocking.
+	/// server.set_nonblocking(true);
+	///
+	/// for i in 1..3 {
+	/// 	let result = match server.accept() {
+	/// 		Ok(wsupgrade) => {
+	/// 			// Do something with the established TcpStream.
+	/// 		}
+	/// 		_ => {
+	/// 			// Nobody tried to connect, move on.
+	/// 		}
+	/// 	};
+	/// 	// Perform another task. Because we have a non-blocking server,
+	/// 	// this will execute independent of whether someone tried to
+	/// 	// establish a connection.
+	/// 	let two = 1+1;
+	/// }
+	/// # }
+	///```
+	pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+		self.listener.set_nonblocking(nonblocking)
+	}
+
+	/// Turns an existing synchronous server into an asynchronous one.
+	/// This will only work if the stream used for this server `S` already implements
+	/// `AsyncRead + AsyncWrite`. Useful if you would like some blocking things to happen
+	/// at the start of your server.
+	#[cfg(feature="async")]
+	pub fn into_async(self, handle: &Handle) -> io::Result<async::Server<S>> {
+		let addr = self.listener.local_addr()?;
+		Ok(WsServer {
+		       listener: AsyncTcpListener::from_listener(self.listener, &addr, handle)?,
+		       ssl_acceptor: self.ssl_acceptor,
+		   })
+	}
+}
+
+/// Synchronous methods for creating an SSL server and accepting incoming connections.
+#[cfg(feature="sync-ssl")]
+impl WsServer<TlsAcceptor, TcpListener> {
+	/// Bind this Server to this socket, utilising the given SslContext
+	///
+	/// # Secure Servers
+	/// ```no_run
+	/// extern crate websocket;
+	/// extern crate native_tls;
+	/// # fn main() {
+	/// use std::thread;
+	/// use std::io::Read;
+	/// use std::fs::File;
+	/// use websocket::Message;
+	/// use websocket::sync::Server;
+	/// use native_tls::{Pkcs12, TlsAcceptor};
+	///
+	/// // In this example we retrieve our keypair and certificate chain from a PKCS #12 archive,
+	/// // but but they can also be retrieved from, for example, individual PEM- or DER-formatted
+	/// // files. See the documentation for the `PKey` and `X509` types for more details.
+	/// let mut file = File::open("identity.pfx").unwrap();
+	/// let mut pkcs12 = vec![];
+	/// file.read_to_end(&mut pkcs12).unwrap();
+	/// let pkcs12 = Pkcs12::from_der(&pkcs12, "hacktheplanet").unwrap();
+	///
+	/// let acceptor = TlsAcceptor::builder(pkcs12).unwrap().build().unwrap();
+	///
+	/// let server = Server::bind_secure("127.0.0.1:1234", acceptor).unwrap();
+	///
+	/// for connection in server.filter_map(Result::ok) {
+	///     // Spawn a new thread for each connection.
+	///     thread::spawn(move || {
+	/// 		    let mut client = connection.accept().unwrap();
+	///
+	/// 		    let message = Message::text("Hello, client!");
+	/// 		    let _ = client.send_message(&message);
+	///
+	/// 		    // ...
+	///     });
+	/// }
+	/// # }
+	/// ```
+	pub fn bind_secure<A>(addr: A, acceptor: TlsAcceptor) -> io::Result<Self>
+		where A: ToSocketAddrs
+	{
+		Ok(Server {
+		       listener: try!(TcpListener::bind(&addr)),
+		       ssl_acceptor: acceptor,
+		   })
+	}
+
+	/// Wait for and accept an incoming WebSocket connection, returning a WebSocketRequest
+	pub fn accept(&mut self) -> AcceptResult<TlsStream<TcpStream>> {
+		let stream = match self.listener.accept() {
+			Ok(s) => s.0,
+			Err(e) => {
+				return Err(InvalidConnection {
+				               stream: None,
+				               parsed: None,
+				               buffer: None,
+				               error: e.into(),
+				           })
+			}
+		};
+
+		let stream = match self.ssl_acceptor.accept(stream) {
+			Ok(s) => s,
+			Err(err) => {
+				return Err(InvalidConnection {
+				               stream: None,
+				               parsed: None,
+				               buffer: None,
+				               error: io::Error::new(io::ErrorKind::Other, err).into(),
+				           })
+			}
+		};
+
+		match stream.into_ws() {
+			Ok(u) => Ok(u),
+			Err((s, r, b, e)) => {
+				Err(InvalidConnection {
+				        stream: Some(s),
+				        parsed: r,
+				        buffer: b,
+				        error: e.into(),
+				    })
+			}
+		}
+	}
+}
+
+#[cfg(feature="sync-ssl")]
+impl Iterator for WsServer<TlsAcceptor, TcpListener> {
+	type Item = AcceptResult<TlsStream<TcpStream>>;
+
+	fn next(&mut self) -> Option<<Self as Iterator>::Item> {
+		Some(self.accept())
+	}
+}
+
+impl WsServer<NoTlsAcceptor, TcpListener> {
+	/// Bind this Server to this socket
+	///
+	/// # Non-secure Servers
+	///
+	/// ```no_run
+	/// extern crate websocket;
+	/// # fn main() {
+	/// use std::thread;
+	/// use websocket::Message;
+	/// use websocket::sync::Server;
+	///
+	/// let server = Server::bind("127.0.0.1:1234").unwrap();
+	///
+	/// for connection in server.filter_map(Result::ok) {
+	///     // Spawn a new thread for each connection.
+	///     thread::spawn(move || {
+	///		      let mut client = connection.accept().unwrap();
+	///
+	///		      let message = Message::text("Hello, client!");
+	///		      let _ = client.send_message(&message);
+	///
+	///		      // ...
+	///    });
+	/// }
+	/// # }
+	/// ```
+	pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<Self> {
+		Ok(Server {
+		       listener: try!(TcpListener::bind(&addr)),
+		       ssl_acceptor: NoTlsAcceptor,
+		   })
+	}
+
+	/// Wait for and accept an incoming WebSocket connection, returning a WebSocketRequest
+	pub fn accept(&mut self) -> AcceptResult<TcpStream> {
+		let stream = match self.listener.accept() {
+			Ok(s) => s.0,
+			Err(e) => {
+				return Err(InvalidConnection {
+				               stream: None,
+				               parsed: None,
+				               buffer: None,
+				               error: e.into(),
+				           })
+			}
+		};
+
+		match stream.into_ws() {
+			Ok(u) => Ok(u),
+			Err((s, r, b, e)) => {
+				Err(InvalidConnection {
+				        stream: Some(s),
+				        parsed: r,
+				        buffer: b,
+				        error: e.into(),
+				    })
+			}
+		}
+	}
+
+	/// Create a new independently owned handle to the underlying socket.
+	pub fn try_clone(&self) -> io::Result<Self> {
+		let inner = try!(self.listener.try_clone());
+		Ok(Server {
+		       listener: inner,
+		       ssl_acceptor: self.ssl_acceptor.clone(),
+		   })
+	}
+}
+
+impl Iterator for WsServer<NoTlsAcceptor, TcpListener> {
+	type Item = AcceptResult<TcpStream>;
+
+	fn next(&mut self) -> Option<<Self as Iterator>::Item> {
+		Some(self.accept())
+	}
+}
+
+mod tests {
+	#[test]
+	// test the set_nonblocking() method for Server<NoSslAcceptor>.
+	// Some of this is copied from
+	// https://doc.rust-lang.org/src/std/net/tcp.rs.html#1413
+	fn set_nonblocking() {
+
+		use super::*;
+
+		// Test unsecure server
+
+		let mut server = Server::bind("127.0.0.1:0").unwrap();
+
+		// Note that if set_nonblocking() doesn't work, but the following
+		// fails to panic for some reason, then the .accept() method below
+		// will block indefinitely.
+		server.set_nonblocking(true).unwrap();
+
+		let result = server.accept();
+		match result {
+			// nobody tried to establish a connection, so we expect an error
+			Ok(_) => panic!("expected error"),
+			Err(e) => {
+				match e.error {
+					HyperIntoWsError::Io(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
+					_ => panic!("unexpected error {}"),
+				}
+			}
+		}
+
+	}
+}
diff --git a/src/server/upgrade/async.rs b/src/server/upgrade/async.rs
new file mode 100644
index 0000000000..ad1c9afa31
--- /dev/null
+++ b/src/server/upgrade/async.rs
@@ -0,0 +1,241 @@
+//! Allows taking an existing stream of data and asynchronously convert it to a
+//! websocket client.
+//!
+//! This module contains the trait that transforms stream into
+//! an intermediate struct called `Upgrade` and the `Upgrade` struct itself.
+//! The `Upgrade` struct is used to inspect details of the websocket connection
+//! (e.g. what protocols it wants to use) and decide wether to accept or reject it.
+use super::{HyperIntoWsError, WsUpgrade, Request, validate};
+use std::io::{self, ErrorKind};
+use tokio_io::codec::{Framed, FramedParts};
+use hyper::header::Headers;
+use hyper::http::h1::Incoming;
+use hyper::status::StatusCode;
+use stream::async::Stream;
+use futures::{Sink, Future};
+use futures::Stream as StreamTrait;
+use futures::sink::Send;
+use codec::http::HttpServerCodec;
+use codec::ws::{MessageCodec, Context};
+use bytes::BytesMut;
+use client::async::ClientNew;
+
+/// An asynchronous websocket upgrade.
+///
+/// This struct is given when a connection is being upgraded to a websocket
+/// request. It implements everything that a normal `WsUpgrade` struct does
+/// along with the final functions to create websocket clients (although this
+/// is done asynchronously).
+///
+/// # Example
+///
+/// ```rust,no_run
+/// use websocket::async::{Core, TcpListener, TcpStream};
+/// use websocket::async::futures::{Stream, Future};
+/// use websocket::async::server::upgrade::IntoWs;
+/// use websocket::sync::Client;
+///
+/// let mut core = Core::new().unwrap();
+/// let handle = core.handle();
+/// let addr = "127.0.0.1:80".parse().unwrap();
+/// let listener = TcpListener::bind(&addr, &handle).unwrap();
+///
+/// let websocket_clients = listener
+///     .incoming().map_err(|e| e.into())
+///     .and_then(|(stream, _)| stream.into_ws().map_err(|e| e.3))
+///     .map(|upgrade| {
+///         if upgrade.protocols().iter().any(|p| p == "super-cool-proto") {
+///             let accepted = upgrade
+///                 .use_protocol("super-cool-proto")
+///                 .accept()
+///                 .map(|_| ()).map_err(|_| ());
+///
+///             handle.spawn(accepted);
+///         } else {
+///             let rejected = upgrade.reject()
+///                 .map(|_| ()).map_err(|_| ());
+///
+///             handle.spawn(rejected);
+///         }
+///     });
+/// ```
+pub type Upgrade<S> = WsUpgrade<S, BytesMut>;
+
+/// These are the extra functions given to `WsUpgrade` with the `async` feature
+/// turned on. A type alias for this specialization of `WsUpgrade` lives in this
+/// module under the name `Upgrade`.
+impl<S> WsUpgrade<S, BytesMut>
+    where S: Stream + 'static
+{
+	/// Asynchronously accept the websocket handshake, then create a client.
+	/// This will asynchronously send a response accepting the connection
+	/// and create a websocket client.
+	pub fn accept(self) -> ClientNew<S> {
+		self.internal_accept(None)
+	}
+
+	/// Asynchronously accept the websocket handshake, then create a client.
+	/// This will asynchronously send a response accepting the connection
+	/// with custom headers in the response and create a websocket client.
+	pub fn accept_with(self, custom_headers: &Headers) -> ClientNew<S> {
+		self.internal_accept(Some(custom_headers))
+	}
+
+	fn internal_accept(mut self, custom_headers: Option<&Headers>) -> ClientNew<S> {
+		let status = self.prepare_headers(custom_headers);
+		let WsUpgrade { headers, stream, request, buffer } = self;
+
+		let duplex = Framed::from_parts(FramedParts {
+		                                    inner: stream,
+		                                    readbuf: buffer,
+		                                    writebuf: BytesMut::with_capacity(0),
+		                                },
+		                                HttpServerCodec);
+
+		let future = duplex.send(Incoming {
+		                             version: request.version,
+		                             subject: status,
+		                             headers: headers.clone(),
+		                         })
+		                   .map(move |s| {
+			                        let codec = MessageCodec::default(Context::Server);
+			                        let client = Framed::from_parts(s.into_parts(), codec);
+			                        (client, headers)
+			                       })
+		                   .map_err(|e| e.into());
+		Box::new(future)
+	}
+
+	/// Asynchronously send a rejection message and deconstruct `self`
+	/// into it's original stream. The stream being returned is framed with the
+	/// `HttpServerCodec` since that was used to send the rejection message.
+	pub fn reject(self) -> Send<Framed<S, HttpServerCodec>> {
+		self.internal_reject(None)
+	}
+
+	/// Asynchronously send a rejection message with custom headers and
+	/// deconstruct `self` into it's original stream.
+	///  The stream being returned is framed with the
+	/// `HttpServerCodec` since that was used to send the rejection message.
+	pub fn reject_with(self, headers: &Headers) -> Send<Framed<S, HttpServerCodec>> {
+		self.internal_reject(Some(headers))
+	}
+
+	fn internal_reject(mut self, headers: Option<&Headers>) -> Send<Framed<S, HttpServerCodec>> {
+		if let Some(custom) = headers {
+			self.headers.extend(custom.iter());
+		}
+		let duplex = Framed::from_parts(FramedParts {
+		                                    inner: self.stream,
+		                                    readbuf: self.buffer,
+		                                    writebuf: BytesMut::with_capacity(0),
+		                                },
+		                                HttpServerCodec);
+		duplex.send(Incoming {
+		                version: self.request.version,
+		                subject: StatusCode::BadRequest,
+		                headers: self.headers,
+		            })
+	}
+}
+
+
+/// Trait to take a stream or similar and attempt to recover the start of a
+/// websocket handshake from it (asynchronously).
+/// Should be used when a stream might contain a request for a websocket session.
+///
+/// If an upgrade request can be parsed, one can accept or deny the handshake with
+/// the `WsUpgrade` struct.
+/// Otherwise the original stream is returned along with an error.
+///
+/// Note: the stream is owned because the websocket client expects to own its stream.
+///
+/// This is already implemented for all async streams, which means all types with
+/// `AsyncRead + AsyncWrite + 'static` (`'static` because the future wants to own
+/// the stream).
+///
+/// # Example
+///
+/// ```rust,no_run
+/// use websocket::async::{Core, TcpListener, TcpStream};
+/// use websocket::async::futures::{Stream, Future};
+/// use websocket::async::server::upgrade::IntoWs;
+/// use websocket::sync::Client;
+///
+/// let mut core = Core::new().unwrap();
+/// let handle = core.handle();
+/// let addr = "127.0.0.1:80".parse().unwrap();
+/// let listener = TcpListener::bind(&addr, &handle).unwrap();
+///
+/// let websocket_clients = listener
+///     .incoming().map_err(|e| e.into())
+///     .and_then(|(stream, _)| stream.into_ws().map_err(|e| e.3))
+///     .map(|upgrade| {
+///         if upgrade.protocols().iter().any(|p| p == "super-cool-proto") {
+///             let accepted = upgrade
+///                 .use_protocol("super-cool-proto")
+///                 .accept()
+///                 .map(|_| ()).map_err(|_| ());
+///
+///             handle.spawn(accepted);
+///         } else {
+///             let rejected = upgrade.reject()
+///                 .map(|_| ()).map_err(|_| ());
+///
+///             handle.spawn(rejected);
+///         }
+///     });
+/// ```
+pub trait IntoWs {
+	/// The type of stream this upgrade process is working with (TcpStream, etc.)
+	type Stream: Stream;
+	/// An error value in case the stream is not asking for a websocket connection
+	/// or something went wrong. It is common to also include the stream here.
+	type Error;
+	/// Attempt to read and parse the start of a Websocket handshake, later
+	/// with the  returned `WsUpgrade` struct, call `accept to start a
+	/// websocket client, and `reject` to send a handshake rejection response.
+	///
+	/// Note: this is the asynchronous version, meaning it will not block when
+	/// trying to read a request.
+	fn into_ws(self) -> Box<Future<Item = Upgrade<Self::Stream>, Error = Self::Error>>;
+}
+
+impl<S> IntoWs for S
+    where S: Stream + 'static
+{
+	type Stream = S;
+	type Error = (S, Option<Request>, BytesMut, HyperIntoWsError);
+
+	fn into_ws(self) -> Box<Future<Item = Upgrade<Self::Stream>, Error = Self::Error>> {
+		let future = self.framed(HttpServerCodec)
+          .into_future()
+          .map_err(|(e, s)| {
+              let FramedParts { inner, readbuf, .. } = s.into_parts();
+              (inner, None, readbuf, e.into())
+          })
+          .and_then(|(m, s)| {
+              let FramedParts { inner, readbuf, .. } = s.into_parts();
+              if let Some(msg) = m {
+                  match validate(&msg.subject.0, &msg.version, &msg.headers) {
+                      Ok(()) => Ok((msg, inner, readbuf)),
+                      Err(e) => Err((inner, None, readbuf, e)),
+                  }
+              } else {
+                  let err = HyperIntoWsError::Io(io::Error::new(
+                      ErrorKind::ConnectionReset,
+                  "Connection dropped before handshake could be read"));
+                  Err((inner, None, readbuf, err))
+              }
+          })
+          .map(|(m, stream, buffer)| {
+              WsUpgrade {
+                  headers: Headers::new(),
+                  stream: stream,
+                  request: m,
+                  buffer: buffer,
+              }
+          });
+		Box::new(future)
+	}
+}
diff --git a/src/server/upgrade/from_hyper.rs b/src/server/upgrade/from_hyper.rs
deleted file mode 100644
index da6ae1e134..0000000000
--- a/src/server/upgrade/from_hyper.rs
+++ /dev/null
@@ -1,88 +0,0 @@
-//! Upgrade a hyper connection to a websocket one.
-//!
-//! Using this method, one can start a hyper server and check if each request
-//! is a websocket upgrade request, if so you can use websockets and hyper on the
-//! same port!
-//!
-//! ```rust,no_run
-//! # extern crate hyper;
-//! # extern crate websocket;
-//! # fn main() {
-//! use hyper::server::{Server, Request, Response};
-//! use websocket::Message;
-//! use websocket::server::upgrade::IntoWs;
-//! use websocket::server::upgrade::from_hyper::HyperRequest;
-//!
-//! Server::http("0.0.0.0:80").unwrap().handle(move |req: Request, res: Response| {
-//!     match HyperRequest(req).into_ws() {
-//!         Ok(upgrade) => {
-//!             // `accept` sends a successful handshake, no need to worry about res
-//!             let mut client = match upgrade.accept() {
-//!                 Ok(c) => c,
-//!                 Err(_) => panic!(),
-//!             };
-//!
-//!             client.send_message(&Message::text("its free real estate"));
-//!         },
-//!
-//!         Err((request, err)) => {
-//!             // continue using the request as normal, "echo uri"
-//!             res.send(b"Try connecting over ws instead.").unwrap();
-//!         },
-//!     };
-//! })
-//! .unwrap();
-//! # }
-//! ```
-
-use hyper::net::NetworkStream;
-use super::{IntoWs, WsUpgrade, Buffer};
-
-pub use hyper::http::h1::Incoming;
-pub use hyper::method::Method;
-pub use hyper::version::HttpVersion;
-pub use hyper::uri::RequestUri;
-pub use hyper::buffer::BufReader;
-use hyper::server::Request;
-pub use hyper::header::{Headers, Upgrade, ProtocolName, Connection, ConnectionOption};
-
-use super::validate;
-use super::HyperIntoWsError;
-
-/// A hyper request is implicitly defined as a stream from other `impl`s of Stream.
-/// Until trait impl specialization comes along, we use this struct to differentiate
-/// a hyper request (which already has parsed headers) from a normal stream.
-pub struct HyperRequest<'a, 'b: 'a>(pub Request<'a, 'b>);
-
-impl<'a, 'b> IntoWs for HyperRequest<'a, 'b> {
-	type Stream = &'a mut &'b mut NetworkStream;
-	type Error = (Request<'a, 'b>, HyperIntoWsError);
-
-	fn into_ws(self) -> Result<WsUpgrade<Self::Stream>, Self::Error> {
-		if let Err(e) = validate(&self.0.method, &self.0.version, &self.0.headers) {
-			return Err((self.0, e));
-		}
-
-		let (_, method, headers, uri, version, reader) =
-			self.0.deconstruct();
-
-		let reader = reader.into_inner();
-		let (buf, pos, cap) = reader.take_buf();
-		let stream = reader.get_mut();
-
-		Ok(WsUpgrade {
-		       headers: Headers::new(),
-		       stream: stream,
-		       buffer: Some(Buffer {
-		                        buf: buf,
-		                        pos: pos,
-		                        cap: cap,
-		                    }),
-		       request: Incoming {
-		           version: version,
-		           headers: headers,
-		           subject: (method, uri),
-		       },
-		   })
-	}
-}
diff --git a/src/server/upgrade/mod.rs b/src/server/upgrade/mod.rs
index d52f3957c9..d1bb0ae42f 100644
--- a/src/server/upgrade/mod.rs
+++ b/src/server/upgrade/mod.rs
@@ -1,47 +1,31 @@
 //! Allows you to take an existing request or stream of data and convert it into a
 //! WebSocket client.
 use std::error::Error;
-use std::net::TcpStream;
 use std::io;
-use std::io::Result as IoResult;
-use std::io::Error as IoError;
 use std::fmt::{self, Formatter, Display};
-use stream::{Stream, AsTcpStream};
+use stream::Stream;
 use header::extensions::Extension;
 use header::{WebSocketAccept, WebSocketKey, WebSocketVersion, WebSocketProtocol,
              WebSocketExtensions, Origin};
-use client::Client;
 
 use unicase::UniCase;
 use hyper::status::StatusCode;
 use hyper::http::h1::Incoming;
 use hyper::method::Method;
-use hyper::version::HttpVersion;
 use hyper::uri::RequestUri;
-use hyper::buffer::BufReader;
-use hyper::http::h1::parse_request;
 use hyper::header::{Headers, Upgrade, Protocol, ProtocolName, Connection, ConnectionOption};
 
-pub mod from_hyper;
+#[cfg(any(feature="sync", feature="async"))]
+use hyper::version::HttpVersion;
 
-/// This crate uses buffered readers to read in the handshake quickly, in order to
-/// interface with other use cases that don't use buffered readers the buffered readers
-/// is deconstructed when it is returned to the user and given as the underlying
-/// reader and the buffer.
-///
-/// This struct represents bytes that have already been read in from the stream.
-/// A slice of valid data in this buffer can be obtained by: `&buf[pos..cap]`.
-#[derive(Debug)]
-pub struct Buffer {
-	/// the contents of the buffered stream data
-	pub buf: Vec<u8>,
-	/// the current position of cursor in the buffer
-	/// Any data before `pos` has already been read and parsed.
-	pub pos: usize,
-	/// the last location of valid data
-	/// Any data after `cap` is not valid.
-	pub cap: usize,
-}
+#[cfg(feature="async")]
+pub mod async;
+
+#[cfg(feature="sync")]
+pub mod sync;
+
+/// A typical request from hyper
+pub type Request = Incoming<(Method, RequestUri)>;
 
 /// Intermediate representation of a half created websocket session.
 /// Should be used to examine the client's handshake
@@ -49,7 +33,11 @@ pub struct Buffer {
 ///
 /// Users should then call `accept` or `reject` to complete the handshake
 /// and start a session.
-pub struct WsUpgrade<S>
+/// Note: if the stream in use is `AsyncRead + AsyncWrite`, then asynchronous
+/// functions will be available when completing the handshake.
+/// Otherwise if the stream is simply `Read + Write` blocking functions will be
+/// available to complete the handshake.
+pub struct WsUpgrade<S, B>
 	where S: Stream
 {
 	/// The headers that will be used in the handshake response.
@@ -59,10 +47,10 @@ pub struct WsUpgrade<S>
 	/// The handshake request, filled with useful metadata.
 	pub request: Request,
 	/// Some buffered data from the stream, if it exists.
-	pub buffer: Option<Buffer>,
+	pub buffer: B,
 }
 
-impl<S> WsUpgrade<S>
+impl<S, B> WsUpgrade<S, B>
     where S: Stream
 {
 	/// Select a protocol to use in the handshake response.
@@ -98,53 +86,6 @@ impl<S> WsUpgrade<S>
 		self
 	}
 
-	/// Accept the handshake request and send a response,
-	/// if nothing goes wrong a client will be created.
-	pub fn accept(self) -> Result<Client<S>, (S, IoError)> {
-		self.accept_with(&Headers::new())
-	}
-
-	/// Accept the handshake request and send a response while
-	/// adding on a few headers. These headers are added before the required
-	/// headers are, so some might be overwritten.
-	pub fn accept_with(mut self, custom_headers: &Headers) -> Result<Client<S>, (S, IoError)> {
-		self.headers.extend(custom_headers.iter());
-		self.headers
-		    .set(WebSocketAccept::new(// NOTE: we know there is a key because this is a valid request
-		                              // i.e. to construct this you must go through the validate function
-		                              self.request.headers.get::<WebSocketKey>().unwrap()));
-		self.headers
-		    .set(Connection(vec![
-			      ConnectionOption::ConnectionHeader(UniCase("Upgrade".to_string()))
-		    ]));
-		self.headers.set(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)]));
-
-		if let Err(e) = self.send(StatusCode::SwitchingProtocols) {
-			return Err((self.stream, e));
-		}
-
-		let stream = match self.buffer {
-			Some(Buffer { buf, pos, cap }) => BufReader::from_parts(self.stream, buf, pos, cap),
-			None => BufReader::new(self.stream),
-		};
-
-		Ok(Client::unchecked(stream, self.headers, false, true))
-	}
-
-	/// Reject the client's request to make a websocket connection.
-	pub fn reject(self) -> Result<S, (S, IoError)> {
-		self.reject_with(&Headers::new())
-	}
-	/// Reject the client's request to make a websocket connection
-	/// and send extra headers.
-	pub fn reject_with(mut self, headers: &Headers) -> Result<S, (S, IoError)> {
-		self.headers.extend(headers.iter());
-		match self.send(StatusCode::BadRequest) {
-			Ok(()) => Ok(self.stream),
-			Err(e) => Err((self.stream, e)),
-		}
-	}
-
 	/// Drop the connection without saying anything.
 	pub fn drop(self) {
 		::std::mem::drop(self);
@@ -183,131 +124,29 @@ impl<S> WsUpgrade<S>
 		self.request.headers.get::<Origin>().map(|o| &o.0 as &str)
 	}
 
-	fn send(&mut self, status: StatusCode) -> IoResult<()> {
+	#[cfg(feature="sync")]
+	fn send(&mut self, status: StatusCode) -> io::Result<()> {
 		try!(write!(&mut self.stream, "{} {}\r\n", self.request.version, status));
 		try!(write!(&mut self.stream, "{}\r\n", self.headers));
 		Ok(())
 	}
-}
-
-impl<S> WsUpgrade<S>
-    where S: Stream + AsTcpStream
-{
-	/// Get a handle to the underlying TCP stream, useful to be able to set
-	/// TCP options, etc.
-	pub fn tcp_stream(&self) -> &TcpStream {
-		self.stream.as_tcp()
-	}
-}
 
-/// Trait to take a stream or similar and attempt to recover the start of a
-/// websocket handshake from it.
-/// Should be used when a stream might contain a request for a websocket session.
-///
-/// If an upgrade request can be parsed, one can accept or deny the handshake with
-/// the `WsUpgrade` struct.
-/// Otherwise the original stream is returned along with an error.
-///
-/// Note: the stream is owned because the websocket client expects to own its stream.
-///
-/// This is already implemented for all Streams, which means all types with Read + Write.
-///
-/// # Example
-///
-/// ```rust,no_run
-/// use std::net::TcpListener;
-/// use std::net::TcpStream;
-/// use websocket::server::upgrade::IntoWs;
-/// use websocket::Client;
-///
-/// let listener = TcpListener::bind("127.0.0.1:80").unwrap();
-///
-/// for stream in listener.incoming().filter_map(Result::ok) {
-///     let mut client: Client<TcpStream> = match stream.into_ws() {
-/// 		    Ok(upgrade) => {
-///             match upgrade.accept() {
-///                 Ok(client) => client,
-///                 Err(_) => panic!(),
-///             }
-///         },
-/// 		    Err(_) => panic!(),
-///     };
-/// }
-/// ```
-pub trait IntoWs {
-	/// The type of stream this upgrade process is working with (TcpStream, etc.)
-	type Stream: Stream;
-	/// An error value in case the stream is not asking for a websocket connection
-	/// or something went wrong. It is common to also include the stream here.
-	type Error;
-	/// Attempt to parse the start of a Websocket handshake, later with the  returned
-	/// `WsUpgrade` struct, call `accept to start a websocket client, and `reject` to
-	/// send a handshake rejection response.
-	fn into_ws(self) -> Result<WsUpgrade<Self::Stream>, Self::Error>;
-}
-
-
-/// A typical request from hyper
-pub type Request = Incoming<(Method, RequestUri)>;
-/// If you have your requests separate from your stream you can use this struct
-/// to upgrade the connection based on the request given
-/// (the request should be a handshake).
-pub struct RequestStreamPair<S: Stream>(pub S, pub Request);
-
-impl<S> IntoWs for S
-    where S: Stream
-{
-	type Stream = S;
-	type Error = (S, Option<Request>, Option<Buffer>, HyperIntoWsError);
-
-	fn into_ws(self) -> Result<WsUpgrade<Self::Stream>, Self::Error> {
-		let mut reader = BufReader::new(self);
-		let request = parse_request(&mut reader);
-
-		let (stream, buf, pos, cap) = reader.into_parts();
-		let buffer = Some(Buffer {
-		                      buf: buf,
-		                      cap: cap,
-		                      pos: pos,
-		                  });
-
-		let request = match request {
-			Ok(r) => r,
-			Err(e) => return Err((stream, None, buffer, e.into())),
-		};
-
-		match validate(&request.subject.0, &request.version, &request.headers) {
-			Ok(_) => {
-				Ok(WsUpgrade {
-				       headers: Headers::new(),
-				       stream: stream,
-				       request: request,
-				       buffer: buffer,
-				   })
-			}
-			Err(e) => Err((stream, Some(request), buffer, e)),
+	#[doc(hidden)]
+	pub fn prepare_headers(&mut self, custom: Option<&Headers>) -> StatusCode {
+		if let Some(headers) = custom {
+			self.headers.extend(headers.iter());
 		}
-	}
-}
+		// NOTE: we know there is a key because this is a valid request
+		// i.e. to construct this you must go through the validate function
+		let key = self.request.headers.get::<WebSocketKey>().unwrap();
+		self.headers.set(WebSocketAccept::new(key));
+		self.headers
+		    .set(Connection(vec![
+			          ConnectionOption::ConnectionHeader(UniCase("Upgrade".to_string()))
+		        ]));
+		self.headers.set(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)]));
 
-impl<S> IntoWs for RequestStreamPair<S>
-    where S: Stream
-{
-	type Stream = S;
-	type Error = (S, Request, HyperIntoWsError);
-
-	fn into_ws(self) -> Result<WsUpgrade<Self::Stream>, Self::Error> {
-		match validate(&self.1.subject.0, &self.1.version, &self.1.headers) {
-			Ok(_) => {
-				Ok(WsUpgrade {
-				       headers: Headers::new(),
-				       stream: self.0,
-				       request: self.1,
-				       buffer: None,
-				   })
-			}
-			Err(e) => Err((self.0, self.1, e)),
-		}
+		StatusCode::SwitchingProtocols
 	}
 }
 
@@ -381,6 +220,17 @@ impl From<::hyper::error::Error> for HyperIntoWsError {
 	}
 }
 
+#[cfg(feature="async")]
+impl From<::codec::http::HttpCodecError> for HyperIntoWsError {
+	fn from(src: ::codec::http::HttpCodecError) -> Self {
+		match src {
+			::codec::http::HttpCodecError::Io(e) => HyperIntoWsError::Io(e),
+			::codec::http::HttpCodecError::Http(e) => HyperIntoWsError::Parsing(e),
+		}
+	}
+}
+
+#[cfg(any(feature="sync", feature="async"))]
 fn validate(
 	method: &Method,
 	version: &HttpVersion,
diff --git a/src/server/upgrade/sync.rs b/src/server/upgrade/sync.rs
new file mode 100644
index 0000000000..7504b61ad7
--- /dev/null
+++ b/src/server/upgrade/sync.rs
@@ -0,0 +1,286 @@
+//! Allows you to take an existing request or stream of data and convert it into a
+//! WebSocket client.
+use std::io;
+use std::net::TcpStream;
+use stream::sync::{Stream, AsTcpStream};
+use server::upgrade::{Request, WsUpgrade, HyperIntoWsError, validate};
+use client::sync::Client;
+
+use hyper::status::StatusCode;
+use hyper::http::h1::Incoming;
+use hyper::buffer::BufReader;
+use hyper::http::h1::parse_request;
+use hyper::header::Headers;
+use hyper::net::NetworkStream;
+
+/// This crate uses buffered readers to read in the handshake quickly, in order to
+/// interface with other use cases that don't use buffered readers the buffered readers
+/// is deconstructed when it is returned to the user and given as the underlying
+/// reader and the buffer.
+///
+/// This struct represents bytes that have already been read in from the stream.
+/// A slice of valid data in this buffer can be obtained by: `&buf[pos..cap]`.
+#[derive(Debug)]
+pub struct Buffer {
+	/// the contents of the buffered stream data
+	pub buf: Vec<u8>,
+	/// the current position of cursor in the buffer
+	/// Any data before `pos` has already been read and parsed.
+	pub pos: usize,
+	/// the last location of valid data
+	/// Any data after `cap` is not valid.
+	pub cap: usize,
+}
+
+/// If you have your requests separate from your stream you can use this struct
+/// to upgrade the connection based on the request given
+/// (the request should be a handshake).
+pub struct RequestStreamPair<S: Stream>(pub S, pub Request);
+
+/// The synchronous specialization of `WsUpgrade`.
+/// See the `WsUpgrade` docs for usage and the extra synchronous methods
+/// given by this specialization.
+pub type Upgrade<S> = WsUpgrade<S, Option<Buffer>>;
+
+/// These methods are the synchronous ways of accepting and rejecting a websocket
+/// handshake.
+impl<S> WsUpgrade<S, Option<Buffer>>
+    where S: Stream
+{
+	/// Accept the handshake request and send a response,
+	/// if nothing goes wrong a client will be created.
+	pub fn accept(self) -> Result<Client<S>, (S, io::Error)> {
+		self.internal_accept(None)
+	}
+
+	/// Accept the handshake request and send a response while
+	/// adding on a few headers. These headers are added before the required
+	/// headers are, so some might be overwritten.
+	pub fn accept_with(self, custom_headers: &Headers) -> Result<Client<S>, (S, io::Error)> {
+		self.internal_accept(Some(custom_headers))
+	}
+
+	fn internal_accept(mut self, headers: Option<&Headers>) -> Result<Client<S>, (S, io::Error)> {
+		let status = self.prepare_headers(headers);
+
+		if let Err(e) = self.send(status) {
+			return Err((self.stream, e));
+		}
+
+		let stream = match self.buffer {
+			Some(Buffer { buf, pos, cap }) => BufReader::from_parts(self.stream, buf, pos, cap),
+			None => BufReader::new(self.stream),
+		};
+
+		Ok(Client::unchecked(stream, self.headers, false, true))
+	}
+
+	/// Reject the client's request to make a websocket connection.
+	pub fn reject(self) -> Result<S, (S, io::Error)> {
+		self.internal_reject(None)
+	}
+
+	/// Reject the client's request to make a websocket connection
+	/// and send extra headers.
+	pub fn reject_with(self, headers: &Headers) -> Result<S, (S, io::Error)> {
+		self.internal_reject(Some(headers))
+	}
+
+	fn internal_reject(mut self, headers: Option<&Headers>) -> Result<S, (S, io::Error)> {
+		if let Some(custom) = headers {
+			self.headers.extend(custom.iter());
+		}
+		match self.send(StatusCode::BadRequest) {
+			Ok(()) => Ok(self.stream),
+			Err(e) => Err((self.stream, e)),
+		}
+	}
+}
+
+impl<S, B> WsUpgrade<S, B>
+    where S: Stream + AsTcpStream
+{
+	/// Get a handle to the underlying TCP stream, useful to be able to set
+	/// TCP options, etc.
+	pub fn tcp_stream(&self) -> &TcpStream {
+		self.stream.as_tcp()
+	}
+}
+
+/// Trait to take a stream or similar and attempt to recover the start of a
+/// websocket handshake from it.
+/// Should be used when a stream might contain a request for a websocket session.
+///
+/// If an upgrade request can be parsed, one can accept or deny the handshake with
+/// the `WsUpgrade` struct.
+/// Otherwise the original stream is returned along with an error.
+///
+/// Note: the stream is owned because the websocket client expects to own its stream.
+///
+/// This is already implemented for all Streams, which means all types with Read + Write.
+///
+/// # Example
+///
+/// ```rust,no_run
+/// use std::net::TcpListener;
+/// use std::net::TcpStream;
+/// use websocket::sync::server::upgrade::IntoWs;
+/// use websocket::sync::Client;
+///
+/// let listener = TcpListener::bind("127.0.0.1:80").unwrap();
+///
+/// for stream in listener.incoming().filter_map(Result::ok) {
+///     let mut client: Client<TcpStream> = match stream.into_ws() {
+/// 		    Ok(upgrade) => {
+///             match upgrade.accept() {
+///                 Ok(client) => client,
+///                 Err(_) => panic!(),
+///             }
+///         },
+/// 		    Err(_) => panic!(),
+///     };
+/// }
+/// ```
+pub trait IntoWs {
+	/// The type of stream this upgrade process is working with (TcpStream, etc.)
+	type Stream: Stream;
+	/// An error value in case the stream is not asking for a websocket connection
+	/// or something went wrong. It is common to also include the stream here.
+	type Error;
+	/// Attempt to parse the start of a Websocket handshake, later with the  returned
+	/// `WsUpgrade` struct, call `accept to start a websocket client, and `reject` to
+	/// send a handshake rejection response.
+	fn into_ws(self) -> Result<Upgrade<Self::Stream>, Self::Error>;
+}
+
+impl<S> IntoWs for S
+    where S: Stream
+{
+	type Stream = S;
+	type Error = (S, Option<Request>, Option<Buffer>, HyperIntoWsError);
+
+	fn into_ws(self) -> Result<Upgrade<Self::Stream>, Self::Error> {
+		let mut reader = BufReader::new(self);
+		let request = parse_request(&mut reader);
+
+		let (stream, buf, pos, cap) = reader.into_parts();
+		let buffer = Some(Buffer {
+		                      buf: buf,
+		                      cap: cap,
+		                      pos: pos,
+		                  });
+
+		let request = match request {
+			Ok(r) => r,
+			Err(e) => return Err((stream, None, buffer, e.into())),
+		};
+
+		match validate(&request.subject.0, &request.version, &request.headers) {
+			Ok(_) => {
+				Ok(WsUpgrade {
+				       headers: Headers::new(),
+				       stream: stream,
+				       request: request,
+				       buffer: buffer,
+				   })
+			}
+			Err(e) => Err((stream, Some(request), buffer, e)),
+		}
+	}
+}
+
+impl<S> IntoWs for RequestStreamPair<S>
+    where S: Stream
+{
+	type Stream = S;
+	type Error = (S, Request, HyperIntoWsError);
+
+	fn into_ws(self) -> Result<Upgrade<Self::Stream>, Self::Error> {
+		match validate(&self.1.subject.0, &self.1.version, &self.1.headers) {
+			Ok(_) => {
+				Ok(WsUpgrade {
+				       headers: Headers::new(),
+				       stream: self.0,
+				       request: self.1,
+				       buffer: None,
+				   })
+			}
+			Err(e) => Err((self.0, self.1, e)),
+		}
+	}
+}
+
+/// Upgrade a hyper connection to a websocket one.
+///
+/// A hyper request is implicitly defined as a stream from other `impl`s of Stream.
+/// Until trait impl specialization comes along, we use this struct to differentiate
+/// a hyper request (which already has parsed headers) from a normal stream.
+///
+/// Using this method, one can start a hyper server and check if each request
+/// is a websocket upgrade request, if so you can use websockets and hyper on the
+/// same port!
+///
+/// ```rust,no_run
+/// # extern crate hyper;
+/// # extern crate websocket;
+/// # fn main() {
+/// use hyper::server::{Server, Request, Response};
+/// use websocket::Message;
+/// use websocket::sync::server::upgrade::IntoWs;
+/// use websocket::sync::server::upgrade::HyperRequest;
+///
+/// Server::http("0.0.0.0:80").unwrap().handle(move |req: Request, res: Response| {
+///     match HyperRequest(req).into_ws() {
+///         Ok(upgrade) => {
+///             // `accept` sends a successful handshake, no need to worry about res
+///             let mut client = match upgrade.accept() {
+///                 Ok(c) => c,
+///                 Err(_) => panic!(),
+///             };
+///
+///             client.send_message(&Message::text("its free real estate"));
+///         },
+///
+///         Err((request, err)) => {
+///             // continue using the request as normal, "echo uri"
+///             res.send(b"Try connecting over ws instead.").unwrap();
+///         },
+///     };
+/// })
+/// .unwrap();
+/// # }
+/// ```
+pub struct HyperRequest<'a, 'b: 'a>(pub ::hyper::server::Request<'a, 'b>);
+
+impl<'a, 'b> IntoWs for HyperRequest<'a, 'b> {
+	type Stream = &'a mut &'b mut NetworkStream;
+	type Error = (::hyper::server::Request<'a, 'b>, HyperIntoWsError);
+
+	fn into_ws(self) -> Result<Upgrade<Self::Stream>, Self::Error> {
+		if let Err(e) = validate(&self.0.method, &self.0.version, &self.0.headers) {
+			return Err((self.0, e));
+		}
+
+		let (_, method, headers, uri, version, reader) =
+			self.0.deconstruct();
+
+		let reader = reader.into_inner();
+		let (buf, pos, cap) = reader.take_buf();
+		let stream = reader.get_mut();
+
+		Ok(Upgrade {
+		       headers: Headers::new(),
+		       stream: stream,
+		       buffer: Some(Buffer {
+		                        buf: buf,
+		                        pos: pos,
+		                        cap: cap,
+		                    }),
+		       request: Incoming {
+		           version: version,
+		           headers: headers,
+		           subject: (method, uri),
+		       },
+		   })
+	}
+}
diff --git a/src/stream.rs b/src/stream.rs
index abbb0d66e7..2e86b4c1c9 100644
--- a/src/stream.rs
+++ b/src/stream.rs
@@ -1,91 +1,14 @@
 //! Provides the default stream type for WebSocket connections.
 
-use std::ops::Deref;
-use std::fmt::Arguments;
 use std::io::{self, Read, Write};
-pub use std::net::TcpStream;
-pub use std::net::Shutdown;
-#[cfg(feature="ssl")]
-pub use openssl::ssl::{SslStream, SslContext};
+use std::fmt::Arguments;
 
 /// Represents a stream that can be read from, and written to.
 /// This is an abstraction around readable and writable things to be able
 /// to speak websockets over ssl, tcp, unix sockets, etc.
 pub trait Stream: Read + Write {}
-
 impl<S> Stream for S where S: Read + Write {}
 
-/// a `Stream` that can also be used as a borrow to a `TcpStream`
-/// this is useful when you want to set `TcpStream` options on a
-/// `Stream` like `nonblocking`.
-pub trait NetworkStream: Read + Write + AsTcpStream {}
-
-impl<S> NetworkStream for S where S: Read + Write + AsTcpStream {}
-
-/// some streams can be split up into separate reading and writing components
-/// `TcpStream` is an example. This trait marks this ability so one can split
-/// up the client into two parts.
-///
-/// Notice however that this is not possible to do with SSL.
-pub trait Splittable {
-	/// The reading component of this type
-	type Reader: Read;
-	/// The writing component of this type
-	type Writer: Write;
-
-	/// Split apart this type into a reading and writing component.
-	fn split(self) -> io::Result<(Self::Reader, Self::Writer)>;
-}
-
-impl<R, W> Splittable for ReadWritePair<R, W>
-	where R: Read,
-	      W: Write
-{
-	type Reader = R;
-	type Writer = W;
-
-	fn split(self) -> io::Result<(R, W)> {
-		Ok((self.0, self.1))
-	}
-}
-
-impl Splittable for TcpStream {
-	type Reader = TcpStream;
-	type Writer = TcpStream;
-
-	fn split(self) -> io::Result<(TcpStream, TcpStream)> {
-		self.try_clone().map(|s| (s, self))
-	}
-}
-
-/// The ability access a borrow to an underlying TcpStream,
-/// so one can set options on the stream such as `nonblocking`.
-pub trait AsTcpStream {
-	/// Get a borrow of the TcpStream
-	fn as_tcp(&self) -> &TcpStream;
-}
-
-impl AsTcpStream for TcpStream {
-	fn as_tcp(&self) -> &TcpStream {
-		self
-	}
-}
-
-#[cfg(feature="ssl")]
-impl AsTcpStream for SslStream<TcpStream> {
-	fn as_tcp(&self) -> &TcpStream {
-		self.get_ref()
-	}
-}
-
-impl<T> AsTcpStream for Box<T>
-    where T: AsTcpStream
-{
-	fn as_tcp(&self) -> &TcpStream {
-		self.deref().as_tcp()
-	}
-}
-
 /// If you would like to combine an input stream and an output stream into a single
 /// stream to talk websockets over then this is the struct for you!
 ///
@@ -137,3 +60,120 @@ impl<R, W> Write for ReadWritePair<R, W>
 		self.1.write_fmt(fmt)
 	}
 }
+
+/// A collection of traits and implementations for async streams.
+#[cfg(feature="async")]
+pub mod async {
+	use std::io::{self, Read, Write};
+	use futures::Poll;
+	pub use super::ReadWritePair;
+	pub use tokio_core::net::TcpStream;
+	pub use tokio_io::{AsyncWrite, AsyncRead};
+	pub use tokio_io::io::{ReadHalf, WriteHalf};
+
+	/// A stream that can be read from and written to asynchronously.
+	/// This let's us abstract over many async streams like tcp, ssl,
+	/// udp, ssh, etc.
+	pub trait Stream: AsyncRead + AsyncWrite {}
+	impl<S> Stream for S where S: AsyncRead + AsyncWrite {}
+
+	impl<R, W> AsyncRead for ReadWritePair<R, W>
+		where R: AsyncRead,
+		      W: Write
+	{
+	}
+
+	impl<R, W> AsyncWrite for ReadWritePair<R, W>
+		where W: AsyncWrite,
+		      R: Read
+	{
+		fn shutdown(&mut self) -> Poll<(), io::Error> {
+			self.1.shutdown()
+		}
+	}
+}
+
+/// A collection of traits and implementations for synchronous streams.
+#[cfg(feature="sync")]
+pub mod sync {
+	pub use super::ReadWritePair;
+	use std::io::{self, Read, Write};
+	use std::ops::Deref;
+	pub use std::net::TcpStream;
+	pub use std::net::Shutdown;
+	#[cfg(feature="sync-ssl")]
+	pub use native_tls::TlsStream;
+
+	pub use super::Stream;
+
+	/// a `Stream` that can also be used as a borrow to a `TcpStream`
+	/// this is useful when you want to set `TcpStream` options on a
+	/// `Stream` like `nonblocking`.
+	pub trait NetworkStream: Read + Write + AsTcpStream {}
+
+	impl<S> NetworkStream for S where S: Read + Write + AsTcpStream {}
+
+	/// some streams can be split up into separate reading and writing components
+	/// `TcpStream` is an example. This trait marks this ability so one can split
+	/// up the client into two parts.
+	///
+	/// Notice however that this is not possible to do with SSL.
+	pub trait Splittable {
+		/// The reading component of this type
+		type Reader: Read;
+		/// The writing component of this type
+		type Writer: Write;
+
+		/// Split apart this type into a reading and writing component.
+		fn split(self) -> io::Result<(Self::Reader, Self::Writer)>;
+	}
+
+	impl<R, W> Splittable for ReadWritePair<R, W>
+		where R: Read,
+		      W: Write
+	{
+		type Reader = R;
+		type Writer = W;
+
+		fn split(self) -> io::Result<(R, W)> {
+			Ok((self.0, self.1))
+		}
+	}
+
+	impl Splittable for TcpStream {
+		type Reader = TcpStream;
+		type Writer = TcpStream;
+
+		fn split(self) -> io::Result<(TcpStream, TcpStream)> {
+			self.try_clone().map(|s| (s, self))
+		}
+	}
+
+	/// The ability access a borrow to an underlying TcpStream,
+	/// so one can set options on the stream such as `nonblocking`.
+	pub trait AsTcpStream {
+		/// Get a borrow of the TcpStream
+		fn as_tcp(&self) -> &TcpStream;
+	}
+
+	impl AsTcpStream for TcpStream {
+		fn as_tcp(&self) -> &TcpStream {
+			self
+		}
+	}
+
+	#[cfg(feature="sync-ssl")]
+	impl AsTcpStream for TlsStream<TcpStream> {
+		fn as_tcp(&self) -> &TcpStream {
+			self.get_ref()
+		}
+	}
+
+	impl<T> AsTcpStream for Box<T>
+        where T: AsTcpStream
+	{
+		fn as_tcp(&self) -> &TcpStream {
+			self.deref().as_tcp()
+		}
+	}
+}
diff --git a/src/ws/dataframe.rs b/src/ws/dataframe.rs
index a82a712b2d..b42e1ad862 100644
--- a/src/ws/dataframe.rs
+++ b/src/ws/dataframe.rs
@@ -3,7 +3,6 @@
 //! optomize the memory footprint of a dataframe for their
 //! own needs, and be able to use custom dataframes quickly
 use std::io::Write;
-use std::borrow::Cow;
 use result::WebSocketResult;
 use ws::util::header as dfh;
 use ws::util::mask::Masker;
@@ -19,28 +18,39 @@ pub trait DataFrame {
 	fn opcode(&self) -> u8;
 	/// Reserved bits of this dataframe
 	fn reserved(&self) -> &[bool; 3];
-	/// Entire payload of the dataframe. If not known then implement
-	/// write_payload as that is the actual method used when sending the
-	/// dataframe over the wire.
-	fn payload(&self) -> Cow<[u8]>;
 
 	/// How long (in bytes) is this dataframe's payload
-	fn size(&self) -> usize {
-		self.payload().len()
+	fn size(&self) -> usize;
+
+	/// Get's the size of the entire dataframe in bytes,
+	/// i.e. header and payload.
+	fn frame_size(&self, masked: bool) -> usize {
+		// one byte for the opcode & reserved & fin
+		1
+        // depending on the size of the payload, add the right payload len bytes
+        + match self.size() {
+            s if s <= 125 => 1,
+            s if s <= 65535 => 3,
+            _ => 9,
+        }
+        // add the mask size if there is one
+        + if masked {
+            4
+        } else {
+            0
+        }
+        // finally add the payload len
+        + self.size()
 	}
 
 	/// Write the payload to a writer
-	fn write_payload<W>(&self, socket: &mut W) -> WebSocketResult<()>
-		where W: Write
-	{
-		try!(socket.write_all(&*self.payload()));
-		Ok(())
-	}
+	fn write_payload(&self, socket: &mut Write) -> WebSocketResult<()>;
+
+	/// Takes the payload out into a vec
+	fn take_payload(self) -> Vec<u8>;
 
 	/// Writes a DataFrame to a Writer.
-	fn write_to<W>(&self, writer: &mut W, mask: bool) -> WebSocketResult<()>
-		where W: Write
-	{
+	fn write_to(&self, writer: &mut Write, mask: bool) -> WebSocketResult<()> {
 		let mut flags = dfh::DataFrameFlags::empty();
 		if self.is_last() {
 			flags.insert(dfh::FIN);
@@ -80,46 +90,3 @@ pub trait DataFrame {
 		Ok(())
 	}
 }
-
-impl<'a, D> DataFrame for &'a D
-    where D: DataFrame
-{
-	#[inline(always)]
-	fn is_last(&self) -> bool {
-		D::is_last(self)
-	}
-
-	#[inline(always)]
-	fn opcode(&self) -> u8 {
-		D::opcode(self)
-	}
-
-	#[inline(always)]
-	fn reserved(&self) -> &[bool; 3] {
-		D::reserved(self)
-	}
-
-	#[inline(always)]
-	fn payload(&self) -> Cow<[u8]> {
-		D::payload(self)
-	}
-
-	#[inline(always)]
-	fn size(&self) -> usize {
-		D::size(self)
-	}
-
-	#[inline(always)]
-	fn write_payload<W>(&self, socket: &mut W) -> WebSocketResult<()>
-		where W: Write
-	{
-		D::write_payload(self, socket)
-	}
-
-	#[inline(always)]
-	fn write_to<W>(&self, writer: &mut W, mask: bool) -> WebSocketResult<()>
-		where W: Write
-	{
-		D::write_to(self, writer, mask)
-	}
-}
diff --git a/src/ws/message.rs b/src/ws/message.rs
index dc02aa1a63..1ab299b424 100644
--- a/src/ws/message.rs
+++ b/src/ws/message.rs
@@ -2,17 +2,18 @@
 //!
 //! See the `ws` module documentation for more information.
 
+use std::io::Write;
+use ws::dataframe::DataFrame as DataFrameable;
 use result::WebSocketResult;
-use ws::dataframe::DataFrame;
 
 /// A trait for WebSocket messages
-pub trait Message<'a, F>: Sized
-	where F: DataFrame
-{
-	/// The iterator type returned by dataframes
-	type DataFrameIterator: Iterator<Item = F>;
-	/// Attempt to form a message from a slice of data frames.
-	fn from_dataframes<D>(frames: Vec<D>) -> WebSocketResult<Self> where D: DataFrame;
-	/// Turns this message into an iterator over data frames
-	fn dataframes(&'a self) -> Self::DataFrameIterator;
+pub trait Message: Sized {
+	/// Writes this message to the writer
+	fn serialize(&self, &mut Write, masked: bool) -> WebSocketResult<()>;
+
+	/// Returns how many bytes this message will take up
+	fn message_size(&self, masked: bool) -> usize;
+
+	/// Attempt to form a message from a series of data frames
+	fn from_dataframes<D: DataFrameable>(frames: Vec<D>) -> WebSocketResult<Self>;
 }
diff --git a/src/ws/mod.rs b/src/ws/mod.rs
index bb70489e27..dd3ee71361 100644
--- a/src/ws/mod.rs
+++ b/src/ws/mod.rs
@@ -38,12 +38,19 @@
 //! To make life easier for a `Receiver`, several utility functions are provided which read
 //! various pieces of data from a Reader. These are found within the `util` module.
 pub use self::message::Message;
+
+#[cfg(feature="sync")]
 pub use self::sender::Sender;
+#[cfg(feature="sync")]
 pub use self::receiver::Receiver;
+#[cfg(feature="sync")]
 pub use self::receiver::{DataFrameIterator, MessageIterator};
 
 pub mod message;
-pub mod sender;
-pub mod receiver;
 pub mod util;
 pub mod dataframe;
+
+#[cfg(feature="sync")]
+pub mod sender;
+#[cfg(feature="sync")]
+pub mod receiver;
diff --git a/src/ws/receiver.rs b/src/ws/receiver.rs
index 3e237166da..4f291f99d9 100644
--- a/src/ws/receiver.rs
+++ b/src/ws/receiver.rs
@@ -4,7 +4,6 @@
 //! See the `ws` module documentation for more information.
 
 use std::io::Read;
-use std::marker::PhantomData;
 use ws::Message;
 use ws::dataframe::DataFrame;
 use result::WebSocketResult;
@@ -14,6 +13,9 @@ pub trait Receiver: Sized {
 	/// The type of dataframe that incoming messages will be serialized to.
 	type F: DataFrame;
 
+	/// The type of message that incoming messages will be serialized to.
+	type M: Message;
+
 	/// Reads a single data frame from this receiver.
 	fn recv_dataframe<R>(&mut self, reader: &mut R) -> WebSocketResult<Self::F> where R: Read;
 
@@ -32,30 +34,20 @@ pub trait Receiver: Sized {
 	}
 
 	/// Reads a single message from this receiver.
-	fn recv_message<'m, D, M, I, R>(&mut self, reader: &mut R) -> WebSocketResult<M>
-		where M: Message<'m, D, DataFrameIterator = I>,
-		      I: Iterator<Item = D>,
-		      D: DataFrame,
-		      R: Read
+	fn recv_message<'m, R>(&mut self, reader: &mut R) -> WebSocketResult<Self::M>
+		where R: Read
 	{
-		let dataframes = try!(self.recv_message_dataframes(reader));
-		Message::from_dataframes(dataframes)
+		let dataframes = self.recv_message_dataframes(reader)?;
+		Self::M::from_dataframes(dataframes)
 	}
 
 	/// Returns an iterator over incoming messages.
-	fn incoming_messages<'a, M, D, R>(
-		&'a mut self,
-		reader: &'a mut R,
-	) -> MessageIterator<'a, Self, D, M, R>
-		where M: Message<'a, D>,
-		      D: DataFrame,
-		      R: Read
+	fn incoming_messages<'a, R>(&'a mut self, reader: &'a mut R) -> MessageIterator<'a, Self, R>
+		where R: Read
 	{
 		MessageIterator {
 			reader: reader,
 			inner: self,
-			_dataframe: PhantomData,
-			_message: PhantomData,
 		}
 	}
 }
@@ -82,29 +74,22 @@ impl<'a, Recv, R> Iterator for DataFrameIterator<'a, Recv, R>
 }
 
 /// An iterator over messages from a Receiver.
-pub struct MessageIterator<'a, Recv, D, M, R>
+pub struct MessageIterator<'a, Recv, R>
 	where Recv: 'a + Receiver,
-	      M: Message<'a, D>,
-	      D: DataFrame,
 	      R: 'a + Read
 {
 	reader: &'a mut R,
 	inner: &'a mut Recv,
-	_dataframe: PhantomData<D>,
-	_message: PhantomData<M>,
 }
 
-impl<'a, Recv, D, M, I, R> Iterator for MessageIterator<'a, Recv, D, M, R>
+impl<'a, Recv, R> Iterator for MessageIterator<'a, Recv, R>
 	where Recv: 'a + Receiver,
-	      M: Message<'a, D, DataFrameIterator = I>,
-	      I: Iterator<Item = D>,
-	      D: DataFrame,
 	      R: Read
 {
-	type Item = WebSocketResult<M>;
+	type Item = WebSocketResult<Recv::M>;
 
 	/// Get the next message from the receiver. Always returns `Some`.
-	fn next(&mut self) -> Option<WebSocketResult<M>> {
+	fn next(&mut self) -> Option<WebSocketResult<Recv::M>> {
 		Some(self.inner.recv_message(self.reader))
 	}
 }
diff --git a/src/ws/sender.rs b/src/ws/sender.rs
index c179fd275d..024b78ad4a 100644
--- a/src/ws/sender.rs
+++ b/src/ws/sender.rs
@@ -9,20 +9,26 @@ use result::WebSocketResult;
 
 /// A trait for sending data frames and messages.
 pub trait Sender {
+	/// Should the messages sent be masked.
+	/// See the [RFC](https://tools.ietf.org/html/rfc6455#section-5.3)
+	/// for more detail.
+	fn is_masked(&self) -> bool;
+
 	/// Sends a single data frame using this sender.
 	fn send_dataframe<D, W>(&mut self, writer: &mut W, dataframe: &D) -> WebSocketResult<()>
 		where D: DataFrame,
-		      W: Write;
+		      W: Write
+	{
+		dataframe.write_to(writer, self.is_masked())?;
+		Ok(())
+	}
 
 	/// Sends a single message using this sender.
-	fn send_message<'m, M, D, W>(&mut self, writer: &mut W, message: &'m M) -> WebSocketResult<()>
-		where M: Message<'m, D>,
-		      D: DataFrame,
+	fn send_message<M, W>(&mut self, writer: &mut W, message: &M) -> WebSocketResult<()>
+		where M: Message,
 		      W: Write
 	{
-		for ref dataframe in message.dataframes() {
-			try!(self.send_dataframe(writer, dataframe));
-		}
+		message.serialize(writer, self.is_masked())?;
 		Ok(())
 	}
 }
diff --git a/src/ws/util/header.rs b/src/ws/util/header.rs
index 89f3de10a4..a69cc62b4e 100644
--- a/src/ws/util/header.rs
+++ b/src/ws/util/header.rs
@@ -32,9 +32,7 @@ pub struct DataFrameHeader {
 }
 
 /// Writes a data frame header.
-pub fn write_header<W>(writer: &mut W, header: DataFrameHeader) -> WebSocketResult<()>
-	where W: Write
-{
+pub fn write_header(writer: &mut Write, header: DataFrameHeader) -> WebSocketResult<()> {
 
 	if header.opcode > 0xF {
 		return Err(WebSocketError::DataFrameError("Invalid data frame opcode"));
diff --git a/src/ws/util/mask.rs b/src/ws/util/mask.rs
index 114727881a..66c552a29d 100644
--- a/src/ws/util/mask.rs
+++ b/src/ws/util/mask.rs
@@ -6,20 +6,16 @@ use std::mem;
 
 /// Struct to pipe data into another writer,
 /// while masking the data being written
-pub struct Masker<'w, W>
-	where W: Write + 'w
-{
+pub struct Masker<'w> {
 	key: [u8; 4],
 	pos: usize,
-	end: &'w mut W,
+	end: &'w mut Write,
 }
 
-impl<'w, W> Masker<'w, W>
-    where W: Write + 'w
-{
+impl<'w> Masker<'w> {
 	/// Create a new Masker with the key and the endpoint
 	/// to be writter to.
-	pub fn new(key: [u8; 4], endpoint: &'w mut W) -> Self {
+	pub fn new(key: [u8; 4], endpoint: &'w mut Write) -> Self {
 		Masker {
 			key: key,
 			pos: 0,
@@ -28,9 +24,7 @@ impl<'w, W> Masker<'w, W>
 	}
 }
 
-impl<'w, W> Write for Masker<'w, W>
-    where W: Write + 'w
-{
+impl<'w> Write for Masker<'w> {
 	fn write(&mut self, data: &[u8]) -> IoResult<usize> {
 		let mut buf = Vec::with_capacity(data.len());
 		for &byte in data.iter() {


Test patch: