1
0
mirror of https://github.com/actix/actix-extras.git synced 2025-06-26 18:37:41 +02:00

move websocket code to submodule

This commit is contained in:
Nikolay Kim
2018-01-10 11:13:29 -08:00
parent d85081b64e
commit 3f3dcf413b
5 changed files with 22 additions and 22 deletions

256
src/ws/context.rs Normal file
View File

@ -0,0 +1,256 @@
use std::mem;
use std::collections::VecDeque;
use futures::{Async, Poll};
use futures::sync::oneshot::Sender;
use futures::unsync::oneshot;
use actix::{Actor, ActorState, ActorContext, AsyncContext,
Address, SyncAddress, Handler, Subscriber, ResponseType, SpawnHandle};
use actix::fut::ActorFuture;
use actix::dev::{queue, AsyncContextApi,
ContextImpl, ContextProtocol, Envelope, ToEnvelope, RemoteEnvelope};
use body::{Body, Binary};
use error::{Error, Result, ErrorInternalServerError};
use httprequest::HttpRequest;
use context::{Frame as ContextFrame, ActorHttpContext, Drain};
use ws::frame::Frame;
use ws::proto::{OpCode, CloseCode};
/// Http actor execution context
pub struct WebsocketContext<A, S=()> where A: Actor<Context=WebsocketContext<A, S>>,
{
inner: ContextImpl<A>,
stream: VecDeque<ContextFrame>,
request: HttpRequest<S>,
disconnected: bool,
}
impl<A, S> ActorContext for WebsocketContext<A, S> where A: Actor<Context=Self>
{
fn stop(&mut self) {
self.inner.stop();
}
fn terminate(&mut self) {
self.inner.terminate()
}
fn state(&self) -> ActorState {
self.inner.state()
}
}
impl<A, S> AsyncContext<A> for WebsocketContext<A, S> where A: Actor<Context=Self>
{
fn spawn<F>(&mut self, fut: F) -> SpawnHandle
where F: ActorFuture<Item=(), Error=(), Actor=A> + 'static
{
self.inner.spawn(fut)
}
fn wait<F>(&mut self, fut: F)
where F: ActorFuture<Item=(), Error=(), Actor=A> + 'static
{
self.inner.wait(fut)
}
fn cancel_future(&mut self, handle: SpawnHandle) -> bool {
self.inner.cancel_future(handle)
}
}
#[doc(hidden)]
impl<A, S> AsyncContextApi<A> for WebsocketContext<A, S> where A: Actor<Context=Self> {
#[inline]
fn unsync_sender(&mut self) -> queue::unsync::UnboundedSender<ContextProtocol<A>> {
self.inner.unsync_sender()
}
#[inline]
fn unsync_address(&mut self) -> Address<A> {
self.inner.unsync_address()
}
#[inline]
fn sync_address(&mut self) -> SyncAddress<A> {
self.inner.sync_address()
}
}
impl<A, S: 'static> WebsocketContext<A, S> where A: Actor<Context=Self> {
#[inline]
pub fn new(req: HttpRequest<S>, actor: A) -> WebsocketContext<A, S> {
WebsocketContext::from_request(req).actor(actor)
}
pub fn from_request(req: HttpRequest<S>) -> WebsocketContext<A, S> {
WebsocketContext {
inner: ContextImpl::new(None),
stream: VecDeque::new(),
request: req,
disconnected: false,
}
}
#[inline]
pub fn actor(mut self, actor: A) -> WebsocketContext<A, S> {
self.inner.set_actor(actor);
self
}
}
impl<A, S> WebsocketContext<A, S> where A: Actor<Context=Self> {
/// Write payload
#[inline]
fn write<B: Into<Binary>>(&mut self, data: B) {
if !self.disconnected {
self.stream.push_back(ContextFrame::Payload(Some(data.into())));
} else {
warn!("Trying to write to disconnected response");
}
}
/// Shared application state
#[inline]
pub fn state(&self) -> &S {
self.request.state()
}
/// Incoming request
#[inline]
pub fn request(&mut self) -> &mut HttpRequest<S> {
&mut self.request
}
/// Send text frame
pub fn text(&mut self, text: &str) {
let mut frame = Frame::message(Vec::from(text), OpCode::Text, true);
let mut buf = Vec::new();
frame.format(&mut buf).unwrap();
self.write(buf);
}
/// Send binary frame
pub fn binary<B: Into<Binary>>(&mut self, data: B) {
let mut frame = Frame::message(data, OpCode::Binary, true);
let mut buf = Vec::new();
frame.format(&mut buf).unwrap();
self.write(buf);
}
/// Send ping frame
pub fn ping(&mut self, message: &str) {
let mut frame = Frame::message(Vec::from(message), OpCode::Ping, true);
let mut buf = Vec::new();
frame.format(&mut buf).unwrap();
self.write(buf);
}
/// Send pong frame
pub fn pong(&mut self, message: &str) {
let mut frame = Frame::message(Vec::from(message), OpCode::Pong, true);
let mut buf = Vec::new();
frame.format(&mut buf).unwrap();
self.write(buf);
}
/// Send close frame
pub fn close(&mut self, code: CloseCode, reason: &str) {
let mut frame = Frame::close(code, reason);
let mut buf = Vec::new();
frame.format(&mut buf).unwrap();
self.write(buf);
}
/// Returns drain future
pub fn drain(&mut self) -> Drain<A> {
let (tx, rx) = oneshot::channel();
self.inner.modify();
self.stream.push_back(ContextFrame::Drain(tx));
Drain::new(rx)
}
/// Check if connection still open
#[inline]
pub fn connected(&self) -> bool {
!self.disconnected
}
}
impl<A, S> WebsocketContext<A, S> where A: Actor<Context=Self> {
#[inline]
#[doc(hidden)]
pub fn subscriber<M>(&mut self) -> Box<Subscriber<M>>
where A: Handler<M>, M: ResponseType + 'static
{
self.inner.subscriber()
}
#[inline]
#[doc(hidden)]
pub fn sync_subscriber<M>(&mut self) -> Box<Subscriber<M> + Send>
where A: Handler<M>,
M: ResponseType + Send + 'static, M::Item: Send, M::Error: Send,
{
self.inner.sync_subscriber()
}
}
impl<A, S> ActorHttpContext for WebsocketContext<A, S> where A: Actor<Context=Self>, S: 'static {
#[inline]
fn disconnected(&mut self) {
self.disconnected = true;
self.stop();
}
fn poll(&mut self) -> Poll<Option<ContextFrame>, Error> {
let ctx: &mut WebsocketContext<A, S> = unsafe {
mem::transmute(self as &mut WebsocketContext<A, S>)
};
if self.inner.alive() {
match self.inner.poll(ctx) {
Ok(Async::NotReady) | Ok(Async::Ready(())) => (),
Err(_) => return Err(ErrorInternalServerError("error").into()),
}
}
// frames
if let Some(frame) = self.stream.pop_front() {
Ok(Async::Ready(Some(frame)))
} else if self.inner.alive() {
Ok(Async::NotReady)
} else {
Ok(Async::Ready(None))
}
}
}
impl<A, S> ToEnvelope<A> for WebsocketContext<A, S>
where A: Actor<Context=WebsocketContext<A, S>>,
{
#[inline]
fn pack<M>(msg: M, tx: Option<Sender<Result<M::Item, M::Error>>>,
channel_on_drop: bool) -> Envelope<A>
where A: Handler<M>,
M: ResponseType + Send + 'static, M::Item: Send, M::Error: Send {
RemoteEnvelope::new(msg, tx, channel_on_drop).into()
}
}
impl<A, S> From<WebsocketContext<A, S>> for Body
where A: Actor<Context=WebsocketContext<A, S>>, S: 'static
{
fn from(ctx: WebsocketContext<A, S>) -> Body {
Body::Actor(Box::new(ctx))
}
}

432
src/ws/frame.rs Normal file
View File

@ -0,0 +1,432 @@
use std::{fmt, mem};
use std::io::{Write, Error, ErrorKind};
use std::iter::FromIterator;
use bytes::BytesMut;
use body::Binary;
use ws::proto::{OpCode, CloseCode};
fn apply_mask(buf: &mut [u8], mask: &[u8; 4]) {
let iter = buf.iter_mut().zip(mask.iter().cycle());
for (byte, &key) in iter {
*byte ^= key
}
}
/// A struct representing a `WebSocket` frame.
#[derive(Debug)]
pub(crate) struct Frame {
finished: bool,
rsv1: bool,
rsv2: bool,
rsv3: bool,
opcode: OpCode,
mask: Option<[u8; 4]>,
payload: Binary,
}
impl Frame {
/// Desctructe frame
pub fn unpack(self) -> (bool, OpCode, Binary) {
(self.finished, self.opcode, self.payload)
}
/// Get the length of the frame.
/// This is the length of the header + the length of the payload.
#[inline]
pub fn len(&self) -> usize {
let mut header_length = 2;
let payload_len = self.payload.len();
if payload_len > 125 {
if payload_len <= u16::max_value() as usize {
header_length += 2;
} else {
header_length += 8;
}
}
if self.mask.is_some() {
header_length += 4;
}
header_length + payload_len
}
/// Create a new data frame.
#[inline]
pub fn message<B: Into<Binary>>(data: B, code: OpCode, finished: bool) -> Frame {
Frame {
finished: finished,
opcode: code,
payload: data.into(),
.. Frame::default()
}
}
/// Create a new Close control frame.
#[inline]
pub fn close(code: CloseCode, reason: &str) -> Frame {
let raw: [u8; 2] = unsafe {
let u: u16 = code.into();
mem::transmute(u.to_be())
};
let payload = if let CloseCode::Empty = code {
Vec::new()
} else {
Vec::from_iter(
raw[..].iter()
.chain(reason.as_bytes().iter())
.cloned())
};
Frame {
payload: payload.into(),
.. Frame::default()
}
}
/// Parse the input stream into a frame.
pub fn parse(buf: &mut BytesMut) -> Result<Option<Frame>, Error> {
let mut idx = 2;
let (frame, length) = {
let mut size = buf.len();
if size < 2 {
return Ok(None)
}
let mut head = [0u8; 2];
size -= 2;
head.copy_from_slice(&buf[..2]);
trace!("Parsed headers {:?}", head);
let first = head[0];
let second = head[1];
trace!("First: {:b}", first);
trace!("Second: {:b}", second);
let finished = first & 0x80 != 0;
let rsv1 = first & 0x40 != 0;
let rsv2 = first & 0x20 != 0;
let rsv3 = first & 0x10 != 0;
let opcode = OpCode::from(first & 0x0F);
trace!("Opcode: {:?}", opcode);
let masked = second & 0x80 != 0;
trace!("Masked: {:?}", masked);
let mut header_length = 2;
let mut length = u64::from(second & 0x7F);
if length == 126 {
if size < 2 {
return Ok(None)
}
let mut length_bytes = [0u8; 2];
length_bytes.copy_from_slice(&buf[idx..idx+2]);
size -= 2;
idx += 2;
length = u64::from(unsafe{
let mut wide: u16 = mem::transmute(length_bytes);
wide = u16::from_be(wide);
wide});
header_length += 2;
} else if length == 127 {
if size < 8 {
return Ok(None)
}
let mut length_bytes = [0u8; 8];
length_bytes.copy_from_slice(&buf[idx..idx+8]);
size -= 8;
idx += 8;
unsafe { length = mem::transmute(length_bytes); }
length = u64::from_be(length);
header_length += 8;
}
trace!("Payload length: {}", length);
let mask = if masked {
let mut mask_bytes = [0u8; 4];
if size < 4 {
return Ok(None)
} else {
header_length += 4;
size -= 4;
mask_bytes.copy_from_slice(&buf[idx..idx+4]);
idx += 4;
Some(mask_bytes)
}
} else {
None
};
let length = length as usize;
if size < length {
return Ok(None)
}
let mut data = Vec::with_capacity(length);
if length > 0 {
data.extend_from_slice(&buf[idx..idx+length]);
}
// Disallow bad opcode
if let OpCode::Bad = opcode {
return Err(
Error::new(
ErrorKind::Other,
format!("Encountered invalid opcode: {}", first & 0x0F)))
}
// control frames must have length <= 125
match opcode {
OpCode::Ping | OpCode::Pong if length > 125 => {
return Err(
Error::new(
ErrorKind::Other,
format!("Rejected WebSocket handshake.Received control frame with length: {}.", length)))
}
OpCode::Close if length > 125 => {
debug!("Received close frame with payload length exceeding 125. Morphing to protocol close frame.");
return Ok(Some(Frame::close(CloseCode::Protocol, "Received close frame with payload length exceeding 125.")))
}
_ => ()
}
// unmask
if let Some(ref mask) = mask {
apply_mask(&mut data, mask);
}
let frame = Frame {
finished: finished,
rsv1: rsv1,
rsv2: rsv2,
rsv3: rsv3,
opcode: opcode,
mask: mask,
payload: data.into(),
};
(frame, header_length + length)
};
buf.split_to(length);
Ok(Some(frame))
}
/// Write a frame out to a buffer
pub fn format<W>(&mut self, w: &mut W) -> Result<(), Error>
where W: Write
{
let mut one = 0u8;
let code: u8 = self.opcode.into();
if self.finished {
one |= 0x80;
}
if self.rsv1 {
one |= 0x40;
}
if self.rsv2 {
one |= 0x20;
}
if self.rsv3 {
one |= 0x10;
}
one |= code;
let mut two = 0u8;
if self.mask.is_some() {
two |= 0x80;
}
if self.payload.len() < 126 {
two |= self.payload.len() as u8;
let headers = [one, two];
w.write_all(&headers)?;
} else if self.payload.len() <= 65_535 {
two |= 126;
let length_bytes: [u8; 2] = unsafe {
let short = self.payload.len() as u16;
mem::transmute(short.to_be())
};
let headers = [one, two, length_bytes[0], length_bytes[1]];
w.write_all(&headers)?;
} else {
two |= 127;
let length_bytes: [u8; 8] = unsafe {
let long = self.payload.len() as u64;
mem::transmute(long.to_be())
};
let headers = [
one,
two,
length_bytes[0],
length_bytes[1],
length_bytes[2],
length_bytes[3],
length_bytes[4],
length_bytes[5],
length_bytes[6],
length_bytes[7],
];
w.write_all(&headers)?;
}
if self.mask.is_some() {
let mask = self.mask.take().unwrap();
let mut payload = Vec::from(self.payload.as_ref());
apply_mask(&mut payload, &mask);
w.write_all(&mask)?;
w.write_all(payload.as_ref())?;
} else {
w.write_all(self.payload.as_ref())?;
}
Ok(())
}
}
impl Default for Frame {
fn default() -> Frame {
Frame {
finished: true,
rsv1: false,
rsv2: false,
rsv3: false,
opcode: OpCode::Close,
mask: None,
payload: Binary::from(&b""[..]),
}
}
}
impl fmt::Display for Frame {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f,
"
<FRAME>
final: {}
reserved: {} {} {}
opcode: {}
length: {}
payload length: {}
payload: 0x{}
</FRAME>",
self.finished,
self.rsv1,
self.rsv2,
self.rsv3,
self.opcode,
// self.mask.map(|mask| format!("{:?}", mask)).unwrap_or("NONE".into()),
self.len(),
self.payload.len(),
self.payload.as_ref().iter().map(
|byte| format!("{:x}", byte)).collect::<String>())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse() {
let mut buf = BytesMut::from(&[0b00000001u8, 0b00000001u8][..]);
assert!(Frame::parse(&mut buf).unwrap().is_none());
buf.extend(b"1");
let frame = Frame::parse(&mut buf).unwrap().unwrap();
println!("FRAME: {}", frame);
assert!(!frame.finished);
assert_eq!(frame.opcode, OpCode::Text);
assert_eq!(frame.payload.as_ref(), &b"1"[..]);
}
#[test]
fn test_parse_length0() {
let mut buf = BytesMut::from(&[0b00000001u8, 0b00000000u8][..]);
let frame = Frame::parse(&mut buf).unwrap().unwrap();
assert!(!frame.finished);
assert_eq!(frame.opcode, OpCode::Text);
assert!(frame.payload.is_empty());
}
#[test]
fn test_parse_length2() {
let mut buf = BytesMut::from(&[0b00000001u8, 126u8][..]);
assert!(Frame::parse(&mut buf).unwrap().is_none());
buf.extend(&[0u8, 4u8][..]);
buf.extend(b"1234");
let frame = Frame::parse(&mut buf).unwrap().unwrap();
assert!(!frame.finished);
assert_eq!(frame.opcode, OpCode::Text);
assert_eq!(frame.payload.as_ref(), &b"1234"[..]);
}
#[test]
fn test_parse_length4() {
let mut buf = BytesMut::from(&[0b00000001u8, 127u8][..]);
assert!(Frame::parse(&mut buf).unwrap().is_none());
buf.extend(&[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 4u8][..]);
buf.extend(b"1234");
let frame = Frame::parse(&mut buf).unwrap().unwrap();
assert!(!frame.finished);
assert_eq!(frame.opcode, OpCode::Text);
assert_eq!(frame.payload.as_ref(), &b"1234"[..]);
}
#[test]
fn test_parse_frame_mask() {
let mut buf = BytesMut::from(&[0b00000001u8, 0b10000001u8][..]);
buf.extend(b"0001");
buf.extend(b"1");
let frame = Frame::parse(&mut buf).unwrap().unwrap();
assert!(!frame.finished);
assert_eq!(frame.opcode, OpCode::Text);
assert_eq!(frame.payload, vec![1u8].into());
}
#[test]
fn test_ping_frame() {
let mut frame = Frame::message(Vec::from("data"), OpCode::Ping, true);
let mut buf = Vec::new();
frame.format(&mut buf).unwrap();
let mut v = vec![137u8, 4u8];
v.extend(b"data");
assert_eq!(buf, v);
}
#[test]
fn test_pong_frame() {
let mut frame = Frame::message(Vec::from("data"), OpCode::Pong, true);
let mut buf = Vec::new();
frame.format(&mut buf).unwrap();
let mut v = vec![138u8, 4u8];
v.extend(b"data");
assert_eq!(buf, v);
}
#[test]
fn test_close_frame() {
let mut frame = Frame::close(CloseCode::Normal, "data");
let mut buf = Vec::new();
frame.format(&mut buf).unwrap();
let mut v = vec![136u8, 6u8, 3u8, 232u8];
v.extend(b"data");
assert_eq!(buf, v);
}
}

