1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-25 08:22:43 +01:00
actix-extras/src/ws/client.rs

485 lines
15 KiB
Rust
Raw Normal View History

2018-01-28 07:03:03 +01:00
//! Http client request
2018-01-30 08:01:20 +01:00
#![allow(unused_imports, dead_code)]
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::time::Duration;
use std::cell::UnsafeCell;
use base64;
use rand;
use cookie::Cookie;
2018-01-28 07:03:03 +01:00
use bytes::BytesMut;
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-01-30 08:01:20 +01:00
use futures::future::{Either, err as FutErr};
2018-01-28 07:03:03 +01:00
use tokio_core::net::TcpStream;
2018-01-30 08:01:20 +01:00
use actix::prelude::*;
use body::Binary;
2018-01-28 07:03:03 +01:00
use error::UrlParseError;
use server::shared::SharedBytes;
use server::{utils, IoStream};
use client::{ClientRequest, ClientRequestBuilder,
HttpResponseParser, HttpResponseParserError, HttpClientWriter};
2018-01-30 22:04:52 +01:00
use client::{Connect, Connection, ClientConnector, ClientConnectorError};
2018-01-28 07:03:03 +01:00
use super::Message;
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 08:01:20 +01:00
pub type WsClientFuture =
Future<Item=(WsClientReader, WsClientWriter), Error=WsClientError>;
2018-01-30 00:45:37 +01:00
2018-01-31 18:28:53 +01:00
/// Websocket client error
2018-01-28 07:03:03 +01:00
#[derive(Fail, Debug)]
pub enum WsClientError {
#[fail(display="Invalid url")]
InvalidUrl,
#[fail(display="Invalid response status")]
InvalidResponseStatus,
#[fail(display="Invalid upgrade header")]
InvalidUpgradeHeader,
#[fail(display="Invalid connection header")]
InvalidConnectionHeader,
#[fail(display="Invalid challenge response")]
InvalidChallengeResponse,
#[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-01-30 08:01:20 +01:00
Connector(ClientConnectorError),
2018-01-28 07:03:03 +01:00
#[fail(display="{}", _0)]
Io(io::Error),
#[fail(display="Disconnected")]
Disconnected,
}
impl From<HttpError> for WsClientError {
fn from(err: HttpError) -> WsClientError {
WsClientError::Http(err)
}
}
impl From<UrlParseError> for WsClientError {
fn from(err: UrlParseError) -> WsClientError {
WsClientError::Url(err)
}
}
2018-01-30 08:01:20 +01:00
impl From<ClientConnectorError> for WsClientError {
fn from(err: ClientConnectorError) -> WsClientError {
WsClientError::Connector(err)
2018-01-28 07:03:03 +01:00
}
}
impl From<io::Error> for WsClientError {
fn from(err: io::Error) -> WsClientError {
WsClientError::Io(err)
}
}
impl From<HttpResponseParserError> for WsClientError {
fn from(err: HttpResponseParserError) -> WsClientError {
WsClientError::ResponseParseError(err)
}
}
/// `WebSocket` client
2018-01-31 00:26:58 +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-01-28 07:03:03 +01:00
pub struct WsClient {
request: ClientRequestBuilder,
2018-01-28 07:03:03 +01:00
err: Option<WsClientError>,
http_err: Option<HttpError>,
origin: Option<HeaderValue>,
protocols: Option<String>,
2018-02-13 01:08:04 +01:00
conn: Addr<Unsync, ClientConnector>,
2018-01-28 07:03:03 +01:00
}
impl WsClient {
2018-01-30 20:17:17 +01:00
/// Create new websocket connection
pub fn new<S: AsRef<str>>(uri: S) -> WsClient {
2018-01-30 20:17:17 +01:00
WsClient::with_connector(uri, ClientConnector::from_registry())
}
/// Create new websocket connection with custom `ClientConnector`
2018-02-13 01:08:04 +01:00
pub fn with_connector<S: AsRef<str>>(uri: S, conn: Addr<Unsync, ClientConnector>) -> WsClient {
2018-01-28 07:03:03 +01:00
let mut cl = WsClient {
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,
conn: conn,
};
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-01-28 07:03:03 +01:00
pub fn protocols<U, V>(&mut self, protos: U) -> &mut Self
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-01-28 07:03:03 +01:00
pub fn cookie<'c>(&mut self, cookie: Cookie<'c>) -> &mut Self {
self.request.cookie(cookie);
2018-01-28 07:03:03 +01:00
self
}
/// Set request Origin
pub fn origin<V>(&mut self, origin: V) -> &mut Self
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-01-30 20:17:17 +01:00
/// Set request header
2018-01-28 07:03:03 +01:00
pub fn header<K, V>(&mut self, key: K, value: V) -> &mut Self
where HeaderName: HttpTryFrom<K>, HeaderValue: HttpTryFrom<V>
2018-01-28 07:03:03 +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-01-30 08:01:20 +01:00
pub fn connect(&mut self) -> Result<Box<WsClientFuture>, WsClientError> {
2018-01-28 07:03:03 +01:00
if let Some(e) = self.err.take() {
return Err(e)
}
if let Some(e) = self.http_err.take() {
return Err(e.into())
}
// origin
if let Some(origin) = self.origin.take() {
self.request.set_header(header::ORIGIN, origin);
2018-01-28 07:03:03 +01:00
}
self.request.set_header(header::UPGRADE, "websocket");
self.request.set_header(header::CONNECTION, "upgrade");
self.request.set_header("SEC-WEBSOCKET-VERSION", "13");
2018-01-28 07:03:03 +01:00
if let Some(protocols) = self.protocols.take() {
self.request.set_header("SEC-WEBSOCKET-PROTOCOL", protocols.as_str());
}
let request = self.request.finish()?;
if request.uri().host().is_none() {
return Err(WsClientError::InvalidUrl)
}
if let Some(scheme) = request.uri().scheme_part() {
if scheme != "http" && scheme != "https" && scheme != "ws" && scheme != "wss" {
return Err(WsClientError::InvalidUrl);
}
} else {
return Err(WsClientError::InvalidUrl);
2018-01-28 07:03:03 +01:00
}
2018-01-30 08:01:20 +01:00
// get connection and start handshake
2018-01-28 07:03:03 +01:00
Ok(Box::new(
2018-02-13 07:56:47 +01:00
self.conn.send(Connect(request.uri().clone()))
2018-01-30 08:01:20 +01:00
.map_err(|_| WsClientError::Disconnected)
.and_then(|res| match res {
Ok(stream) => Either::A(WsHandshake::new(stream, request)),
Err(err) => Either::B(FutErr(err.into())),
})
))
2018-01-28 07:03:03 +01:00
}
}
2018-01-30 08:01:20 +01:00
struct WsInner {
conn: Connection,
writer: HttpClientWriter,
2018-01-28 07:03:03 +01:00
parser: HttpResponseParser,
parser_buf: BytesMut,
closed: bool,
error_sent: bool,
}
2018-01-30 08:01:20 +01:00
struct WsHandshake {
inner: Option<WsInner>,
2018-01-28 07:03:03 +01:00
request: ClientRequest,
sent: bool,
key: String,
}
2018-01-30 08:01:20 +01:00
impl WsHandshake {
fn new(conn: Connection, mut request: ClientRequest) -> WsHandshake {
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);
request.headers_mut().insert(
2018-01-28 07:03:03 +01:00
HeaderName::try_from("SEC-WEBSOCKET-KEY").unwrap(),
HeaderValue::try_from(key.as_str()).unwrap());
let inner = WsInner {
2018-01-30 08:01:20 +01:00
conn: conn,
writer: HttpClientWriter::new(SharedBytes::default()),
parser: HttpResponseParser::default(),
2018-01-28 07:03:03 +01:00
parser_buf: BytesMut::new(),
closed: false,
error_sent: false,
};
WsHandshake {
key: key,
inner: Some(inner),
request: request,
sent: false,
}
}
}
2018-01-30 08:01:20 +01:00
impl Future for WsHandshake {
type Item = (WsClientReader, WsClientWriter);
2018-01-28 07:03:03 +01:00
type Error = WsClientError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let mut inner = self.inner.take().unwrap();
if !self.sent {
self.sent = true;
inner.writer.start(&mut self.request);
}
2018-01-30 08:01:20 +01:00
if let Err(err) = inner.writer.poll_completed(&mut inner.conn, false) {
2018-01-28 07:03:03 +01:00
return Err(err.into())
}
2018-01-30 08:01:20 +01:00
match inner.parser.parse(&mut inner.conn, &mut inner.parser_buf) {
2018-01-28 07:03:03 +01:00
Ok(Async::Ready(resp)) => {
// verify response
if resp.status() != StatusCode::SWITCHING_PROTOCOLS {
return Err(WsClientError::InvalidResponseStatus)
}
// 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 {
return Err(WsClientError::InvalidUpgradeHeader)
}
// Check for "CONNECTION" header
let has_hdr = if let Some(conn) = resp.headers().get(header::CONNECTION) {
if let Ok(s) = conn.to_str() {
s.to_lowercase().contains("upgrade")
} else { false }
} else { false };
if !has_hdr {
return Err(WsClientError::InvalidConnectionHeader)
}
let match_key = if let Some(key) = resp.headers().get(
HeaderName::try_from("SEC-WEBSOCKET-ACCEPT").unwrap())
{
2018-01-31 00:13:33 +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";
2018-01-28 07:03:03 +01:00
let mut sha1 = Sha1::new();
sha1.update(self.key.as_ref());
sha1.update(WS_GUID);
key.as_bytes() == base64::encode(&sha1.digest().bytes()).as_bytes()
} else {
false
};
if !match_key {
return Err(WsClientError::InvalidChallengeResponse)
}
let inner = Rc::new(UnsafeCell::new(inner));
2018-01-28 07:03:03 +01:00
Ok(Async::Ready(
2018-01-30 00:45:37 +01:00
(WsClientReader{inner: Rc::clone(&inner)},
WsClientWriter{inner: inner})))
2018-01-28 07:03:03 +01:00
},
Ok(Async::NotReady) => {
self.inner = Some(inner);
Ok(Async::NotReady)
},
Err(err) => Err(err.into())
}
}
}
2018-01-30 08:01:20 +01:00
pub struct WsClientReader {
inner: Rc<UnsafeCell<WsInner>>
2018-01-28 07:03:03 +01:00
}
2018-01-31 00:13:33 +01:00
impl fmt::Debug for WsClientReader {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "WsClientReader()")
}
}
2018-01-30 08:01:20 +01:00
impl WsClientReader {
2018-01-28 07:03:03 +01:00
#[inline]
fn as_mut(&mut self) -> &mut WsInner {
2018-01-28 07:03:03 +01:00
unsafe{ &mut *self.inner.get() }
}
}
2018-01-30 08:01:20 +01:00
impl Stream for WsClientReader {
2018-01-28 07:03:03 +01:00
type Item = Message;
type Error = WsClientError;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
let inner = self.as_mut();
let mut done = false;
match utils::read_from_io(&mut inner.conn, &mut inner.parser_buf) {
2018-01-28 07:03:03 +01:00
Ok(Async::Ready(0)) => {
done = true;
inner.closed = true;
2018-01-28 07:03:03 +01:00
},
Ok(Async::Ready(_)) | Ok(Async::NotReady) => (),
Err(err) =>
return Err(err.into())
}
// write
let _ = inner.writer.poll_completed(&mut inner.conn, false);
2018-01-28 07:03:03 +01:00
// read
2018-02-10 05:43:14 +01:00
match Frame::parse(&mut inner.parser_buf, false) {
2018-01-28 07:03:03 +01:00
Ok(Some(frame)) => {
// trace!("WsFrame {}", frame);
let (_finished, opcode, payload) = frame.unpack();
match opcode {
OpCode::Continue => unimplemented!(),
OpCode::Bad =>
Ok(Async::Ready(Some(Message::Error))),
OpCode::Close => {
inner.closed = true;
inner.error_sent = true;
2018-01-28 07:03:03 +01:00
Ok(Async::Ready(Some(Message::Closed)))
},
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)))),
Err(_) =>
Ok(Async::Ready(Some(Message::Error))),
}
}
}
}
Ok(None) => {
if done {
Ok(Async::Ready(None))
} else if inner.closed {
if !inner.error_sent {
inner.error_sent = true;
2018-01-28 07:03:03 +01:00
Ok(Async::Ready(Some(Message::Closed)))
} else {
Ok(Async::Ready(None))
}
} else {
Ok(Async::NotReady)
}
},
Err(err) => {
inner.closed = true;
inner.error_sent = true;
2018-01-28 07:03:03 +01:00
Err(err.into())
}
}
}
}
2018-01-30 08:01:20 +01:00
pub struct WsClientWriter {
inner: Rc<UnsafeCell<WsInner>>
2018-01-28 07:03:03 +01:00
}
2018-01-30 08:01:20 +01:00
impl WsClientWriter {
2018-01-28 07:03:03 +01:00
#[inline]
fn as_mut(&mut self) -> &mut WsInner {
2018-01-28 07:03:03 +01:00
unsafe{ &mut *self.inner.get() }
}
}
2018-01-30 08:01:20 +01:00
impl WsClientWriter {
2018-01-28 07:03:03 +01:00
/// Write payload
#[inline]
2018-02-10 09:05:20 +01:00
fn write(&mut self, data: &Binary) {
if !self.as_mut().closed {
2018-02-10 09:05:20 +01:00
let _ = self.as_mut().writer.write(data);
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-02-10 09:05:20 +01:00
pub fn text<T: Into<String>>(&mut self, text: T) {
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-10 09:05:20 +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-10 09:05:20 +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-10 09:05:20 +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-10 09:05:20 +01:00
self.write(&Frame::close(code, reason, true));
2018-01-28 07:03:03 +01:00
}
}