use bytes::Bytes;
use bytes::BytesMut;
use fastwebsockets::{Frame, OpCode, Payload, WebSocket, WebSocketError};
use futures::FutureExt;
use futures::future::BoxFuture;
use std::fmt::Debug;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
type FutureResult<S, T> = Result<(WebSocket<S>, T), WebSocketError>;
struct PayloadFrame {
opcode: OpCode,
payload: Bytes,
}
enum ReadState<S> {
Idle,
Reading(BoxFuture<'static, FutureResult<S, PayloadFrame>>),
}
enum WriteState<S> {
Idle,
Writing(BoxFuture<'static, FutureResult<S, ()>>),
}
enum PendingOp {
Write(usize),
Flush,
Close,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PayloadType {
Binary,
Text,
}
impl From<PayloadType> for OpCode {
fn from(value: PayloadType) -> Self {
match value {
PayloadType::Binary => OpCode::Binary,
PayloadType::Text => OpCode::Text,
}
}
}
fn make_io_err(e: WebSocketError) -> io::Error {
io::Error::other(format!("Websocket error: {}", e))
}
fn read<S>(mut websocket: WebSocket<S>) -> BoxFuture<'static, FutureResult<S, PayloadFrame>>
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
async move {
match websocket.read_frame().await {
Ok(frame) => {
let payload = match frame.payload {
Payload::BorrowedMut(buf) => Bytes::from(buf.to_vec()),
Payload::Borrowed(buf) => Bytes::from(buf.to_vec()),
Payload::Owned(vec) => Bytes::from(vec),
Payload::Bytes(bytes) => bytes.freeze(),
};
let owned = PayloadFrame {
opcode: frame.opcode,
payload,
};
Ok((websocket, owned))
}
Err(e) => Err(e),
}
}
.boxed()
}
fn write<S>(
mut websocket: WebSocket<S>,
payload: BytesMut,
payload_type: PayloadType,
) -> BoxFuture<'static, FutureResult<S, ()>>
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
async move {
let frame = Frame::new(true, payload_type.into(), None, Payload::Bytes(payload));
match websocket.write_frame(frame).await {
Ok(()) => Ok((websocket, ())),
Err(e) => Err(e),
}
}
.boxed()
}
fn flush<S>(mut websocket: WebSocket<S>) -> BoxFuture<'static, FutureResult<S, ()>>
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
async move {
match websocket.flush().await {
Ok(()) => Ok((websocket, ())),
Err(e) => Err(e),
}
}
.boxed()
}
fn close<S>(mut websocket: WebSocket<S>) -> BoxFuture<'static, FutureResult<S, ()>>
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
async move {
let frame = Frame::close_raw(Vec::new().into());
match websocket.write_frame(frame).await {
Ok(()) => Ok((websocket, ())),
Err(e) => Err(e),
}
}
.boxed()
}
pub struct WebSocketStream<S> {
websocket: Option<WebSocket<S>>,
read_buf: BytesMut,
read_state: ReadState<S>,
write_state: WriteState<S>,
pending_op: Option<PendingOp>,
payload_type: PayloadType,
closed: bool,
}
impl<S> WebSocketStream<S>
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
pub fn new(mut websocket: WebSocket<S>, payload_type: PayloadType) -> Self {
websocket.set_auto_pong(true);
websocket.set_auto_close(true);
Self {
websocket: Some(websocket),
read_buf: BytesMut::with_capacity(8 * 1024),
read_state: ReadState::Idle,
write_state: WriteState::Idle,
pending_op: None,
payload_type,
closed: false,
}
}
pub fn into_inner(mut self) -> Option<WebSocket<S>> {
self.websocket.take()
}
pub fn is_closed(&self) -> bool {
self.closed
}
fn poll_drive(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<Option<PendingOp>>> {
match &mut self.write_state {
WriteState::Idle => Poll::Ready(Ok(None)),
WriteState::Writing(fut) => match fut.as_mut().poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(Ok((websocket, ()))) => {
self.websocket = Some(websocket);
self.write_state = WriteState::Idle;
Poll::Ready(Ok(self.pending_op.take()))
}
Poll::Ready(Err(e)) => {
self.write_state = WriteState::Idle;
self.pending_op = None;
Poll::Ready(Err(make_io_err(e)))
}
},
}
}
}
impl<S> AsyncRead for WebSocketStream<S>
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
if !self.read_buf.is_empty() {
let to_copy = std::cmp::min(self.read_buf.len(), buf.remaining());
buf.put_slice(&self.read_buf.split_to(to_copy));
return Poll::Ready(Ok(()));
}
if self.closed {
return Poll::Ready(Ok(()));
}
loop {
match &mut self.read_state {
ReadState::Idle => {
let websocket = match self.websocket.take() {
Some(websocket) => websocket,
None => {
return Poll::Ready(Err(io::Error::other("Websocket not available")));
}
};
let future = read(websocket);
self.read_state = ReadState::Reading(future);
}
ReadState::Reading(fut) => {
match fut.as_mut().poll(cx) {
Poll::Pending => return Poll::Pending,
Poll::Ready(res) => {
self.read_state = ReadState::Idle;
match res {
Ok((websocket, frame)) => {
self.websocket = Some(websocket);
match frame.opcode {
OpCode::Binary | OpCode::Text | OpCode::Continuation => {
if matches!(frame.opcode, OpCode::Binary | OpCode::Text)
&& frame.opcode != self.payload_type.into()
{
return Poll::Ready(Err(io::Error::other(
"The received data type is different \
from the stream data type",
)));
}
let payload = frame.payload;
if payload.is_empty() {
continue;
}
return if payload.len() <= buf.remaining() {
buf.put_slice(&payload);
Poll::Ready(Ok(()))
} else {
let take = buf.remaining();
buf.put_slice(&payload[..take]);
self.read_buf.extend_from_slice(&payload[take..]);
Poll::Ready(Ok(()))
};
}
OpCode::Close => {
self.closed = true;
return Poll::Ready(Ok(()));
}
_ => {
continue;
}
}
}
Err(e) => {
return Poll::Ready(Err(make_io_err(e)));
}
}
}
}
}
}
}
}
}
impl<S> AsyncWrite for WebSocketStream<S>
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
loop {
match self.poll_drive(cx) {
Poll::Pending => return Poll::Pending,
Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
Poll::Ready(Ok(Some(PendingOp::Write(n)))) => return Poll::Ready(Ok(n)),
Poll::Ready(Ok(Some(_))) => {
continue;
}
Poll::Ready(Ok(None)) => {
let websocket = match self.websocket.take() {
Some(websocket) => websocket,
None => {
return Poll::Ready(Err(io::Error::other("Websocket not available")));
}
};
let payload = BytesMut::from(buf);
let len = payload.len();
self.pending_op = Some(PendingOp::Write(len));
self.write_state =
WriteState::Writing(write(websocket, payload, self.payload_type));
}
}
}
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
loop {
match self.poll_drive(cx) {
Poll::Pending => return Poll::Pending,
Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
Poll::Ready(Ok(Some(PendingOp::Flush))) => return Poll::Ready(Ok(())),
Poll::Ready(Ok(Some(_))) => {
continue;
}
Poll::Ready(Ok(None)) => {
let websocket = match self.websocket.take() {
Some(websocket) => websocket,
None => {
return Poll::Ready(Err(io::Error::other("Websocket not available")));
}
};
self.pending_op = Some(PendingOp::Flush);
self.write_state = WriteState::Writing(flush(websocket));
}
}
}
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
loop {
match self.poll_drive(cx) {
Poll::Pending => return Poll::Pending,
Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
Poll::Ready(Ok(Some(PendingOp::Close))) => return Poll::Ready(Ok(())),
Poll::Ready(Ok(Some(_))) => {
continue;
}
Poll::Ready(Ok(None)) => {
let websocket = match self.websocket.take() {
Some(websocket) => websocket,
None => {
return Poll::Ready(Err(io::Error::other("Websocket not available")));
}
};
self.pending_op = Some(PendingOp::Close);
self.write_state = WriteState::Writing(close(websocket));
}
}
}
}
}
impl<S> Debug for WebSocketStream<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn read_state_name<T>(s: &ReadState<T>) -> &'static str {
match s {
ReadState::Idle => "Idle",
ReadState::Reading(_) => "Reading",
}
}
fn write_state_name<T>(s: &WriteState<T>) -> &'static str {
match s {
WriteState::Idle => "Idle",
WriteState::Writing(_) => "Writing",
}
}
fn pending_op_name(op: &Option<PendingOp>) -> &'static str {
match op {
None => "None",
Some(PendingOp::Write(_)) => "Write",
Some(PendingOp::Flush) => "Flush",
Some(PendingOp::Close) => "Close",
}
}
f.debug_struct("WebSocketStream")
.field("read_buf_len", &self.read_buf.len())
.field("read_state", &read_state_name(&self.read_state))
.field("write_state", &write_state_name(&self.write_state))
.field("pending_op", &pending_op_name(&self.pending_op))
.field("closed", &self.closed)
.finish()
}
}