348
src/ws/mod.rs Normal file
View File

@ -0,0 +1,348 @@
//! `WebSocket` support for Actix
//!
//! To setup a `WebSocket`, first do web socket handshake then on success convert `Payload`
//! into a `WsStream` stream and then use `WsWriter` to communicate with the peer.
//!
//! ## Example
//!
//! ```rust
//! # extern crate actix;
//! # extern crate actix_web;
//! # use actix::*;
//! # use actix_web::*;
//! use actix_web::ws;
//!
//! // do websocket handshake and start actor
//! fn ws_index(req: HttpRequest) -> Result<HttpResponse> {
//! ws::start(req, Ws)
//! }
//!
//! struct Ws;
//!
//! impl Actor for Ws {
//! type Context = ws::WebsocketContext<Self>;
//! }
//!
//! // Define Handler for ws::Message message
//! impl Handler<ws::Message> for Ws {
//! type Result = ();
//!
//! fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) {
//! match msg {
//! ws::Message::Ping(msg) => ctx.pong(&msg),
//! ws::Message::Text(text) => ctx.text(&text),
//! ws::Message::Binary(bin) => ctx.binary(bin),
//! _ => (),
//! }
//! }
//! }
//! #
//! # fn main() {
//! # Application::new()
//! # .resource("/ws/", |r| r.f(ws_index)) // <- register websocket route
//! # .finish();
//! # }
//! ```
use bytes::BytesMut;
use http::{Method, StatusCode, header};
use futures::{Async, Poll, Stream};
use actix::{Actor, AsyncContext, ResponseType, Handler};
use body::Binary;
use payload::ReadAny;
use error::{Error, WsHandshakeError};
use httprequest::HttpRequest;
use httpresponse::{ConnectionType, HttpResponse, HttpResponseBuilder};
mod frame;
mod proto;
mod context;
use ws::frame::Frame;
use ws::proto::{hash_key, OpCode};
pub use ws::proto::CloseCode;
pub use ws::context::WebsocketContext;
const SEC_WEBSOCKET_ACCEPT: &str = "SEC-WEBSOCKET-ACCEPT";
const SEC_WEBSOCKET_KEY: &str = "SEC-WEBSOCKET-KEY";
const SEC_WEBSOCKET_VERSION: &str = "SEC-WEBSOCKET-VERSION";
// const SEC_WEBSOCKET_PROTOCOL: &'static str = "SEC-WEBSOCKET-PROTOCOL";
/// `WebSocket` Message
#[derive(Debug)]
pub enum Message {
Text(String),
Binary(Binary),
Ping(String),
Pong(String),
Close,
Closed,
Error
}
impl ResponseType for Message {
type Item = ();
type Error = ();
}
/// Do websocket handshake and start actor
pub fn start<A, S>(mut req: HttpRequest<S>, actor: A) -> Result<HttpResponse, Error>
where A: Actor<Context=WebsocketContext<A, S>> + Handler<Message>,
S: 'static
{
let mut resp = handshake(&req)?;
let stream = WsStream::new(req.payload_mut().readany());
let mut ctx = WebsocketContext::new(req, actor);
ctx.add_message_stream(stream);
Ok(resp.body(ctx)?)
}
/// Prepare `WebSocket` handshake response.
///
/// This function returns handshake `HttpResponse`, ready to send to peer.
/// It does not perform any IO.
///
// /// `protocols` is a sequence of known protocols. On successful handshake,
// /// the returned response headers contain the first protocol in this list
// /// which the server also knows.
pub fn handshake<S>(req: &HttpRequest<S>) -> Result<HttpResponseBuilder, WsHandshakeError> {
// WebSocket accepts only GET
if *req.method() != Method::GET {
return Err(WsHandshakeError::GetMethodRequired)
}
// Check for "UPGRADE" to websocket header
let has_hdr = if let Some(hdr) = req.headers().get(header::UPGRADE) {
if let Ok(s) = hdr.to_str() {
s.to_lowercase().contains("websocket")
} else {
false
}
} else {
false
};
if !has_hdr {
return Err(WsHandshakeError::NoWebsocketUpgrade)
}
// Upgrade connection
if !req.upgrade() {
return Err(WsHandshakeError::NoConnectionUpgrade)
}
// check supported version
if !req.headers().contains_key(SEC_WEBSOCKET_VERSION) {
return Err(WsHandshakeError::NoVersionHeader)
}
let supported_ver = {
if let Some(hdr) = req.headers().get(SEC_WEBSOCKET_VERSION) {
hdr == "13" || hdr == "8" || hdr == "7"
} else {
false
}
};
if !supported_ver {
return Err(WsHandshakeError::UnsupportedVersion)
}
// check client handshake for validity
if !req.headers().contains_key(SEC_WEBSOCKET_KEY) {
return Err(WsHandshakeError::BadWebsocketKey)
}
let key = {
let key = req.headers().get(SEC_WEBSOCKET_KEY).unwrap();
hash_key(key.as_ref())
};
Ok(HttpResponse::build(StatusCode::SWITCHING_PROTOCOLS)
.connection_type(ConnectionType::Upgrade)
.header(header::UPGRADE, "websocket")
.header(header::TRANSFER_ENCODING, "chunked")
.header(SEC_WEBSOCKET_ACCEPT, key.as_str())
.take())
}
/// Maps `Payload` stream into stream of `ws::Message` items
pub struct WsStream {
rx: ReadAny,
buf: BytesMut,
closed: bool,
error_sent: bool,
}
impl WsStream {
pub fn new(payload: ReadAny) -> WsStream {
WsStream { rx: payload,
buf: BytesMut::new(),
closed: false,
error_sent: false }
}
}
impl Stream for WsStream {
type Item = Message;
type Error = ();
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
let mut done = false;
if !self.closed {
loop {
match self.rx.poll() {
Ok(Async::Ready(Some(chunk))) => {
self.buf.extend_from_slice(&chunk)
}
Ok(Async::Ready(None)) => {
done = true;
self.closed = true;
break;
}
Ok(Async::NotReady) => break,
Err(_) => {
self.closed = true;
break;
}
}
}
}
loop {
match Frame::parse(&mut self.buf) {
Ok(Some(frame)) => {
// trace!("WsFrame {}", frame);
let (_finished, opcode, payload) = frame.unpack();
match opcode {
OpCode::Continue => continue,
OpCode::Bad =>
return Ok(Async::Ready(Some(Message::Error))),
OpCode::Close => {
self.closed = true;
self.error_sent = true;
return Ok(Async::Ready(Some(Message::Closed)))
},
OpCode::Ping =>
return Ok(Async::Ready(Some(
Message::Ping(
String::from_utf8_lossy(payload.as_ref()).into())))),
OpCode::Pong =>
return Ok(Async::Ready(Some(
Message::Pong(
String::from_utf8_lossy(payload.as_ref()).into())))),
OpCode::Binary =>
return Ok(Async::Ready(Some(Message::Binary(payload)))),
OpCode::Text => {
let tmp = Vec::from(payload.as_ref());
match String::from_utf8(tmp) {
Ok(s) =>
return Ok(Async::Ready(Some(Message::Text(s)))),
Err(_) =>
return Ok(Async::Ready(Some(Message::Error))),
}
}
}
}
Ok(None) => {
if done {
return Ok(Async::Ready(None))
} else if self.closed {
if !self.error_sent {
self.error_sent = true;
return Ok(Async::Ready(Some(Message::Closed)))
} else {
return Ok(Async::Ready(None))
}
} else {
return Ok(Async::NotReady)
}
},
Err(_) => {
self.closed = true;
self.error_sent = true;
return Ok(Async::Ready(Some(Message::Error)));
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
use http::{Method, HeaderMap, Version, Uri, header};
#[test]
fn test_handshake() {
let req = HttpRequest::new(Method::POST, Uri::from_str("/").unwrap(),
Version::HTTP_11, HeaderMap::new(), None);
assert_eq!(WsHandshakeError::GetMethodRequired, handshake(&req).err().unwrap());
let req = HttpRequest::new(Method::GET, Uri::from_str("/").unwrap(),
Version::HTTP_11, HeaderMap::new(), None);
assert_eq!(WsHandshakeError::NoWebsocketUpgrade, handshake(&req).err().unwrap());
let mut headers = HeaderMap::new();
headers.insert(header::UPGRADE,
header::HeaderValue::from_static("test"));
let req = HttpRequest::new(Method::GET, Uri::from_str("/").unwrap(),
Version::HTTP_11, headers, None);
assert_eq!(WsHandshakeError::NoWebsocketUpgrade, handshake(&req).err().unwrap());
let mut headers = HeaderMap::new();
headers.insert(header::UPGRADE,
header::HeaderValue::from_static("websocket"));
let req = HttpRequest::new(Method::GET, Uri::from_str("/").unwrap(),
Version::HTTP_11, headers, None);
assert_eq!(WsHandshakeError::NoConnectionUpgrade, handshake(&req).err().unwrap());
let mut headers = HeaderMap::new();
headers.insert(header::UPGRADE,
header::HeaderValue::from_static("websocket"));
headers.insert(header::CONNECTION,
header::HeaderValue::from_static("upgrade"));
let req = HttpRequest::new(Method::GET, Uri::from_str("/").unwrap(),
Version::HTTP_11, headers, None);
assert_eq!(WsHandshakeError::NoVersionHeader, handshake(&req).err().unwrap());
let mut headers = HeaderMap::new();
headers.insert(header::UPGRADE,
header::HeaderValue::from_static("websocket"));
headers.insert(header::CONNECTION,
header::HeaderValue::from_static("upgrade"));
headers.insert(SEC_WEBSOCKET_VERSION,
header::HeaderValue::from_static("5"));
let req = HttpRequest::new(Method::GET, Uri::from_str("/").unwrap(),
Version::HTTP_11, headers, None);
assert_eq!(WsHandshakeError::UnsupportedVersion, handshake(&req).err().unwrap());
let mut headers = HeaderMap::new();
headers.insert(header::UPGRADE,
header::HeaderValue::from_static("websocket"));
headers.insert(header::CONNECTION,
header::HeaderValue::from_static("upgrade"));
headers.insert(SEC_WEBSOCKET_VERSION,
header::HeaderValue::from_static("13"));
let req = HttpRequest::new(Method::GET, Uri::from_str("/").unwrap(),
Version::HTTP_11, headers, None);
assert_eq!(WsHandshakeError::BadWebsocketKey, handshake(&req).err().unwrap());
let mut headers = HeaderMap::new();
headers.insert(header::UPGRADE,
header::HeaderValue::from_static("websocket"));
headers.insert(header::CONNECTION,
header::HeaderValue::from_static("upgrade"));
headers.insert(SEC_WEBSOCKET_VERSION,
header::HeaderValue::from_static("13"));
headers.insert(SEC_WEBSOCKET_KEY,
header::HeaderValue::from_static("13"));
let req = HttpRequest::new(Method::GET, Uri::from_str("/").unwrap(),
Version::HTTP_11, headers, None);
assert_eq!(StatusCode::SWITCHING_PROTOCOLS,
handshake(&req).unwrap().finish().unwrap().status());
}
}

349
src/ws/proto.rs Normal file
View File

@ -0,0 +1,349 @@
use std::fmt;
use std::convert::{Into, From};
use sha1;
use self::OpCode::*;
/// Operation codes as part of rfc6455.
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub(crate) enum OpCode {
/// Indicates a continuation frame of a fragmented message.
Continue,
/// Indicates a text data frame.
Text,
/// Indicates a binary data frame.
Binary,
/// Indicates a close control frame.
Close,
/// Indicates a ping control frame.
Ping,
/// Indicates a pong control frame.
Pong,
/// Indicates an invalid opcode was received.
Bad,
}
impl fmt::Display for OpCode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Continue => write!(f, "CONTINUE"),
Text => write!(f, "TEXT"),
Binary => write!(f, "BINARY"),
Close => write!(f, "CLOSE"),
Ping => write!(f, "PING"),
Pong => write!(f, "PONG"),
Bad => write!(f, "BAD"),
}
}
}
impl Into<u8> for OpCode {
fn into(self) -> u8 {
match self {
Continue => 0,
Text => 1,
Binary => 2,
Close => 8,
Ping => 9,
Pong => 10,
Bad => {
debug_assert!(false, "Attempted to convert invalid opcode to u8. This is a bug.");
8 // if this somehow happens, a close frame will help us tear down quickly
}
}
}
}
impl From<u8> for OpCode {
fn from(byte: u8) -> OpCode {
match byte {
0 => Continue,
1 => Text,
2 => Binary,
8 => Close,
9 => Ping,
10 => Pong,
_ => Bad
}
}
}
use self::CloseCode::*;
/// Status code used to indicate why an endpoint is closing the `WebSocket` connection.
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum CloseCode {
/// Indicates a normal closure, meaning that the purpose for
/// which the connection was established has been fulfilled.
Normal,
/// Indicates that an endpoint is "going away", such as a server
/// going down or a browser having navigated away from a page.
Away,
/// Indicates that an endpoint is terminating the connection due
/// to a protocol error.
Protocol,
/// Indicates that an endpoint is terminating the connection
/// because it has received a type of data it cannot accept (e.g., an
/// endpoint that understands only text data MAY send this if it
/// receives a binary message).
Unsupported,
/// Indicates that no status code was included in a closing frame. This
/// close code makes it possible to use a single method, `on_close` to
/// handle even cases where no close code was provided.
Status,
/// Indicates an abnormal closure. If the abnormal closure was due to an
/// error, this close code will not be used. Instead, the `on_error` method
/// of the handler will be called with the error. However, if the connection
/// is simply dropped, without an error, this close code will be sent to the
/// handler.
Abnormal,
/// Indicates that an endpoint is terminating the connection
/// because it has received data within a message that was not
/// consistent with the type of the message (e.g., non-UTF-8 [RFC3629]
/// data within a text message).
Invalid,
/// Indicates that an endpoint is terminating the connection
/// because it has received a message that violates its policy. This
/// is a generic status code that can be returned when there is no
/// other more suitable status code (e.g., Unsupported or Size) or if there
/// is a need to hide specific details about the policy.
Policy,
/// Indicates that an endpoint is terminating the connection
/// because it has received a message that is too big for it to
/// process.
Size,
/// Indicates that an endpoint (client) is terminating the
/// connection because it has expected the server to negotiate one or
/// more extension, but the server didn't return them in the response
/// message of the WebSocket handshake. The list of extensions that
/// are needed should be given as the reason for closing.
/// Note that this status code is not used by the server, because it
/// can fail the WebSocket handshake instead.
Extension,
/// Indicates that a server is terminating the connection because
/// it encountered an unexpected condition that prevented it from
/// fulfilling the request.
Error,
/// Indicates that the server is restarting. A client may choose to reconnect,
/// and if it does, it should use a randomized delay of 5-30 seconds between attempts.
Restart,
/// Indicates that the server is overloaded and the client should either connect
/// to a different IP (when multiple targets exist), or reconnect to the same IP
/// when a user has performed an action.
Again,
#[doc(hidden)]
Tls,
#[doc(hidden)]
Empty,
#[doc(hidden)]
Other(u16),
}
impl Into<u16> for CloseCode {
fn into(self) -> u16 {
match self {
Normal => 1000,
Away => 1001,
Protocol => 1002,
Unsupported => 1003,
Status => 1005,
Abnormal => 1006,
Invalid => 1007,
Policy => 1008,
Size => 1009,
Extension => 1010,
Error => 1011,
Restart => 1012,
Again => 1013,
Tls => 1015,
Empty => 0,
Other(code) => code,
}
}
}
impl From<u16> for CloseCode {
fn from(code: u16) -> CloseCode {
match code {
1000 => Normal,
1001 => Away,
1002 => Protocol,
1003 => Unsupported,
1005 => Status,
1006 => Abnormal,
1007 => Invalid,
1008 => Policy,
1009 => Size,
1010 => Extension,
1011 => Error,
1012 => Restart,
1013 => Again,
1015 => Tls,
0 => Empty,
_ => Other(code),
}
}
}
static WS_GUID: &'static str = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
static BASE64: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// TODO: hash is always same size, we dont need String
pub(crate) fn hash_key(key: &[u8]) -> String {
let mut hasher = sha1::Sha1::new();
hasher.update(key);
hasher.update(WS_GUID.as_bytes());
encode_base64(&hasher.digest().bytes())
}
// This code is based on rustc_serialize base64 STANDARD
fn encode_base64(data: &[u8]) -> String {
let len = data.len();
let mod_len = len % 3;
let mut encoded = vec![b'='; (len + 2) / 3 * 4];
{
let mut in_iter = data[..len - mod_len].iter().map(|&c| u32::from(c));
let mut out_iter = encoded.iter_mut();
let enc = |val| BASE64[val as usize];
let mut write = |val| *out_iter.next().unwrap() = val;
while let (Some(one), Some(two), Some(three)) = (in_iter.next(), in_iter.next(), in_iter.next()) {
let g24 = one << 16 | two << 8 | three;
write(enc((g24 >> 18) & 63));
write(enc((g24 >> 12) & 63));
write(enc((g24 >> 6 ) & 63));
write(enc(g24 & 63));
}
match mod_len {
1 => {
let pad = u32::from(data[len-1]) << 16;
write(enc((pad >> 18) & 63));
write(enc((pad >> 12) & 63));
}
2 => {
let pad = u32::from(data[len-2]) << 16 | u32::from(data[len-1]) << 8;
write(enc((pad >> 18) & 63));
write(enc((pad >> 12) & 63));
write(enc((pad >> 6) & 63));
}
_ => (),
}
}
String::from_utf8(encoded).unwrap()
}
#[cfg(test)]
mod test {
#![allow(unused_imports, unused_variables, dead_code)]
use super::*;
macro_rules! opcode_into {
($from:expr => $opcode:pat) => {
match OpCode::from($from) {
e @ $opcode => (),
e => panic!("{:?}", e)
}
}
}
macro_rules! opcode_from {
($from:expr => $opcode:pat) => {
let res: u8 = $from.into();
match res {
e @ $opcode => (),
e => panic!("{:?}", e)
}
}
}
#[test]
fn test_to_opcode() {
opcode_into!(0 => OpCode::Continue);
opcode_into!(1 => OpCode::Text);
opcode_into!(2 => OpCode::Binary);
opcode_into!(8 => OpCode::Close);
opcode_into!(9 => OpCode::Ping);
opcode_into!(10 => OpCode::Pong);
opcode_into!(99 => OpCode::Bad);
}
#[test]
fn test_from_opcode() {
opcode_from!(OpCode::Continue => 0);
opcode_from!(OpCode::Text => 1);
opcode_from!(OpCode::Binary => 2);
opcode_from!(OpCode::Close => 8);
opcode_from!(OpCode::Ping => 9);
opcode_from!(OpCode::Pong => 10);
}
#[test]
#[should_panic]
fn test_from_opcode_debug() {
opcode_from!(OpCode::Bad => 99);
}
#[test]
fn test_from_opcode_display() {
assert_eq!(format!("{}", OpCode::Continue), "CONTINUE");
assert_eq!(format!("{}", OpCode::Text), "TEXT");
assert_eq!(format!("{}", OpCode::Binary), "BINARY");
assert_eq!(format!("{}", OpCode::Close), "CLOSE");
assert_eq!(format!("{}", OpCode::Ping), "PING");
assert_eq!(format!("{}", OpCode::Pong), "PONG");
assert_eq!(format!("{}", OpCode::Bad), "BAD");
}
#[test]
fn closecode_from_u16() {
assert_eq!(CloseCode::from(1000u16), CloseCode::Normal);
assert_eq!(CloseCode::from(1001u16), CloseCode::Away);
assert_eq!(CloseCode::from(1002u16), CloseCode::Protocol);
assert_eq!(CloseCode::from(1003u16), CloseCode::Unsupported);
assert_eq!(CloseCode::from(1005u16), CloseCode::Status);
assert_eq!(CloseCode::from(1006u16), CloseCode::Abnormal);
assert_eq!(CloseCode::from(1007u16), CloseCode::Invalid);
assert_eq!(CloseCode::from(1008u16), CloseCode::Policy);
assert_eq!(CloseCode::from(1009u16), CloseCode::Size);
assert_eq!(CloseCode::from(1010u16), CloseCode::Extension);
assert_eq!(CloseCode::from(1011u16), CloseCode::Error);
assert_eq!(CloseCode::from(1012u16), CloseCode::Restart);
assert_eq!(CloseCode::from(1013u16), CloseCode::Again);
assert_eq!(CloseCode::from(1015u16), CloseCode::Tls);
assert_eq!(CloseCode::from(0u16), CloseCode::Empty);
assert_eq!(CloseCode::from(2000u16), CloseCode::Other(2000));
}
#[test]
fn closecode_into_u16() {
assert_eq!(1000u16, Into::<u16>::into(CloseCode::Normal));
assert_eq!(1001u16, Into::<u16>::into(CloseCode::Away));
assert_eq!(1002u16, Into::<u16>::into(CloseCode::Protocol));
assert_eq!(1003u16, Into::<u16>::into(CloseCode::Unsupported));
assert_eq!(1005u16, Into::<u16>::into(CloseCode::Status));
assert_eq!(1006u16, Into::<u16>::into(CloseCode::Abnormal));
assert_eq!(1007u16, Into::<u16>::into(CloseCode::Invalid));
assert_eq!(1008u16, Into::<u16>::into(CloseCode::Policy));
assert_eq!(1009u16, Into::<u16>::into(CloseCode::Size));
assert_eq!(1010u16, Into::<u16>::into(CloseCode::Extension));
assert_eq!(1011u16, Into::<u16>::into(CloseCode::Error));
assert_eq!(1012u16, Into::<u16>::into(CloseCode::Restart));
assert_eq!(1013u16, Into::<u16>::into(CloseCode::Again));
assert_eq!(1015u16, Into::<u16>::into(CloseCode::Tls));
assert_eq!(0u16, Into::<u16>::into(CloseCode::Empty));
assert_eq!(2000u16, Into::<u16>::into(CloseCode::Other(2000)));
}
}