Flush queued input after reconnect contact
ci / test (push) Has been cancelled
ci / fuzz-smoke (push) Has been cancelled
ci / windows-client (push) Has been cancelled
ci / package-release (linux-x86_64, ubuntu-latest) (push) Has been cancelled
ci / package-release (macos-aarch64, macos-14) (push) Has been cancelled
ci / package-release (macos-x86_64, macos-13) (push) Has been cancelled
ci / package-release (windows-x86_64, windows-latest) (push) Has been cancelled
ci / remote-bench (push) Has been cancelled

This commit is contained in:
DuProcess
2026-07-03 16:35:22 -04:00
parent a8ba852f16
commit 237ad52bef
5 changed files with 124 additions and 8 deletions
Generated
+1 -1
View File
@@ -436,7 +436,7 @@ dependencies = [
[[package]] [[package]]
name = "dosh" name = "dosh"
version = "0.1.16" version = "0.1.17"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64", "base64",
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "dosh" name = "dosh"
version = "0.1.16" version = "0.1.17"
edition = "2024" edition = "2024"
license = "MIT" license = "MIT"
+19 -1
View File
@@ -1,4 +1,4 @@
.PHONY: build test fmt clippy release-check install package-release package-release-linux package-release-windows publish-release bench-report bench-local bench-local-json bench-docker-ssh bench-docker-mosh fuzz-smoke fuzz-deep soak-local tui-harness .PHONY: build test fmt clippy release-check 1.0-check reconnect-check hostile-network-check persistence-check package-check install package-release package-release-linux package-release-windows publish-release bench-report bench-local bench-local-json bench-docker-ssh bench-docker-mosh fuzz-smoke fuzz-deep soak-local tui-harness
build: build:
cargo build --release cargo build --release
@@ -19,6 +19,24 @@ release-check:
cargo test cargo test
$(MAKE) tui-harness $(MAKE) tui-harness
1.0-check: release-check reconnect-check hostile-network-check persistence-check package-check
reconnect-check:
cargo test --test integration_smoke resume_updates_udp_endpoint_for_roaming -- --nocapture
DOSH_SOAK_SECONDS=$${DOSH_SOAK_SECONDS:-300} cargo test --test integration_smoke sleep_roaming_soak_30m -- --ignored --nocapture
hostile-network-check:
cargo test --test hostile_network -- --nocapture
persistence-check:
cargo test --test integration_smoke session_survives_server_restart_same_shell_and_screen -- --nocapture
cargo test --test integration_smoke multiple_persistent_named_sessions_survive_restart_independently -- --nocapture
package-check:
$(MAKE) package-release-linux
$(MAKE) package-release-windows
sh scripts/verify-release-artifacts.sh
install: install:
sh install.sh --from-current sh install.sh --from-current
+46
View File
@@ -0,0 +1,46 @@
#!/usr/bin/env sh
set -eu
repo_root="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
cd "$repo_root"
version="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | sed -n '1p')"
out_dir="${DOSH_PACKAGE_DIR:-target/dosh-release}"
artifact_version() {
artifact="$1"
case "$artifact" in
*.tar.gz)
tar -xOf "$artifact" dosh/VERSION 2>/dev/null | tr -d '\r\n'
;;
*.zip)
unzip -p "$artifact" dosh/VERSION 2>/dev/null | tr -d '\r\n'
;;
*)
return 1
;;
esac
}
failed=0
for artifact in \
"$out_dir/dosh-linux-x86_64.tar.gz" \
"$out_dir/dosh-macos-aarch64.tar.gz" \
"$out_dir/dosh-windows-x86_64.zip"; do
if [ ! -f "$artifact" ]; then
echo "missing release artifact: $artifact" >&2
failed=1
continue
fi
if [ ! -f "$artifact.sha256" ]; then
echo "missing release checksum: $artifact.sha256" >&2
failed=1
fi
actual="$(artifact_version "$artifact" || true)"
if [ "$actual" != "$version" ]; then
echo "artifact version mismatch: $artifact has ${actual:-unknown}, expected $version" >&2
failed=1
fi
done
exit "$failed"
+57 -5
View File
@@ -4610,6 +4610,22 @@ async fn run_terminal(
} }
PacketKind::Pong => { PacketKind::Pong => {
last_packet_at = Instant::now(); last_packet_at = Instant::now();
if should_flush_terminal_input_after_contact(
forward_only,
&cred.mode,
pending_user_input.is_empty(),
) {
flush_pending_user_input(
&socket,
addr,
&cred,
&mut send_seq,
&mut predictor,
&mut pending_user_input,
&mut pending_user_input_bytes,
)
.await?;
}
} }
PacketKind::Rekey => { PacketKind::Rekey => {
// Server-initiated transport rekey (spec §11). The Rekey is // Server-initiated transport rekey (spec §11). The Rekey is
@@ -5439,6 +5455,14 @@ fn should_hold_during_startup_gate(
mode == StartupGateMode::HoldAll || already_queued || should_hold_post_submit_input(bytes) mode == StartupGateMode::HoldAll || already_queued || should_hold_post_submit_input(bytes)
} }
fn should_flush_terminal_input_after_contact(
forward_only: bool,
mode: &str,
pending_empty: bool,
) -> bool {
!forward_only && !pending_empty && mode != "view-only" && mode != "forward-only"
}
async fn flush_pending_user_input( async fn flush_pending_user_input(
socket: &UdpSocket, socket: &UdpSocket,
addr: SocketAddr, addr: SocketAddr,
@@ -7095,11 +7119,11 @@ mod tests {
release_tag_download_url, release_tag_from_effective_url, release_version_from_tag, release_tag_download_url, release_tag_from_effective_url, release_version_from_tag,
render_status_clear, render_status_overlay, requested_env, resolved_startup_command, render_status_clear, render_status_overlay, requested_env, resolved_startup_command,
retransmit_stream_opens, rewrite_forward_command, selected_predict_mode, selected_udp_host, retransmit_stream_opens, rewrite_forward_command, selected_predict_mode, selected_udp_host,
server_version_mismatch, should_hold_during_startup_gate, should_hold_post_submit_input, server_version_mismatch, should_flush_terminal_input_after_contact,
split_after_command_submit, ssh_destination_host, ssh_username, ssh_with_user, should_hold_during_startup_gate, should_hold_post_submit_input, split_after_command_submit,
startup_command, status_ssh_target, strip_stale_mouse_reports, toml_bare_key_or_quoted, ssh_destination_host, ssh_username, ssh_with_user, startup_command, status_ssh_target,
update_check_requested, update_version_status, upsert_managed_block, valid_forward_host, strip_stale_mouse_reports, toml_bare_key_or_quoted, update_check_requested,
vscode_safe_alias, update_version_status, upsert_managed_block, valid_forward_host, vscode_safe_alias,
}; };
use dosh::config::{ClientConfig, CommandExtension, HostConfig}; use dosh::config::{ClientConfig, CommandExtension, HostConfig};
use dosh::native::EnvVar; use dosh::native::EnvVar;
@@ -7879,6 +7903,34 @@ mod tests {
assert_eq!(post_submit_hold_duration(b"x"), POST_SUBMIT_ALL_INPUT_HOLD); assert_eq!(post_submit_hold_duration(b"x"), POST_SUBMIT_ALL_INPUT_HOLD);
} }
#[test]
fn contact_flushes_pending_terminal_input_for_interactive_sessions() {
assert!(should_flush_terminal_input_after_contact(
false, "normal", false
));
assert!(should_flush_terminal_input_after_contact(
false,
"read-write",
false
));
assert!(!should_flush_terminal_input_after_contact(
false, "normal", true
));
assert!(!should_flush_terminal_input_after_contact(
true, "normal", false
));
assert!(!should_flush_terminal_input_after_contact(
false,
"view-only",
false
));
assert!(!should_flush_terminal_input_after_contact(
false,
"forward-only",
false
));
}
#[test] #[test]
fn reconnect_refreshes_live_send_address_from_credentials() { fn reconnect_refreshes_live_send_address_from_credentials() {
let mut addr = "127.0.0.1:50000".parse().unwrap(); let mut addr = "127.0.0.1:50000".parse().unwrap();