//! Example for usage of the Device to Mediator Protocol state machine, doing a real handshake with the //! mediator server and an exemplary payload flow loop. #![expect(unused_crate_dependencies, reason = "Example triggered false positive")] #![expect(
clippy::integer_division_remainder_used,
reason = "Some internal of tokio::select triggers this"
)]
use core::time::Duration;
use anyhow::{Result, anyhow, bail}; use clap::Parser; use futures_util::{SinkExt as _, TryStreamExt as _}; use libthreema::{
cli::{FullIdentityConfig, FullIdentityConfigOptions},
d2m::{
D2mContext, D2mProtocol, D2mStateUpdate,
payload::{BeginTransaction, IncomingPayload, OutgoingPayload, Reflect, ReflectFlags},
},
https::cli::https_client_builder,
utils::logging::init_stderr_logging,
}; use rand::random; use reqwest::StatusCode; use tokio::{
net::TcpStream,
signal,
sync::mpsc,
time::{self, Instant},
}; use tokio_tungstenite::{
MaybeTlsStream, WebSocketStream, connect_async,
tungstenite::protocol::{CloseFrame, Message, frame::coding::CloseCode},
}; use tracing::{Level, debug, error, info, trace, warn};
/// Payload queues for the main process struct PayloadQueuesForD2mPingPong {
incoming: mpsc::Receiver<IncomingPayload>,
outgoing: mpsc::Sender<OutgoingPayload>,
}
/// Payload queues for the protocol flow runner struct PayloadQueuesForProtocol {
incoming: mpsc::Sender<IncomingPayload>,
outgoing: mpsc::Receiver<OutgoingPayload>,
}
struct D2mProtocolRunner { /// The WebSocket stream
stream: WebSocketStream<MaybeTlsStream<TcpStream>>,
/// An instance of the [`D2mProtocol`] state machine
protocol: D2mProtocol,
} impl D2mProtocolRunner { /// Initiate a D2M protocol connection #[tracing::instrument(skip_all)] asyncfn new(context: D2mContext) -> Result<Self> { // Create the protocol let (d2m_protocol, url) = D2mProtocol::new(context);
// Connect via WebSocket
debug!(?url, "Establishing WebSocket connection to mediator server"); let (stream, response) = connect_async(url).await?; if response.status() != StatusCode::SWITCHING_PROTOCOLS {
bail!( "Expected response to switch protocols ({expected}), got {actual}",
expected = StatusCode::SWITCHING_PROTOCOLS,
actual = response.status(),
);
}
Ok(Self {
stream,
protocol: d2m_protocol,
})
}
/// Do the handshake with the mediator server by exchanging the following messages: /// /// ```txt /// C -- client-info --> S (was already sent as part of the URL's path) /// C <- server-hello -- S /// C -- client-hello -> S /// C <- server-info --- S /// ``` asyncfn run_handshake_flow(&mutself) -> Result<()> { for iteration in1_usize.. {
trace!("Iteration #{iteration}");
// Receive datagram and add it let datagram = self.receive().await?; self.protocol.add_datagrams(vec![datagram])?;
// We do not expect an incoming payload at this stage iflet Some(incoming_payload) = instruction.incoming_payload { let message = "Unexpected incoming payload during handshake";
error!(?incoming_payload, message);
bail!(message)
}
// Check if we've completed the handshake iflet Some(D2mStateUpdate::PostHandshake(server_info)) = instruction.state_update {
info!(?server_info, "Handshake completed"); break;
}
}
Ok(())
}
/// Run the payload exchange flow until stopped. #[tracing::instrument(skip_all)] asyncfn run_payload_flow(&mutself, mut queues: PayloadQueuesForProtocol) -> Result<()> { for iteration in1_usize.. {
trace!("Payload flow iteration #{iteration}");
// Poll for any pending instruction letmut instruction = self.protocol.poll()?; if instruction.is_none() { // No pending instruction left, wait for more input
instruction = tokio::select! { // Forward any incoming datagrams from the WebSocket transport
datagram = self.receive() => { // Add datagram (poll in the next iteration) self.protocol.add_datagrams(vec![datagram?])?;
None
},
let Some(instruction) = instruction else { continue;
};
// We do not expect any state updates at this stage iflet Some(state_update) = instruction.state_update { let message = "Unexpected state update after handshake";
error!(?state_update, message);
bail!(message)
}
#[tracing::instrument(skip_all)] asyncfn shutdown(mutself) -> Result<()> {
info!("Shutting down WebSocket connection");
// Normal closure, e.g. when the user is explicitly disconnecting
Ok(self
.stream
.close(Some(CloseFrame {
code: CloseCode::Normal,
reason: "Bye".into(),
}))
.await?)
}
#[tracing::instrument(skip_all)] asyncfn receive(&mutself) -> Result<Vec<u8>> { let datagram = loop { let message = self
.stream
.try_next()
.await?
.ok_or(anyhow!("WebSocket reading end closed"))?; match message {
Message::Binary(bytes) => break bytes.to_vec(),
Message::Text(text) => {
bail!("Received unexpected text message: {}", text.as_str())
},
Message::Ping(bytes) => { // WARNING: There's a slight chance that the pong is lost when this is cancelled!
debug!(ping_length = bytes.len(), "Received ping, responding with a pong"); self.stream.feed(Message::Pong(bytes)).await?;
debug!("Pong sent");
},
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.