use mtp_codec::{CommunicationType, CommunicationValue, DataType, DataValue}; use mtp_common::{CommunicationError, PipeError}; use mtp_transport::{PipeReader, PipeWriter, Policy, TransportEvent}; use std::collections::HashMap; use std::sync::Arc; use tokio::sync::{Mutex, mpsc}; /// The sender operations needed by the transport-independent pipe protocol. pub trait PipeSender: Clone + Send + Sync + 'static { type Writer: tokio::io::AsyncWrite + Send + Unpin + 'static; fn send_pipe_message( &self, message: &CommunicationValue, ) -> impl std::future::Future> + Send; fn open_pipe_stream( &self, pipe_id: u32, description: &str, ) -> impl std::future::Future, CommunicationError>> + Send; } /// The receiver operations needed by the transport-independent pipe protocol. pub trait PipeReceiver

: Clone + Send + Sync + 'static where P: tokio::io::AsyncRead + Send + Unpin + 'static, { fn receive_pipe_event( &self, ) -> impl std::future::Future, CommunicationError>> + Send; } impl PipeSender for mtp_transport::Sender { type Writer = wtransport::SendStream; async fn send_pipe_message( &self, message: &CommunicationValue, ) -> Result<(), CommunicationError> { self.send(message).await } async fn open_pipe_stream( &self, pipe_id: u32, description: &str, ) -> Result, CommunicationError> { self.open_pipe(pipe_id, description).await } } impl PipeReceiver for mtp_transport::Receiver { async fn receive_pipe_event( &self, ) -> Result, CommunicationError> { self.receive_event().await } } impl PipeSender for mtp_transport::GenericSender where C: mtp_transport::TransportConnection, C::SendStream: tokio::io::AsyncWrite + Send + Unpin + 'static, { type Writer = C::SendStream; async fn send_pipe_message( &self, message: &CommunicationValue, ) -> Result<(), CommunicationError> { self.send(message).await } async fn open_pipe_stream( &self, pipe_id: u32, description: &str, ) -> Result, CommunicationError> { self.open_pipe(pipe_id, description).await } } impl PipeReceiver for mtp_transport::GenericReceiver where C: mtp_transport::TransportConnection, C::RecvStream: tokio::io::AsyncRead + Send + Unpin + 'static, { async fn receive_pipe_event( &self, ) -> Result, CommunicationError> { self.receive_event().await } } pub struct PipeHandle { pub(crate) pipe_id: u32, pub(crate) description: String, pub(crate) sender: S, pub(crate) response_rx: tokio::sync::oneshot::Receiver>, } impl PipeHandle { pub fn pipe_id(&self) -> u32 { self.pipe_id } pub fn description(&self) -> &str { &self.description } pub async fn wait(self) -> Result>, PipeError> { match self.response_rx.await { Ok(Ok(true)) => self .sender .open_pipe_stream(self.pipe_id, &self.description) .await .map(Some) .map_err(PipeError::from), Ok(Ok(false)) => Ok(None), Ok(Err(error)) => Err(error), Err(_) => Err(PipeError::StreamClosed), } } } pub struct PipeRequest { pub(crate) pipe_id: u32, pub(crate) description: String, pub(crate) sender: S, pub(crate) dispatcher: Arc>, } impl PipeRequest where S: PipeSender, P: tokio::io::AsyncRead + Send + Unpin + 'static, { pub fn id(&self) -> u32 { self.pipe_id } pub fn description(&self) -> &str { &self.description } pub async fn accept(self) -> Result, PipeError> { let (pipe_tx, pipe_rx) = tokio::sync::oneshot::channel(); self.dispatcher .pending_pipes .lock() .await .insert(self.pipe_id, pipe_tx); let response = CommunicationValue::new(CommunicationType::PipeResponse) .with_id(self.pipe_id) .add_typed_default(DataType::Accepted, DataValue::BoolTrue); self.sender .send_pipe_message(&response) .await .map_err(PipeError::from)?; tokio::time::timeout(self.dispatcher.policy.read_timeout, pipe_rx) .await .map_err(|_| PipeError::HandshakeTimeout)? .map_err(|_| PipeError::StreamClosed) } pub async fn deny(self) -> Result<(), PipeError> { let response = CommunicationValue::new(CommunicationType::PipeResponse) .with_id(self.pipe_id) .add_typed_default(DataType::Accepted, DataValue::BoolFalse); self.sender .send_pipe_message(&response) .await .map_err(PipeError::from) } } pub(crate) struct PipeDispatcher

{ pub(crate) pending_creations: Mutex>>>, pub(crate) pending_pipes: Mutex>>>, pub(crate) policy: Arc, } pub(crate) async fn run_dispatcher( receiver: R, sender: S, app_tx: mpsc::Sender>, pipe_req_tx: mpsc::Sender>, dispatcher: Arc>, ) where S: PipeSender, R: PipeReceiver

, P: tokio::io::AsyncRead + Send + Unpin + 'static, { let pipe_req_type = CommunicationType::PipeRequest.try_to_id(&mtp_codec::TypeMap::latest()); let pipe_resp_type = CommunicationType::PipeResponse.try_to_id(&mtp_codec::TypeMap::latest()); loop { match receiver.receive_pipe_event().await { Ok(TransportEvent::Message(message)) => { if Some(message.get_type()) == pipe_req_type { let request = PipeRequest { pipe_id: message.get_id(), description: message .get_str(DataType::Description) .unwrap_or("") .to_owned(), sender: sender.clone(), dispatcher: dispatcher.clone(), }; let _ = pipe_req_tx.send(request).await; continue; } if Some(message.get_type()) == pipe_resp_type { let mut pending = dispatcher.pending_creations.lock().await; if let Some(reply) = pending.remove(&message.get_id()) { let _ = reply.send(Ok(message.get_bool(DataType::Accepted).unwrap_or(false))); } continue; } if app_tx.send(Ok(message)).await.is_err() { break; } } Ok(TransportEvent::Pipe(reader)) => { let pipe_id = reader.pipe_id(); let mut pending = dispatcher.pending_pipes.lock().await; if let Some(reply) = pending.remove(&pipe_id) { let _ = reply.send(reader); continue; } drop(pending); let request = PipeRequest { pipe_id, description: reader.description().to_owned(), sender: sender.clone(), dispatcher: dispatcher.clone(), }; let _ = pipe_req_tx.send(request).await; } Err(error) => { if app_tx.send(Err(error)).await.is_err() { break; } } } } }