General Upgrade, NEW: WebServers, Better Docs
Some checks failed
CI / checks (push) Failing after 1m51s
Some checks failed
CI / checks (push) Failing after 1m51s
This commit is contained in:
parent
5f11d476b6
commit
04019f1477
119 changed files with 10024 additions and 4882 deletions
2
host/Cargo.lock
generated
2
host/Cargo.lock
generated
|
|
@ -4,4 +4,4 @@ version = 4
|
|||
|
||||
[[package]]
|
||||
name = "host"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
[package]
|
||||
name = "mtp-host"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
mtp-common = { version = "0.1.0", path = "../common" }
|
||||
mtp-codec = { version = "0.1.0", path = "../codec", features = ["registry"] }
|
||||
mtp-transport = { version = "0.1.0", path = "../transport", features = ["host"] }
|
||||
mtp-crypto = { version = "0.1.0", path = "../crypto", optional = true }
|
||||
mtp-common = { version = "0.2.0", path = "../common" }
|
||||
mtp-codec = { version = "0.2.0", path = "../codec", features = ["registry"] }
|
||||
mtp-transport = { version = "0.2.0", path = "../transport", features = ["host"] }
|
||||
mtp-crypto = { version = "0.2.0", path = "../crypto", optional = true }
|
||||
rand = "0.8"
|
||||
tokio = { version = "1", features = ["time", "sync"] }
|
||||
tokio = { version = "1", features = ["macros", "rt", "time", "sync"] }
|
||||
tracing = "0.1"
|
||||
|
||||
[features]
|
||||
|
||||
|
|
|
|||
163
host/src/config.rs
Normal file
163
host/src/config.rs
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
use std::net::IpAddr;
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
use std::pin::Pin;
|
||||
#[cfg(feature = "crypto")]
|
||||
use tokio::time::Duration;
|
||||
|
||||
pub use mtp_transport::Policy;
|
||||
|
||||
/// Callback that looks up a registered client by ID.
|
||||
///
|
||||
/// Called during login to retrieve a client's public key bundle for signature
|
||||
/// verification, and also during guest ID generation to check whether a random
|
||||
/// candidate collides with a registered client. When used for collision
|
||||
/// checking the `description` argument is `None`.
|
||||
#[cfg(feature = "crypto")]
|
||||
pub type GetExistingClient = Box<
|
||||
dyn Fn(
|
||||
u64,
|
||||
Option<String>,
|
||||
)
|
||||
-> Pin<Box<dyn std::future::Future<Output = Option<mtp_crypto::PublicKeyBundle>> + Send>>
|
||||
+ Send
|
||||
+ Sync,
|
||||
>;
|
||||
|
||||
/// Callback that assigns a guest (unauthenticated) client ID.
|
||||
///
|
||||
/// Return `Some(id)` to accept the guest with the given ID, or `None` to reject
|
||||
/// the connection. The returned ID must fit in 48 bits
|
||||
/// (`id <= mtp_codec::MAX_WIRE_ID`); values outside that range are rejected
|
||||
/// automatically.
|
||||
///
|
||||
/// When set to `None` on `HostConfig`, the built-in generator produces a random
|
||||
/// 48-bit ID that avoids collisions with registered clients.
|
||||
#[cfg(feature = "crypto")]
|
||||
pub type GuestIdGenerator =
|
||||
Box<dyn Fn() -> Pin<Box<dyn std::future::Future<Output = Option<u64>> + Send>> + Send + Sync>;
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
pub type CompleteRegister = Box<
|
||||
dyn Fn(
|
||||
mtp_crypto::PublicKeyBundle,
|
||||
Option<String>,
|
||||
) -> Pin<Box<dyn std::future::Future<Output = u64> + Send>>
|
||||
+ Send
|
||||
+ Sync,
|
||||
>;
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AuthenticationPolicy {
|
||||
ForceAuthentication,
|
||||
AllowAuthentication,
|
||||
Unauthenticated,
|
||||
}
|
||||
|
||||
pub struct HostConfig {
|
||||
pub ip: IpAddr,
|
||||
pub port: u16,
|
||||
pub tls_fullchain: Vec<u8>,
|
||||
pub tls_key: Vec<u8>,
|
||||
|
||||
pub policy: Policy,
|
||||
pub send_pongs: bool,
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
pub authentication_policy: AuthenticationPolicy,
|
||||
#[cfg(feature = "crypto")]
|
||||
pub auth_timeout: Duration,
|
||||
#[cfg(feature = "crypto")]
|
||||
pub require_pq: bool,
|
||||
#[cfg(feature = "crypto")]
|
||||
pub host_keyring: mtp_crypto::Keyring,
|
||||
#[cfg(feature = "crypto")]
|
||||
pub get_existing_client: GetExistingClient,
|
||||
#[cfg(feature = "crypto")]
|
||||
pub guest_id_generator: Option<GuestIdGenerator>,
|
||||
#[cfg(feature = "crypto")]
|
||||
pub complete_register: CompleteRegister,
|
||||
}
|
||||
|
||||
impl HostConfig {
|
||||
pub fn new(ip: IpAddr, port: u16, tls_fullchain: Vec<u8>, tls_key: Vec<u8>) -> Self {
|
||||
Self {
|
||||
ip,
|
||||
port,
|
||||
tls_fullchain,
|
||||
tls_key,
|
||||
policy: Policy::default(),
|
||||
send_pongs: true,
|
||||
#[cfg(feature = "crypto")]
|
||||
authentication_policy: AuthenticationPolicy::Unauthenticated,
|
||||
#[cfg(feature = "crypto")]
|
||||
auth_timeout: Duration::from_secs(30),
|
||||
#[cfg(feature = "crypto")]
|
||||
require_pq: true,
|
||||
#[cfg(feature = "crypto")]
|
||||
host_keyring: mtp_crypto::Keyring::new(
|
||||
mtp_crypto::KemPublicKey::new(Vec::new()),
|
||||
mtp_crypto::KemPrivateKey::new(Vec::new()),
|
||||
mtp_crypto::SignaturePqPublicKey::new(Vec::new()),
|
||||
mtp_crypto::SignaturePqPrivateKey::new(Vec::new()),
|
||||
mtp_crypto::SignaturePublicKey::new(Vec::new()),
|
||||
mtp_crypto::SignaturePrivateKey::new(Vec::new()),
|
||||
),
|
||||
#[cfg(feature = "crypto")]
|
||||
get_existing_client: Box::new(|_, _| Box::pin(async { None })),
|
||||
#[cfg(feature = "crypto")]
|
||||
guest_id_generator: None,
|
||||
#[cfg(feature = "crypto")]
|
||||
complete_register: Box::new(|_, _| Box::pin(async { 0 })),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_policy(mut self, policy: Policy) -> Self {
|
||||
self.policy = policy;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_pongs(mut self, send_pongs: bool) -> Self {
|
||||
self.send_pongs = send_pongs;
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
pub fn with_authentication(
|
||||
mut self,
|
||||
host_keyring: mtp_crypto::Keyring,
|
||||
get_existing_client: GetExistingClient,
|
||||
complete_register: CompleteRegister,
|
||||
) -> Self {
|
||||
self.authentication_policy = AuthenticationPolicy::ForceAuthentication;
|
||||
self.host_keyring = host_keyring;
|
||||
self.get_existing_client = Box::new(get_existing_client);
|
||||
self.complete_register = Box::new(complete_register);
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
pub fn with_authentication_policy(mut self, policy: AuthenticationPolicy) -> Self {
|
||||
self.authentication_policy = policy;
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
pub fn with_auth_timeout(mut self, timeout: Duration) -> Self {
|
||||
self.auth_timeout = timeout;
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
pub fn with_require_pq(mut self, require_pq: bool) -> Self {
|
||||
self.require_pq = require_pq;
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
pub fn with_guest_id_generator(mut self, generator: GuestIdGenerator) -> Self {
|
||||
self.guest_id_generator = Some(generator);
|
||||
self
|
||||
}
|
||||
}
|
||||
126
host/src/connection.rs
Normal file
126
host/src/connection.rs
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
use mtp_codec::{CommunicationValue, Version, registry::VersionedCodec};
|
||||
use mtp_common::CommunicationError;
|
||||
#[cfg(feature = "pipes")]
|
||||
use std::sync::Arc;
|
||||
#[cfg(feature = "pipes")]
|
||||
use tokio::sync::{Mutex, mpsc};
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
use crate::error::random_client_id;
|
||||
#[cfg(feature = "pipes")]
|
||||
use crate::pipe::{PipeDispatcher, PipeRequest};
|
||||
|
||||
mod connection_capability {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
pub trait MtpSenderLike: connection_capability::Sealed + Clone + Send + Sync {}
|
||||
pub trait MtpReceiverLike: connection_capability::Sealed + Clone + Send + Sync {
|
||||
fn receive_message(
|
||||
&self,
|
||||
) -> impl std::future::Future<Output = Result<CommunicationValue, CommunicationError>> + Send;
|
||||
}
|
||||
|
||||
impl connection_capability::Sealed for mtp_transport::Sender {}
|
||||
impl MtpSenderLike for mtp_transport::Sender {}
|
||||
impl connection_capability::Sealed for mtp_transport::Receiver {}
|
||||
impl MtpReceiverLike for mtp_transport::Receiver {
|
||||
async fn receive_message(&self) -> Result<CommunicationValue, CommunicationError> {
|
||||
self.receive().await
|
||||
}
|
||||
}
|
||||
impl<C: mtp_transport::TransportConnection> connection_capability::Sealed
|
||||
for mtp_transport::GenericSender<C>
|
||||
{
|
||||
}
|
||||
impl<C: mtp_transport::TransportConnection> MtpSenderLike for mtp_transport::GenericSender<C> {}
|
||||
impl<C: mtp_transport::TransportConnection> connection_capability::Sealed
|
||||
for mtp_transport::GenericReceiver<C>
|
||||
{
|
||||
}
|
||||
impl<C: mtp_transport::TransportConnection> MtpReceiverLike for mtp_transport::GenericReceiver<C> {
|
||||
async fn receive_message(&self) -> Result<CommunicationValue, CommunicationError> {
|
||||
self.receive().await
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MTPConnection<S = mtp_transport::Sender, R = mtp_transport::Receiver> {
|
||||
pub version: Version,
|
||||
pub codec: VersionedCodec,
|
||||
pub sender: S,
|
||||
pub receiver: R,
|
||||
/// The WebTransport request path used to establish this connection.
|
||||
///
|
||||
/// Legacy `MTPHost` connections do not have an HTTP router in front of
|
||||
/// them, so they always use the root path. Alternative hosts can retain
|
||||
/// the CONNECT request path when constructing an MTP connection.
|
||||
pub path: String,
|
||||
#[cfg(feature = "pipes")]
|
||||
pub(crate) app_rx: Mutex<mpsc::Receiver<Result<CommunicationValue, CommunicationError>>>,
|
||||
#[cfg(feature = "pipes")]
|
||||
pub(crate) pipe_req_rx: Mutex<mpsc::Receiver<PipeRequest>>,
|
||||
#[cfg(feature = "pipes")]
|
||||
pub(crate) pipe_dispatcher: Arc<PipeDispatcher>,
|
||||
pub description: Option<String>,
|
||||
pub(crate) _dispatcher_task: tokio::task::JoinHandle<()>,
|
||||
#[cfg(feature = "crypto")]
|
||||
pub auth_state: crate::error::AuthState,
|
||||
#[cfg(feature = "crypto")]
|
||||
pub client_id: u64,
|
||||
#[cfg(feature = "crypto")]
|
||||
pub client_public_key: Option<mtp_crypto::PublicKeyBundle>,
|
||||
}
|
||||
|
||||
impl<S, R> MTPConnection<S, R> {
|
||||
/// Construct an MTP connection from an alternative transport backend.
|
||||
///
|
||||
/// Native `MTPHost` users continue to receive the default
|
||||
/// `MTPConnection<Sender, Receiver>` type. HTTP/3 WebTransport hosts use
|
||||
/// this constructor with their stream adapters while retaining the shared
|
||||
/// version, codec, path, and metadata representation.
|
||||
pub fn from_transport_parts(
|
||||
version: Version,
|
||||
codec: VersionedCodec,
|
||||
sender: S,
|
||||
receiver: R,
|
||||
path: String,
|
||||
description: Option<String>,
|
||||
) -> Self {
|
||||
#[cfg(feature = "pipes")]
|
||||
let (_, app_rx) = mpsc::channel::<Result<CommunicationValue, CommunicationError>>(1);
|
||||
#[cfg(feature = "pipes")]
|
||||
let (_, pipe_req_rx) = mpsc::channel::<PipeRequest>(1);
|
||||
#[cfg(feature = "pipes")]
|
||||
let dispatcher = Arc::new(PipeDispatcher::default_for_external());
|
||||
Self {
|
||||
version,
|
||||
codec,
|
||||
sender,
|
||||
receiver,
|
||||
path,
|
||||
#[cfg(feature = "pipes")]
|
||||
app_rx: Mutex::new(app_rx),
|
||||
#[cfg(feature = "pipes")]
|
||||
pipe_req_rx: Mutex::new(pipe_req_rx),
|
||||
#[cfg(feature = "pipes")]
|
||||
pipe_dispatcher: dispatcher,
|
||||
description,
|
||||
_dispatcher_task: tokio::spawn(async {}),
|
||||
#[cfg(feature = "crypto")]
|
||||
auth_state: crate::error::AuthState::Unauthenticated,
|
||||
#[cfg(feature = "crypto")]
|
||||
client_id: random_client_id(),
|
||||
#[cfg(feature = "crypto")]
|
||||
client_public_key: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "pipes"))]
|
||||
impl<S: MtpSenderLike, R: MtpReceiverLike> MTPConnection<S, R> {
|
||||
pub async fn receive(&self) -> Result<CommunicationValue, CommunicationError> {
|
||||
let mut message = self.receiver.receive_message().await?;
|
||||
message.set_type_map(self.codec.type_map());
|
||||
Ok(message)
|
||||
}
|
||||
}
|
||||
88
host/src/error.rs
Normal file
88
host/src/error.rs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
use mtp_codec::{CommunicationType, CommunicationValue, DataType, DataValue, Version};
|
||||
use mtp_common::{CommunicationError, RejectionReason};
|
||||
use mtp_transport::Sender;
|
||||
use std::{error::Error, fmt};
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
pub(crate) fn random_client_id() -> u64 {
|
||||
rand::random::<u64>() & mtp_codec::MAX_WIRE_ID
|
||||
}
|
||||
|
||||
pub(crate) async fn send_rejection(sender: &Sender, reason: RejectionReason) {
|
||||
let response = match &reason {
|
||||
RejectionReason::BadVersion { supported_versions } => {
|
||||
CommunicationValue::new(CommunicationType::ErrorBadVersion)
|
||||
.add_typed_default(
|
||||
DataType::Version,
|
||||
DataValue::Str(supported_versions.join(",")),
|
||||
)
|
||||
.add_typed_default(DataType::ErrorMessage, DataValue::Str(reason.to_string()))
|
||||
}
|
||||
_ => CommunicationValue::new(CommunicationType::IdentificationResponse)
|
||||
.add_typed_default(DataType::Connected, DataValue::BoolFalse)
|
||||
.add_typed_default(DataType::ErrorMessage, DataValue::Str(reason.to_string())),
|
||||
};
|
||||
let _ = sender.send(&response).await;
|
||||
}
|
||||
|
||||
pub(crate) async fn send_accepted(
|
||||
sender: &Sender,
|
||||
version: &Version,
|
||||
assigned_id: Option<u64>,
|
||||
) -> Result<(), CommunicationError> {
|
||||
let mut response = CommunicationValue::new(CommunicationType::IdentificationResponse)
|
||||
.add_typed_default(DataType::Connected, DataValue::BoolTrue)
|
||||
.add_typed_default(DataType::Version, DataValue::Str(version.to_string()));
|
||||
if let Some(id) = assigned_id {
|
||||
response = response.add_typed_default(DataType::Id, DataValue::UnsignedNumber(id as u128));
|
||||
}
|
||||
sender.send(&response).await?;
|
||||
sender.finish_stream().await
|
||||
}
|
||||
|
||||
pub(crate) fn extract_version(msg: &CommunicationValue) -> Option<Version> {
|
||||
let value = msg.get_data(DataType::Version);
|
||||
match value {
|
||||
DataValue::Str(s) => Version::parse(s.as_str()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum AcceptError {
|
||||
Receive(CommunicationError),
|
||||
MissingVersion,
|
||||
UnsupportedVersion(Version),
|
||||
AuthenticationFailed(String),
|
||||
AuthenticationTimedOut,
|
||||
Send(CommunicationError),
|
||||
}
|
||||
|
||||
impl fmt::Display for AcceptError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Receive(error) => write!(f, "failed to receive opening message: {error}"),
|
||||
Self::MissingVersion => write!(
|
||||
f,
|
||||
"opening message did not include a valid protocol version"
|
||||
),
|
||||
Self::UnsupportedVersion(version) => {
|
||||
write!(f, "unsupported protocol version: {version}")
|
||||
}
|
||||
Self::AuthenticationFailed(reason) => write!(f, "authentication failed: {reason}"),
|
||||
Self::AuthenticationTimedOut => write!(f, "authentication handshake timed out"),
|
||||
Self::Send(error) => write!(f, "failed to send handshake message: {error}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for AcceptError {}
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum AuthState {
|
||||
Unauthenticated,
|
||||
Pending,
|
||||
Authenticated,
|
||||
Failed,
|
||||
}
|
||||
1056
host/src/handshake.rs
Normal file
1056
host/src/handshake.rs
Normal file
File diff suppressed because it is too large
Load diff
1276
host/src/lib.rs
1276
host/src/lib.rs
File diff suppressed because it is too large
Load diff
196
host/src/pipe.rs
Normal file
196
host/src/pipe.rs
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
use mtp_codec::{CommunicationType, CommunicationValue, DataType, DataValue};
|
||||
|
||||
use mtp_common::PipeError;
|
||||
|
||||
use mtp_transport::{PipeReader, Policy, Receiver, Sender};
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use tokio::sync::{Mutex, mpsc};
|
||||
|
||||
use tracing::debug;
|
||||
|
||||
pub struct PipeHandle {
|
||||
pub(crate) pipe_id: u32,
|
||||
pub(crate) description: String,
|
||||
pub(crate) sender: Sender,
|
||||
pub(crate) response_rx: tokio::sync::oneshot::Receiver<Result<bool, PipeError>>,
|
||||
}
|
||||
|
||||
impl PipeHandle {
|
||||
pub fn pipe_id(&self) -> u32 {
|
||||
self.pipe_id
|
||||
}
|
||||
|
||||
pub fn description(&self) -> &str {
|
||||
&self.description
|
||||
}
|
||||
|
||||
pub async fn wait(self) -> Result<Option<mtp_transport::PipeWriter>, PipeError> {
|
||||
match self.response_rx.await {
|
||||
Ok(Ok(true)) => {
|
||||
let writer = self
|
||||
.sender
|
||||
.open_pipe(self.pipe_id, &self.description)
|
||||
.await
|
||||
.map_err(PipeError::from)?;
|
||||
Ok(Some(writer))
|
||||
}
|
||||
Ok(Ok(false)) => Ok(None),
|
||||
Ok(Err(e)) => Err(e),
|
||||
Err(_) => Err(PipeError::StreamClosed),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PipeRequest {
|
||||
pub(crate) pipe_id: u32,
|
||||
pub(crate) description: String,
|
||||
pub(crate) sender: Sender,
|
||||
pub(crate) dispatcher: Arc<PipeDispatcher>,
|
||||
}
|
||||
|
||||
impl PipeRequest {
|
||||
pub fn id(&self) -> u32 {
|
||||
self.pipe_id
|
||||
}
|
||||
|
||||
pub fn description(&self) -> &str {
|
||||
&self.description
|
||||
}
|
||||
|
||||
pub async fn accept(self) -> Result<PipeReader, PipeError> {
|
||||
let (pipe_tx, pipe_rx) = tokio::sync::oneshot::channel();
|
||||
{
|
||||
let mut pending = self.dispatcher.pending_pipes.lock().await;
|
||||
pending.insert(self.pipe_id, pipe_tx);
|
||||
}
|
||||
|
||||
let resp = CommunicationValue::new(CommunicationType::PipeResponse)
|
||||
.with_id(self.pipe_id)
|
||||
.add_typed_default(DataType::Accepted, DataValue::BoolTrue);
|
||||
self.sender.send(&resp).await.map_err(PipeError::from)?;
|
||||
|
||||
let timeout = self.dispatcher.policy.read_timeout;
|
||||
tokio::time::timeout(timeout, pipe_rx)
|
||||
.await
|
||||
.map_err(|_| PipeError::HandshakeTimeout)?
|
||||
.map_err(|_| PipeError::StreamClosed)
|
||||
}
|
||||
|
||||
pub async fn deny(self) -> Result<(), PipeError> {
|
||||
let resp = CommunicationValue::new(CommunicationType::PipeResponse)
|
||||
.with_id(self.pipe_id)
|
||||
.add_typed_default(DataType::Accepted, DataValue::BoolFalse);
|
||||
self.sender.send(&resp).await.map_err(PipeError::from)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct PipeDispatcher {
|
||||
pub(crate) pending_creations:
|
||||
Mutex<HashMap<u32, tokio::sync::oneshot::Sender<Result<bool, PipeError>>>>,
|
||||
pub(crate) pending_pipes: Mutex<HashMap<u32, tokio::sync::oneshot::Sender<PipeReader>>>,
|
||||
pub(crate) policy: Arc<Policy>,
|
||||
}
|
||||
|
||||
impl PipeDispatcher {
|
||||
pub(crate) fn default_for_external() -> Self {
|
||||
Self {
|
||||
pending_creations: Mutex::new(HashMap::new()),
|
||||
pending_pipes: Mutex::new(HashMap::new()),
|
||||
policy: Arc::new(Policy::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn run_dispatcher(
|
||||
receiver: Receiver,
|
||||
sender: Sender,
|
||||
app_tx: mpsc::Sender<Result<CommunicationValue, mtp_common::CommunicationError>>,
|
||||
pipe_req_tx: mpsc::Sender<PipeRequest>,
|
||||
dispatcher: Arc<PipeDispatcher>,
|
||||
) {
|
||||
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_event().await {
|
||||
Ok(mtp_transport::TransportEvent::Message(msg)) => {
|
||||
debug!(
|
||||
target = "mtp.host",
|
||||
message_type = ?msg.get_type(),
|
||||
message_id = msg.get_id(),
|
||||
"dispatcher received message"
|
||||
);
|
||||
if Some(msg.get_type()) == pipe_req_type {
|
||||
let pipe_id = msg.get_id();
|
||||
let description = msg.get_str(DataType::Description).unwrap_or("").to_string();
|
||||
debug!(
|
||||
target = "mtp.host",
|
||||
pipe_id, description, "dispatcher classified pipe request"
|
||||
);
|
||||
let req = PipeRequest {
|
||||
pipe_id,
|
||||
description,
|
||||
sender: sender.clone(),
|
||||
dispatcher: dispatcher.clone(),
|
||||
};
|
||||
let _ = pipe_req_tx.send(req).await;
|
||||
continue;
|
||||
}
|
||||
|
||||
if Some(msg.get_type()) == pipe_resp_type {
|
||||
let pipe_id = msg.get_id();
|
||||
let accepted = msg.get_bool(DataType::Accepted).unwrap_or(false);
|
||||
debug!(
|
||||
target = "mtp.host",
|
||||
pipe_id, accepted, "dispatcher classified pipe response"
|
||||
);
|
||||
let mut pending = dispatcher.pending_creations.lock().await;
|
||||
if let Some(tx) = pending.remove(&pipe_id) {
|
||||
let _ = tx.send(Ok(accepted));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if app_tx.send(Ok(msg)).await.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(mtp_transport::TransportEvent::Pipe(reader)) => {
|
||||
let pipe_id = reader.pipe_id();
|
||||
debug!(
|
||||
target = "mtp.host",
|
||||
pipe_id,
|
||||
description = reader.description(),
|
||||
"dispatcher received pipe stream"
|
||||
);
|
||||
let mut pending = dispatcher.pending_pipes.lock().await;
|
||||
if let Some(tx) = pending.remove(&pipe_id) {
|
||||
let _ = tx.send(reader);
|
||||
continue;
|
||||
}
|
||||
|
||||
debug!(
|
||||
target = "mtp.host",
|
||||
pipe_id, "dispatcher treating pipe stream as pipe request"
|
||||
);
|
||||
let req = PipeRequest {
|
||||
pipe_id,
|
||||
description: reader.description().to_string(),
|
||||
sender: sender.clone(),
|
||||
dispatcher: dispatcher.clone(),
|
||||
};
|
||||
let _ = pipe_req_tx.send(req).await;
|
||||
}
|
||||
Err(e) => {
|
||||
if app_tx.send(Err(e)).await.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue