Remoting: Fix connecting to servers with long hostnames (#20093)

Closes #20018

Release Notes:

- Remoting: Fixed connecting to hosts with long (>~50 character)
hostnames
This commit is contained in:
Conrad Irwin 2024-11-01 13:52:21 -06:00 committed by GitHub
parent 273173ec8a
commit b5c38e9a09
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 50 additions and 34 deletions

1
Cargo.lock generated
View file

@ -14765,7 +14765,6 @@ dependencies = [
"parking_lot", "parking_lot",
"postage", "postage",
"project", "project",
"release_channel",
"remote", "remote",
"schemars", "schemars",
"serde", "serde",

View file

@ -16,6 +16,7 @@ use gpui::{
}; };
use picker::Picker; use picker::Picker;
use project::Project; use project::Project;
use remote::ssh_session::ConnectionIdentifier;
use remote::SshConnectionOptions; use remote::SshConnectionOptions;
use remote::SshRemoteClient; use remote::SshRemoteClient;
use settings::update_settings_file; use settings::update_settings_file;
@ -384,7 +385,7 @@ impl RemoteServerProjects {
let ssh_prompt = cx.new_view(|cx| SshPrompt::new(&connection_options, cx)); let ssh_prompt = cx.new_view(|cx| SshPrompt::new(&connection_options, cx));
let connection = connect_over_ssh( let connection = connect_over_ssh(
connection_options.remote_server_identifier(), ConnectionIdentifier::Setup,
connection_options.clone(), connection_options.clone(),
ssh_prompt.clone(), ssh_prompt.clone(),
cx, cx,
@ -475,7 +476,7 @@ impl RemoteServerProjects {
.clone(); .clone();
let connect = connect_over_ssh( let connect = connect_over_ssh(
connection_options.remote_server_identifier(), ConnectionIdentifier::Setup,
connection_options.clone(), connection_options.clone(),
prompt, prompt,
cx, cx,

View file

@ -14,6 +14,7 @@ use gpui::{AppContext, Model};
use language::CursorShape; use language::CursorShape;
use markdown::{Markdown, MarkdownStyle}; use markdown::{Markdown, MarkdownStyle};
use release_channel::ReleaseChannel; use release_channel::ReleaseChannel;
use remote::ssh_session::ConnectionIdentifier;
use remote::{SshConnectionOptions, SshPlatform, SshRemoteClient}; use remote::{SshConnectionOptions, SshPlatform, SshRemoteClient};
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -534,7 +535,7 @@ pub fn is_connecting_over_ssh(workspace: &Workspace, cx: &AppContext) -> bool {
} }
pub fn connect_over_ssh( pub fn connect_over_ssh(
unique_identifier: String, unique_identifier: ConnectionIdentifier,
connection_options: SshConnectionOptions, connection_options: SshConnectionOptions,
ui: View<SshPrompt>, ui: View<SshPrompt>,
cx: &mut WindowContext, cx: &mut WindowContext,

View file

@ -202,17 +202,6 @@ impl SshConnectionOptions {
host host
} }
} }
// Uniquely identifies dev server projects on a remote host. Needs to be
// stable for the same dev server project.
pub fn remote_server_identifier(&self) -> String {
let mut identifier = format!("dev-server-{:?}", self.host);
if let Some(username) = self.username.as_ref() {
identifier.push('-');
identifier.push_str(&username);
}
identifier
}
} }
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
@ -520,14 +509,43 @@ pub enum SshRemoteEvent {
impl EventEmitter<SshRemoteEvent> for SshRemoteClient {} impl EventEmitter<SshRemoteEvent> for SshRemoteClient {}
// Identifies the socket on the remote server so that reconnects
// can re-join the same project.
pub enum ConnectionIdentifier {
Setup,
Workspace(i64),
}
impl ConnectionIdentifier {
// This string gets used in a socket name, and so must be relatively short.
// The total length of:
// /home/{username}/.local/share/zed/server_state/{name}/stdout.sock
// Must be less than about 100 characters
// https://unix.stackexchange.com/questions/367008/why-is-socket-path-length-limited-to-a-hundred-chars
// So our strings should be at most 20 characters or so.
fn to_string(&self, cx: &AppContext) -> String {
let identifier_prefix = match ReleaseChannel::global(cx) {
ReleaseChannel::Stable => "".to_string(),
release_channel => format!("{}-", release_channel.dev_name()),
};
match self {
Self::Setup => format!("{identifier_prefix}setup"),
Self::Workspace(workspace_id) => {
format!("{identifier_prefix}workspace-{workspace_id}",)
}
}
}
}
impl SshRemoteClient { impl SshRemoteClient {
pub fn new( pub fn new(
unique_identifier: String, unique_identifier: ConnectionIdentifier,
connection_options: SshConnectionOptions, connection_options: SshConnectionOptions,
cancellation: oneshot::Receiver<()>, cancellation: oneshot::Receiver<()>,
delegate: Arc<dyn SshClientDelegate>, delegate: Arc<dyn SshClientDelegate>,
cx: &mut AppContext, cx: &mut AppContext,
) -> Task<Result<Option<Model<Self>>>> { ) -> Task<Result<Option<Model<Self>>>> {
let unique_identifier = unique_identifier.to_string(cx);
cx.spawn(|mut cx| async move { cx.spawn(|mut cx| async move {
let success = Box::pin(async move { let success = Box::pin(async move {
let (outgoing_tx, outgoing_rx) = mpsc::unbounded::<Envelope>(); let (outgoing_tx, outgoing_rx) = mpsc::unbounded::<Envelope>();
@ -1096,7 +1114,15 @@ impl SshRemoteClient {
) -> Model<Self> { ) -> Model<Self> {
let (_tx, rx) = oneshot::channel(); let (_tx, rx) = oneshot::channel();
client_cx client_cx
.update(|cx| Self::new("fake".to_string(), opts, rx, Arc::new(fake::Delegate), cx)) .update(|cx| {
Self::new(
ConnectionIdentifier::Setup,
opts,
rx,
Arc::new(fake::Delegate),
cx,
)
})
.await .await
.unwrap() .unwrap()
.unwrap() .unwrap()

View file

@ -2,7 +2,7 @@ use crate::headless_project::HeadlessProject;
use client::{Client, UserStore}; use client::{Client, UserStore};
use clock::FakeSystemClock; use clock::FakeSystemClock;
use fs::{FakeFs, Fs}; use fs::{FakeFs, Fs};
use gpui::{Context, Model, TestAppContext}; use gpui::{Context, Model, SemanticVersion, TestAppContext};
use http_client::{BlockedHttpClient, FakeHttpClient}; use http_client::{BlockedHttpClient, FakeHttpClient};
use language::{ use language::{
language_settings::{language_settings, AllLanguageSettings}, language_settings::{language_settings, AllLanguageSettings},
@ -1184,6 +1184,9 @@ pub async fn init_test(
server_cx: &mut TestAppContext, server_cx: &mut TestAppContext,
) -> (Model<Project>, Model<HeadlessProject>) { ) -> (Model<Project>, Model<HeadlessProject>) {
let server_fs = server_fs.clone(); let server_fs = server_fs.clone();
cx.update(|cx| {
release_channel::init(SemanticVersion::default(), cx);
});
init_logger(); init_logger();
let (opts, ssh_server_client) = SshRemoteClient::fake_server(cx, server_cx); let (opts, ssh_server_client) = SshRemoteClient::fake_server(cx, server_cx);

View file

@ -49,7 +49,6 @@ parking_lot.workspace = true
postage.workspace = true postage.workspace = true
project.workspace = true project.workspace = true
task.workspace = true task.workspace = true
release_channel.workspace = true
remote.workspace = true remote.workspace = true
schemars.workspace = true schemars.workspace = true
serde.workspace = true serde.workspace = true

View file

@ -64,8 +64,7 @@ use postage::stream::Stream;
use project::{ use project::{
DirectoryLister, Project, ProjectEntryId, ProjectPath, ResolvedPath, Worktree, WorktreeId, DirectoryLister, Project, ProjectEntryId, ProjectPath, ResolvedPath, Worktree, WorktreeId,
}; };
use release_channel::ReleaseChannel; use remote::{ssh_session::ConnectionIdentifier, SshClientDelegate, SshConnectionOptions};
use remote::{SshClientDelegate, SshConnectionOptions};
use serde::Deserialize; use serde::Deserialize;
use session::AppSession; use session::AppSession;
use settings::Settings; use settings::Settings;
@ -5496,26 +5495,14 @@ pub fn open_ssh_project(
paths: Vec<PathBuf>, paths: Vec<PathBuf>,
cx: &mut AppContext, cx: &mut AppContext,
) -> Task<Result<()>> { ) -> Task<Result<()>> {
let release_channel = ReleaseChannel::global(cx);
cx.spawn(|mut cx| async move { cx.spawn(|mut cx| async move {
let (serialized_ssh_project, workspace_id, serialized_workspace) = let (serialized_ssh_project, workspace_id, serialized_workspace) =
serialize_ssh_project(connection_options.clone(), paths.clone(), &cx).await?; serialize_ssh_project(connection_options.clone(), paths.clone(), &cx).await?;
let identifier_prefix = match release_channel {
ReleaseChannel::Stable => None,
_ => Some(format!("{}-", release_channel.dev_name())),
};
let unique_identifier = format!(
"{}workspace-{}",
identifier_prefix.unwrap_or_default(),
workspace_id.0
);
let session = match cx let session = match cx
.update(|cx| { .update(|cx| {
remote::SshRemoteClient::new( remote::SshRemoteClient::new(
unique_identifier, ConnectionIdentifier::Workspace(workspace_id.0),
connection_options, connection_options,
cancel_rx, cancel_rx,
delegate, delegate,