Bound terminal output under sustained load
ci / test (push) Canceled after 0s
ci / fuzz-smoke (push) Canceled after 0s
ci / macos-client (macos-aarch64, macos-14) (push) Canceled after 0s
ci / macos-client (macos-x86_64, macos-13) (push) Canceled after 0s
ci / windows-client (push) Canceled after 0s
ci / package-release (linux-x86_64, ubuntu-latest, , , ) (push) Canceled after 0s
ci / package-release (macos-aarch64, macos-14, , , ) (push) Canceled after 0s
ci / package-release (macos-x86_64, macos-13, , , ) (push) Canceled after 0s
ci / package-release (windows-aarch64, windows-latest, aarch64, windows, aarch64-pc-windows-msvc) (push) Canceled after 0s
ci / package-release (windows-x86_64, windows-latest, , , ) (push) Canceled after 0s
ci / remote-bench (push) Canceled after 0s
ci / publish-gitea-release (push) Canceled after 0s
ci / test (push) Canceled after 0s
ci / fuzz-smoke (push) Canceled after 0s
ci / macos-client (macos-aarch64, macos-14) (push) Canceled after 0s
ci / macos-client (macos-x86_64, macos-13) (push) Canceled after 0s
ci / windows-client (push) Canceled after 0s
ci / package-release (linux-x86_64, ubuntu-latest, , , ) (push) Canceled after 0s
ci / package-release (macos-aarch64, macos-14, , , ) (push) Canceled after 0s
ci / package-release (macos-x86_64, macos-13, , , ) (push) Canceled after 0s
ci / package-release (windows-aarch64, windows-latest, aarch64, windows, aarch64-pc-windows-msvc) (push) Canceled after 0s
ci / package-release (windows-x86_64, windows-latest, , , ) (push) Canceled after 0s
ci / remote-bench (push) Canceled after 0s
ci / publish-gitea-release (push) Canceled after 0s
This commit is contained in:
+33
-28
@@ -27,7 +27,9 @@ use dosh::protocol::{
|
||||
StreamClose, StreamData, StreamEof, StreamOpen, StreamOpenOk, StreamOpenReject,
|
||||
StreamWindowAdjust, TicketAttachBody, TicketAttachEnvelope, TicketAttachOkEnvelope,
|
||||
};
|
||||
use dosh::pty::{PtyHandle, PtyOutput, adopt_pty_from_fd, spawn_pty_session};
|
||||
use dosh::pty::{
|
||||
PTY_OUTPUT_QUEUE_CAPACITY, PtyHandle, PtyOutput, adopt_pty_from_fd, spawn_pty_session,
|
||||
};
|
||||
use dosh::udp::{is_transient_udp_error, is_transient_udp_send_error};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
|
||||
@@ -209,7 +211,7 @@ async fn serve(config_path: Option<std::path::PathBuf>) -> Result<()> {
|
||||
],
|
||||
);
|
||||
|
||||
let (pty_tx, mut pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, mut pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let state = Arc::new(Mutex::new(ServerState::new(
|
||||
config.clone(),
|
||||
secret,
|
||||
@@ -242,6 +244,7 @@ async fn serve(config_path: Option<std::path::PathBuf>) -> Result<()> {
|
||||
let retransmit_socket = Arc::clone(&socket);
|
||||
tokio::spawn(async move {
|
||||
let mut interval = tokio::time::interval(Duration::from_millis(100));
|
||||
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
|
||||
loop {
|
||||
interval.tick().await;
|
||||
if let Err(err) = retransmit_pending(&retransmit_state, &retransmit_socket).await {
|
||||
@@ -256,6 +259,7 @@ async fn serve(config_path: Option<std::path::PathBuf>) -> Result<()> {
|
||||
let cleanup_state = Arc::clone(&state);
|
||||
tokio::spawn(async move {
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(5));
|
||||
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
|
||||
loop {
|
||||
interval.tick().await;
|
||||
cleanup_disconnected_clients(&cleanup_state);
|
||||
@@ -270,6 +274,7 @@ async fn serve(config_path: Option<std::path::PathBuf>) -> Result<()> {
|
||||
let flush_state = Arc::clone(&state);
|
||||
tokio::spawn(async move {
|
||||
let mut interval = tokio::time::interval(SCREEN_PERSIST_MAX_AGE);
|
||||
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
|
||||
loop {
|
||||
interval.tick().await;
|
||||
flush_persistent_screens(&flush_state);
|
||||
@@ -293,7 +298,7 @@ async fn serve(config_path: Option<std::path::PathBuf>) -> Result<()> {
|
||||
struct ServerState {
|
||||
config: ServerConfig,
|
||||
secret: [u8; 32],
|
||||
pty_tx: mpsc::UnboundedSender<PtyOutput>,
|
||||
pty_tx: mpsc::Sender<PtyOutput>,
|
||||
sessions: HashMap<String, Session>,
|
||||
pending_native: HashMap<[u8; 16], PendingNativeAuth>,
|
||||
next_server_stream_id: u64,
|
||||
@@ -504,7 +509,7 @@ impl ServerState {
|
||||
fn new(
|
||||
config: ServerConfig,
|
||||
secret: [u8; 32],
|
||||
pty_tx: mpsc::UnboundedSender<PtyOutput>,
|
||||
pty_tx: mpsc::Sender<PtyOutput>,
|
||||
) -> Self {
|
||||
let per_minute = config.native_auth_rate_limit_per_minute;
|
||||
Self {
|
||||
@@ -4517,7 +4522,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn persists_terminal_sessions_when_enabled() {
|
||||
let (pty_tx, _rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let config = ServerConfig {
|
||||
persist_sessions: true,
|
||||
prewarm_sessions: vec!["default".to_string()],
|
||||
@@ -4531,7 +4536,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn persist_disabled_never_persists() {
|
||||
let (pty_tx, _rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let config = ServerConfig {
|
||||
persist_sessions: false,
|
||||
prewarm_sessions: vec!["default".to_string()],
|
||||
@@ -4655,7 +4660,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn no_client_pty_output_before_first_reattach_keeps_restored_screen() {
|
||||
let restored = b"\x1b[?1049lRESTORED_AFTER_RESTART".to_vec();
|
||||
let (pty_tx, _rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let state = Arc::new(Mutex::new(ServerState::new(
|
||||
ServerConfig {
|
||||
persist_sessions: true,
|
||||
@@ -4733,7 +4738,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn unknown_resume_reject_keeps_client_id() {
|
||||
let (pty_tx, _rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let state = Arc::new(Mutex::new(ServerState::new(
|
||||
ServerConfig::default(),
|
||||
[0u8; 32],
|
||||
@@ -4957,7 +4962,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn client_index_stays_in_sync_with_session_clients() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let mut state = ServerState::new(ServerConfig::default(), [0u8; 32], pty_tx);
|
||||
state
|
||||
.ensure_session("work", 80, 24, "forward-only", &[])
|
||||
@@ -5004,7 +5009,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn cleanup_purges_timed_out_clients_from_index() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let config = ServerConfig {
|
||||
client_timeout_secs: 1,
|
||||
..ServerConfig::default()
|
||||
@@ -5052,7 +5057,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn forged_plaintext_detach_does_not_remove_client() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let mut state = ServerState::new(ServerConfig::default(), [0u8; 32], pty_tx);
|
||||
state
|
||||
.ensure_session("work", 80, 24, "forward-only", &[])
|
||||
@@ -5078,7 +5083,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn authenticated_detach_removes_client() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let mut state = ServerState::new(ServerConfig::default(), [0u8; 32], pty_tx);
|
||||
state
|
||||
.ensure_session("work", 80, 24, "forward-only", &[])
|
||||
@@ -5109,7 +5114,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn duplicate_stream_open_for_open_stream_resends_ok() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let mut state = ServerState::new(ServerConfig::default(), [0u8; 32], pty_tx);
|
||||
let client_id = [11u8; 16];
|
||||
let session_key = [12u8; 32];
|
||||
@@ -5173,7 +5178,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn retired_stream_open_is_rejected_not_reopened() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let mut state = ServerState::new(ServerConfig::default(), [0u8; 32], pty_tx);
|
||||
let client_id = [15u8; 16];
|
||||
let session_key = [16u8; 32];
|
||||
@@ -5243,7 +5248,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn pending_server_stream_open_is_retransmitted() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let mut state = ServerState::new(ServerConfig::default(), [0u8; 32], pty_tx);
|
||||
let client_id = [13u8; 16];
|
||||
let session_key = [14u8; 32];
|
||||
@@ -5298,7 +5303,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn server_stream_retransmit_uses_observed_rtt() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let mut state = ServerState::new(ServerConfig::default(), [0u8; 32], pty_tx);
|
||||
let client_id = [15u8; 16];
|
||||
let session_key = [16u8; 32];
|
||||
@@ -5357,7 +5362,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn forward_only_session_does_not_allocate_pty() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let mut state = ServerState::new(ServerConfig::default(), [0u8; 32], pty_tx);
|
||||
|
||||
state
|
||||
@@ -5369,7 +5374,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn cleanup_reaps_abandoned_sessions_but_keeps_prewarmed() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let config = ServerConfig {
|
||||
client_timeout_secs: 1,
|
||||
prewarm_sessions: vec!["default".to_string()],
|
||||
@@ -5454,7 +5459,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn stream_data_waits_for_open_and_credit() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let mut state = ServerState::new(ServerConfig::default(), [0u8; 32], pty_tx);
|
||||
let client_id = [7u8; 16];
|
||||
let session_key = [9u8; 32];
|
||||
@@ -5558,7 +5563,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn stream_eof_to_client_does_not_retire_stream() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let mut state = ServerState::new(ServerConfig::default(), [0u8; 32], pty_tx);
|
||||
let client_id = [13u8; 16];
|
||||
let session_key = [14u8; 32];
|
||||
@@ -5647,7 +5652,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn pending_server_stream_eof_is_retransmitted() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let mut state = ServerState::new(ServerConfig::default(), [0u8; 32], pty_tx);
|
||||
let client_id = [17u8; 16];
|
||||
let session_key = [18u8; 32];
|
||||
@@ -5707,7 +5712,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn pending_server_stream_close_is_retransmitted() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let mut state = ServerState::new(ServerConfig::default(), [0u8; 32], pty_tx);
|
||||
let client_id = [19u8; 16];
|
||||
let session_key = [20u8; 32];
|
||||
@@ -5763,7 +5768,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn pending_server_stream_close_expires_after_attempt_cap() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let mut state = ServerState::new(ServerConfig::default(), [0u8; 32], pty_tx);
|
||||
let client_id = [21u8; 16];
|
||||
let session_key = [22u8; 32];
|
||||
@@ -5808,7 +5813,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn pending_server_stream_window_adjust_is_retransmitted() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let mut state = ServerState::new(ServerConfig::default(), [0u8; 32], pty_tx);
|
||||
let client_id = [23u8; 16];
|
||||
let session_key = [24u8; 32];
|
||||
@@ -5871,7 +5876,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn pending_server_stream_window_adjust_expires_after_attempt_cap() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let mut state = ServerState::new(ServerConfig::default(), [0u8; 32], pty_tx);
|
||||
let client_id = [25u8; 16];
|
||||
let session_key = [26u8; 32];
|
||||
@@ -5922,7 +5927,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn server_stream_send_splits_large_writes() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let mut state = ServerState::new(ServerConfig::default(), [0u8; 32], pty_tx);
|
||||
let client_id = [11u8; 16];
|
||||
let session_key = [12u8; 32];
|
||||
@@ -6029,7 +6034,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn blocked_stream_data_does_not_block_terminal_frames() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let mut state = ServerState::new(ServerConfig::default(), [0u8; 32], pty_tx);
|
||||
let client_id = [8u8; 16];
|
||||
let session_key = [10u8; 32];
|
||||
@@ -6134,7 +6139,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn live_terminal_output_is_never_replaced_by_paced_snapshots() {
|
||||
let (pty_tx, _pty_rx) = mpsc::unbounded_channel();
|
||||
let (pty_tx, _pty_rx) = mpsc::channel(PTY_OUTPUT_QUEUE_CAPACITY);
|
||||
let config = ServerConfig {
|
||||
output_frame_interval_ms: 1000,
|
||||
..ServerConfig::default()
|
||||
|
||||
Reference in New Issue
Block a user