diff options
Diffstat (limited to 'vendor/gix/src/remote/connect.rs')
-rw-r--r-- | vendor/gix/src/remote/connect.rs | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/vendor/gix/src/remote/connect.rs b/vendor/gix/src/remote/connect.rs new file mode 100644 index 000000000..8e656975e --- /dev/null +++ b/vendor/gix/src/remote/connect.rs @@ -0,0 +1,166 @@ +#![allow(clippy::result_large_err)] +use gix_protocol::transport::client::Transport; + +use crate::{remote::Connection, Progress, Remote}; + +mod error { + use crate::{bstr::BString, config, remote}; + + /// The error returned by [connect()][crate::Remote::connect()]. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error("Could not obtain options for connecting via ssh")] + SshOptions(#[from] config::ssh_connect_options::Error), + #[error("Could not obtain the current directory")] + CurrentDir(#[from] std::io::Error), + #[error("Could not access remote repository at \"{}\"", directory.display())] + InvalidRemoteRepositoryPath { directory: std::path::PathBuf }, + #[error(transparent)] + SchemePermission(#[from] config::protocol::allow::Error), + #[error("Protocol {scheme:?} of url {url:?} is denied per configuration")] + ProtocolDenied { url: BString, scheme: gix_url::Scheme }, + #[error(transparent)] + Connect(#[from] gix_protocol::transport::client::connect::Error), + #[error("The {} url was missing - don't know where to establish a connection to", direction.as_str())] + MissingUrl { direction: remote::Direction }, + #[error("Protocol named {given:?} is not a valid protocol. Choose between 1 and 2")] + UnknownProtocol { given: BString }, + #[error("Could not verify that \"{}\" url is a valid git directory before attempting to use it", url.to_bstring())] + FileUrl { + source: Box<gix_discover::is_git::Error>, + url: gix_url::Url, + }, + } + + impl gix_protocol::transport::IsSpuriousError for Error { + /// Return `true` if retrying might result in a different outcome due to IO working out differently. + fn is_spurious(&self) -> bool { + match self { + Error::Connect(err) => err.is_spurious(), + _ => false, + } + } + } +} +pub use error::Error; + +/// Establishing connections to remote hosts (without performing a git-handshake). +impl<'repo> Remote<'repo> { + /// Create a new connection using `transport` to communicate, with `progress` to indicate changes. + /// + /// Note that this method expects the `transport` to be created by the user, which would involve the [`url()`][Self::url()]. + /// It's meant to be used when async operation is needed with runtimes of the user's choice. + pub fn to_connection_with_transport<T, P>(&self, transport: T, progress: P) -> Connection<'_, 'repo, T, P> + where + T: Transport, + P: Progress, + { + Connection { + remote: self, + authenticate: None, + transport_options: None, + transport, + progress, + } + } + + /// Connect to the url suitable for `direction` and return a handle through which operations can be performed. + /// + /// Note that the `protocol.version` configuration key affects the transport protocol used to connect, + /// with `2` being the default. + /// + /// The transport used for connection can be configured via `transport_mut().configure()` assuming the actually + /// used transport is well known. If that's not the case, the transport can be created by hand and passed to + /// [to_connection_with_transport()][Self::to_connection_with_transport()]. + #[cfg(any(feature = "blocking-network-client", feature = "async-network-client-async-std"))] + #[gix_protocol::maybe_async::maybe_async] + pub async fn connect<P>( + &self, + direction: crate::remote::Direction, + progress: P, + ) -> Result<Connection<'_, 'repo, Box<dyn Transport + Send>, P>, Error> + where + P: Progress, + { + let (url, version) = self.sanitized_url_and_version(direction)?; + #[cfg(feature = "blocking-network-client")] + let scheme_is_ssh = url.scheme == gix_url::Scheme::Ssh; + let transport = gix_protocol::transport::connect( + url, + gix_protocol::transport::client::connect::Options { + version, + #[cfg(feature = "blocking-network-client")] + ssh: scheme_is_ssh + .then(|| self.repo.ssh_connect_options()) + .transpose()? + .unwrap_or_default(), + }, + ) + .await?; + Ok(self.to_connection_with_transport(transport, progress)) + } + + /// Produce the sanitized URL and protocol version to use as obtained by querying the repository configuration. + /// + /// This can be useful when using custom transports to allow additional configuration. + pub fn sanitized_url_and_version( + &self, + direction: crate::remote::Direction, + ) -> Result<(gix_url::Url, gix_protocol::transport::Protocol), Error> { + fn sanitize(mut url: gix_url::Url) -> Result<gix_url::Url, Error> { + if url.scheme == gix_url::Scheme::File { + let mut dir = gix_path::to_native_path_on_windows(url.path.as_ref()); + let kind = gix_discover::is_git(dir.as_ref()) + .or_else(|_| { + dir.to_mut().push(gix_discover::DOT_GIT_DIR); + gix_discover::is_git(dir.as_ref()) + }) + .map_err(|err| Error::FileUrl { + source: err.into(), + url: url.clone(), + })?; + let (git_dir, _work_dir) = gix_discover::repository::Path::from_dot_git_dir( + dir.clone().into_owned(), + kind, + std::env::current_dir()?, + ) + .ok_or_else(|| Error::InvalidRemoteRepositoryPath { + directory: dir.into_owned(), + })? + .into_repository_and_work_tree_directories(); + url.path = gix_path::into_bstr(git_dir).into_owned(); + } + Ok(url) + } + + use gix_protocol::transport::Protocol; + let version = self + .repo + .config + .resolved + .integer("protocol", None, "version") + .unwrap_or(Ok(2)) + .map_err(|err| Error::UnknownProtocol { given: err.input }) + .and_then(|num| { + Ok(match num { + 1 => Protocol::V1, + 2 => Protocol::V2, + num => { + return Err(Error::UnknownProtocol { + given: num.to_string().into(), + }) + } + }) + })?; + + let url = self.url(direction).ok_or(Error::MissingUrl { direction })?.to_owned(); + if !self.repo.config.url_scheme()?.allow(&url.scheme) { + return Err(Error::ProtocolDenied { + url: url.to_bstring(), + scheme: url.scheme, + }); + } + Ok((sanitize(url)?, version)) + } +} |