Previously `transport_mut()` was supposed to be used for calling `configure()`, but that doesn't work anymore as `configure()` can only effectivey be called once the initialization of the Connection is complete, as it may depend on the Remote name AND the credential provider for proxy auth credential acquisition. Thus we allow callers to set the transport options they need in advance for it to be used when needed.
Unified · Split
| @@ -39,6 +39,7 @@ impl<'repo> Remote<'repo> { · 38 unmodified lines | |||
| 39 | Connection { | 39 | Connection { |
| 40 | remote: self, | 40 | remote: self, |
| 41 | authenticate: None, | 41 | authenticate: None, |
| 42 | transport_config: None, | ||
| 42 | transport, | 43 | transport, |
| 43 | progress, | 44 | progress, |
| 44 | } | 45 | } |
| @@ -19,6 +19,18 @@ impl<'a, 'repo, T, P> Connection<'a, 'repo, T, P> { · 18 unmodified lines | |||
| 19 | self.authenticate = Some(Box::new(helper)); | 19 | self.authenticate = Some(Box::new(helper)); |
| 20 | self | 20 | self |
| 21 | } | 21 | } |
| 22 | |||
| 23 | /// Provide configuration to be used before the first handshake is conducted. | ||
| 24 | /// It's typically created by initializing it with [`Repository::transport_options()`][crate::Repository::transport_options()], which | ||
| 25 | /// is also the default if this isn't set explicitly. Note that all of the default configuration is created from `git` | ||
| 26 | /// configuration, which can also be manipulated through overrides to affect the default configuration. | ||
| 27 | /// | ||
| 28 | /// Use this method to provide transport configuration with custom backend configuration that is not configurable by other means and | ||
| 29 | /// custom to the application at hand. | ||
| 30 | pub fn with_transport_options(mut self, config: Box<dyn std::any::Any>) -> Self { | ||
| 31 | self.transport_config = Some(config); | ||
| 32 | self | ||
| 33 | } | ||
| 22 | } | 34 | } |
| 23 | 35 | ||
| 24 | /// Access | 36 | /// Access |
| @@ -41,13 +53,10 @@ impl<'a, 'repo, T, P> Connection<'a, 'repo, T, P> { · 16 unmodified lines | |||
| 41 | self.remote | 53 | self.remote |
| 42 | } | 54 | } |
| 43 | 55 | ||
| 44 | /// Provide a mutable transport to allow configuring it with [`configure()`][git_protocol::transport::client::TransportWithoutIO::configure()]. | 56 | /// Provide a mutable transport to allow interacting with it according to its actual type. |
| 45 | /// | 57 | /// Note that the caller _should not_ call [`configure()`][git_protocol::transport::client::TransportWithoutIO::configure()] |
| 46 | /// Please note that it is pre-configured, but can be configured again right after the [`Connection`] was instantiated without | 58 | /// as we will call it automatically before performing the handshake. Instead, to bring in custom configuration, |
| 47 | /// the previous configuration having ever been used. | 59 | /// call [`with_transport_options()`][Connection::with_transport_options()]. |
| 48 | /// | ||
| 49 | /// Also note that all of the default configuration is created from `git` configuration, which can also be manipulated through overrides | ||
| 50 | /// to affect the default configuration. | ||
| 51 | pub fn transport_mut(&mut self) -> &mut T { | 60 | pub fn transport_mut(&mut self) -> &mut T { |
| 52 | &mut self.transport | 61 | &mut self.transport |
| 53 | } | 62 | } |
| @@ -15,6 +15,7 @@ pub type AuthenticateFn<'a> = Box<dyn FnMut(git_credentials::helper::Action) -> · 14 unmodified lines | |||
| 15 | pub struct Connection<'a, 'repo, T, P> { | 15 | pub struct Connection<'a, 'repo, T, P> { |
| 16 | pub(crate) remote: &'a Remote<'repo>, | 16 | pub(crate) remote: &'a Remote<'repo>, |
| 17 | pub(crate) authenticate: Option<AuthenticateFn<'a>>, | 17 | pub(crate) authenticate: Option<AuthenticateFn<'a>>, |
| 18 | pub(crate) transport_config: Option<Box<dyn std::any::Any>>, | ||
| 18 | pub(crate) transport: T, | 19 | pub(crate) transport: T, |
| 19 | pub(crate) progress: P, | 20 | pub(crate) progress: P, |
| 20 | } | 21 | } |
| @@ -13,6 +13,13 @@ use crate::{ · 12 unmodified lines | |||
| 13 | #[derive(Debug, thiserror::Error)] | 13 | #[derive(Debug, thiserror::Error)] |
| 14 | #[allow(missing_docs)] | 14 | #[allow(missing_docs)] |
| 15 | pub enum Error { | 15 | pub enum Error { |
| 16 | #[error("Failed to configure the transport before connecting to {url:?}")] | ||
| 17 | GatherTransportConfig { | ||
| 18 | url: BString, | ||
| 19 | source: crate::config::transport::Error, | ||
| 20 | }, | ||
| 21 | #[error("Failed to configure the transport layer")] | ||
| 22 | ConfigureTransport(#[from] Box<dyn std::error::Error + Send + Sync + 'static>), | ||
| 16 | #[error(transparent)] | 23 | #[error(transparent)] |
| 17 | Handshake(#[from] git_protocol::fetch::handshake::Error), | 24 | Handshake(#[from] git_protocol::fetch::handshake::Error), |
| 18 | #[error("The object format {format:?} as used by the remote is unsupported")] | 25 | #[error("The object format {format:?} as used by the remote is unsupported")] |
| @@ -139,6 +146,7 @@ where · 120 unmodified lines | |||
| 139 | extra_parameters: Vec<(String, Option<String>)>, | 146 | extra_parameters: Vec<(String, Option<String>)>, |
| 140 | ) -> Result<HandshakeWithRefs, Error> { | 147 | ) -> Result<HandshakeWithRefs, Error> { |
| 141 | let mut credentials_storage; | 148 | let mut credentials_storage; |
| 149 | let url = self.transport.to_url(); | ||
| 142 | let authenticate = match self.authenticate.as_mut() { | 150 | let authenticate = match self.authenticate.as_mut() { |
| 143 | Some(f) => f, | 151 | Some(f) => f, |
| 144 | None => { | 152 | None => { |
| @@ -146,13 +154,25 @@ where · 1 unmodified line | |||
| 146 | .remote | 154 | .remote |
| 147 | .url(Direction::Fetch) | 155 | .url(Direction::Fetch) |
| 148 | .map(ToOwned::to_owned) | 156 | .map(ToOwned::to_owned) |
| 149 | .unwrap_or_else(|| { | 157 | .unwrap_or_else(|| git_url::parse(url.as_ref()).expect("valid URL to be provided by transport")); |
| 150 | git_url::parse(self.transport.to_url().as_ref()).expect("valid URL to be provided by transport") | ||
| 151 | }); | ||
| 152 | credentials_storage = self.configured_credentials(url)?; | 158 | credentials_storage = self.configured_credentials(url)?; |
| 153 | &mut credentials_storage | 159 | &mut credentials_storage |
| 154 | } | 160 | } |
| 155 | }; | 161 | }; |
| 162 | |||
| 163 | if self.transport_config.is_none() { | ||
| 164 | self.transport_config = | ||
| 165 | self.remote | ||
| 166 | .repo | ||
| 167 | .transport_options(url.as_ref()) | ||
| 168 | .map_err(|err| Error::GatherTransportConfig { | ||
| 169 | source: err, | ||
| 170 | url: url.into_owned(), | ||
| 171 | })?; | ||
| 172 | } | ||
| 173 | if let Some(config) = self.transport_config.as_ref() { | ||
| 174 | self.transport.configure(&**config)?; | ||
| 175 | } | ||
| 156 | let mut outcome = | 176 | let mut outcome = |
| 157 | git_protocol::fetch::handshake(&mut self.transport, authenticate, extra_parameters, &mut self.progress) | 177 | git_protocol::fetch::handshake(&mut self.transport, authenticate, extra_parameters, &mut self.progress) |
| 158 | .await?; | 178 | .await?; |
| @@ -6,11 +6,11 @@ use std::convert::{TryFrom, TryInto}; · 5 unmodified lines | |||
| 6 | 6 | ||
| 7 | impl crate::Repository { | 7 | impl crate::Repository { |
| 8 | /// Produce configuration suitable for `url`, as differentiated by its protocol/scheme, to be passed to a transport instance via | 8 | /// Produce configuration suitable for `url`, as differentiated by its protocol/scheme, to be passed to a transport instance via |
| 9 | /// [configure()][git_transport::client::TransportWithoutIO::configure()]. | 9 | /// [configure()][git_transport::client::TransportWithoutIO::configure()] (via `&**config` to pass the contained `Any` and not the `Box`). |
| 10 | /// `None` is returned if there is no known configuration. | 10 | /// `None` is returned if there is no known configuration. |
| 11 | /// | 11 | /// |
| 12 | /// Note that the caller may cast the instance themselves to modify it before passing it on. | 12 | /// Note that the caller may cast the instance themselves to modify it before passing it on. |
| 13 | pub fn transport_config<'a>( | 13 | pub fn transport_options<'a>( |
| 14 | &self, | 14 | &self, |
| 15 | url: impl Into<&'a BStr>, | 15 | url: impl Into<&'a BStr>, |
| 16 | ) -> Result<Option<Box<dyn Any>>, crate::config::transport::Error> { | 16 | ) -> Result<Option<Box<dyn Any>>, crate::config::transport::Error> { |
| @@ -2,4 +2,4 @@ mod config_snapshot; · 1 unmodified line | |||
| 2 | mod identity; | 2 | mod identity; |
| 3 | mod remote; | 3 | mod remote; |
| 4 | #[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))] | 4 | #[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))] |
| 5 | mod transport_config; | 5 | mod transport_options; |
| @@ -23,7 +23,7 @@ mod http { · 22 unmodified lines | |||
| 23 | fn simple_configuration() { | 23 | fn simple_configuration() { |
| 24 | let repo = repo("http-config"); | 24 | let repo = repo("http-config"); |
| 25 | let http_config = repo | 25 | let http_config = repo |
| 26 | .transport_config("https://example.com/does/not/matter") | 26 | .transport_options("https://example.com/does/not/matter") |
| 27 | .expect("valid configuration") | 27 | .expect("valid configuration") |
| 28 | .expect("configuration available for http"); | 28 | .expect("configuration available for http"); |
| 29 | let git_transport::client::http::Options { | 29 | let git_transport::client::http::Options { |