gix_protocol/handshake/
function.rs1use gix_features::{progress, progress::Progress};
2use gix_transport::{client, Service};
3use maybe_async::maybe_async;
4
5use super::Error;
6#[cfg(feature = "async-client")]
7use crate::transport::client::async_io::{SetServiceResponse, Transport};
8#[cfg(feature = "blocking-client")]
9use crate::transport::client::blocking_io::{SetServiceResponse, Transport};
10use crate::Handshake;
11use crate::{credentials, handshake::refs};
12
13#[allow(clippy::result_large_err)]
19#[maybe_async]
20pub async fn handshake<AuthFn, T>(
21 mut transport: T,
22 service: Service,
23 mut authenticate: AuthFn,
24 extra_parameters: Vec<(String, Option<String>)>,
25 progress: &mut impl Progress,
26) -> Result<Handshake, Error>
27where
28 AuthFn: FnMut(credentials::helper::Action) -> credentials::protocol::Result,
29 T: Transport,
30{
31 let _span = gix_features::trace::detail!("gix_protocol::handshake()", service = ?service, extra_parameters = ?extra_parameters);
32 let (server_protocol_version, refs, capabilities) = {
33 progress.init(None, progress::steps());
34 progress.set_name("handshake".into());
35 progress.step();
36
37 let extra_parameters: Vec<_> = extra_parameters
38 .iter()
39 .map(|(k, v)| (k.as_str(), v.as_deref()))
40 .collect();
41 let supported_versions: Vec<_> = transport.supported_protocol_versions().into();
42
43 let result = transport.handshake(service, &extra_parameters).await;
44 let SetServiceResponse {
45 actual_protocol,
46 capabilities,
47 refs,
48 } = match result {
49 Ok(v) => Ok(v),
50 Err(client::Error::Io(ref err)) if err.kind() == std::io::ErrorKind::PermissionDenied => {
51 drop(result); let url = transport.to_url().into_owned();
53 progress.set_name("authentication".into());
54 let credentials::protocol::Outcome { identity, next } =
55 authenticate(credentials::helper::Action::get_for_url(url.clone()))?
56 .ok_or(Error::EmptyCredentials)?;
57 transport.set_identity(identity)?;
58 progress.step();
59 progress.set_name("handshake (authenticated)".into());
60 match transport.handshake(service, &extra_parameters).await {
61 Ok(v) => {
62 authenticate(next.store())?;
63 Ok(v)
64 }
65 Err(client::Error::Io(err)) if err.kind() == std::io::ErrorKind::PermissionDenied => {
67 authenticate(next.erase())?;
68 return Err(Error::InvalidCredentials { url, source: err });
69 }
70 Err(err) => Err(err),
74 }
75 }
76 Err(err) => Err(err),
77 }?;
78
79 if !supported_versions.is_empty() && !supported_versions.contains(&actual_protocol) {
80 return Err(Error::TransportProtocolPolicyViolation {
81 actual_version: actual_protocol,
82 });
83 }
84
85 let parsed_refs = match refs {
86 Some(mut refs) => {
87 assert!(
88 matches!(
89 actual_protocol,
90 gix_transport::Protocol::V0 | gix_transport::Protocol::V1
91 ),
92 "Only V(0|1) auto-responds with refs"
93 );
94 Some(
95 refs::from_v1_refs_received_as_part_of_handshake_and_capabilities(&mut refs, capabilities.iter())
96 .await?,
97 )
98 }
99 None => None,
100 };
101 (actual_protocol, parsed_refs, capabilities)
102 }; let (refs, v1_shallow_updates) = refs
105 .map(|(refs, shallow)| (Some(refs), Some(shallow)))
106 .unwrap_or_default();
107
108 Ok(Handshake {
109 server_protocol_version,
110 refs,
111 v1_shallow_updates,
112 capabilities,
113 })
114}