remote: Add ssh timeout setting (#44823)

Marco Mihai Condrache created

Closes #21527

Release Notes:

- Added a setting to specify the ssh connection timeout

---------

Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>

Change summary

crates/recent_projects/src/remote_servers.rs |  1 +
crates/remote/src/transport/ssh.rs           | 18 +++++++++++++++---
crates/settings/src/settings_content.rs      |  3 +++
crates/zed/src/zed/open_listener.rs          |  1 +
4 files changed, 20 insertions(+), 3 deletions(-)

Detailed changes

crates/recent_projects/src/remote_servers.rs 🔗

@@ -1525,6 +1525,7 @@ impl RemoteServerProjects {
                     args: connection_options.args.unwrap_or_default(),
                     upload_binary_over_ssh: None,
                     port_forwards: connection_options.port_forwards,
+                    connection_timeout: connection_options.connection_timeout,
                 })
         });
     }

crates/remote/src/transport/ssh.rs 🔗

@@ -55,6 +55,7 @@ pub struct SshConnectionOptions {
     pub password: Option<String>,
     pub args: Option<Vec<String>>,
     pub port_forwards: Option<Vec<SshPortForwardOption>>,
+    pub connection_timeout: Option<u16>,
 
     pub nickname: Option<String>,
     pub upload_binary_over_ssh: bool,
@@ -71,6 +72,7 @@ impl From<settings::SshConnection> for SshConnectionOptions {
             nickname: val.nickname,
             upload_binary_over_ssh: val.upload_binary_over_ssh.unwrap_or_default(),
             port_forwards: val.port_forwards,
+            connection_timeout: val.connection_timeout,
         }
     }
 }
@@ -670,7 +672,12 @@ impl SshRemoteConnection {
 
         delegate.set_status(Some("Downloading remote development server on host"), cx);
 
-        const CONNECT_TIMEOUT_SECS: &str = "10";
+        let connection_timeout = self
+            .socket
+            .connection_options
+            .connection_timeout
+            .unwrap_or(10)
+            .to_string();
 
         match self
             .socket
@@ -681,7 +688,7 @@ impl SshRemoteConnection {
                     "-f",
                     "-L",
                     "--connect-timeout",
-                    CONNECT_TIMEOUT_SECS,
+                    &connection_timeout,
                     url,
                     "-o",
                     &tmp_path_gz.display(self.path_style()),
@@ -709,7 +716,7 @@ impl SshRemoteConnection {
                         "wget",
                         &[
                             "--connect-timeout",
-                            CONNECT_TIMEOUT_SECS,
+                            &connection_timeout,
                             "--tries",
                             "1",
                             url,
@@ -1226,6 +1233,7 @@ impl SshConnectionOptions {
             password: None,
             nickname: None,
             upload_binary_over_ssh: false,
+            connection_timeout: None,
         })
     }
 
@@ -1252,6 +1260,10 @@ impl SshConnectionOptions {
     pub fn additional_args(&self) -> Vec<String> {
         let mut args = self.additional_args_for_scp();
 
+        if let Some(timeout) = self.connection_timeout {
+            args.extend(["-o".to_string(), format!("ConnectTimeout={}", timeout)]);
+        }
+
         if let Some(forwards) = &self.port_forwards {
             args.extend(forwards.iter().map(|pf| {
                 let local_host = match &pf.local_host {

crates/settings/src/settings_content.rs 🔗

@@ -930,6 +930,9 @@ pub struct SshConnection {
     pub upload_binary_over_ssh: Option<bool>,
 
     pub port_forwards: Option<Vec<SshPortForwardOption>>,
+    /// Timeout in seconds for SSH connection and downloading the remote server binary.
+    /// Defaults to 10 seconds if not specified.
+    pub connection_timeout: Option<u16>,
 }
 
 #[derive(Clone, Default, Serialize, Deserialize, PartialEq, JsonSchema, MergeFrom, Debug)]

crates/zed/src/zed/open_listener.rs 🔗

@@ -686,6 +686,7 @@ mod tests {
                 port_forwards: None,
                 nickname: None,
                 upload_binary_over_ssh: false,
+                connection_timeout: None,
             })
         );
         assert_eq!(request.open_paths, vec!["/"]);