use std::{
collections::{HashMap, VecDeque},
fmt,
sync::Arc,
sync::atomic::{AtomicBool, AtomicU64, AtomicUsize},
};
use libmoshpit::{EncryptedFrame, TerminalMessage};
use tokio::sync::{Mutex, mpsc::Sender};
use tokio_util::sync::CancellationToken;
use uuid::Uuid;
pub(crate) const SCROLLBACK_CAPACITY: usize = 65_536;
#[derive(Debug)]
pub(crate) struct SessionOutputHandle {
pub kex_uuid: Uuid,
pub data_tx: Option<Sender<EncryptedFrame>>,
pub control_tx: Option<Sender<EncryptedFrame>>,
pub conn_token: Option<CancellationToken>,
pub udp_port: Option<u16>,
}
pub(crate) struct SessionRecord {
pub term_tx: Sender<TerminalMessage>,
pub output_handle: Arc<Mutex<SessionOutputHandle>>,
pub scrollback: Arc<Mutex<VecDeque<u8>>>,
pub server_emulator: Arc<Mutex<vt100::Parser>>,
pub dirty_counter: Arc<AtomicU64>,
pub diff_in_flight: Arc<AtomicBool>,
pub effective_mtu: Arc<AtomicUsize>,
}
impl fmt::Debug for SessionRecord {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SessionRecord")
.field("output_handle", &self.output_handle)
.field("scrollback", &self.scrollback)
.finish_non_exhaustive()
}
}
pub(crate) type FullSessionRegistry = Arc<Mutex<HashMap<Uuid, SessionRecord>>>;
pub(crate) fn new_full_registry() -> FullSessionRegistry {
Arc::new(Mutex::new(HashMap::new()))
}
#[cfg(test)]
mod test {
use std::{
collections::VecDeque,
sync::Arc,
sync::atomic::{AtomicBool, AtomicU64, AtomicUsize},
};
use libmoshpit::{EncryptedFrame, TerminalMessage};
use tokio::sync::{Mutex, mpsc::channel};
use uuid::Uuid;
use super::{SCROLLBACK_CAPACITY, SessionOutputHandle, SessionRecord, new_full_registry};
#[test]
fn scrollback_capacity_is_64kib() {
assert_eq!(SCROLLBACK_CAPACITY, 65_536);
}
#[tokio::test]
async fn new_full_registry_is_empty() {
let registry = new_full_registry();
assert!(registry.lock().await.is_empty());
}
#[test]
fn session_output_handle_debug() {
let handle = SessionOutputHandle {
kex_uuid: Uuid::nil(),
data_tx: None::<tokio::sync::mpsc::Sender<EncryptedFrame>>,
control_tx: None::<tokio::sync::mpsc::Sender<EncryptedFrame>>,
conn_token: None,
udp_port: None,
};
let s = format!("{handle:?}");
assert!(s.contains("SessionOutputHandle"));
assert!(s.contains("kex_uuid"));
}
#[tokio::test]
async fn session_record_debug() {
let (term_tx, _term_rx) = channel::<TerminalMessage>(1);
let (data_tx, _data_rx) = channel::<EncryptedFrame>(1);
let (_ctrl_tx, _ctrl_rx) = channel::<EncryptedFrame>(1);
let output_handle = Arc::new(Mutex::new(SessionOutputHandle {
kex_uuid: Uuid::nil(),
data_tx: Some(data_tx),
control_tx: None,
conn_token: None,
udp_port: None,
}));
let scrollback = Arc::new(Mutex::new(VecDeque::<u8>::new()));
let server_emulator = Arc::new(Mutex::new(vt100::Parser::new(24, 80, 0)));
let dirty_counter = Arc::new(AtomicU64::new(1));
let diff_in_flight = Arc::new(AtomicBool::new(false));
let effective_mtu = Arc::new(AtomicUsize::new(1200));
let record = SessionRecord {
term_tx,
output_handle,
scrollback,
server_emulator,
dirty_counter,
diff_in_flight,
effective_mtu,
};
let s = format!("{record:?}");
assert!(s.contains("SessionRecord"));
assert!(s.contains("output_handle"));
}
}