2018-01-28 07:03:03 +01:00
|
|
|
//! Http client request
|
2018-01-31 00:13:33 +01:00
|
|
|
use std::{fmt, io, str};
|
2018-01-28 07:03:03 +01:00
|
|
|
use std::rc::Rc;
|
|
|
|
use std::cell::UnsafeCell;
|
|
|
|
|
|
|
|
use base64;
|
|
|
|
use rand;
|
2018-02-27 00:26:27 +01:00
|
|
|
use bytes::Bytes;
|
2018-01-29 20:39:26 +01:00
|
|
|
use cookie::Cookie;
|
|
|
|
use http::{HttpTryFrom, StatusCode, Error as HttpError};
|
2018-01-28 07:03:03 +01:00
|
|
|
use http::header::{self, HeaderName, HeaderValue};
|
|
|
|
use sha1::Sha1;
|
|
|
|
use futures::{Async, Future, Poll, Stream};
|
2018-02-26 22:58:23 +01:00
|
|
|
use futures::unsync::mpsc::{unbounded, UnboundedSender};
|
|
|
|
use byteorder::{ByteOrder, NetworkEndian};
|
2018-01-28 07:03:03 +01:00
|
|
|
|
2018-01-30 08:01:20 +01:00
|
|
|
use actix::prelude::*;
|
|
|
|
|
2018-02-26 22:58:23 +01:00
|
|
|
use body::{Body, Binary};
|
2018-02-27 19:09:24 +01:00
|
|
|
use error::UrlParseError;
|
2018-03-07 00:18:04 +01:00
|
|
|
use header::IntoHeaderValue;
|
2018-02-26 22:58:23 +01:00
|
|
|
use payload::PayloadHelper;
|
2018-02-28 00:03:28 +01:00
|
|
|
use httpmessage::HttpMessage;
|
2018-01-28 07:03:03 +01:00
|
|
|
|
2018-02-26 22:58:23 +01:00
|
|
|
use client::{ClientRequest, ClientRequestBuilder, ClientResponse,
|
2018-02-27 00:26:27 +01:00
|
|
|
ClientConnector, SendRequest, SendRequestError,
|
|
|
|
HttpResponseParserError};
|
2018-01-28 07:03:03 +01:00
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
use super::{Message, ProtocolError};
|
2018-02-10 09:05:20 +01:00
|
|
|
use super::frame::Frame;
|
2018-01-28 07:03:03 +01:00
|
|
|
use super::proto::{CloseCode, OpCode};
|
|
|
|
|
2018-01-30 00:45:37 +01:00
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
/// Backward compatibility
|
|
|
|
#[doc(hidden)]
|
|
|
|
#[deprecated(since="0.4.2", note="please use `ws::Client` instead")]
|
|
|
|
pub type WsClient = Client;
|
|
|
|
#[doc(hidden)]
|
|
|
|
#[deprecated(since="0.4.2", note="please use `ws::ClientError` instead")]
|
|
|
|
pub type WsClientError = ClientError;
|
|
|
|
#[doc(hidden)]
|
|
|
|
#[deprecated(since="0.4.2", note="please use `ws::ClientReader` instead")]
|
|
|
|
pub type WsClientReader = ClientReader;
|
|
|
|
#[doc(hidden)]
|
|
|
|
#[deprecated(since="0.4.2", note="please use `ws::ClientWriter` instead")]
|
|
|
|
pub type WsClientWriter = ClientWriter;
|
|
|
|
#[doc(hidden)]
|
|
|
|
#[deprecated(since="0.4.2", note="please use `ws::ClientHandshake` instead")]
|
|
|
|
pub type WsClientHandshake = ClientHandshake;
|
|
|
|
|
|
|
|
|
2018-01-31 18:28:53 +01:00
|
|
|
/// Websocket client error
|
2018-01-28 07:03:03 +01:00
|
|
|
#[derive(Fail, Debug)]
|
2018-03-02 20:29:55 +01:00
|
|
|
pub enum ClientError {
|
2018-01-28 07:03:03 +01:00
|
|
|
#[fail(display="Invalid url")]
|
|
|
|
InvalidUrl,
|
|
|
|
#[fail(display="Invalid response status")]
|
2018-02-28 00:38:57 +01:00
|
|
|
InvalidResponseStatus(StatusCode),
|
2018-01-28 07:03:03 +01:00
|
|
|
#[fail(display="Invalid upgrade header")]
|
|
|
|
InvalidUpgradeHeader,
|
|
|
|
#[fail(display="Invalid connection header")]
|
2018-02-28 00:38:57 +01:00
|
|
|
InvalidConnectionHeader(HeaderValue),
|
|
|
|
#[fail(display="Missing CONNECTION header")]
|
|
|
|
MissingConnectionHeader,
|
|
|
|
#[fail(display="Missing SEC-WEBSOCKET-ACCEPT header")]
|
|
|
|
MissingWebSocketAcceptHeader,
|
2018-01-28 07:03:03 +01:00
|
|
|
#[fail(display="Invalid challenge response")]
|
2018-02-28 00:38:57 +01:00
|
|
|
InvalidChallengeResponse(String, HeaderValue),
|
2018-01-28 07:03:03 +01:00
|
|
|
#[fail(display="Http parsing error")]
|
|
|
|
Http(HttpError),
|
|
|
|
#[fail(display="Url parsing error")]
|
|
|
|
Url(UrlParseError),
|
|
|
|
#[fail(display="Response parsing error")]
|
|
|
|
ResponseParseError(HttpResponseParserError),
|
|
|
|
#[fail(display="{}", _0)]
|
2018-02-26 22:58:23 +01:00
|
|
|
SendRequest(SendRequestError),
|
|
|
|
#[fail(display="{}", _0)]
|
2018-03-02 20:29:55 +01:00
|
|
|
Protocol(#[cause] ProtocolError),
|
2018-01-28 07:03:03 +01:00
|
|
|
#[fail(display="{}", _0)]
|
|
|
|
Io(io::Error),
|
|
|
|
#[fail(display="Disconnected")]
|
|
|
|
Disconnected,
|
|
|
|
}
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
impl From<HttpError> for ClientError {
|
|
|
|
fn from(err: HttpError) -> ClientError {
|
|
|
|
ClientError::Http(err)
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
impl From<UrlParseError> for ClientError {
|
|
|
|
fn from(err: UrlParseError) -> ClientError {
|
|
|
|
ClientError::Url(err)
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
impl From<SendRequestError> for ClientError {
|
|
|
|
fn from(err: SendRequestError) -> ClientError {
|
|
|
|
ClientError::SendRequest(err)
|
2018-02-26 22:58:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
impl From<ProtocolError> for ClientError {
|
|
|
|
fn from(err: ProtocolError) -> ClientError {
|
|
|
|
ClientError::Protocol(err)
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
impl From<io::Error> for ClientError {
|
|
|
|
fn from(err: io::Error) -> ClientError {
|
|
|
|
ClientError::Io(err)
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
impl From<HttpResponseParserError> for ClientError {
|
|
|
|
fn from(err: HttpResponseParserError) -> ClientError {
|
|
|
|
ClientError::ResponseParseError(err)
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-10 02:20:28 +01:00
|
|
|
/// `WebSocket` client
|
2018-01-31 00:26:58 +01:00
|
|
|
///
|
2018-02-10 02:20:28 +01:00
|
|
|
/// Example of `WebSocket` client usage is available in
|
2018-01-31 00:26:58 +01:00
|
|
|
/// [websocket example](
|
|
|
|
/// https://github.com/actix/actix-web/blob/master/examples/websocket/src/client.rs#L24)
|
2018-03-02 20:29:55 +01:00
|
|
|
pub struct Client {
|
2018-01-29 20:39:26 +01:00
|
|
|
request: ClientRequestBuilder,
|
2018-03-02 20:29:55 +01:00
|
|
|
err: Option<ClientError>,
|
2018-01-28 07:03:03 +01:00
|
|
|
http_err: Option<HttpError>,
|
|
|
|
origin: Option<HeaderValue>,
|
|
|
|
protocols: Option<String>,
|
2018-02-13 01:08:04 +01:00
|
|
|
conn: Addr<Unsync, ClientConnector>,
|
2018-02-27 19:09:24 +01:00
|
|
|
max_size: usize,
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
impl Client {
|
2018-01-28 07:03:03 +01:00
|
|
|
|
2018-01-30 20:17:17 +01:00
|
|
|
/// Create new websocket connection
|
2018-03-02 20:29:55 +01:00
|
|
|
pub fn new<S: AsRef<str>>(uri: S) -> Client {
|
|
|
|
Client::with_connector(uri, ClientConnector::from_registry())
|
2018-01-30 20:17:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Create new websocket connection with custom `ClientConnector`
|
2018-03-02 20:29:55 +01:00
|
|
|
pub fn with_connector<S: AsRef<str>>(uri: S, conn: Addr<Unsync, ClientConnector>) -> Client {
|
|
|
|
let mut cl = Client {
|
2018-01-29 20:39:26 +01:00
|
|
|
request: ClientRequest::build(),
|
2018-01-28 07:03:03 +01:00
|
|
|
err: None,
|
|
|
|
http_err: None,
|
|
|
|
origin: None,
|
2018-01-30 20:17:17 +01:00
|
|
|
protocols: None,
|
2018-02-27 19:09:24 +01:00
|
|
|
max_size: 65_536,
|
2018-02-26 23:33:56 +01:00
|
|
|
conn,
|
2018-01-30 20:17:17 +01:00
|
|
|
};
|
2018-01-29 20:39:26 +01:00
|
|
|
cl.request.uri(uri.as_ref());
|
2018-01-28 07:03:03 +01:00
|
|
|
cl
|
|
|
|
}
|
|
|
|
|
2018-01-30 20:17:17 +01:00
|
|
|
/// Set supported websocket protocols
|
2018-02-24 05:36:50 +01:00
|
|
|
pub fn protocols<U, V>(mut self, protos: U) -> Self
|
2018-01-28 07:03:03 +01:00
|
|
|
where U: IntoIterator<Item=V> + 'static,
|
|
|
|
V: AsRef<str>
|
|
|
|
{
|
|
|
|
let mut protos = protos.into_iter()
|
|
|
|
.fold(String::new(), |acc, s| {acc + s.as_ref() + ","});
|
|
|
|
protos.pop();
|
|
|
|
self.protocols = Some(protos);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2018-01-30 20:17:17 +01:00
|
|
|
/// Set cookie for handshake request
|
2018-02-24 06:14:21 +01:00
|
|
|
pub fn cookie(mut self, cookie: Cookie) -> Self {
|
2018-01-29 20:39:26 +01:00
|
|
|
self.request.cookie(cookie);
|
2018-01-28 07:03:03 +01:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set request Origin
|
2018-02-24 05:36:50 +01:00
|
|
|
pub fn origin<V>(mut self, origin: V) -> Self
|
2018-01-28 07:03:03 +01:00
|
|
|
where HeaderValue: HttpTryFrom<V>
|
|
|
|
{
|
|
|
|
match HeaderValue::try_from(origin) {
|
|
|
|
Ok(value) => self.origin = Some(value),
|
|
|
|
Err(e) => self.http_err = Some(e.into()),
|
|
|
|
}
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2018-02-27 19:09:24 +01:00
|
|
|
/// Set max frame size
|
|
|
|
///
|
|
|
|
/// By default max size is set to 64kb
|
|
|
|
pub fn max_frame_size(mut self, size: usize) -> Self {
|
|
|
|
self.max_size = size;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2018-03-09 19:09:13 +01:00
|
|
|
/// Set write buffer capacity
|
|
|
|
///
|
|
|
|
/// Default buffer capacity is 32kb
|
|
|
|
pub fn write_buffer_capacity(mut self, cap: usize) -> Self {
|
|
|
|
self.request.write_buffer_capacity(cap);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2018-01-30 20:17:17 +01:00
|
|
|
/// Set request header
|
2018-02-24 05:36:50 +01:00
|
|
|
pub fn header<K, V>(mut self, key: K, value: V) -> Self
|
2018-03-07 00:18:04 +01:00
|
|
|
where HeaderName: HttpTryFrom<K>, V: IntoHeaderValue
|
2018-01-28 07:03:03 +01:00
|
|
|
{
|
2018-01-29 20:39:26 +01:00
|
|
|
self.request.header(key, value);
|
2018-01-28 07:03:03 +01:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2018-01-30 20:17:17 +01:00
|
|
|
/// Connect to websocket server and do ws handshake
|
2018-03-02 20:29:55 +01:00
|
|
|
pub fn connect(&mut self) -> ClientHandshake {
|
2018-01-28 07:03:03 +01:00
|
|
|
if let Some(e) = self.err.take() {
|
2018-03-02 20:29:55 +01:00
|
|
|
ClientHandshake::error(e)
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
2018-02-24 06:14:21 +01:00
|
|
|
else if let Some(e) = self.http_err.take() {
|
2018-03-02 20:29:55 +01:00
|
|
|
ClientHandshake::error(e.into())
|
2018-02-24 06:14:21 +01:00
|
|
|
} else {
|
|
|
|
// origin
|
|
|
|
if let Some(origin) = self.origin.take() {
|
|
|
|
self.request.set_header(header::ORIGIN, origin);
|
|
|
|
}
|
2018-01-28 07:03:03 +01:00
|
|
|
|
2018-02-24 06:14:21 +01:00
|
|
|
self.request.upgrade();
|
|
|
|
self.request.set_header(header::UPGRADE, "websocket");
|
|
|
|
self.request.set_header(header::CONNECTION, "upgrade");
|
2018-02-28 23:16:55 +01:00
|
|
|
self.request.set_header(header::SEC_WEBSOCKET_VERSION, "13");
|
2018-02-28 00:14:33 +01:00
|
|
|
self.request.with_connector(self.conn.clone());
|
2018-01-28 07:03:03 +01:00
|
|
|
|
2018-02-24 06:14:21 +01:00
|
|
|
if let Some(protocols) = self.protocols.take() {
|
2018-02-28 23:16:55 +01:00
|
|
|
self.request.set_header(header::SEC_WEBSOCKET_PROTOCOL, protocols.as_str());
|
2018-02-24 06:14:21 +01:00
|
|
|
}
|
|
|
|
let request = match self.request.finish() {
|
|
|
|
Ok(req) => req,
|
2018-03-02 20:29:55 +01:00
|
|
|
Err(err) => return ClientHandshake::error(err.into()),
|
2018-02-24 06:14:21 +01:00
|
|
|
};
|
2018-01-29 20:39:26 +01:00
|
|
|
|
2018-02-24 06:14:21 +01:00
|
|
|
if request.uri().host().is_none() {
|
2018-03-02 20:29:55 +01:00
|
|
|
return ClientHandshake::error(ClientError::InvalidUrl)
|
2018-02-24 06:14:21 +01:00
|
|
|
}
|
|
|
|
if let Some(scheme) = request.uri().scheme_part() {
|
|
|
|
if scheme != "http" && scheme != "https" && scheme != "ws" && scheme != "wss" {
|
2018-03-02 20:29:55 +01:00
|
|
|
return ClientHandshake::error(ClientError::InvalidUrl)
|
2018-02-24 06:14:21 +01:00
|
|
|
}
|
|
|
|
} else {
|
2018-03-02 20:29:55 +01:00
|
|
|
return ClientHandshake::error(ClientError::InvalidUrl)
|
2018-01-29 20:39:26 +01:00
|
|
|
}
|
2018-01-28 07:03:03 +01:00
|
|
|
|
2018-02-24 06:14:21 +01:00
|
|
|
// start handshake
|
2018-03-02 20:29:55 +01:00
|
|
|
ClientHandshake::new(request, self.max_size)
|
2018-02-24 06:14:21 +01:00
|
|
|
}
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
struct Inner {
|
2018-02-26 22:58:23 +01:00
|
|
|
tx: UnboundedSender<Bytes>,
|
|
|
|
rx: PayloadHelper<ClientResponse>,
|
2018-01-28 07:03:03 +01:00
|
|
|
closed: bool,
|
|
|
|
}
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
pub struct ClientHandshake {
|
2018-02-26 22:58:23 +01:00
|
|
|
request: Option<SendRequest>,
|
|
|
|
tx: Option<UnboundedSender<Bytes>>,
|
2018-01-28 07:03:03 +01:00
|
|
|
key: String,
|
2018-03-02 20:29:55 +01:00
|
|
|
error: Option<ClientError>,
|
2018-02-27 19:09:24 +01:00
|
|
|
max_size: usize,
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
impl ClientHandshake {
|
|
|
|
fn new(mut request: ClientRequest, max_size: usize) -> ClientHandshake
|
2018-02-24 06:14:21 +01:00
|
|
|
{
|
2018-01-28 07:03:03 +01:00
|
|
|
// 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)
|
|
|
|
let sec_key: [u8; 16] = rand::random();
|
|
|
|
let key = base64::encode(&sec_key);
|
|
|
|
|
2018-02-27 19:09:24 +01:00
|
|
|
request.headers_mut().insert(
|
2018-02-28 23:16:55 +01:00
|
|
|
header::SEC_WEBSOCKET_KEY,
|
2018-02-27 19:09:24 +01:00
|
|
|
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()))));
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
ClientHandshake {
|
2018-02-27 19:09:24 +01:00
|
|
|
key,
|
|
|
|
max_size,
|
2018-02-28 00:14:33 +01:00
|
|
|
request: Some(request.send()),
|
2018-02-27 19:09:24 +01:00
|
|
|
tx: Some(tx),
|
|
|
|
error: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
fn error(err: ClientError) -> ClientHandshake {
|
|
|
|
ClientHandshake {
|
2018-02-27 19:09:24 +01:00
|
|
|
key: String::new(),
|
|
|
|
request: None,
|
|
|
|
tx: None,
|
|
|
|
error: Some(err),
|
|
|
|
max_size: 0
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
impl Future for ClientHandshake {
|
|
|
|
type Item = (ClientReader, ClientWriter);
|
|
|
|
type Error = ClientError;
|
2018-01-28 07:03:03 +01:00
|
|
|
|
|
|
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
2018-02-24 06:14:21 +01:00
|
|
|
if let Some(err) = self.error.take() {
|
|
|
|
return Err(err)
|
|
|
|
}
|
|
|
|
|
2018-02-26 22:58:23 +01:00
|
|
|
let resp = match self.request.as_mut().unwrap().poll()? {
|
|
|
|
Async::Ready(response) => {
|
|
|
|
self.request.take();
|
|
|
|
response
|
|
|
|
},
|
|
|
|
Async::NotReady => return Ok(Async::NotReady)
|
|
|
|
};
|
2018-01-28 07:03:03 +01:00
|
|
|
|
2018-02-26 22:58:23 +01:00
|
|
|
// verify response
|
|
|
|
if resp.status() != StatusCode::SWITCHING_PROTOCOLS {
|
2018-03-02 20:29:55 +01:00
|
|
|
return Err(ClientError::InvalidResponseStatus(resp.status()))
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
2018-02-26 22:58:23 +01:00
|
|
|
// Check for "UPGRADE" to websocket header
|
|
|
|
let has_hdr = if let Some(hdr) = resp.headers().get(header::UPGRADE) {
|
|
|
|
if let Ok(s) = hdr.to_str() {
|
|
|
|
s.to_lowercase().contains("websocket")
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
};
|
|
|
|
if !has_hdr {
|
2018-02-28 00:38:57 +01:00
|
|
|
trace!("Invalid upgrade header");
|
2018-03-02 20:29:55 +01:00
|
|
|
return Err(ClientError::InvalidUpgradeHeader)
|
2018-02-26 22:58:23 +01:00
|
|
|
}
|
|
|
|
// Check for "CONNECTION" header
|
2018-02-28 00:38:57 +01:00
|
|
|
if let Some(conn) = resp.headers().get(header::CONNECTION) {
|
2018-02-26 22:58:23 +01:00
|
|
|
if let Ok(s) = conn.to_str() {
|
2018-02-28 00:38:57 +01:00
|
|
|
if !s.to_lowercase().contains("upgrade") {
|
|
|
|
trace!("Invalid connection header: {}", s);
|
2018-03-02 20:29:55 +01:00
|
|
|
return Err(ClientError::InvalidConnectionHeader(conn.clone()))
|
2018-02-28 00:38:57 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
trace!("Invalid connection header: {:?}", conn);
|
2018-03-02 20:29:55 +01:00
|
|
|
return Err(ClientError::InvalidConnectionHeader(conn.clone()))
|
2018-02-28 00:38:57 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
trace!("Missing connection header");
|
2018-03-02 20:29:55 +01:00
|
|
|
return Err(ClientError::MissingConnectionHeader)
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
|
2018-02-28 23:16:55 +01:00
|
|
|
if let Some(key) = resp.headers().get(header::SEC_WEBSOCKET_ACCEPT)
|
2018-02-26 22:58:23 +01:00
|
|
|
{
|
|
|
|
// 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";
|
|
|
|
let mut sha1 = Sha1::new();
|
|
|
|
sha1.update(self.key.as_ref());
|
|
|
|
sha1.update(WS_GUID);
|
2018-02-28 00:38:57 +01:00
|
|
|
let encoded = base64::encode(&sha1.digest().bytes());
|
|
|
|
if key.as_bytes() != encoded.as_bytes() {
|
|
|
|
trace!(
|
|
|
|
"Invalid challenge response: expected: {} received: {:?}",
|
|
|
|
encoded, key);
|
2018-03-02 20:29:55 +01:00
|
|
|
return Err(ClientError::InvalidChallengeResponse(encoded, key.clone()));
|
2018-02-28 00:38:57 +01:00
|
|
|
}
|
2018-02-26 22:58:23 +01:00
|
|
|
} else {
|
2018-02-28 00:38:57 +01:00
|
|
|
trace!("Missing SEC-WEBSOCKET-ACCEPT header");
|
2018-03-02 20:29:55 +01:00
|
|
|
return Err(ClientError::MissingWebSocketAcceptHeader)
|
2018-02-26 22:58:23 +01:00
|
|
|
};
|
2018-01-28 07:03:03 +01:00
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
let inner = Inner {
|
2018-02-26 22:58:23 +01:00
|
|
|
tx: self.tx.take().unwrap(),
|
|
|
|
rx: PayloadHelper::new(resp),
|
|
|
|
closed: false,
|
|
|
|
};
|
2018-01-28 07:03:03 +01:00
|
|
|
|
2018-02-26 22:58:23 +01:00
|
|
|
let inner = Rc::new(UnsafeCell::new(inner));
|
|
|
|
Ok(Async::Ready(
|
2018-03-02 20:29:55 +01:00
|
|
|
(ClientReader{inner: Rc::clone(&inner), max_size: self.max_size},
|
|
|
|
ClientWriter{inner})))
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
pub struct ClientReader {
|
|
|
|
inner: Rc<UnsafeCell<Inner>>,
|
2018-02-27 19:09:24 +01:00
|
|
|
max_size: usize,
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
impl fmt::Debug for ClientReader {
|
2018-01-31 00:13:33 +01:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2018-03-02 20:29:55 +01:00
|
|
|
write!(f, "ws::ClientReader()")
|
2018-01-31 00:13:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
impl ClientReader {
|
2018-01-28 07:03:03 +01:00
|
|
|
#[inline]
|
2018-03-02 20:29:55 +01:00
|
|
|
fn as_mut(&mut self) -> &mut Inner {
|
2018-01-28 07:03:03 +01:00
|
|
|
unsafe{ &mut *self.inner.get() }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
impl Stream for ClientReader {
|
2018-01-28 07:03:03 +01:00
|
|
|
type Item = Message;
|
2018-03-02 20:29:55 +01:00
|
|
|
type Error = ProtocolError;
|
2018-01-28 07:03:03 +01:00
|
|
|
|
|
|
|
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
2018-02-27 19:09:24 +01:00
|
|
|
let max_size = self.max_size;
|
2018-01-28 07:03:03 +01:00
|
|
|
let inner = self.as_mut();
|
2018-02-26 22:58:23 +01:00
|
|
|
if inner.closed {
|
|
|
|
return Ok(Async::Ready(None))
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// read
|
2018-02-27 19:09:24 +01:00
|
|
|
match Frame::parse(&mut inner.rx, false, max_size) {
|
2018-02-26 22:58:23 +01:00
|
|
|
Ok(Async::Ready(Some(frame))) => {
|
2018-02-27 19:09:24 +01:00
|
|
|
let (finished, opcode, payload) = frame.unpack();
|
|
|
|
|
|
|
|
// continuation is not supported
|
|
|
|
if !finished {
|
|
|
|
inner.closed = true;
|
2018-03-02 20:29:55 +01:00
|
|
|
return Err(ProtocolError::NoContinuation)
|
2018-02-27 19:09:24 +01:00
|
|
|
}
|
2018-01-28 07:03:03 +01:00
|
|
|
|
|
|
|
match opcode {
|
|
|
|
OpCode::Continue => unimplemented!(),
|
2018-02-27 19:09:24 +01:00
|
|
|
OpCode::Bad => {
|
|
|
|
inner.closed = true;
|
2018-03-02 20:29:55 +01:00
|
|
|
Err(ProtocolError::BadOpCode)
|
2018-02-27 19:09:24 +01:00
|
|
|
},
|
2018-01-28 07:03:03 +01:00
|
|
|
OpCode::Close => {
|
2018-02-10 02:20:28 +01:00
|
|
|
inner.closed = true;
|
2018-02-26 22:58:23 +01:00
|
|
|
let code = NetworkEndian::read_uint(payload.as_ref(), 2) as u16;
|
2018-02-27 19:09:24 +01:00
|
|
|
Ok(Async::Ready(Some(Message::Close(CloseCode::from(code)))))
|
2018-01-28 07:03:03 +01:00
|
|
|
},
|
|
|
|
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)))),
|
2018-02-27 19:09:24 +01:00
|
|
|
Err(_) => {
|
|
|
|
inner.closed = true;
|
2018-03-02 20:29:55 +01:00
|
|
|
Err(ProtocolError::BadEncoding)
|
2018-02-27 19:09:24 +01:00
|
|
|
}
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-02-26 22:58:23 +01:00
|
|
|
Ok(Async::Ready(None)) => Ok(Async::Ready(None)),
|
|
|
|
Ok(Async::NotReady) => Ok(Async::NotReady),
|
2018-02-27 19:09:24 +01:00
|
|
|
Err(e) => {
|
2018-02-10 02:20:28 +01:00
|
|
|
inner.closed = true;
|
2018-02-27 19:09:24 +01:00
|
|
|
Err(e)
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
pub struct ClientWriter {
|
|
|
|
inner: Rc<UnsafeCell<Inner>>
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
impl ClientWriter {
|
2018-01-28 07:03:03 +01:00
|
|
|
#[inline]
|
2018-03-02 20:29:55 +01:00
|
|
|
fn as_mut(&mut self) -> &mut Inner {
|
2018-01-28 07:03:03 +01:00
|
|
|
unsafe{ &mut *self.inner.get() }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-02 20:29:55 +01:00
|
|
|
impl ClientWriter {
|
2018-01-28 07:03:03 +01:00
|
|
|
|
|
|
|
/// Write payload
|
|
|
|
#[inline]
|
2018-02-26 22:58:23 +01:00
|
|
|
fn write(&mut self, mut data: Binary) {
|
2018-02-10 02:20:28 +01:00
|
|
|
if !self.as_mut().closed {
|
2018-02-26 22:58:23 +01:00
|
|
|
let _ = self.as_mut().tx.unbounded_send(data.take());
|
2018-01-28 07:03:03 +01:00
|
|
|
} else {
|
|
|
|
warn!("Trying to write to disconnected response");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Send text frame
|
2018-02-10 05:43:14 +01:00
|
|
|
#[inline]
|
2018-03-04 19:18:42 +01:00
|
|
|
pub fn text<T: Into<Binary>>(&mut self, text: T) {
|
2018-02-20 07:48:27 +01:00
|
|
|
self.write(Frame::message(text.into(), OpCode::Text, true, true));
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Send binary frame
|
2018-02-10 05:43:14 +01:00
|
|
|
#[inline]
|
2018-01-28 07:03:03 +01:00
|
|
|
pub fn binary<B: Into<Binary>>(&mut self, data: B) {
|
2018-02-20 07:48:27 +01:00
|
|
|
self.write(Frame::message(data, OpCode::Binary, true, true));
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Send ping frame
|
2018-02-10 05:43:14 +01:00
|
|
|
#[inline]
|
2018-01-28 07:03:03 +01:00
|
|
|
pub fn ping(&mut self, message: &str) {
|
2018-02-20 07:48:27 +01:00
|
|
|
self.write(Frame::message(Vec::from(message), OpCode::Ping, true, true));
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Send pong frame
|
2018-02-10 05:43:14 +01:00
|
|
|
#[inline]
|
2018-01-28 07:03:03 +01:00
|
|
|
pub fn pong(&mut self, message: &str) {
|
2018-02-20 07:48:27 +01:00
|
|
|
self.write(Frame::message(Vec::from(message), OpCode::Pong, true, true));
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Send close frame
|
2018-02-10 05:43:14 +01:00
|
|
|
#[inline]
|
2018-01-28 07:03:03 +01:00
|
|
|
pub fn close(&mut self, code: CloseCode, reason: &str) {
|
2018-02-20 07:48:27 +01:00
|
|
|
self.write(Frame::close(code, reason, true));
|
2018-01-28 07:03:03 +01:00
|
|
|
}
|
|
|
|
}
|