use async_trait::async_trait;
use futures_util::{Sink, SinkExt, Stream, StreamExt};
use std::time::Duration;
use tokio::time;
use tungstenite::Message;
use crate::context::ConnectionContext;
use crate::error::HandshakeError;
#[async_trait]
pub trait HandshakeSender: Send {
async fn send_msg(&mut self, message: Message) -> Result<(), HandshakeError>;
}
#[async_trait]
pub trait HandshakeReceiver: Send {
async fn recv_msg(&mut self) -> Result<Option<Message>, HandshakeError>;
}
#[async_trait]
impl<S> HandshakeSender for S
where
S: Sink<Message, Error = tungstenite::Error> + Unpin + Send,
{
async fn send_msg(&mut self, message: Message) -> Result<(), HandshakeError> {
SinkExt::send(self, message)
.await
.map_err(HandshakeError::from)
}
}
#[async_trait]
impl<R> HandshakeReceiver for R
where
R: Stream<Item = Result<Message, tungstenite::Error>> + Unpin + Send,
{
async fn recv_msg(&mut self) -> Result<Option<Message>, HandshakeError> {
match StreamExt::next(self).await {
Some(Ok(msg)) => Ok(Some(msg)),
Some(Err(e)) => Err(HandshakeError::from(e)),
None => Ok(None),
}
}
}
#[async_trait]
pub trait Handshaker: Send + Sync {
async fn handshake(
&self,
sender: &mut dyn HandshakeSender,
receiver: &mut dyn HandshakeReceiver,
context: &ConnectionContext,
) -> Result<(), HandshakeError>;
fn is_retryable(&self, error: &HandshakeError) -> bool {
error.is_retryable()
}
fn name(&self) -> &'static str {
"handshaker"
}
fn timeout(&self) -> Option<Duration> {
Some(Duration::from_secs(30))
}
async fn handshake_with_timeout(
&self,
sender: &mut dyn HandshakeSender,
receiver: &mut dyn HandshakeReceiver,
context: &ConnectionContext,
) -> Result<(), HandshakeError> {
if let Some(timeout) = self.timeout() {
match time::timeout(timeout, self.handshake(sender, receiver, context)).await {
Ok(res) => res,
Err(_elapsed) => Err(HandshakeError::Timeout(timeout)),
}
} else {
self.handshake(sender, receiver, context).await
}
}
}
pub type BoxHandshaker = Box<dyn Handshaker>;
pub fn boxed<H: Handshaker + 'static>(h: H) -> BoxHandshaker {
Box::new(h)
}