summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src/repository/config/transport.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix/src/repository/config/transport.rs')
-rw-r--r--vendor/gix/src/repository/config/transport.rs425
1 files changed, 425 insertions, 0 deletions
diff --git a/vendor/gix/src/repository/config/transport.rs b/vendor/gix/src/repository/config/transport.rs
new file mode 100644
index 000000000..dcfbc0bf6
--- /dev/null
+++ b/vendor/gix/src/repository/config/transport.rs
@@ -0,0 +1,425 @@
+#![allow(clippy::result_large_err)]
+use std::any::Any;
+
+use crate::bstr::BStr;
+
+impl crate::Repository {
+ /// Produce configuration suitable for `url`, as differentiated by its protocol/scheme, to be passed to a transport instance via
+ /// [configure()][gix_transport::client::TransportWithoutIO::configure()] (via `&**config` to pass the contained `Any` and not the `Box`).
+ /// `None` is returned if there is no known configuration. If `remote_name` is not `None`, the remote's name may contribute to
+ /// configuration overrides, typically for the HTTP transport.
+ ///
+ /// Note that the caller may cast the instance themselves to modify it before passing it on.
+ ///
+ /// For transports that support proxy authentication, the
+ /// [default authentication method](crate::config::Snapshot::credential_helpers()) will be used with the url of the proxy
+ /// if it contains a user name.
+ #[cfg_attr(
+ not(any(
+ feature = "blocking-http-transport-reqwest",
+ feature = "blocking-http-transport-curl"
+ )),
+ allow(unused_variables)
+ )]
+ pub fn transport_options<'a>(
+ &self,
+ url: impl Into<&'a BStr>,
+ remote_name: Option<&BStr>,
+ ) -> Result<Option<Box<dyn Any>>, crate::config::transport::Error> {
+ let url = gix_url::parse(url.into())?;
+ use gix_url::Scheme::*;
+
+ match &url.scheme {
+ Http | Https => {
+ #[cfg(not(any(
+ feature = "blocking-http-transport-reqwest",
+ feature = "blocking-http-transport-curl"
+ )))]
+ {
+ Ok(None)
+ }
+ #[cfg(any(
+ feature = "blocking-http-transport-reqwest",
+ feature = "blocking-http-transport-curl"
+ ))]
+ {
+ use std::{
+ borrow::Cow,
+ sync::{Arc, Mutex},
+ };
+
+ use gix_transport::client::{
+ http,
+ http::options::{ProxyAuthMethod, SslVersion, SslVersionRangeInclusive},
+ };
+
+ use crate::{
+ config,
+ config::{
+ cache::util::ApplyLeniency,
+ tree::{gitoxide, Key, Remote},
+ },
+ };
+ fn try_cow_to_string(
+ v: Cow<'_, BStr>,
+ lenient: bool,
+ key_str: impl Into<Cow<'static, BStr>>,
+ key: &'static config::tree::keys::String,
+ ) -> Result<Option<String>, config::transport::Error> {
+ key.try_into_string(v)
+ .map_err(|err| config::transport::Error::IllformedUtf8 {
+ source: err,
+ key: key_str.into(),
+ })
+ .map(Some)
+ .with_leniency(lenient)
+ }
+
+ fn cow_bstr(v: &str) -> Cow<'_, BStr> {
+ Cow::Borrowed(v.into())
+ }
+
+ fn proxy_auth_method(
+ value_and_key: Option<(
+ Cow<'_, BStr>,
+ Cow<'static, BStr>,
+ &'static config::tree::http::ProxyAuthMethod,
+ )>,
+ ) -> Result<ProxyAuthMethod, config::transport::Error> {
+ let value = value_and_key
+ .map(|(method, key, key_type)| {
+ key_type.try_into_proxy_auth_method(method).map_err(|err| {
+ config::transport::http::Error::InvalidProxyAuthMethod { source: err, key }
+ })
+ })
+ .transpose()?
+ .unwrap_or_default();
+ Ok(value)
+ }
+
+ fn ssl_version(
+ config: &gix_config::File<'static>,
+ key_str: &'static str,
+ key: &'static config::tree::http::SslVersion,
+ mut filter: fn(&gix_config::file::Metadata) -> bool,
+ lenient: bool,
+ ) -> Result<Option<SslVersion>, config::transport::Error> {
+ debug_assert_eq!(
+ key_str,
+ key.logical_name(),
+ "BUG: hardcoded and generated key names must match"
+ );
+ config
+ .string_filter_by_key(key_str, &mut filter)
+ .filter(|v| !v.is_empty())
+ .map(|v| {
+ key.try_into_ssl_version(v)
+ .map_err(crate::config::transport::http::Error::from)
+ })
+ .transpose()
+ .with_leniency(lenient)
+ .map_err(Into::into)
+ }
+
+ fn proxy(
+ value: Option<(Cow<'_, BStr>, Cow<'static, BStr>, &'static config::tree::keys::String)>,
+ lenient: bool,
+ ) -> Result<Option<String>, config::transport::Error> {
+ Ok(value
+ .and_then(|(v, k, key)| try_cow_to_string(v, lenient, k.clone(), key).transpose())
+ .transpose()?
+ .map(|mut proxy| {
+ if !proxy.trim().is_empty() && !proxy.contains("://") {
+ proxy.insert_str(0, "http://");
+ proxy
+ } else {
+ proxy
+ }
+ }))
+ }
+
+ let mut opts = http::Options::default();
+ let config = &self.config.resolved;
+ let mut trusted_only = self.filter_config_section();
+ let lenient = self.config.lenient_config;
+ opts.extra_headers = {
+ let key = "http.extraHeader";
+ debug_assert_eq!(key, &config::tree::Http::EXTRA_HEADER.logical_name());
+ config
+ .strings_filter_by_key(key, &mut trusted_only)
+ .map(|values| config::tree::Http::EXTRA_HEADER.try_into_extra_header(values))
+ .transpose()
+ .map_err(|err| config::transport::Error::IllformedUtf8 {
+ source: err,
+ key: Cow::Borrowed(key.into()),
+ })?
+ .unwrap_or_default()
+ };
+
+ opts.follow_redirects = {
+ let key = "http.followRedirects";
+
+ config::tree::Http::FOLLOW_REDIRECTS
+ .try_into_follow_redirects(
+ config.string_filter_by_key(key, &mut trusted_only).unwrap_or_default(),
+ || {
+ config
+ .boolean_filter_by_key(key, &mut trusted_only)
+ .transpose()
+ .with_leniency(lenient)
+ },
+ )
+ .map_err(config::transport::http::Error::InvalidFollowRedirects)?
+ };
+
+ opts.low_speed_time_seconds = config
+ .integer_filter_by_key("http.lowSpeedTime", &mut trusted_only)
+ .map(|value| config::tree::Http::LOW_SPEED_TIME.try_into_u64(value))
+ .transpose()
+ .with_leniency(lenient)
+ .map_err(config::transport::http::Error::from)?
+ .unwrap_or_default();
+ opts.low_speed_limit_bytes_per_second = config
+ .integer_filter_by_key("http.lowSpeedLimit", &mut trusted_only)
+ .map(|value| config::tree::Http::LOW_SPEED_LIMIT.try_into_u32(value))
+ .transpose()
+ .with_leniency(lenient)
+ .map_err(config::transport::http::Error::from)?
+ .unwrap_or_default();
+ opts.proxy = proxy(
+ remote_name
+ .and_then(|name| {
+ config
+ .string_filter("remote", Some(name), Remote::PROXY.name, &mut trusted_only)
+ .map(|v| (v, Cow::Owned(format!("remote.{name}.proxy").into()), &Remote::PROXY))
+ })
+ .or_else(|| {
+ let key = "http.proxy";
+ debug_assert_eq!(key, config::tree::Http::PROXY.logical_name());
+ let http_proxy = config
+ .string_filter_by_key(key, &mut trusted_only)
+ .map(|v| (v, cow_bstr(key), &config::tree::Http::PROXY))
+ .or_else(|| {
+ let key = "gitoxide.http.proxy";
+ debug_assert_eq!(key, gitoxide::Http::PROXY.logical_name());
+ config
+ .string_filter_by_key(key, &mut trusted_only)
+ .map(|v| (v, cow_bstr(key), &gitoxide::Http::PROXY))
+ });
+ if url.scheme == Https {
+ http_proxy.or_else(|| {
+ let key = "gitoxide.https.proxy";
+ debug_assert_eq!(key, gitoxide::Https::PROXY.logical_name());
+ config
+ .string_filter_by_key(key, &mut trusted_only)
+ .map(|v| (v, cow_bstr(key), &gitoxide::Https::PROXY))
+ })
+ } else {
+ http_proxy
+ }
+ })
+ .or_else(|| {
+ let key = "gitoxide.http.allProxy";
+ debug_assert_eq!(key, gitoxide::Http::ALL_PROXY.logical_name());
+ config
+ .string_filter_by_key(key, &mut trusted_only)
+ .map(|v| (v, cow_bstr(key), &gitoxide::Http::ALL_PROXY))
+ }),
+ lenient,
+ )?;
+ {
+ let key = "gitoxide.http.noProxy";
+ debug_assert_eq!(key, gitoxide::Http::NO_PROXY.logical_name());
+ opts.no_proxy = config
+ .string_filter_by_key(key, &mut trusted_only)
+ .and_then(|v| {
+ try_cow_to_string(v, lenient, Cow::Borrowed(key.into()), &gitoxide::Http::NO_PROXY)
+ .transpose()
+ })
+ .transpose()?;
+ }
+ opts.proxy_auth_method = proxy_auth_method({
+ let key = "gitoxide.http.proxyAuthMethod";
+ debug_assert_eq!(key, gitoxide::Http::PROXY_AUTH_METHOD.logical_name());
+ config
+ .string_filter_by_key(key, &mut trusted_only)
+ .map(|v| (v, Cow::Borrowed(key.into()), &gitoxide::Http::PROXY_AUTH_METHOD))
+ .or_else(|| {
+ remote_name
+ .and_then(|name| {
+ config
+ .string_filter("remote", Some(name), "proxyAuthMethod", &mut trusted_only)
+ .map(|v| {
+ (
+ v,
+ Cow::Owned(format!("remote.{name}.proxyAuthMethod").into()),
+ &Remote::PROXY_AUTH_METHOD,
+ )
+ })
+ })
+ .or_else(|| {
+ let key = "http.proxyAuthMethod";
+ debug_assert_eq!(key, config::tree::Http::PROXY_AUTH_METHOD.logical_name());
+ config.string_filter_by_key(key, &mut trusted_only).map(|v| {
+ (v, Cow::Borrowed(key.into()), &config::tree::Http::PROXY_AUTH_METHOD)
+ })
+ })
+ })
+ })?;
+ opts.proxy_authenticate = opts
+ .proxy
+ .as_deref()
+ .filter(|url| !url.is_empty())
+ .map(|url| gix_url::parse(url.into()))
+ .transpose()?
+ .filter(|url| url.user().is_some())
+ .map(|url| -> Result<_, config::transport::http::Error> {
+ let (mut cascade, action_with_normalized_url, prompt_opts) =
+ self.config_snapshot().credential_helpers(url)?;
+ Ok((
+ action_with_normalized_url,
+ Arc::new(Mutex::new(move |action| cascade.invoke(action, prompt_opts.clone())))
+ as Arc<Mutex<http::options::AuthenticateFn>>,
+ ))
+ })
+ .transpose()?;
+ opts.connect_timeout = {
+ let key = "gitoxide.http.connectTimeout";
+ config
+ .integer_filter_by_key(key, &mut trusted_only)
+ .map(|v| {
+ debug_assert_eq!(key, gitoxide::Http::CONNECT_TIMEOUT.logical_name());
+ gitoxide::Http::CONNECT_TIMEOUT
+ .try_into_duration(v)
+ .map_err(crate::config::transport::http::Error::from)
+ })
+ .transpose()
+ .with_leniency(lenient)?
+ };
+ {
+ let key = "http.userAgent";
+ opts.user_agent = config
+ .string_filter_by_key(key, &mut trusted_only)
+ .and_then(|v| {
+ try_cow_to_string(
+ v,
+ lenient,
+ Cow::Borrowed(key.into()),
+ &config::tree::Http::USER_AGENT,
+ )
+ .transpose()
+ })
+ .transpose()?
+ .or_else(|| Some(crate::env::agent().into()));
+ }
+
+ {
+ let key = "http.version";
+ opts.http_version = config
+ .string_filter_by_key(key, &mut trusted_only)
+ .map(|v| {
+ config::tree::Http::VERSION
+ .try_into_http_version(v)
+ .map_err(config::transport::http::Error::InvalidHttpVersion)
+ })
+ .transpose()?;
+ }
+
+ {
+ opts.verbose = config
+ .boolean_filter(
+ "gitoxide",
+ Some("http".into()),
+ gitoxide::Http::VERBOSE.name,
+ &mut trusted_only,
+ )
+ .and_then(Result::ok)
+ .unwrap_or_default();
+ }
+
+ let may_use_cainfo = {
+ let key = "http.schannelUseSSLCAInfo";
+ config
+ .boolean_filter_by_key(key, &mut trusted_only)
+ .map(|value| config::tree::Http::SCHANNEL_USE_SSL_CA_INFO.enrich_error(value))
+ .transpose()
+ .with_leniency(lenient)
+ .map_err(config::transport::http::Error::from)?
+ .unwrap_or(true)
+ };
+
+ if may_use_cainfo {
+ let key = "http.sslCAInfo";
+ debug_assert_eq!(key, config::tree::Http::SSL_CA_INFO.logical_name());
+ opts.ssl_ca_info = config
+ .path_filter_by_key(key, &mut trusted_only)
+ .map(|p| {
+ use crate::config::cache::interpolate_context;
+ p.interpolate(interpolate_context(
+ self.install_dir().ok().as_deref(),
+ self.config.home_dir().as_deref(),
+ ))
+ .map(|cow| cow.into_owned())
+ })
+ .transpose()
+ .with_leniency(lenient)
+ .map_err(|err| config::transport::Error::InterpolatePath { source: err, key })?;
+ }
+
+ {
+ opts.ssl_version = ssl_version(
+ config,
+ "http.sslVersion",
+ &config::tree::Http::SSL_VERSION,
+ trusted_only,
+ lenient,
+ )?
+ .map(|v| SslVersionRangeInclusive { min: v, max: v });
+ let min_max = ssl_version(
+ config,
+ "gitoxide.http.sslVersionMin",
+ &gitoxide::Http::SSL_VERSION_MIN,
+ trusted_only,
+ lenient,
+ )
+ .and_then(|min| {
+ ssl_version(
+ config,
+ "gitoxide.http.sslVersionMax",
+ &gitoxide::Http::SSL_VERSION_MAX,
+ trusted_only,
+ lenient,
+ )
+ .map(|max| min.and_then(|min| max.map(|max| (min, max))))
+ })?;
+ if let Some((min, max)) = min_max {
+ let v = opts.ssl_version.get_or_insert(SslVersionRangeInclusive {
+ min: SslVersion::TlsV1_3,
+ max: SslVersion::TlsV1_3,
+ });
+ v.min = min;
+ v.max = max;
+ }
+ }
+
+ #[cfg(feature = "blocking-http-transport-curl")]
+ {
+ let key = "http.schannelCheckRevoke";
+ let schannel_check_revoke = config
+ .boolean_filter_by_key(key, &mut trusted_only)
+ .map(|value| config::tree::Http::SCHANNEL_CHECK_REVOKE.enrich_error(value))
+ .transpose()
+ .with_leniency(lenient)
+ .map_err(config::transport::http::Error::from)?;
+ let backend = gix_protocol::transport::client::http::curl::Options { schannel_check_revoke };
+ opts.backend =
+ Some(Arc::new(Mutex::new(backend)) as Arc<Mutex<dyn Any + Send + Sync + 'static>>);
+ }
+
+ Ok(Some(Box::new(opts)))
+ }
+ }
+ File | Git | Ssh | Ext(_) => Ok(None),
+ }
+ }
+}