use std::collections::HashMap;
use std::io;
use std::marker::PhantomData;
use std::pin::Pin;
use futures::stream::FuturesUnordered;
use futures::{SinkExt, StreamExt};
use tokio::sync::mpsc;
use tokio_util::sync::CancellationToken;
use crate::proto::routing::message::Data;
use crate::proto::routing::{Command, RoutingCodec, RoutingMessage};
use crate::sync::{ChannelCommand, ChannelSink, ChannelStream, SharedStream};
use crate::util::{DemuxedDecodedSink, EndMethod, MuxedEncodedStream};
use crate::{Identify, Protocol, SessionConnector};
pub struct RoutingServer<P, S>
where
P: Protocol + 'static,
P::Message: Identify,
S: SessionConnector<Item = P> + 'static,
{
stream: ChannelStream<RoutingMessage, SharedStream<MuxedEncodedStream<P>>>,
sink: ChannelSink<RoutingMessage>,
_connector: PhantomData<S>,
}
impl<P, S> RoutingServer<P, S>
where
P: Protocol + Send,
P::Message: Identify + Send,
P::SinkHalf: Send,
P::StreamHalf: Identify + Send,
P::Codec: Send,
S: SessionConnector<Item = P> + Send,
{
pub async fn new(connector: S) -> Self {
let streams = SharedStream::new(MuxedEncodedStream::new(P::codec()));
let sinks = DemuxedDecodedSink::new(P::codec());
let (sink_tx_ctrl, ctrl_rx_sink) = mpsc::channel(1);
let (ctrl_tx_stream, stream_rx_ctrl) = mpsc::unbounded_channel();
let token = CancellationToken::new();
let controller = Controller {
rx: ctrl_rx_sink,
tx: Some(ctrl_tx_stream),
stream_token: token.clone(),
streams: streams.clone(),
sinks,
connector,
connecting: FuturesUnordered::new(),
pending: HashMap::new(),
};
controller.into_background();
Self {
stream: ChannelStream::new(stream_rx_ctrl, streams, None, Some(token)),
sink: ChannelSink::new(sink_tx_ctrl),
_connector: PhantomData,
}
}
}
impl<P, S> Protocol for RoutingServer<P, S>
where
P: Protocol,
P::Message: Identify,
S: SessionConnector<Item = P>,
{
type Message = RoutingMessage;
type Codec = RoutingCodec;
type StreamHalf = ChannelStream<RoutingMessage, SharedStream<MuxedEncodedStream<P>>>;
type SinkHalf = ChannelSink<RoutingMessage>;
fn into_split(self) -> (Self::StreamHalf, Self::SinkHalf) {
(self.stream, self.sink)
}
fn codec() -> Self::Codec {
RoutingCodec::new()
}
}
type BoxedConnectFut<P> = Box<dyn Future<Output = (u64, io::Result<P>)> + Send>;
pub struct Controller<P, S>
where
P: Protocol,
P::Message: Identify,
S: SessionConnector<Item = P>,
{
rx: mpsc::Receiver<ChannelCommand<RoutingMessage>>,
tx: Option<mpsc::UnboundedSender<RoutingMessage>>,
stream_token: CancellationToken,
streams: SharedStream<MuxedEncodedStream<P>>,
sinks: DemuxedDecodedSink<P>,
connector: S,
connecting: FuturesUnordered<Pin<BoxedConnectFut<P>>>,
pending: HashMap<u64, PendingSession<P>>,
}
#[derive(Debug, Eq, PartialEq, Clone)]
enum ControllerError {
StreamSendFailed,
StreamSenderDropped,
SinkSendFailed,
SinkFlushFailed,
SinkCloseFailed,
}
enum PendingSession<P: Protocol> {
Connecting,
Connected(P),
}
impl<P, S> Controller<P, S>
where
P: Protocol + Send + 'static,
P::Message: Identify + Send,
P::SinkHalf: Send,
P::StreamHalf: Identify + Send,
P::Codec: Send,
S: SessionConnector<Item = P> + Send + 'static,
{
fn into_background(self) {
tokio::spawn(async move {
let result = self.run().await;
log::debug!("Controller finished: {result:?}");
result
});
}
async fn run(mut self) -> Result<(), ControllerError> {
loop {
tokio::select! {
result = self.rx.recv(), if !(self.rx.is_closed() && self.rx.is_empty()) => {
if let Some(cmd) = result {
if let Some(payload) = self.process_cmd(cmd).await? {
self.send_sink(payload).await?;
}
} else {
let _ = self.send_stream(Command::ShutRead);
}
}
_ = self.stream_token.cancelled(), if self.tx.is_some() => {
let _ = self.send_stream(Command::ShutWrite);
self.tx.take();
}
result = self.connecting.next(), if !self.connecting.is_empty() => {
if let Some(res) = result {
if let Err(e) = self.process_connect_result(res)
&& self.tx.is_none() && self.rx.is_closed() && self.rx.is_empty() {
return Err(e);
}
}
}
else => return Ok(())
}
}
}
fn send_stream(&mut self, cmd: Command) -> Result<(), ControllerError> {
if let Some(sender) = self.tx.as_mut() {
sender
.send(RoutingMessage::from(cmd))
.map_err(|_| ControllerError::StreamSendFailed)
} else {
Err(ControllerError::StreamSenderDropped)
}
}
async fn send_sink(&mut self, payload: Data) -> Result<(), ControllerError> {
return self
.sinks
.send(payload.chunk)
.await
.map_err(|_| ControllerError::SinkSendFailed);
}
async fn flush_sink(&mut self) -> Result<(), ControllerError> {
self.sinks
.flush()
.await
.map_err(|_| ControllerError::SinkFlushFailed)
}
async fn close_sink(&mut self) -> Result<(), ControllerError> {
self.sinks
.close()
.await
.map_err(|_| ControllerError::SinkCloseFailed)
}
async fn process_cmd(
&mut self,
cmd: ChannelCommand<RoutingMessage>,
) -> Result<Option<Data>, ControllerError> {
match cmd {
ChannelCommand::Message(msg) => self.process_msg(msg).await,
ChannelCommand::ExplicitFlush => self.flush_sink().await.map(|_| None),
ChannelCommand::ExplicitClose => self.close_sink().await.map(|_| None),
}
}
async fn process_msg(&mut self, msg: RoutingMessage) -> Result<Option<Data>, ControllerError> {
match msg.command {
Command::Connect((sid, target)) => {
let connect_fut = self.connector.connect(sid, target);
self.connecting.push(Box::pin(connect_fut));
self.pending.insert(sid, PendingSession::Connecting);
}
Command::ConnectOk(_) => {}
Command::ConnectErr(_) => {}
Command::Add(sid) => {
if let Some(pending) = self.pending.remove(&sid)
&& let PendingSession::Connected(session) = pending
&& self.sinks.try_reserve_id(sid)
{
let (stream, sink) = session.into_split();
if self.sinks.try_add_with_reserved_id(sink, sid).is_ok() {
self.streams.lock().await.add(stream);
return Ok(None);
} else {
let _ = self.sinks.remove(&sid);
}
}
self.send_stream(Command::Remove(sid))?;
}
Command::Remove(sid) => {
self.pending.remove(&sid);
self.sinks.remove(&sid);
self.streams.lock().await.remove(&sid);
}
Command::Forward(payload) => return Ok(Some(payload)),
Command::ShutRead => {
let mut streams = self.streams.lock().await;
streams.clear();
streams.set_end_method(EndMethod::Now);
}
Command::ShutWrite => {
self.streams.lock().await.set_end_method(EndMethod::Empty);
let _ = self.sinks.close().await;
}
}
Ok(None)
}
fn process_connect_result(
&mut self,
result: (u64, Result<P, io::Error>),
) -> Result<(), ControllerError> {
let (sid, conn_result) = result;
if let Ok(session) = conn_result
&& let Some(pending) = self.pending.get_mut(&sid)
&& let PendingSession::Connecting = pending
{
*pending = PendingSession::Connected(session);
self.send_stream(Command::ConnectOk(sid))
} else {
self.send_stream(Command::ConnectErr(sid))
}
}
}