Chunk terminal output below MTU
This commit is contained in:
+14
-1
@@ -7,6 +7,12 @@ use std::sync::{Arc, Mutex};
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
|
// Keep live terminal output comfortably below common path MTUs after Dosh's
|
||||||
|
// protocol header, AEAD tag, UDP/IP headers, and bincode framing. Full-screen
|
||||||
|
// TUIs often write several KiB on the first draw; sending that as one UDP
|
||||||
|
// datagram can fragment and vanish, leaving only a blank alternate screen.
|
||||||
|
const PTY_OUTPUT_CHUNK_BYTES: usize = 1024;
|
||||||
|
|
||||||
/// Backing for a PTY master held by the server.
|
/// Backing for a PTY master held by the server.
|
||||||
///
|
///
|
||||||
/// `Owned` means this process spawned the shell as a child and is responsible
|
/// `Owned` means this process spawned the shell as a child and is responsible
|
||||||
@@ -264,12 +270,14 @@ fn spawn_reader_thread(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
|
for chunk in buf[..n].chunks(PTY_OUTPUT_CHUNK_BYTES) {
|
||||||
let _ = tx.send(PtyOutput {
|
let _ = tx.send(PtyOutput {
|
||||||
session: reader_session.clone(),
|
session: reader_session.clone(),
|
||||||
bytes: buf[..n].to_vec(),
|
bytes: chunk.to_vec(),
|
||||||
exited: false,
|
exited: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let _ = tx.send(PtyOutput {
|
let _ = tx.send(PtyOutput {
|
||||||
session: reader_session.clone(),
|
session: reader_session.clone(),
|
||||||
@@ -297,4 +305,9 @@ mod tests {
|
|||||||
assert!(!terminfo_available("definitely-not-a-real-terminal-xyz"));
|
assert!(!terminfo_available("definitely-not-a-real-terminal-xyz"));
|
||||||
assert!(!terminfo_available(""));
|
assert!(!terminfo_available(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pty_output_chunk_size_stays_mtu_safe() {
|
||||||
|
assert!(PTY_OUTPUT_CHUNK_BYTES <= 1200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1382,6 +1382,57 @@ fn tui_control_sequences_survive_transport_verbatim() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn large_tui_paint_is_delivered_in_mtu_safe_frames() {
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let port = free_udp_port();
|
||||||
|
let config = write_server_config(&dir, port);
|
||||||
|
let mut server = start_server(&dir, &config);
|
||||||
|
let (socket, bootstrap, ok) = direct_attach(&config, port, "read-write");
|
||||||
|
|
||||||
|
let input = Input {
|
||||||
|
bytes: b"printf '\\033[?1049h'; yes DOSH_TUI_BIG_PAINT | head -n 300 | tr -d '\\n'; printf '\\033[?1049l'\n".to_vec(),
|
||||||
|
};
|
||||||
|
send_encrypted(
|
||||||
|
&socket,
|
||||||
|
port,
|
||||||
|
PacketKind::Input,
|
||||||
|
ok.client_id,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
&bootstrap.session_key,
|
||||||
|
&protocol::to_body(&input).unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut text = String::new();
|
||||||
|
let mut saw_split_frame = false;
|
||||||
|
let deadline = std::time::Instant::now() + Duration::from_secs(3);
|
||||||
|
while std::time::Instant::now() < deadline && text.matches("DOSH_TUI_BIG_PAINT").count() < 200
|
||||||
|
{
|
||||||
|
if let Some((_header, frame)) = recv_frame(&socket, &bootstrap.session_key) {
|
||||||
|
if frame.bytes.len() <= 1400 && frame.bytes.len() >= 900 {
|
||||||
|
saw_split_frame = true;
|
||||||
|
}
|
||||||
|
text.push_str(&String::from_utf8_lossy(&frame.bytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = server.kill();
|
||||||
|
let _ = server.wait();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
text.contains("\x1b[?1049h") && text.matches("DOSH_TUI_BIG_PAINT").count() >= 200,
|
||||||
|
"large alternate-screen paint did not survive transport, enter_alt={} markers={} bytes={}",
|
||||||
|
text.contains("\x1b[?1049h"),
|
||||||
|
text.matches("DOSH_TUI_BIG_PAINT").count(),
|
||||||
|
text.len()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
saw_split_frame,
|
||||||
|
"expected large TUI paint to be split into MTU-safe frames"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn resume_snapshot_preserves_alternate_screen_mode() {
|
fn resume_snapshot_preserves_alternate_screen_mode() {
|
||||||
let dir = tempfile::tempdir().unwrap();
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user