2021-05-13 11:24:32 +00:00
|
|
|
use std::{
|
|
|
|
cell::Cell,
|
2021-06-17 16:57:58 +00:00
|
|
|
convert::Infallible,
|
2021-05-13 11:24:32 +00:00
|
|
|
task::{Context, Poll},
|
|
|
|
};
|
2019-12-17 07:34:25 +00:00
|
|
|
|
2019-04-09 00:49:27 +00:00
|
|
|
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
2021-05-13 11:24:32 +00:00
|
|
|
use actix_http::{
|
2021-12-04 19:40:47 +00:00
|
|
|
body::{BodySize, BoxBody},
|
2021-05-13 11:24:32 +00:00
|
|
|
h1,
|
|
|
|
ws::{self, CloseCode, Frame, Item, Message},
|
|
|
|
Error, HttpService, Request, Response,
|
|
|
|
};
|
2019-12-12 17:08:38 +00:00
|
|
|
use actix_http_test::test_server;
|
2019-12-17 07:34:25 +00:00
|
|
|
use actix_service::{fn_factory, Service};
|
2019-12-12 08:06:54 +00:00
|
|
|
use bytes::Bytes;
|
2021-06-17 16:57:58 +00:00
|
|
|
use derive_more::{Display, Error, From};
|
2021-05-13 11:24:32 +00:00
|
|
|
use futures_core::future::LocalBoxFuture;
|
2021-03-11 03:48:38 +00:00
|
|
|
use futures_util::{SinkExt as _, StreamExt as _};
|
2019-04-09 00:49:27 +00:00
|
|
|
|
2021-05-13 11:24:32 +00:00
|
|
|
#[derive(Clone)]
|
|
|
|
struct WsService(Cell<bool>);
|
2021-04-01 14:26:13 +00:00
|
|
|
|
2021-05-13 11:24:32 +00:00
|
|
|
impl WsService {
|
2019-12-17 07:34:25 +00:00
|
|
|
fn new() -> Self {
|
2021-05-13 11:24:32 +00:00
|
|
|
WsService(Cell::new(false))
|
2019-12-17 07:34:25 +00:00
|
|
|
}
|
2019-11-19 12:54:19 +00:00
|
|
|
|
2021-02-07 01:00:40 +00:00
|
|
|
fn set_polled(&self) {
|
2021-05-13 11:24:32 +00:00
|
|
|
self.0.set(true);
|
2019-12-17 07:34:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn was_polled(&self) -> bool {
|
2021-05-13 11:24:32 +00:00
|
|
|
self.0.get()
|
2019-12-17 07:34:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-17 16:57:58 +00:00
|
|
|
#[derive(Debug, Display, Error, From)]
|
|
|
|
enum WsServiceError {
|
2023-03-13 17:17:02 +00:00
|
|
|
#[display(fmt = "HTTP error")]
|
2021-06-17 16:57:58 +00:00
|
|
|
Http(actix_http::Error),
|
|
|
|
|
2023-03-13 17:17:02 +00:00
|
|
|
#[display(fmt = "WS handshake error")]
|
2021-06-17 16:57:58 +00:00
|
|
|
Ws(actix_http::ws::HandshakeError),
|
|
|
|
|
2023-03-13 17:17:02 +00:00
|
|
|
#[display(fmt = "I/O error")]
|
2021-06-17 16:57:58 +00:00
|
|
|
Io(std::io::Error),
|
|
|
|
|
|
|
|
#[display(fmt = "dispatcher error")]
|
|
|
|
Dispatcher,
|
|
|
|
}
|
|
|
|
|
2021-12-04 19:40:47 +00:00
|
|
|
impl From<WsServiceError> for Response<BoxBody> {
|
2021-06-17 16:57:58 +00:00
|
|
|
fn from(err: WsServiceError) -> Self {
|
|
|
|
match err {
|
|
|
|
WsServiceError::Http(err) => err.into(),
|
|
|
|
WsServiceError::Ws(err) => err.into(),
|
|
|
|
WsServiceError::Io(_err) => unreachable!(),
|
2021-12-08 06:01:11 +00:00
|
|
|
WsServiceError::Dispatcher => {
|
|
|
|
Response::internal_server_error().set_body(BoxBody::new(format!("{}", err)))
|
|
|
|
}
|
2021-06-17 16:57:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-13 11:24:32 +00:00
|
|
|
impl<T> Service<(Request, Framed<T, h1::Codec>)> for WsService
|
2019-12-17 07:34:25 +00:00
|
|
|
where
|
|
|
|
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
|
|
|
{
|
|
|
|
type Response = ();
|
2021-06-17 16:57:58 +00:00
|
|
|
type Error = WsServiceError;
|
2021-05-13 11:24:32 +00:00
|
|
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
2019-12-17 07:34:25 +00:00
|
|
|
|
2021-05-13 11:24:32 +00:00
|
|
|
fn poll_ready(&self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
2019-12-17 07:34:25 +00:00
|
|
|
self.set_polled();
|
|
|
|
Poll::Ready(Ok(()))
|
|
|
|
}
|
|
|
|
|
2021-02-07 01:00:40 +00:00
|
|
|
fn call(&self, (req, mut framed): (Request, Framed<T, h1::Codec>)) -> Self::Future {
|
2021-05-13 11:24:32 +00:00
|
|
|
assert!(self.was_polled());
|
|
|
|
|
|
|
|
Box::pin(async move {
|
|
|
|
let res = ws::handshake(req.head())?.message_body(())?;
|
|
|
|
|
|
|
|
framed.send((res, BodySize::None).into()).await?;
|
2019-12-17 07:34:25 +00:00
|
|
|
|
2021-05-13 11:24:32 +00:00
|
|
|
let framed = framed.replace_codec(ws::Codec::new());
|
2019-12-17 07:34:25 +00:00
|
|
|
|
2021-06-17 16:57:58 +00:00
|
|
|
ws::Dispatcher::with(framed, service)
|
|
|
|
.await
|
|
|
|
.map_err(|_| WsServiceError::Dispatcher)?;
|
2019-12-17 07:34:25 +00:00
|
|
|
|
2021-05-13 11:24:32 +00:00
|
|
|
Ok(())
|
|
|
|
})
|
2019-12-17 07:34:25 +00:00
|
|
|
}
|
2019-04-09 00:49:27 +00:00
|
|
|
}
|
|
|
|
|
2021-05-13 11:24:32 +00:00
|
|
|
async fn service(msg: Frame) -> Result<Message, Error> {
|
2019-04-09 00:49:27 +00:00
|
|
|
let msg = match msg {
|
2021-05-13 11:24:32 +00:00
|
|
|
Frame::Ping(msg) => Message::Pong(msg),
|
2021-12-08 06:01:11 +00:00
|
|
|
Frame::Text(text) => Message::Text(String::from_utf8_lossy(&text).into_owned().into()),
|
2021-05-13 11:24:32 +00:00
|
|
|
Frame::Binary(bin) => Message::Binary(bin),
|
|
|
|
Frame::Continuation(item) => Message::Continuation(item),
|
|
|
|
Frame::Close(reason) => Message::Close(reason),
|
2021-06-17 16:57:58 +00:00
|
|
|
_ => return Err(ws::ProtocolError::BadOpCode.into()),
|
2019-04-09 00:49:27 +00:00
|
|
|
};
|
2021-05-13 11:24:32 +00:00
|
|
|
|
2019-11-19 12:54:19 +00:00
|
|
|
Ok(msg)
|
2019-04-09 00:49:27 +00:00
|
|
|
}
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
#[actix_rt::test]
|
2022-01-31 17:30:34 +00:00
|
|
|
async fn simple() {
|
2021-05-13 11:24:32 +00:00
|
|
|
let mut srv = test_server(|| {
|
|
|
|
HttpService::build()
|
2021-06-17 16:57:58 +00:00
|
|
|
.upgrade(fn_factory(|| async {
|
|
|
|
Ok::<_, Infallible>(WsService::new())
|
|
|
|
}))
|
|
|
|
.finish(|_| async { Ok::<_, Infallible>(Response::not_found()) })
|
2021-05-13 11:24:32 +00:00
|
|
|
.tcp()
|
2020-03-08 07:42:45 +00:00
|
|
|
})
|
|
|
|
.await;
|
2019-04-09 00:49:27 +00:00
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
// client service
|
|
|
|
let mut framed = srv.ws().await.unwrap();
|
2021-05-13 11:24:32 +00:00
|
|
|
framed.send(Message::Text("text".into())).await.unwrap();
|
2019-04-09 00:49:27 +00:00
|
|
|
|
2021-05-13 11:24:32 +00:00
|
|
|
let item = framed.next().await.unwrap().unwrap();
|
|
|
|
assert_eq!(item, Frame::Text(Bytes::from_static(b"text")));
|
2019-04-09 00:49:27 +00:00
|
|
|
|
2021-05-13 11:24:32 +00:00
|
|
|
framed.send(Message::Binary("text".into())).await.unwrap();
|
|
|
|
|
|
|
|
let item = framed.next().await.unwrap().unwrap();
|
|
|
|
assert_eq!(item, Frame::Binary(Bytes::from_static(&b"text"[..])));
|
|
|
|
|
|
|
|
framed.send(Message::Ping("text".into())).await.unwrap();
|
|
|
|
let item = framed.next().await.unwrap().unwrap();
|
|
|
|
assert_eq!(item, Frame::Pong("text".to_string().into()));
|
2019-04-09 00:49:27 +00:00
|
|
|
|
2019-12-12 08:06:54 +00:00
|
|
|
framed
|
2021-05-13 11:24:32 +00:00
|
|
|
.send(Message::Continuation(Item::FirstText("text".into())))
|
2019-12-12 08:06:54 +00:00
|
|
|
.await
|
|
|
|
.unwrap();
|
2021-05-13 11:24:32 +00:00
|
|
|
let item = framed.next().await.unwrap().unwrap();
|
2019-12-12 08:06:54 +00:00
|
|
|
assert_eq!(
|
2021-05-13 11:24:32 +00:00
|
|
|
item,
|
|
|
|
Frame::Continuation(Item::FirstText(Bytes::from_static(b"text")))
|
2019-12-12 08:06:54 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
assert!(framed
|
2021-05-13 11:24:32 +00:00
|
|
|
.send(Message::Continuation(Item::FirstText("text".into())))
|
2019-12-12 08:06:54 +00:00
|
|
|
.await
|
|
|
|
.is_err());
|
|
|
|
assert!(framed
|
2021-05-13 11:24:32 +00:00
|
|
|
.send(Message::Continuation(Item::FirstBinary("text".into())))
|
2019-12-12 08:06:54 +00:00
|
|
|
.await
|
|
|
|
.is_err());
|
|
|
|
|
|
|
|
framed
|
2021-05-13 11:24:32 +00:00
|
|
|
.send(Message::Continuation(Item::Continue("text".into())))
|
2019-12-12 08:06:54 +00:00
|
|
|
.await
|
|
|
|
.unwrap();
|
2021-05-13 11:24:32 +00:00
|
|
|
let item = framed.next().await.unwrap().unwrap();
|
2019-12-12 08:06:54 +00:00
|
|
|
assert_eq!(
|
2021-05-13 11:24:32 +00:00
|
|
|
item,
|
|
|
|
Frame::Continuation(Item::Continue(Bytes::from_static(b"text")))
|
2019-12-12 08:06:54 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
framed
|
2021-05-13 11:24:32 +00:00
|
|
|
.send(Message::Continuation(Item::Last("text".into())))
|
2019-12-12 08:06:54 +00:00
|
|
|
.await
|
|
|
|
.unwrap();
|
2021-05-13 11:24:32 +00:00
|
|
|
let item = framed.next().await.unwrap().unwrap();
|
2019-12-12 08:06:54 +00:00
|
|
|
assert_eq!(
|
2021-05-13 11:24:32 +00:00
|
|
|
item,
|
|
|
|
Frame::Continuation(Item::Last(Bytes::from_static(b"text")))
|
2019-12-12 08:06:54 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
assert!(framed
|
2021-05-13 11:24:32 +00:00
|
|
|
.send(Message::Continuation(Item::Continue("text".into())))
|
2019-12-12 08:06:54 +00:00
|
|
|
.await
|
|
|
|
.is_err());
|
|
|
|
|
|
|
|
assert!(framed
|
2021-05-13 11:24:32 +00:00
|
|
|
.send(Message::Continuation(Item::Last("text".into())))
|
2019-12-12 08:06:54 +00:00
|
|
|
.await
|
|
|
|
.is_err());
|
|
|
|
|
2019-11-26 05:25:50 +00:00
|
|
|
framed
|
2021-05-13 11:24:32 +00:00
|
|
|
.send(Message::Close(Some(CloseCode::Normal.into())))
|
2019-11-26 05:25:50 +00:00
|
|
|
.await
|
|
|
|
.unwrap();
|
2019-04-09 00:49:27 +00:00
|
|
|
|
2021-05-13 11:24:32 +00:00
|
|
|
let item = framed.next().await.unwrap().unwrap();
|
|
|
|
assert_eq!(item, Frame::Close(Some(CloseCode::Normal.into())));
|
2019-04-09 00:49:27 +00:00
|
|
|
}
|