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