use
{
crate :: { import::*, WsEvent, WsErr } ,
super :: { notifier::Notifier } ,
};
#[ cfg(test) ] mod closer_send ;
#[ cfg(test) ] mod notify_errors ;
#[ cfg(test) ] mod no_double_close ;
#[ derive( Debug, Clone ) ]
enum State
{
Ready,
Closing(CloseFrame<'static>),
Flushing,
SinkError,
Closed,
}
impl PartialEq for State
{
fn eq( &self, other: &Self ) -> bool
{
std::mem::discriminant( self ) == std::mem::discriminant( other )
}
}
pub(super) struct Closer
{
state: State,
}
impl Closer
{
pub(super) fn new() -> Self
{
Self{ state: State::Ready }
}
pub(super) fn queue( &mut self, frame: CloseFrame<'static> ) -> Result<(), ()>
{
if self.state != State::Ready
{
return Err(())
}
self.state = State::Closing( frame );
Ok(())
}
pub(super) fn run
(
mut self : Pin<&mut Self> ,
mut socket : impl Sink<tungstenite::Message, Error=TungErr> + Unpin ,
ph : &mut Notifier ,
cx : &mut Context<'_> ,
)
-> Poll< Result<(), ()> >
{
match &self.state
{
State::Ready => Ok (()).into() ,
State::SinkError => Err(()).into() ,
State::Closed => Err(()).into() ,
State::Closing( frame ) =>
{
let ready = Pin::new( &mut socket ).as_mut().poll_ready( cx );
match ready
{
Poll::Pending =>
{
Poll::Pending
}
Poll::Ready(Err(e)) =>
{
ph.queue( WsEvent::Error( Arc::new( e.into() )) );
self.state = State::SinkError;
Err(()).into()
}
Poll::Ready(Ok(())) =>
{
if let Err(e) = Pin::new( &mut socket ).as_mut().start_send( TungMessage::Close( Some(frame.clone()) ) )
{
ph.queue( WsEvent::Error( Arc::new( e.into() )) );
self.state = State::SinkError;
}
match Pin::new( &mut socket ).as_mut().poll_flush( cx )
{
Poll::Pending =>
{
self.state = State::Flushing;
Poll::Pending
}
Poll::Ready(Ok(())) =>
{
self.state = State::Closed;
Ok(()).into()
}
Poll::Ready(Err(e)) =>
{
ph.queue( WsEvent::Error( Arc::new( e.into() )) );
self.state = State::SinkError;
Err(()).into()
}
}
}
}
}
State::Flushing =>
{
match Pin::new( &mut socket ).as_mut().poll_flush( cx )
{
Poll::Pending =>
{
self.state = State::Flushing;
Poll::Pending
}
Poll::Ready(Ok(())) =>
{
self.state = State::Closed;
Err(()).into()
}
Poll::Ready(Err(e)) =>
{
ph.queue( WsEvent::Error( Arc::new( WsErr::from(e) )) );
self.state = State::SinkError;
Err(()).into()
}
}
}
}
}
}