use core::pin::Pin;
use core::task::{Context, Poll, ready};
use std::io;
use futures_util::{Sink, SinkExt as _, Stream, StreamExt as _};
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_tungstenite::tungstenite;
pub(crate) fn websocket_compat<S>(stream: S) -> impl AsyncRead + AsyncWrite + Unpin + Send + 'static
where
S: Stream<Item = Result<tungstenite::Message, tungstenite::Error>>
+ Sink<tungstenite::Message, Error = tungstenite::Error>
+ Unpin
+ Send
+ 'static,
{
let compat = stream
.filter_map(|item| {
let mapped = item
.map(|msg| match msg {
tungstenite::Message::Text(s) => Some(WsReadMsg::Payload(tungstenite::Bytes::from(s))),
tungstenite::Message::Binary(data) => Some(WsReadMsg::Payload(data)),
tungstenite::Message::Ping(_) | tungstenite::Message::Pong(_) => None,
tungstenite::Message::Close(_) => Some(WsReadMsg::Close),
tungstenite::Message::Frame(_) => unreachable!("raw frames are never returned when reading"),
})
.transpose();
core::future::ready(mapped)
})
.with(|item| {
core::future::ready(Ok::<_, tungstenite::Error>(tungstenite::Message::Binary(
tungstenite::Bytes::from(item),
)))
});
WsStream::new(compat)
}
enum WsReadMsg {
Payload(tungstenite::Bytes),
Close,
}
struct WsStream<S> {
inner: S,
read_buf: Option<tungstenite::Bytes>,
}
impl<S> WsStream<S> {
fn new(inner: S) -> Self {
Self { inner, read_buf: None }
}
}
impl<S, E> AsyncRead for WsStream<S>
where
S: Stream<Item = Result<WsReadMsg, E>> + Unpin,
E: core::error::Error + Send + Sync + 'static,
{
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> Poll<io::Result<()>> {
let this = &mut *self;
let mut data = if let Some(data) = this.read_buf.take() {
data
} else {
match ready!(Pin::new(&mut this.inner).poll_next(cx)) {
Some(Ok(WsReadMsg::Payload(data))) => data,
Some(Ok(WsReadMsg::Close)) => return Poll::Ready(Ok(())),
Some(Err(e)) => return Poll::Ready(Err(io::Error::other(e))),
None => return Poll::Ready(Ok(())),
}
};
let bytes_to_copy = core::cmp::min(buf.remaining(), data.len());
let dest = buf.initialize_unfilled_to(bytes_to_copy);
dest.copy_from_slice(&data.split_to(bytes_to_copy));
buf.advance(bytes_to_copy);
if !data.is_empty() {
this.read_buf = Some(data);
}
Poll::Ready(Ok(()))
}
}
impl<S, E> AsyncWrite for WsStream<S>
where
S: Sink<Vec<u8>, Error = E> + Unpin,
E: core::error::Error + Send + Sync + 'static,
{
fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>> {
let this = &mut *self;
let _ = Pin::new(&mut this.inner).poll_flush(cx);
if let Err(e) = ready!(Pin::new(&mut this.inner).poll_ready(cx)) {
return Poll::Ready(Err(io::Error::other(e)));
}
if let Err(e) = Pin::new(&mut this.inner).start_send(buf.to_vec()) {
return Poll::Ready(Err(io::Error::other(e)));
}
Poll::Ready(Ok(buf.len()))
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let res = ready!(Pin::new(&mut self.inner).poll_flush(cx));
Poll::Ready(res.map_err(io::Error::other))
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let res = ready!(Pin::new(&mut self.inner).poll_close(cx));
Poll::Ready(res.map_err(io::Error::other))
}
}