mirror of
https://github.com/actix/actix-extras.git
synced 2025-06-26 02:19:22 +02:00
add rustfmt config
This commit is contained in:
203
src/ws/client.rs
203
src/ws/client.rs
@ -1,67 +1,65 @@
|
||||
//! Http client request
|
||||
use std::{fmt, io, str};
|
||||
use std::rc::Rc;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
use std::{fmt, io, str};
|
||||
|
||||
use base64;
|
||||
use rand;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use bytes::Bytes;
|
||||
use cookie::Cookie;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use http::{HttpTryFrom, StatusCode, Error as HttpError};
|
||||
use http::header::{self, HeaderName, HeaderValue};
|
||||
use sha1::Sha1;
|
||||
use futures::{Async, Future, Poll, Stream};
|
||||
use futures::unsync::mpsc::{unbounded, UnboundedSender};
|
||||
use futures::{Async, Future, Poll, Stream};
|
||||
use http::header::{self, HeaderName, HeaderValue};
|
||||
use http::{Error as HttpError, HttpTryFrom, StatusCode};
|
||||
use rand;
|
||||
use sha1::Sha1;
|
||||
|
||||
use actix::prelude::*;
|
||||
|
||||
use body::{Body, Binary};
|
||||
use body::{Binary, Body};
|
||||
use error::{Error, UrlParseError};
|
||||
use header::IntoHeaderValue;
|
||||
use payload::PayloadHelper;
|
||||
use httpmessage::HttpMessage;
|
||||
use payload::PayloadHelper;
|
||||
|
||||
use client::{ClientRequest, ClientRequestBuilder, ClientResponse,
|
||||
ClientConnector, SendRequest, SendRequestError,
|
||||
HttpResponseParserError};
|
||||
use client::{ClientConnector, ClientRequest, ClientRequestBuilder, ClientResponse,
|
||||
HttpResponseParserError, SendRequest, SendRequestError};
|
||||
|
||||
use super::{Message, ProtocolError};
|
||||
use super::frame::Frame;
|
||||
use super::proto::{CloseCode, OpCode};
|
||||
|
||||
use super::{Message, ProtocolError};
|
||||
|
||||
/// Websocket client error
|
||||
#[derive(Fail, Debug)]
|
||||
pub enum ClientError {
|
||||
#[fail(display="Invalid url")]
|
||||
#[fail(display = "Invalid url")]
|
||||
InvalidUrl,
|
||||
#[fail(display="Invalid response status")]
|
||||
#[fail(display = "Invalid response status")]
|
||||
InvalidResponseStatus(StatusCode),
|
||||
#[fail(display="Invalid upgrade header")]
|
||||
#[fail(display = "Invalid upgrade header")]
|
||||
InvalidUpgradeHeader,
|
||||
#[fail(display="Invalid connection header")]
|
||||
#[fail(display = "Invalid connection header")]
|
||||
InvalidConnectionHeader(HeaderValue),
|
||||
#[fail(display="Missing CONNECTION header")]
|
||||
#[fail(display = "Missing CONNECTION header")]
|
||||
MissingConnectionHeader,
|
||||
#[fail(display="Missing SEC-WEBSOCKET-ACCEPT header")]
|
||||
#[fail(display = "Missing SEC-WEBSOCKET-ACCEPT header")]
|
||||
MissingWebSocketAcceptHeader,
|
||||
#[fail(display="Invalid challenge response")]
|
||||
#[fail(display = "Invalid challenge response")]
|
||||
InvalidChallengeResponse(String, HeaderValue),
|
||||
#[fail(display="Http parsing error")]
|
||||
#[fail(display = "Http parsing error")]
|
||||
Http(Error),
|
||||
#[fail(display="Url parsing error")]
|
||||
#[fail(display = "Url parsing error")]
|
||||
Url(UrlParseError),
|
||||
#[fail(display="Response parsing error")]
|
||||
#[fail(display = "Response parsing error")]
|
||||
ResponseParseError(HttpResponseParserError),
|
||||
#[fail(display="{}", _0)]
|
||||
#[fail(display = "{}", _0)]
|
||||
SendRequest(SendRequestError),
|
||||
#[fail(display="{}", _0)]
|
||||
#[fail(display = "{}", _0)]
|
||||
Protocol(#[cause] ProtocolError),
|
||||
#[fail(display="{}", _0)]
|
||||
#[fail(display = "{}", _0)]
|
||||
Io(io::Error),
|
||||
#[fail(display="Disconnected")]
|
||||
#[fail(display = "Disconnected")]
|
||||
Disconnected,
|
||||
}
|
||||
|
||||
@ -117,14 +115,15 @@ pub struct Client {
|
||||
}
|
||||
|
||||
impl Client {
|
||||
|
||||
/// Create new websocket connection
|
||||
pub fn new<S: AsRef<str>>(uri: S) -> Client {
|
||||
Client::with_connector(uri, ClientConnector::from_registry())
|
||||
}
|
||||
|
||||
/// Create new websocket connection with custom `ClientConnector`
|
||||
pub fn with_connector<S: AsRef<str>>(uri: S, conn: Addr<Unsync, ClientConnector>) -> Client {
|
||||
pub fn with_connector<S: AsRef<str>>(
|
||||
uri: S, conn: Addr<Unsync, ClientConnector>
|
||||
) -> Client {
|
||||
let mut cl = Client {
|
||||
request: ClientRequest::build(),
|
||||
err: None,
|
||||
@ -140,11 +139,13 @@ impl Client {
|
||||
|
||||
/// Set supported websocket protocols
|
||||
pub fn protocols<U, V>(mut self, protos: U) -> Self
|
||||
where U: IntoIterator<Item=V> + 'static,
|
||||
V: AsRef<str>
|
||||
where
|
||||
U: IntoIterator<Item = V> + 'static,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let mut protos = protos.into_iter()
|
||||
.fold(String::new(), |acc, s| {acc + s.as_ref() + ","});
|
||||
let mut protos = protos
|
||||
.into_iter()
|
||||
.fold(String::new(), |acc, s| acc + s.as_ref() + ",");
|
||||
protos.pop();
|
||||
self.protocols = Some(protos);
|
||||
self
|
||||
@ -158,7 +159,8 @@ impl Client {
|
||||
|
||||
/// Set request Origin
|
||||
pub fn origin<V>(mut self, origin: V) -> Self
|
||||
where HeaderValue: HttpTryFrom<V>
|
||||
where
|
||||
HeaderValue: HttpTryFrom<V>,
|
||||
{
|
||||
match HeaderValue::try_from(origin) {
|
||||
Ok(value) => self.origin = Some(value),
|
||||
@ -185,7 +187,9 @@ impl Client {
|
||||
|
||||
/// Set request header
|
||||
pub fn header<K, V>(mut self, key: K, value: V) -> Self
|
||||
where HeaderName: HttpTryFrom<K>, V: IntoHeaderValue
|
||||
where
|
||||
HeaderName: HttpTryFrom<K>,
|
||||
V: IntoHeaderValue,
|
||||
{
|
||||
self.request.header(key, value);
|
||||
self
|
||||
@ -204,8 +208,7 @@ impl Client {
|
||||
pub fn connect(&mut self) -> ClientHandshake {
|
||||
if let Some(e) = self.err.take() {
|
||||
ClientHandshake::error(e)
|
||||
}
|
||||
else if let Some(e) = self.http_err.take() {
|
||||
} else if let Some(e) = self.http_err.take() {
|
||||
ClientHandshake::error(Error::from(e).into())
|
||||
} else {
|
||||
// origin
|
||||
@ -216,11 +219,13 @@ impl Client {
|
||||
self.request.upgrade();
|
||||
self.request.set_header(header::UPGRADE, "websocket");
|
||||
self.request.set_header(header::CONNECTION, "upgrade");
|
||||
self.request.set_header(header::SEC_WEBSOCKET_VERSION, "13");
|
||||
self.request
|
||||
.set_header(header::SEC_WEBSOCKET_VERSION, "13");
|
||||
self.request.with_connector(self.conn.clone());
|
||||
|
||||
if let Some(protocols) = self.protocols.take() {
|
||||
self.request.set_header(header::SEC_WEBSOCKET_PROTOCOL, protocols.as_str());
|
||||
self.request
|
||||
.set_header(header::SEC_WEBSOCKET_PROTOCOL, protocols.as_str());
|
||||
}
|
||||
let request = match self.request.finish() {
|
||||
Ok(req) => req,
|
||||
@ -228,14 +233,16 @@ impl Client {
|
||||
};
|
||||
|
||||
if request.uri().host().is_none() {
|
||||
return ClientHandshake::error(ClientError::InvalidUrl)
|
||||
return ClientHandshake::error(ClientError::InvalidUrl);
|
||||
}
|
||||
if let Some(scheme) = request.uri().scheme_part() {
|
||||
if scheme != "http" && scheme != "https" && scheme != "ws" && scheme != "wss" {
|
||||
return ClientHandshake::error(ClientError::InvalidUrl)
|
||||
if scheme != "http" && scheme != "https" && scheme != "ws"
|
||||
&& scheme != "wss"
|
||||
{
|
||||
return ClientHandshake::error(ClientError::InvalidUrl);
|
||||
}
|
||||
} else {
|
||||
return ClientHandshake::error(ClientError::InvalidUrl)
|
||||
return ClientHandshake::error(ClientError::InvalidUrl);
|
||||
}
|
||||
|
||||
// start handshake
|
||||
@ -263,8 +270,7 @@ pub struct ClientHandshake {
|
||||
}
|
||||
|
||||
impl ClientHandshake {
|
||||
fn new(mut request: ClientRequest, max_size: usize) -> ClientHandshake
|
||||
{
|
||||
fn new(mut request: ClientRequest, max_size: usize) -> ClientHandshake {
|
||||
// Generate a random key for the `Sec-WebSocket-Key` header.
|
||||
// a base64-encoded (see Section 4 of [RFC4648]) value that,
|
||||
// when decoded, is 16 bytes in length (RFC 6455)
|
||||
@ -273,12 +279,13 @@ impl ClientHandshake {
|
||||
|
||||
request.headers_mut().insert(
|
||||
header::SEC_WEBSOCKET_KEY,
|
||||
HeaderValue::try_from(key.as_str()).unwrap());
|
||||
HeaderValue::try_from(key.as_str()).unwrap(),
|
||||
);
|
||||
|
||||
let (tx, rx) = unbounded();
|
||||
request.set_body(Body::Streaming(
|
||||
Box::new(rx.map_err(|_| io::Error::new(
|
||||
io::ErrorKind::Other, "disconnected").into()))));
|
||||
request.set_body(Body::Streaming(Box::new(rx.map_err(|_| {
|
||||
io::Error::new(io::ErrorKind::Other, "disconnected").into()
|
||||
}))));
|
||||
|
||||
ClientHandshake {
|
||||
key,
|
||||
@ -329,20 +336,20 @@ impl Future for ClientHandshake {
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
if let Some(err) = self.error.take() {
|
||||
return Err(err)
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let resp = match self.request.as_mut().unwrap().poll()? {
|
||||
Async::Ready(response) => {
|
||||
self.request.take();
|
||||
response
|
||||
},
|
||||
Async::NotReady => return Ok(Async::NotReady)
|
||||
}
|
||||
Async::NotReady => return Ok(Async::NotReady),
|
||||
};
|
||||
|
||||
// verify response
|
||||
if resp.status() != StatusCode::SWITCHING_PROTOCOLS {
|
||||
return Err(ClientError::InvalidResponseStatus(resp.status()))
|
||||
return Err(ClientError::InvalidResponseStatus(resp.status()));
|
||||
}
|
||||
// Check for "UPGRADE" to websocket header
|
||||
let has_hdr = if let Some(hdr) = resp.headers().get(header::UPGRADE) {
|
||||
@ -356,26 +363,25 @@ impl Future for ClientHandshake {
|
||||
};
|
||||
if !has_hdr {
|
||||
trace!("Invalid upgrade header");
|
||||
return Err(ClientError::InvalidUpgradeHeader)
|
||||
return Err(ClientError::InvalidUpgradeHeader);
|
||||
}
|
||||
// Check for "CONNECTION" header
|
||||
if let Some(conn) = resp.headers().get(header::CONNECTION) {
|
||||
if let Ok(s) = conn.to_str() {
|
||||
if !s.to_lowercase().contains("upgrade") {
|
||||
trace!("Invalid connection header: {}", s);
|
||||
return Err(ClientError::InvalidConnectionHeader(conn.clone()))
|
||||
return Err(ClientError::InvalidConnectionHeader(conn.clone()));
|
||||
}
|
||||
} else {
|
||||
trace!("Invalid connection header: {:?}", conn);
|
||||
return Err(ClientError::InvalidConnectionHeader(conn.clone()))
|
||||
return Err(ClientError::InvalidConnectionHeader(conn.clone()));
|
||||
}
|
||||
} else {
|
||||
trace!("Missing connection header");
|
||||
return Err(ClientError::MissingConnectionHeader)
|
||||
return Err(ClientError::MissingConnectionHeader);
|
||||
}
|
||||
|
||||
if let Some(key) = resp.headers().get(header::SEC_WEBSOCKET_ACCEPT)
|
||||
{
|
||||
if let Some(key) = resp.headers().get(header::SEC_WEBSOCKET_ACCEPT) {
|
||||
// field is constructed by concatenating /key/
|
||||
// with the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" (RFC 6455)
|
||||
const WS_GUID: &[u8] = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
@ -386,12 +392,17 @@ impl Future for ClientHandshake {
|
||||
if key.as_bytes() != encoded.as_bytes() {
|
||||
trace!(
|
||||
"Invalid challenge response: expected: {} received: {:?}",
|
||||
encoded, key);
|
||||
return Err(ClientError::InvalidChallengeResponse(encoded, key.clone()));
|
||||
encoded,
|
||||
key
|
||||
);
|
||||
return Err(ClientError::InvalidChallengeResponse(
|
||||
encoded,
|
||||
key.clone(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
trace!("Missing SEC-WEBSOCKET-ACCEPT header");
|
||||
return Err(ClientError::MissingWebSocketAcceptHeader)
|
||||
return Err(ClientError::MissingWebSocketAcceptHeader);
|
||||
};
|
||||
|
||||
let inner = Inner {
|
||||
@ -401,13 +412,16 @@ impl Future for ClientHandshake {
|
||||
};
|
||||
|
||||
let inner = Rc::new(UnsafeCell::new(inner));
|
||||
Ok(Async::Ready(
|
||||
(ClientReader{inner: Rc::clone(&inner), max_size: self.max_size},
|
||||
ClientWriter{inner})))
|
||||
Ok(Async::Ready((
|
||||
ClientReader {
|
||||
inner: Rc::clone(&inner),
|
||||
max_size: self.max_size,
|
||||
},
|
||||
ClientWriter { inner },
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct ClientReader {
|
||||
inner: Rc<UnsafeCell<Inner>>,
|
||||
max_size: usize,
|
||||
@ -422,7 +436,7 @@ impl fmt::Debug for ClientReader {
|
||||
impl ClientReader {
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut Inner {
|
||||
unsafe{ &mut *self.inner.get() }
|
||||
unsafe { &mut *self.inner.get() }
|
||||
}
|
||||
}
|
||||
|
||||
@ -434,7 +448,7 @@ impl Stream for ClientReader {
|
||||
let max_size = self.max_size;
|
||||
let inner = self.as_mut();
|
||||
if inner.closed {
|
||||
return Ok(Async::Ready(None))
|
||||
return Ok(Async::Ready(None));
|
||||
}
|
||||
|
||||
// read
|
||||
@ -447,31 +461,29 @@ impl Stream for ClientReader {
|
||||
OpCode::Continue => {
|
||||
inner.closed = true;
|
||||
Err(ProtocolError::NoContinuation)
|
||||
},
|
||||
}
|
||||
OpCode::Bad => {
|
||||
inner.closed = true;
|
||||
Err(ProtocolError::BadOpCode)
|
||||
},
|
||||
}
|
||||
OpCode::Close => {
|
||||
inner.closed = true;
|
||||
let code = NetworkEndian::read_uint(payload.as_ref(), 2) as u16;
|
||||
Ok(Async::Ready(Some(Message::Close(CloseCode::from(code)))))
|
||||
},
|
||||
OpCode::Ping =>
|
||||
Ok(Async::Ready(Some(
|
||||
Message::Ping(
|
||||
String::from_utf8_lossy(payload.as_ref()).into())))),
|
||||
OpCode::Pong =>
|
||||
Ok(Async::Ready(Some(
|
||||
Message::Pong(
|
||||
String::from_utf8_lossy(payload.as_ref()).into())))),
|
||||
OpCode::Binary =>
|
||||
Ok(Async::Ready(Some(Message::Binary(payload)))),
|
||||
Ok(Async::Ready(Some(Message::Close(CloseCode::from(
|
||||
code,
|
||||
)))))
|
||||
}
|
||||
OpCode::Ping => Ok(Async::Ready(Some(Message::Ping(
|
||||
String::from_utf8_lossy(payload.as_ref()).into(),
|
||||
)))),
|
||||
OpCode::Pong => Ok(Async::Ready(Some(Message::Pong(
|
||||
String::from_utf8_lossy(payload.as_ref()).into(),
|
||||
)))),
|
||||
OpCode::Binary => Ok(Async::Ready(Some(Message::Binary(payload)))),
|
||||
OpCode::Text => {
|
||||
let tmp = Vec::from(payload.as_ref());
|
||||
match String::from_utf8(tmp) {
|
||||
Ok(s) =>
|
||||
Ok(Async::Ready(Some(Message::Text(s)))),
|
||||
Ok(s) => Ok(Async::Ready(Some(Message::Text(s)))),
|
||||
Err(_) => {
|
||||
inner.closed = true;
|
||||
Err(ProtocolError::BadEncoding)
|
||||
@ -491,18 +503,17 @@ impl Stream for ClientReader {
|
||||
}
|
||||
|
||||
pub struct ClientWriter {
|
||||
inner: Rc<UnsafeCell<Inner>>
|
||||
inner: Rc<UnsafeCell<Inner>>,
|
||||
}
|
||||
|
||||
impl ClientWriter {
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut Inner {
|
||||
unsafe{ &mut *self.inner.get() }
|
||||
unsafe { &mut *self.inner.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientWriter {
|
||||
|
||||
/// Write payload
|
||||
#[inline]
|
||||
fn write(&mut self, mut data: Binary) {
|
||||
@ -528,13 +539,23 @@ impl ClientWriter {
|
||||
/// Send ping frame
|
||||
#[inline]
|
||||
pub fn ping(&mut self, message: &str) {
|
||||
self.write(Frame::message(Vec::from(message), OpCode::Ping, true, true));
|
||||
self.write(Frame::message(
|
||||
Vec::from(message),
|
||||
OpCode::Ping,
|
||||
true,
|
||||
true,
|
||||
));
|
||||
}
|
||||
|
||||
/// Send pong frame
|
||||
#[inline]
|
||||
pub fn pong(&mut self, message: &str) {
|
||||
self.write(Frame::message(Vec::from(message), OpCode::Pong, true, true));
|
||||
self.write(Frame::message(
|
||||
Vec::from(message),
|
||||
OpCode::Pong,
|
||||
true,
|
||||
true,
|
||||
));
|
||||
}
|
||||
|
||||
/// Send close frame
|
||||
|
@ -1,25 +1,26 @@
|
||||
use std::mem;
|
||||
use futures::{Async, Poll};
|
||||
use futures::sync::oneshot::Sender;
|
||||
use futures::unsync::oneshot;
|
||||
use futures::{Async, Poll};
|
||||
use smallvec::SmallVec;
|
||||
use std::mem;
|
||||
|
||||
use actix::{Actor, ActorState, ActorContext, AsyncContext,
|
||||
Addr, Handler, Message, Syn, Unsync, SpawnHandle};
|
||||
use actix::dev::{ContextImpl, SyncEnvelope, ToEnvelope};
|
||||
use actix::fut::ActorFuture;
|
||||
use actix::dev::{ContextImpl, ToEnvelope, SyncEnvelope};
|
||||
use actix::{Actor, ActorContext, ActorState, Addr, AsyncContext, Handler, Message,
|
||||
SpawnHandle, Syn, Unsync};
|
||||
|
||||
use body::{Body, Binary};
|
||||
use body::{Binary, Body};
|
||||
use context::{ActorHttpContext, Drain, Frame as ContextFrame};
|
||||
use error::{Error, ErrorInternalServerError};
|
||||
use httprequest::HttpRequest;
|
||||
use context::{Frame as ContextFrame, ActorHttpContext, Drain};
|
||||
|
||||
use ws::frame::Frame;
|
||||
use ws::proto::{OpCode, CloseCode};
|
||||
|
||||
use ws::proto::{CloseCode, OpCode};
|
||||
|
||||
/// Execution context for `WebSockets` actors
|
||||
pub struct WebsocketContext<A, S=()> where A: Actor<Context=WebsocketContext<A, S>>,
|
||||
pub struct WebsocketContext<A, S = ()>
|
||||
where
|
||||
A: Actor<Context = WebsocketContext<A, S>>,
|
||||
{
|
||||
inner: ContextImpl<A>,
|
||||
stream: Option<SmallVec<[ContextFrame; 4]>>,
|
||||
@ -27,7 +28,9 @@ pub struct WebsocketContext<A, S=()> where A: Actor<Context=WebsocketContext<A,
|
||||
disconnected: bool,
|
||||
}
|
||||
|
||||
impl<A, S> ActorContext for WebsocketContext<A, S> where A: Actor<Context=Self>
|
||||
impl<A, S> ActorContext for WebsocketContext<A, S>
|
||||
where
|
||||
A: Actor<Context = Self>,
|
||||
{
|
||||
fn stop(&mut self) {
|
||||
self.inner.stop();
|
||||
@ -40,16 +43,20 @@ impl<A, S> ActorContext for WebsocketContext<A, S> where A: Actor<Context=Self>
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, S> AsyncContext<A> for WebsocketContext<A, S> where A: Actor<Context=Self>
|
||||
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
|
||||
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
|
||||
where
|
||||
F: ActorFuture<Item = (), Error = (), Actor = A> + 'static,
|
||||
{
|
||||
self.inner.wait(fut)
|
||||
}
|
||||
@ -57,8 +64,8 @@ impl<A, S> AsyncContext<A> for WebsocketContext<A, S> where A: Actor<Context=Sel
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
fn waiting(&self) -> bool {
|
||||
self.inner.waiting() || self.inner.state() == ActorState::Stopping ||
|
||||
self.inner.state() == ActorState::Stopped
|
||||
self.inner.waiting() || self.inner.state() == ActorState::Stopping
|
||||
|| self.inner.state() == ActorState::Stopped
|
||||
}
|
||||
|
||||
fn cancel_future(&mut self, handle: SpawnHandle) -> bool {
|
||||
@ -78,8 +85,10 @@ impl<A, S> AsyncContext<A> for WebsocketContext<A, S> where A: Actor<Context=Sel
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, S: 'static> WebsocketContext<A, S> where A: Actor<Context=Self> {
|
||||
|
||||
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)
|
||||
@ -101,8 +110,10 @@ impl<A, S: 'static> WebsocketContext<A, S> where A: Actor<Context=Self> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, S> WebsocketContext<A, S> where A: Actor<Context=Self> {
|
||||
|
||||
impl<A, S> WebsocketContext<A, S>
|
||||
where
|
||||
A: Actor<Context = Self>,
|
||||
{
|
||||
/// Write payload
|
||||
#[inline]
|
||||
fn write(&mut self, data: Binary) {
|
||||
@ -145,13 +156,23 @@ impl<A, S> WebsocketContext<A, S> where A: Actor<Context=Self> {
|
||||
/// Send ping frame
|
||||
#[inline]
|
||||
pub fn ping(&mut self, message: &str) {
|
||||
self.write(Frame::message(Vec::from(message), OpCode::Ping, true, false));
|
||||
self.write(Frame::message(
|
||||
Vec::from(message),
|
||||
OpCode::Ping,
|
||||
true,
|
||||
false,
|
||||
));
|
||||
}
|
||||
|
||||
/// Send pong frame
|
||||
#[inline]
|
||||
pub fn pong(&mut self, message: &str) {
|
||||
self.write(Frame::message(Vec::from(message), OpCode::Pong, true, false));
|
||||
self.write(Frame::message(
|
||||
Vec::from(message),
|
||||
OpCode::Pong,
|
||||
true,
|
||||
false,
|
||||
));
|
||||
}
|
||||
|
||||
/// Send close frame
|
||||
@ -191,8 +212,11 @@ impl<A, S> WebsocketContext<A, S> where A: Actor<Context=Self> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, S> ActorHttpContext for WebsocketContext<A, S> where A: Actor<Context=Self>, S: 'static {
|
||||
|
||||
impl<A, S> ActorHttpContext for WebsocketContext<A, S>
|
||||
where
|
||||
A: Actor<Context = Self>,
|
||||
S: 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn disconnected(&mut self) {
|
||||
self.disconnected = true;
|
||||
@ -200,12 +224,11 @@ impl<A, S> ActorHttpContext for WebsocketContext<A, S> where A: Actor<Context=Se
|
||||
}
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<SmallVec<[ContextFrame; 4]>>, Error> {
|
||||
let ctx: &mut WebsocketContext<A, S> = unsafe {
|
||||
mem::transmute(self as &mut WebsocketContext<A, S>)
|
||||
};
|
||||
let ctx: &mut WebsocketContext<A, S> =
|
||||
unsafe { mem::transmute(self as &mut WebsocketContext<A, S>) };
|
||||
|
||||
if self.inner.alive() && self.inner.poll(ctx).is_err() {
|
||||
return Err(ErrorInternalServerError("error"))
|
||||
return Err(ErrorInternalServerError("error"));
|
||||
}
|
||||
|
||||
// frames
|
||||
@ -220,8 +243,10 @@ impl<A, S> ActorHttpContext for WebsocketContext<A, S> where A: Actor<Context=Se
|
||||
}
|
||||
|
||||
impl<A, M, S> ToEnvelope<Syn, A, M> for WebsocketContext<A, S>
|
||||
where A: Actor<Context=WebsocketContext<A, S>> + Handler<M>,
|
||||
M: Message + Send + 'static, M::Result: Send
|
||||
where
|
||||
A: Actor<Context = WebsocketContext<A, S>> + Handler<M>,
|
||||
M: Message + Send + 'static,
|
||||
M::Result: Send,
|
||||
{
|
||||
fn pack(msg: M, tx: Option<Sender<M::Result>>) -> SyncEnvelope<A> {
|
||||
SyncEnvelope::new(msg, tx)
|
||||
@ -229,7 +254,9 @@ impl<A, M, S> ToEnvelope<Syn, A, M> for WebsocketContext<A, S>
|
||||
}
|
||||
|
||||
impl<A, S> From<WebsocketContext<A, S>> for Body
|
||||
where A: Actor<Context=WebsocketContext<A, S>>, S: 'static
|
||||
where
|
||||
A: Actor<Context = WebsocketContext<A, S>>,
|
||||
S: 'static,
|
||||
{
|
||||
fn from(ctx: WebsocketContext<A, S>) -> Body {
|
||||
Body::Actor(Box::new(ctx))
|
||||
|
144
src/ws/frame.rs
144
src/ws/frame.rs
@ -1,17 +1,17 @@
|
||||
use std::{fmt, mem, ptr};
|
||||
use std::iter::FromIterator;
|
||||
use bytes::{Bytes, BytesMut, BufMut};
|
||||
use byteorder::{ByteOrder, BigEndian, NetworkEndian};
|
||||
use byteorder::{BigEndian, ByteOrder, NetworkEndian};
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
use futures::{Async, Poll, Stream};
|
||||
use rand;
|
||||
use std::iter::FromIterator;
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
use body::Binary;
|
||||
use error::{PayloadError};
|
||||
use error::PayloadError;
|
||||
use payload::PayloadHelper;
|
||||
|
||||
use ws::ProtocolError;
|
||||
use ws::proto::{OpCode, CloseCode};
|
||||
use ws::mask::apply_mask;
|
||||
use ws::proto::{CloseCode, OpCode};
|
||||
|
||||
/// A struct representing a `WebSocket` frame.
|
||||
#[derive(Debug)]
|
||||
@ -22,7 +22,6 @@ pub struct Frame {
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
|
||||
/// Destruct frame
|
||||
pub fn unpack(self) -> (bool, OpCode, Binary) {
|
||||
(self.finished, self.opcode, self.payload)
|
||||
@ -40,20 +39,22 @@ impl Frame {
|
||||
Vec::new()
|
||||
} else {
|
||||
Vec::from_iter(
|
||||
raw[..].iter()
|
||||
raw[..]
|
||||
.iter()
|
||||
.chain(reason.as_bytes().iter())
|
||||
.cloned())
|
||||
.cloned(),
|
||||
)
|
||||
};
|
||||
|
||||
Frame::message(payload, OpCode::Close, true, genmask)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="cargo-clippy", allow(type_complexity))]
|
||||
fn read_copy_md<S>(pl: &mut PayloadHelper<S>,
|
||||
server: bool,
|
||||
max_size: usize
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
|
||||
fn read_copy_md<S>(
|
||||
pl: &mut PayloadHelper<S>, server: bool, max_size: usize
|
||||
) -> Poll<Option<(usize, bool, OpCode, usize, Option<u32>)>, ProtocolError>
|
||||
where S: Stream<Item=Bytes, Error=PayloadError>
|
||||
where
|
||||
S: Stream<Item = Bytes, Error = PayloadError>,
|
||||
{
|
||||
let mut idx = 2;
|
||||
let buf = match pl.copy(2)? {
|
||||
@ -68,16 +69,16 @@ impl Frame {
|
||||
// check masking
|
||||
let masked = second & 0x80 != 0;
|
||||
if !masked && server {
|
||||
return Err(ProtocolError::UnmaskedFrame)
|
||||
return Err(ProtocolError::UnmaskedFrame);
|
||||
} else if masked && !server {
|
||||
return Err(ProtocolError::MaskedFrame)
|
||||
return Err(ProtocolError::MaskedFrame);
|
||||
}
|
||||
|
||||
// Op code
|
||||
let opcode = OpCode::from(first & 0x0F);
|
||||
|
||||
if let OpCode::Bad = opcode {
|
||||
return Err(ProtocolError::InvalidOpcode(first & 0x0F))
|
||||
return Err(ProtocolError::InvalidOpcode(first & 0x0F));
|
||||
}
|
||||
|
||||
let len = second & 0x7F;
|
||||
@ -105,7 +106,7 @@ impl Frame {
|
||||
|
||||
// check for max allowed size
|
||||
if length > max_size {
|
||||
return Err(ProtocolError::Overflow)
|
||||
return Err(ProtocolError::Overflow);
|
||||
}
|
||||
|
||||
let mask = if server {
|
||||
@ -115,25 +116,32 @@ impl Frame {
|
||||
Async::NotReady => return Ok(Async::NotReady),
|
||||
};
|
||||
|
||||
let mask: &[u8] = &buf[idx..idx+4];
|
||||
let mask_u32: u32 = unsafe {ptr::read_unaligned(mask.as_ptr() as *const u32)};
|
||||
let mask: &[u8] = &buf[idx..idx + 4];
|
||||
let mask_u32: u32 =
|
||||
unsafe { ptr::read_unaligned(mask.as_ptr() as *const u32) };
|
||||
idx += 4;
|
||||
Some(mask_u32)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Async::Ready(Some((idx, finished, opcode, length, mask))))
|
||||
Ok(Async::Ready(Some((
|
||||
idx,
|
||||
finished,
|
||||
opcode,
|
||||
length,
|
||||
mask,
|
||||
))))
|
||||
}
|
||||
|
||||
fn read_chunk_md(chunk: &[u8], server: bool, max_size: usize)
|
||||
-> Poll<(usize, bool, OpCode, usize, Option<u32>), ProtocolError>
|
||||
{
|
||||
fn read_chunk_md(
|
||||
chunk: &[u8], server: bool, max_size: usize
|
||||
) -> Poll<(usize, bool, OpCode, usize, Option<u32>), ProtocolError> {
|
||||
let chunk_len = chunk.len();
|
||||
|
||||
let mut idx = 2;
|
||||
if chunk_len < 2 {
|
||||
return Ok(Async::NotReady)
|
||||
return Ok(Async::NotReady);
|
||||
}
|
||||
|
||||
let first = chunk[0];
|
||||
@ -143,29 +151,29 @@ impl Frame {
|
||||
// check masking
|
||||
let masked = second & 0x80 != 0;
|
||||
if !masked && server {
|
||||
return Err(ProtocolError::UnmaskedFrame)
|
||||
return Err(ProtocolError::UnmaskedFrame);
|
||||
} else if masked && !server {
|
||||
return Err(ProtocolError::MaskedFrame)
|
||||
return Err(ProtocolError::MaskedFrame);
|
||||
}
|
||||
|
||||
// Op code
|
||||
let opcode = OpCode::from(first & 0x0F);
|
||||
|
||||
if let OpCode::Bad = opcode {
|
||||
return Err(ProtocolError::InvalidOpcode(first & 0x0F))
|
||||
return Err(ProtocolError::InvalidOpcode(first & 0x0F));
|
||||
}
|
||||
|
||||
let len = second & 0x7F;
|
||||
let length = if len == 126 {
|
||||
if chunk_len < 4 {
|
||||
return Ok(Async::NotReady)
|
||||
return Ok(Async::NotReady);
|
||||
}
|
||||
let len = NetworkEndian::read_uint(&chunk[idx..], 2) as usize;
|
||||
idx += 2;
|
||||
len
|
||||
} else if len == 127 {
|
||||
if chunk_len < 10 {
|
||||
return Ok(Async::NotReady)
|
||||
return Ok(Async::NotReady);
|
||||
}
|
||||
let len = NetworkEndian::read_uint(&chunk[idx..], 8) as usize;
|
||||
idx += 8;
|
||||
@ -176,16 +184,17 @@ impl Frame {
|
||||
|
||||
// check for max allowed size
|
||||
if length > max_size {
|
||||
return Err(ProtocolError::Overflow)
|
||||
return Err(ProtocolError::Overflow);
|
||||
}
|
||||
|
||||
let mask = if server {
|
||||
if chunk_len < idx + 4 {
|
||||
return Ok(Async::NotReady)
|
||||
return Ok(Async::NotReady);
|
||||
}
|
||||
|
||||
let mask: &[u8] = &chunk[idx..idx+4];
|
||||
let mask_u32: u32 = unsafe {ptr::read_unaligned(mask.as_ptr() as *const u32)};
|
||||
let mask: &[u8] = &chunk[idx..idx + 4];
|
||||
let mask_u32: u32 =
|
||||
unsafe { ptr::read_unaligned(mask.as_ptr() as *const u32) };
|
||||
idx += 4;
|
||||
Some(mask_u32)
|
||||
} else {
|
||||
@ -196,9 +205,11 @@ impl Frame {
|
||||
}
|
||||
|
||||
/// Parse the input stream into a frame.
|
||||
pub fn parse<S>(pl: &mut PayloadHelper<S>, server: bool, max_size: usize)
|
||||
-> Poll<Option<Frame>, ProtocolError>
|
||||
where S: Stream<Item=Bytes, Error=PayloadError>
|
||||
pub fn parse<S>(
|
||||
pl: &mut PayloadHelper<S>, server: bool, max_size: usize
|
||||
) -> Poll<Option<Frame>, ProtocolError>
|
||||
where
|
||||
S: Stream<Item = Bytes, Error = PayloadError>,
|
||||
{
|
||||
// try to parse ws frame md from one chunk
|
||||
let result = match pl.get_chunk()? {
|
||||
@ -229,7 +240,10 @@ impl Frame {
|
||||
// no need for body
|
||||
if length == 0 {
|
||||
return Ok(Async::Ready(Some(Frame {
|
||||
finished, opcode, payload: Binary::from("") })));
|
||||
finished,
|
||||
opcode,
|
||||
payload: Binary::from(""),
|
||||
})));
|
||||
}
|
||||
|
||||
let data = match pl.read_exact(length)? {
|
||||
@ -245,26 +259,32 @@ impl Frame {
|
||||
}
|
||||
OpCode::Close if length > 125 => {
|
||||
debug!("Received close frame with payload length exceeding 125. Morphing to protocol close frame.");
|
||||
return Ok(Async::Ready(Some(Frame::default())))
|
||||
return Ok(Async::Ready(Some(Frame::default())));
|
||||
}
|
||||
_ => ()
|
||||
_ => (),
|
||||
}
|
||||
|
||||
// unmask
|
||||
if let Some(mask) = mask {
|
||||
#[allow(mutable_transmutes)]
|
||||
let p: &mut [u8] = unsafe{let ptr: &[u8] = &data; mem::transmute(ptr)};
|
||||
let p: &mut [u8] = unsafe {
|
||||
let ptr: &[u8] = &data;
|
||||
mem::transmute(ptr)
|
||||
};
|
||||
apply_mask(p, mask);
|
||||
}
|
||||
|
||||
Ok(Async::Ready(Some(Frame {
|
||||
finished, opcode, payload: data.into() })))
|
||||
finished,
|
||||
opcode,
|
||||
payload: data.into(),
|
||||
})))
|
||||
}
|
||||
|
||||
/// Generate binary representation
|
||||
pub fn message<B: Into<Binary>>(data: B, code: OpCode,
|
||||
finished: bool, genmask: bool) -> Binary
|
||||
{
|
||||
pub fn message<B: Into<Binary>>(
|
||||
data: B, code: OpCode, finished: bool, genmask: bool
|
||||
) -> Binary {
|
||||
let payload = data.into();
|
||||
let one: u8 = if finished {
|
||||
0x80 | Into::<u8>::into(code)
|
||||
@ -286,19 +306,19 @@ impl Frame {
|
||||
let mut buf = BytesMut::with_capacity(p_len + 4);
|
||||
buf.put_slice(&[one, two | 126]);
|
||||
{
|
||||
let buf_mut = unsafe{buf.bytes_mut()};
|
||||
let buf_mut = unsafe { buf.bytes_mut() };
|
||||
BigEndian::write_u16(&mut buf_mut[..2], payload_len as u16);
|
||||
}
|
||||
unsafe{buf.advance_mut(2)};
|
||||
unsafe { buf.advance_mut(2) };
|
||||
buf
|
||||
} else {
|
||||
let mut buf = BytesMut::with_capacity(p_len + 10);
|
||||
buf.put_slice(&[one, two | 127]);
|
||||
{
|
||||
let buf_mut = unsafe{buf.bytes_mut()};
|
||||
let buf_mut = unsafe { buf.bytes_mut() };
|
||||
BigEndian::write_u64(&mut buf_mut[..8], payload_len as u64);
|
||||
}
|
||||
unsafe{buf.advance_mut(8)};
|
||||
unsafe { buf.advance_mut(8) };
|
||||
buf
|
||||
};
|
||||
|
||||
@ -308,7 +328,7 @@ impl Frame {
|
||||
{
|
||||
let buf_mut = buf.bytes_mut();
|
||||
*(buf_mut as *mut _ as *mut u32) = mask;
|
||||
buf_mut[4..payload_len+4].copy_from_slice(payload.as_ref());
|
||||
buf_mut[4..payload_len + 4].copy_from_slice(payload.as_ref());
|
||||
apply_mask(&mut buf_mut[4..], mask);
|
||||
}
|
||||
buf.advance_mut(payload_len + 4);
|
||||
@ -333,7 +353,8 @@ impl Default for Frame {
|
||||
|
||||
impl fmt::Display for Frame {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f,
|
||||
write!(
|
||||
f,
|
||||
"
|
||||
<FRAME>
|
||||
final: {}
|
||||
@ -341,11 +362,15 @@ impl fmt::Display for Frame {
|
||||
payload length: {}
|
||||
payload: 0x{}
|
||||
</FRAME>",
|
||||
self.finished,
|
||||
self.opcode,
|
||||
self.payload.len(),
|
||||
self.payload.as_ref().iter().map(
|
||||
|byte| format!("{:x}", byte)).collect::<String>())
|
||||
self.finished,
|
||||
self.opcode,
|
||||
self.payload.len(),
|
||||
self.payload
|
||||
.as_ref()
|
||||
.iter()
|
||||
.map(|byte| format!("{:x}", byte))
|
||||
.collect::<String>()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -360,7 +385,7 @@ mod tests {
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn extract(frm: Poll<Option<Frame>, ProtocolError>) -> Frame {
|
||||
match frm {
|
||||
Ok(Async::Ready(Some(frame))) => frame,
|
||||
@ -370,8 +395,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
let mut buf = PayloadHelper::new(
|
||||
once(Ok(BytesMut::from(&[0b0000_0001u8, 0b0000_0001u8][..]).freeze())));
|
||||
let mut buf = PayloadHelper::new(once(Ok(BytesMut::from(
|
||||
&[0b0000_0001u8, 0b0000_0001u8][..],
|
||||
).freeze())));
|
||||
assert!(is_none(&Frame::parse(&mut buf, false, 1024)));
|
||||
|
||||
let mut buf = BytesMut::from(&[0b0000_0001u8, 0b0000_0001u8][..]);
|
||||
|
@ -20,7 +20,7 @@ fn apply_mask_fallback(buf: &mut [u8], mask: &[u8; 4]) {
|
||||
|
||||
/// Faster version of `apply_mask()` which operates on 8-byte blocks.
|
||||
#[inline]
|
||||
#[cfg_attr(feature="cargo-clippy", allow(cast_lossless))]
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
|
||||
fn apply_mask_fast32(buf: &mut [u8], mask_u32: u32) {
|
||||
let mut ptr = buf.as_mut_ptr();
|
||||
let mut len = buf.len();
|
||||
@ -85,13 +85,16 @@ fn apply_mask_fast32(buf: &mut [u8], mask_u32: u32) {
|
||||
|
||||
// Possible last block.
|
||||
if len > 0 {
|
||||
unsafe { xor_mem(ptr, mask_u32, len); }
|
||||
unsafe {
|
||||
xor_mem(ptr, mask_u32, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
// TODO: copy_nonoverlapping here compiles to call memcpy. While it is not so inefficient,
|
||||
// it could be done better. The compiler does not see that len is limited to 3.
|
||||
// TODO: copy_nonoverlapping here compiles to call memcpy. While it is not so
|
||||
// inefficient, it could be done better. The compiler does not see that len is
|
||||
// limited to 3.
|
||||
unsafe fn xor_mem(ptr: *mut u8, mask: u32, len: usize) {
|
||||
let mut b: u32 = uninitialized();
|
||||
#[allow(trivial_casts)]
|
||||
@ -103,19 +106,17 @@ unsafe fn xor_mem(ptr: *mut u8, mask: u32, len: usize) {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{apply_mask_fallback, apply_mask_fast32};
|
||||
use std::ptr;
|
||||
use super::{apply_mask_fallback, apply_mask_fast32};
|
||||
|
||||
#[test]
|
||||
fn test_apply_mask() {
|
||||
let mask = [
|
||||
0x6d, 0xb6, 0xb2, 0x80,
|
||||
];
|
||||
let mask_u32: u32 = unsafe {ptr::read_unaligned(mask.as_ptr() as *const u32)};
|
||||
let mask = [0x6d, 0xb6, 0xb2, 0x80];
|
||||
let mask_u32: u32 = unsafe { ptr::read_unaligned(mask.as_ptr() as *const u32) };
|
||||
|
||||
let unmasked = vec![
|
||||
0xf3, 0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x82,
|
||||
0xff, 0xfe, 0x00, 0x17, 0x74, 0xf9, 0x12, 0x03,
|
||||
0xf3, 0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x82, 0xff, 0xfe, 0x00, 0x17,
|
||||
0x74, 0xf9, 0x12, 0x03,
|
||||
];
|
||||
|
||||
// Check masking with proper alignment.
|
||||
|
363
src/ws/mod.rs
363
src/ws/mod.rs
@ -1,7 +1,8 @@
|
||||
//! `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.
|
||||
//! 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
|
||||
//!
|
||||
@ -42,62 +43,61 @@
|
||||
//! # .finish();
|
||||
//! # }
|
||||
//! ```
|
||||
use bytes::Bytes;
|
||||
use http::{Method, StatusCode, header};
|
||||
use futures::{Async, Poll, Stream};
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use bytes::Bytes;
|
||||
use futures::{Async, Poll, Stream};
|
||||
use http::{header, Method, StatusCode};
|
||||
|
||||
use actix::{Actor, AsyncContext, StreamHandler};
|
||||
|
||||
use body::Binary;
|
||||
use payload::PayloadHelper;
|
||||
use error::{Error, PayloadError, ResponseError};
|
||||
use httpmessage::HttpMessage;
|
||||
use httprequest::HttpRequest;
|
||||
use httpresponse::{ConnectionType, HttpResponse, HttpResponseBuilder};
|
||||
use payload::PayloadHelper;
|
||||
|
||||
mod frame;
|
||||
mod proto;
|
||||
mod context;
|
||||
mod mask;
|
||||
mod client;
|
||||
mod context;
|
||||
mod frame;
|
||||
mod mask;
|
||||
mod proto;
|
||||
|
||||
pub use self::frame::Frame;
|
||||
pub use self::proto::OpCode;
|
||||
pub use self::proto::CloseCode;
|
||||
pub use self::client::{Client, ClientError, ClientHandshake, ClientReader, ClientWriter};
|
||||
pub use self::context::WebsocketContext;
|
||||
pub use self::client::{Client, ClientError,
|
||||
ClientReader, ClientWriter, ClientHandshake};
|
||||
pub use self::frame::Frame;
|
||||
pub use self::proto::CloseCode;
|
||||
pub use self::proto::OpCode;
|
||||
|
||||
/// Websocket protocol errors
|
||||
#[derive(Fail, Debug)]
|
||||
pub enum ProtocolError {
|
||||
/// Received an unmasked frame from client
|
||||
#[fail(display="Received an unmasked frame from client")]
|
||||
#[fail(display = "Received an unmasked frame from client")]
|
||||
UnmaskedFrame,
|
||||
/// Received a masked frame from server
|
||||
#[fail(display="Received a masked frame from server")]
|
||||
#[fail(display = "Received a masked frame from server")]
|
||||
MaskedFrame,
|
||||
/// Encountered invalid opcode
|
||||
#[fail(display="Invalid opcode: {}", _0)]
|
||||
#[fail(display = "Invalid opcode: {}", _0)]
|
||||
InvalidOpcode(u8),
|
||||
/// Invalid control frame length
|
||||
#[fail(display="Invalid control frame length: {}", _0)]
|
||||
#[fail(display = "Invalid control frame length: {}", _0)]
|
||||
InvalidLength(usize),
|
||||
/// Bad web socket op code
|
||||
#[fail(display="Bad web socket op code")]
|
||||
#[fail(display = "Bad web socket op code")]
|
||||
BadOpCode,
|
||||
/// A payload reached size limit.
|
||||
#[fail(display="A payload reached size limit.")]
|
||||
#[fail(display = "A payload reached size limit.")]
|
||||
Overflow,
|
||||
/// Continuation is not supported
|
||||
#[fail(display="Continuation is not supported.")]
|
||||
#[fail(display = "Continuation is not supported.")]
|
||||
NoContinuation,
|
||||
/// Bad utf-8 encoding
|
||||
#[fail(display="Bad utf-8 encoding.")]
|
||||
#[fail(display = "Bad utf-8 encoding.")]
|
||||
BadEncoding,
|
||||
/// Payload error
|
||||
#[fail(display="Payload error: {}", _0)]
|
||||
#[fail(display = "Payload error: {}", _0)]
|
||||
Payload(#[cause] PayloadError),
|
||||
}
|
||||
|
||||
@ -113,42 +113,46 @@ impl From<PayloadError> for ProtocolError {
|
||||
#[derive(Fail, PartialEq, Debug)]
|
||||
pub enum HandshakeError {
|
||||
/// Only get method is allowed
|
||||
#[fail(display="Method not allowed")]
|
||||
#[fail(display = "Method not allowed")]
|
||||
GetMethodRequired,
|
||||
/// Upgrade header if not set to websocket
|
||||
#[fail(display="Websocket upgrade is expected")]
|
||||
#[fail(display = "Websocket upgrade is expected")]
|
||||
NoWebsocketUpgrade,
|
||||
/// Connection header is not set to upgrade
|
||||
#[fail(display="Connection upgrade is expected")]
|
||||
#[fail(display = "Connection upgrade is expected")]
|
||||
NoConnectionUpgrade,
|
||||
/// Websocket version header is not set
|
||||
#[fail(display="Websocket version header is required")]
|
||||
#[fail(display = "Websocket version header is required")]
|
||||
NoVersionHeader,
|
||||
/// Unsupported websocket version
|
||||
#[fail(display="Unsupported version")]
|
||||
#[fail(display = "Unsupported version")]
|
||||
UnsupportedVersion,
|
||||
/// Websocket key is not set or wrong
|
||||
#[fail(display="Unknown websocket key")]
|
||||
#[fail(display = "Unknown websocket key")]
|
||||
BadWebsocketKey,
|
||||
}
|
||||
|
||||
impl ResponseError for HandshakeError {
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
match *self {
|
||||
HandshakeError::GetMethodRequired => {
|
||||
HttpResponse::MethodNotAllowed().header(header::ALLOW, "GET").finish()
|
||||
}
|
||||
HandshakeError::GetMethodRequired => HttpResponse::MethodNotAllowed()
|
||||
.header(header::ALLOW, "GET")
|
||||
.finish(),
|
||||
HandshakeError::NoWebsocketUpgrade => HttpResponse::BadRequest()
|
||||
.reason("No WebSocket UPGRADE header found").finish(),
|
||||
.reason("No WebSocket UPGRADE header found")
|
||||
.finish(),
|
||||
HandshakeError::NoConnectionUpgrade => HttpResponse::BadRequest()
|
||||
.reason("No CONNECTION upgrade").finish(),
|
||||
.reason("No CONNECTION upgrade")
|
||||
.finish(),
|
||||
HandshakeError::NoVersionHeader => HttpResponse::BadRequest()
|
||||
.reason("Websocket version header is required").finish(),
|
||||
.reason("Websocket version header is required")
|
||||
.finish(),
|
||||
HandshakeError::UnsupportedVersion => HttpResponse::BadRequest()
|
||||
.reason("Unsupported version").finish(),
|
||||
.reason("Unsupported version")
|
||||
.finish(),
|
||||
HandshakeError::BadWebsocketKey => HttpResponse::BadRequest()
|
||||
.reason("Handshake error").finish(),
|
||||
.reason("Handshake error")
|
||||
.finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -165,8 +169,9 @@ pub enum Message {
|
||||
|
||||
/// Do websocket handshake and start actor
|
||||
pub fn start<A, S>(req: HttpRequest<S>, actor: A) -> Result<HttpResponse, Error>
|
||||
where A: Actor<Context=WebsocketContext<A, S>> + StreamHandler<Message, ProtocolError>,
|
||||
S: 'static
|
||||
where
|
||||
A: Actor<Context = WebsocketContext<A, S>> + StreamHandler<Message, ProtocolError>,
|
||||
S: 'static,
|
||||
{
|
||||
let mut resp = handshake(&req)?;
|
||||
let stream = WsStream::new(req.clone());
|
||||
@ -185,10 +190,12 @@ pub fn start<A, S>(req: HttpRequest<S>, actor: A) -> Result<HttpResponse, Error>
|
||||
// /// `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, HandshakeError> {
|
||||
pub fn handshake<S>(
|
||||
req: &HttpRequest<S>
|
||||
) -> Result<HttpResponseBuilder, HandshakeError> {
|
||||
// WebSocket accepts only GET
|
||||
if *req.method() != Method::GET {
|
||||
return Err(HandshakeError::GetMethodRequired)
|
||||
return Err(HandshakeError::GetMethodRequired);
|
||||
}
|
||||
|
||||
// Check for "UPGRADE" to websocket header
|
||||
@ -202,17 +209,19 @@ pub fn handshake<S>(req: &HttpRequest<S>) -> Result<HttpResponseBuilder, Handsha
|
||||
false
|
||||
};
|
||||
if !has_hdr {
|
||||
return Err(HandshakeError::NoWebsocketUpgrade)
|
||||
return Err(HandshakeError::NoWebsocketUpgrade);
|
||||
}
|
||||
|
||||
// Upgrade connection
|
||||
if !req.upgrade() {
|
||||
return Err(HandshakeError::NoConnectionUpgrade)
|
||||
return Err(HandshakeError::NoConnectionUpgrade);
|
||||
}
|
||||
|
||||
// check supported version
|
||||
if !req.headers().contains_key(header::SEC_WEBSOCKET_VERSION) {
|
||||
return Err(HandshakeError::NoVersionHeader)
|
||||
if !req.headers()
|
||||
.contains_key(header::SEC_WEBSOCKET_VERSION)
|
||||
{
|
||||
return Err(HandshakeError::NoVersionHeader);
|
||||
}
|
||||
let supported_ver = {
|
||||
if let Some(hdr) = req.headers().get(header::SEC_WEBSOCKET_VERSION) {
|
||||
@ -222,12 +231,12 @@ pub fn handshake<S>(req: &HttpRequest<S>) -> Result<HttpResponseBuilder, Handsha
|
||||
}
|
||||
};
|
||||
if !supported_ver {
|
||||
return Err(HandshakeError::UnsupportedVersion)
|
||||
return Err(HandshakeError::UnsupportedVersion);
|
||||
}
|
||||
|
||||
// check client handshake for validity
|
||||
if !req.headers().contains_key(header::SEC_WEBSOCKET_KEY) {
|
||||
return Err(HandshakeError::BadWebsocketKey)
|
||||
return Err(HandshakeError::BadWebsocketKey);
|
||||
}
|
||||
let key = {
|
||||
let key = req.headers().get(header::SEC_WEBSOCKET_KEY).unwrap();
|
||||
@ -235,11 +244,11 @@ pub fn handshake<S>(req: &HttpRequest<S>) -> Result<HttpResponseBuilder, Handsha
|
||||
};
|
||||
|
||||
Ok(HttpResponse::build(StatusCode::SWITCHING_PROTOCOLS)
|
||||
.connection_type(ConnectionType::Upgrade)
|
||||
.header(header::UPGRADE, "websocket")
|
||||
.header(header::TRANSFER_ENCODING, "chunked")
|
||||
.header(header::SEC_WEBSOCKET_ACCEPT, key.as_str())
|
||||
.take())
|
||||
.connection_type(ConnectionType::Upgrade)
|
||||
.header(header::UPGRADE, "websocket")
|
||||
.header(header::TRANSFER_ENCODING, "chunked")
|
||||
.header(header::SEC_WEBSOCKET_ACCEPT, key.as_str())
|
||||
.take())
|
||||
}
|
||||
|
||||
/// Maps `Payload` stream into stream of `ws::Message` items
|
||||
@ -249,12 +258,16 @@ pub struct WsStream<S> {
|
||||
max_size: usize,
|
||||
}
|
||||
|
||||
impl<S> WsStream<S> where S: Stream<Item=Bytes, Error=PayloadError> {
|
||||
impl<S> WsStream<S>
|
||||
where
|
||||
S: Stream<Item = Bytes, Error = PayloadError>,
|
||||
{
|
||||
/// Create new websocket frames stream
|
||||
pub fn new(stream: S) -> WsStream<S> {
|
||||
WsStream { rx: PayloadHelper::new(stream),
|
||||
closed: false,
|
||||
max_size: 65_536,
|
||||
WsStream {
|
||||
rx: PayloadHelper::new(stream),
|
||||
closed: false,
|
||||
max_size: 65_536,
|
||||
}
|
||||
}
|
||||
|
||||
@ -267,13 +280,16 @@ impl<S> WsStream<S> where S: Stream<Item=Bytes, Error=PayloadError> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Stream for WsStream<S> where S: Stream<Item=Bytes, Error=PayloadError> {
|
||||
impl<S> Stream for WsStream<S>
|
||||
where
|
||||
S: Stream<Item = Bytes, Error = PayloadError>,
|
||||
{
|
||||
type Item = Message;
|
||||
type Error = ProtocolError;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
if self.closed {
|
||||
return Ok(Async::Ready(None))
|
||||
return Ok(Async::Ready(None));
|
||||
}
|
||||
|
||||
match Frame::parse(&mut self.rx, true, self.max_size) {
|
||||
@ -283,7 +299,7 @@ impl<S> Stream for WsStream<S> where S: Stream<Item=Bytes, Error=PayloadError> {
|
||||
// continuation is not supported
|
||||
if !finished {
|
||||
self.closed = true;
|
||||
return Err(ProtocolError::NoContinuation)
|
||||
return Err(ProtocolError::NoContinuation);
|
||||
}
|
||||
|
||||
match opcode {
|
||||
@ -295,23 +311,21 @@ impl<S> Stream for WsStream<S> where S: Stream<Item=Bytes, Error=PayloadError> {
|
||||
OpCode::Close => {
|
||||
self.closed = true;
|
||||
let code = NetworkEndian::read_uint(payload.as_ref(), 2) as u16;
|
||||
Ok(Async::Ready(
|
||||
Some(Message::Close(CloseCode::from(code)))))
|
||||
},
|
||||
OpCode::Ping =>
|
||||
Ok(Async::Ready(Some(
|
||||
Message::Ping(
|
||||
String::from_utf8_lossy(payload.as_ref()).into())))),
|
||||
OpCode::Pong =>
|
||||
Ok(Async::Ready(Some(
|
||||
Message::Pong(String::from_utf8_lossy(payload.as_ref()).into())))),
|
||||
OpCode::Binary =>
|
||||
Ok(Async::Ready(Some(Message::Binary(payload)))),
|
||||
Ok(Async::Ready(Some(Message::Close(CloseCode::from(
|
||||
code,
|
||||
)))))
|
||||
}
|
||||
OpCode::Ping => Ok(Async::Ready(Some(Message::Ping(
|
||||
String::from_utf8_lossy(payload.as_ref()).into(),
|
||||
)))),
|
||||
OpCode::Pong => Ok(Async::Ready(Some(Message::Pong(
|
||||
String::from_utf8_lossy(payload.as_ref()).into(),
|
||||
)))),
|
||||
OpCode::Binary => Ok(Async::Ready(Some(Message::Binary(payload)))),
|
||||
OpCode::Text => {
|
||||
let tmp = Vec::from(payload.as_ref());
|
||||
match String::from_utf8(tmp) {
|
||||
Ok(s) =>
|
||||
Ok(Async::Ready(Some(Message::Text(s)))),
|
||||
Ok(s) => Ok(Async::Ready(Some(Message::Text(s)))),
|
||||
Err(_) => {
|
||||
self.closed = true;
|
||||
Err(ProtocolError::BadEncoding)
|
||||
@ -333,77 +347,168 @@ impl<S> Stream for WsStream<S> where S: Stream<Item=Bytes, Error=PayloadError> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use http::{header, HeaderMap, Method, Uri, Version};
|
||||
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!(HandshakeError::GetMethodRequired, handshake(&req).err().unwrap());
|
||||
let req = HttpRequest::new(
|
||||
Method::POST,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
HeaderMap::new(),
|
||||
None,
|
||||
);
|
||||
assert_eq!(
|
||||
HandshakeError::GetMethodRequired,
|
||||
handshake(&req).err().unwrap()
|
||||
);
|
||||
|
||||
let req = HttpRequest::new(Method::GET, Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11, HeaderMap::new(), None);
|
||||
assert_eq!(HandshakeError::NoWebsocketUpgrade, handshake(&req).err().unwrap());
|
||||
let req = HttpRequest::new(
|
||||
Method::GET,
|
||||
Uri::from_str("/").unwrap(),
|
||||
Version::HTTP_11,
|
||||
HeaderMap::new(),
|
||||
None,
|
||||
);
|
||||
assert_eq!(
|
||||
HandshakeError::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!(HandshakeError::NoWebsocketUpgrade, handshake(&req).err().unwrap());
|
||||
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!(
|
||||
HandshakeError::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!(HandshakeError::NoConnectionUpgrade, handshake(&req).err().unwrap());
|
||||
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!(
|
||||
HandshakeError::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!(HandshakeError::NoVersionHeader, handshake(&req).err().unwrap());
|
||||
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!(
|
||||
HandshakeError::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(header::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!(HandshakeError::UnsupportedVersion, handshake(&req).err().unwrap());
|
||||
headers.insert(
|
||||
header::UPGRADE,
|
||||
header::HeaderValue::from_static("websocket"),
|
||||
);
|
||||
headers.insert(
|
||||
header::CONNECTION,
|
||||
header::HeaderValue::from_static("upgrade"),
|
||||
);
|
||||
headers.insert(
|
||||
header::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!(
|
||||
HandshakeError::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(header::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!(HandshakeError::BadWebsocketKey, handshake(&req).err().unwrap());
|
||||
headers.insert(
|
||||
header::UPGRADE,
|
||||
header::HeaderValue::from_static("websocket"),
|
||||
);
|
||||
headers.insert(
|
||||
header::CONNECTION,
|
||||
header::HeaderValue::from_static("upgrade"),
|
||||
);
|
||||
headers.insert(
|
||||
header::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!(
|
||||
HandshakeError::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(header::SEC_WEBSOCKET_VERSION,
|
||||
header::HeaderValue::from_static("13"));
|
||||
headers.insert(header::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().status());
|
||||
headers.insert(
|
||||
header::UPGRADE,
|
||||
header::HeaderValue::from_static("websocket"),
|
||||
);
|
||||
headers.insert(
|
||||
header::CONNECTION,
|
||||
header::HeaderValue::from_static("upgrade"),
|
||||
);
|
||||
headers.insert(
|
||||
header::SEC_WEBSOCKET_VERSION,
|
||||
header::HeaderValue::from_static("13"),
|
||||
);
|
||||
headers.insert(
|
||||
header::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().status()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
116
src/ws/proto.rs
116
src/ws/proto.rs
@ -1,7 +1,7 @@
|
||||
use std::fmt;
|
||||
use std::convert::{Into, From};
|
||||
use sha1;
|
||||
use base64;
|
||||
use sha1;
|
||||
use std::convert::{From, Into};
|
||||
use std::fmt;
|
||||
|
||||
use self::OpCode::*;
|
||||
/// Operation codes as part of rfc6455.
|
||||
@ -26,52 +26,54 @@ pub enum OpCode {
|
||||
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"),
|
||||
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
|
||||
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
|
||||
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.
|
||||
/// 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
|
||||
@ -125,12 +127,13 @@ pub enum CloseCode {
|
||||
/// 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.
|
||||
/// 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.
|
||||
/// 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,
|
||||
@ -141,31 +144,29 @@ pub enum CloseCode {
|
||||
}
|
||||
|
||||
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,
|
||||
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,
|
||||
@ -182,7 +183,7 @@ impl From<u16> for CloseCode {
|
||||
1012 => Restart,
|
||||
1013 => Again,
|
||||
1015 => Tls,
|
||||
0 => Empty,
|
||||
0 => Empty,
|
||||
_ => Other(code),
|
||||
}
|
||||
}
|
||||
@ -200,7 +201,6 @@ pub(crate) fn hash_key(key: &[u8]) -> String {
|
||||
base64::encode(&hasher.digest().bytes())
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#![allow(unused_imports, unused_variables, dead_code)]
|
||||
@ -210,9 +210,9 @@ mod test {
|
||||
($from:expr => $opcode:pat) => {
|
||||
match OpCode::from($from) {
|
||||
e @ $opcode => (),
|
||||
e => unreachable!("{:?}", e)
|
||||
e => unreachable!("{:?}", e),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! opcode_from {
|
||||
@ -220,9 +220,9 @@ mod test {
|
||||
let res: u8 = $from.into();
|
||||
match res {
|
||||
e @ $opcode => (),
|
||||
e => unreachable!("{:?}", e)
|
||||
e => unreachable!("{:?}", e),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
Reference in New Issue
Block a user