From 6ede0a7c532b69d8649cd56f541ef5e4e35d80c2 Mon Sep 17 00:00:00 2001 From: Jonathan Liebig Date: Sun, 6 Sep 2026 15:11:38 +0200 Subject: [PATCH] fix: cancel oversized endpoint commands before sending --- src/client/endpoint_commands.rs | 6 ++ src/client/shell/tests/endpoint_requests.rs | 70 +++++++++++++-------- 2 files changed, 49 insertions(+), 27 deletions(-) diff --git a/src/client/endpoint_commands.rs b/src/client/endpoint_commands.rs index 8e77b796..2ab15351 100644 --- a/src/client/endpoint_commands.rs +++ b/src/client/endpoint_commands.rs @@ -111,6 +111,12 @@ impl EndpointCommands { continue; } }; + // The server disconnects clients that send commands above this wire limit. + if request.len() > crate::server::client_commands::MAX_ENDPOINT_COMMAND_BYTES { + tracing::warn!(%request_id, "endpoint request exceeds the server size limit"); + cancelled.push(request_id); + continue; + } let message = ClientMessage::ClientShellEndpointRequest { boot_id: queued.boot_id.clone(), request, diff --git a/src/client/shell/tests/endpoint_requests.rs b/src/client/shell/tests/endpoint_requests.rs index ab5a3cb2..2820f59a 100644 --- a/src/client/shell/tests/endpoint_requests.rs +++ b/src/client/shell/tests/endpoint_requests.rs @@ -149,40 +149,56 @@ fn dispatcher_cancels_worktree_requests_on_frozen_surface_or_failed_send() { } #[test] -fn stale_queued_request_is_cancelled_without_blocking_the_current_generation() { +fn unusable_queued_request_is_cancelled_without_blocking_the_next_request() { use crate::client::endpoint::{EndpointNegotiation, EndpointRegistry}; use crate::client::endpoint_commands::EndpointCommands; - let (mut state, actions) = pending_popup(); - let stale_id = request_id(&actions).to_owned(); - let current = state.focus_endpoint_target(ClientEndpointFocusTarget::Workspace("ws_1".into())); - let current_id = request_id(¤t).to_owned(); - let mut commands = EndpointCommands::default(); - for (generation, actions) in [(1, actions), (2, current)] { - for action in actions { - let ClientShellAction::Endpoint { - endpoint_id, - boot_id, - request, - } = action - else { + for oversized in [false, true] { + let (mut state, mut actions) = pending_popup(); + if oversized { + let ClientShellAction::Endpoint { request, .. } = &mut actions[0] else { panic!("expected endpoint request"); }; - commands.enqueue(endpoint_id, generation, boot_id, request); + request.method = crate::api::schema::Method::PaneSelectionReadChecked( + crate::api::schema::PaneSelectionReadCheckedParams { + pane_id: "pane_1".into(), + anchor: crate::api::schema::PaneTextPoint { row: 0, col: 0 }, + cursor: crate::api::schema::PaneTextPoint { row: 511, col: 511 }, + expected_cells: vec!["x".into(); 512 * 512], + }, + ); } + let stale_id = request_id(&actions).to_owned(); + let current = + state.focus_endpoint_target(ClientEndpointFocusTarget::Workspace("ws_1".into())); + let current_id = request_id(¤t).to_owned(); + let mut commands = EndpointCommands::default(); + for (generation, actions) in [(if oversized { 2 } else { 1 }, actions), (2, current)] { + for action in actions { + let ClientShellAction::Endpoint { + endpoint_id, + boot_id, + request, + } = action + else { + panic!("expected endpoint request"); + }; + commands.enqueue(endpoint_id, generation, boot_id, request); + } + } + let mut endpoints = EndpointRegistry::new( + TestTransport { fail: false }, + 2, + EndpointNegotiation::default(), + ); + let cancelled = commands.send_next(&ClientEndpointId::Local, &mut endpoints); + assert_eq!(cancelled, vec![stale_id.clone()]); + state.cancel_endpoint_request(&stale_id); + assert!(!state.popup_pending); + assert!(!commands.accepts_response(&ClientEndpointId::Local, 1, "boot-1", &stale_id)); + assert!(commands.accepts_response(&ClientEndpointId::Local, 2, "boot-1", ¤t_id)); + assert!(state.pending_requests.contains_key(¤t_id)); } - let mut endpoints = EndpointRegistry::new( - TestTransport { fail: false }, - 2, - EndpointNegotiation::default(), - ); - let cancelled = commands.send_next(&ClientEndpointId::Local, &mut endpoints); - assert_eq!(cancelled, vec![stale_id.clone()]); - state.cancel_endpoint_request(&stale_id); - assert!(!state.popup_pending); - assert!(!commands.accepts_response(&ClientEndpointId::Local, 1, "boot-1", &stale_id)); - assert!(commands.accepts_response(&ClientEndpointId::Local, 2, "boot-1", ¤t_id)); - assert!(state.pending_requests.contains_key(¤t_id)); } #[test]