From 9918693037dce8aa4bb6f08741b6812923486c18 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 19 Jun 2024 11:26:03 +0200 Subject: Merging upstream version 1.76.0+dfsg1. Signed-off-by: Daniel Baumann --- .../gix-protocol/src/fetch/arguments/async_io.rs | 10 ++++++--- .../src/fetch/arguments/blocking_io.rs | 10 ++++++--- vendor/gix-protocol/src/fetch/arguments/mod.rs | 6 ++++- vendor/gix-protocol/src/fetch/response/async_io.rs | 15 ++++++++++--- .../gix-protocol/src/fetch/response/blocking_io.rs | 19 +++++++++++----- vendor/gix-protocol/src/fetch/tests.rs | 11 +++++---- vendor/gix-protocol/src/fetch_fn.rs | 26 +++++++++++++++------- vendor/gix-protocol/src/handshake/function.rs | 1 + vendor/gix-protocol/src/lib.rs | 6 ++--- vendor/gix-protocol/src/ls_refs.rs | 6 ++++- vendor/gix-protocol/src/remote_progress.rs | 13 +++++------ vendor/gix-protocol/src/util.rs | 3 +++ 12 files changed, 88 insertions(+), 38 deletions(-) (limited to 'vendor/gix-protocol/src') diff --git a/vendor/gix-protocol/src/fetch/arguments/async_io.rs b/vendor/gix-protocol/src/fetch/arguments/async_io.rs index fc876d02c..e92d8faab 100644 --- a/vendor/gix-protocol/src/fetch/arguments/async_io.rs +++ b/vendor/gix-protocol/src/fetch/arguments/async_io.rs @@ -9,7 +9,7 @@ impl Arguments { &mut self, transport: &'a mut T, add_done_argument: bool, - ) -> Result, client::Error> { + ) -> Result + Unpin + 'a>, client::Error> { if self.haves.is_empty() { assert!(add_done_argument, "If there are no haves, is_done must be true."); } @@ -19,8 +19,11 @@ impl Arguments { transport.connection_persists_across_multiple_requests(), add_done_argument, )?; - let mut line_writer = - transport.request(client::WriteMode::OneLfTerminatedLinePerWriteCall, on_into_read)?; + let mut line_writer = transport.request( + client::WriteMode::OneLfTerminatedLinePerWriteCall, + on_into_read, + self.trace, + )?; let had_args = !self.args.is_empty(); for arg in self.args.drain(..) { line_writer.write_all(&arg).await?; @@ -47,6 +50,7 @@ impl Arguments { Command::Fetch.as_str(), self.features.iter().filter(|(_, v)| v.is_some()).cloned(), Some(std::mem::replace(&mut self.args, retained_state).into_iter()), + self.trace, ) .await } diff --git a/vendor/gix-protocol/src/fetch/arguments/blocking_io.rs b/vendor/gix-protocol/src/fetch/arguments/blocking_io.rs index c946d46e1..0852f5a88 100644 --- a/vendor/gix-protocol/src/fetch/arguments/blocking_io.rs +++ b/vendor/gix-protocol/src/fetch/arguments/blocking_io.rs @@ -10,7 +10,7 @@ impl Arguments { &mut self, transport: &'a mut T, add_done_argument: bool, - ) -> Result, client::Error> { + ) -> Result + Unpin + 'a>, client::Error> { if self.haves.is_empty() { assert!(add_done_argument, "If there are no haves, is_done must be true."); } @@ -20,8 +20,11 @@ impl Arguments { transport.connection_persists_across_multiple_requests(), add_done_argument, )?; - let mut line_writer = - transport.request(client::WriteMode::OneLfTerminatedLinePerWriteCall, on_into_read)?; + let mut line_writer = transport.request( + client::WriteMode::OneLfTerminatedLinePerWriteCall, + on_into_read, + self.trace, + )?; let had_args = !self.args.is_empty(); for arg in self.args.drain(..) { line_writer.write_all(&arg)?; @@ -47,6 +50,7 @@ impl Arguments { Command::Fetch.as_str(), self.features.iter().filter(|(_, v)| v.is_some()).cloned(), Some(std::mem::replace(&mut self.args, retained_state).into_iter()), + self.trace, ) } } diff --git a/vendor/gix-protocol/src/fetch/arguments/mod.rs b/vendor/gix-protocol/src/fetch/arguments/mod.rs index 50145bb15..8ad391d5f 100644 --- a/vendor/gix-protocol/src/fetch/arguments/mod.rs +++ b/vendor/gix-protocol/src/fetch/arguments/mod.rs @@ -23,6 +23,8 @@ pub struct Arguments { features_for_first_want: Option>, #[cfg(any(feature = "async-client", feature = "blocking-client"))] version: gix_transport::Protocol, + + trace: bool, } impl Arguments { @@ -194,8 +196,9 @@ impl Arguments { } /// Create a new instance to help setting up arguments to send to the server as part of a `fetch` operation /// for which `features` are the available and configured features to use. + /// If `trace` is `true`, all packetlines received or sent will be passed to the facilities of the `gix-trace` crate. #[cfg(any(feature = "async-client", feature = "blocking-client"))] - pub fn new(version: gix_transport::Protocol, features: Vec) -> Self { + pub fn new(version: gix_transport::Protocol, features: Vec, trace: bool) -> Self { use crate::Command; let has = |name: &str| features.iter().any(|f| f.0 == name); let filter = has("filter"); @@ -242,6 +245,7 @@ impl Arguments { ref_in_want, deepen_since, features_for_first_want, + trace, } } } diff --git a/vendor/gix-protocol/src/fetch/response/async_io.rs b/vendor/gix-protocol/src/fetch/response/async_io.rs index c510a1ad4..547881331 100644 --- a/vendor/gix-protocol/src/fetch/response/async_io.rs +++ b/vendor/gix-protocol/src/fetch/response/async_io.rs @@ -10,7 +10,7 @@ use crate::fetch::{ async fn parse_v2_section( line: &mut String, - reader: &mut (impl client::ExtendedBufRead + Unpin), + reader: &mut (impl client::ExtendedBufRead<'_> + Unpin), res: &mut Vec, parse: impl Fn(&str) -> Result, ) -> Result { @@ -37,10 +37,16 @@ impl Response { /// and if `true` we will keep parsing until we get a pack as the client already signalled to the server that it's done. /// This way of doing things allows us to exploit knowledge about more recent versions of the protocol, which keeps code easier /// and more localized without having to support all the cruft that there is. + /// + /// `wants_to_negotiate` should be `false` for clones which is when we don't have sent any haves. The reason for this flag to exist + /// is to predict how to parse V1 output only, and neither `client_expects_pack` nor `wants_to_negotiate` are relevant for V2. + /// This ugliness is in place to avoid having to resort to an [an even more complex ugliness](https://github.com/git/git/blob/9e49351c3060e1fa6e0d2de64505b7becf157f28/fetch-pack.c#L583-L594) + /// that `git` has to use to predict how many acks are supposed to be read. We also genuinely hope that this covers it all…. pub async fn from_line_reader( version: Protocol, - reader: &mut (impl client::ExtendedBufRead + Unpin), + reader: &mut (impl client::ExtendedBufRead<'_> + Unpin), client_expects_pack: bool, + wants_to_negotiate: bool, ) -> Result { match version { Protocol::V0 | Protocol::V1 => { @@ -89,7 +95,10 @@ impl Response { ); // When the server sends ready, we know there is going to be a pack so no need to stop early. saw_ready |= matches!(acks.last(), Some(Acknowledgement::Ready)); - if let Some(Acknowledgement::Nak) = acks.last().filter(|_| !client_expects_pack && !saw_ready) { + if let Some(Acknowledgement::Nak) = acks.last().filter(|_| !client_expects_pack || !saw_ready) { + if !wants_to_negotiate { + continue; + } break 'lines false; } }; diff --git a/vendor/gix-protocol/src/fetch/response/blocking_io.rs b/vendor/gix-protocol/src/fetch/response/blocking_io.rs index 309f5a7c5..08460ae39 100644 --- a/vendor/gix-protocol/src/fetch/response/blocking_io.rs +++ b/vendor/gix-protocol/src/fetch/response/blocking_io.rs @@ -8,9 +8,9 @@ use crate::fetch::{ Response, }; -fn parse_v2_section( +fn parse_v2_section<'a, T>( line: &mut String, - reader: &mut impl client::ExtendedBufRead, + reader: &mut impl client::ExtendedBufRead<'a>, res: &mut Vec, parse: impl Fn(&str) -> Result, ) -> Result { @@ -37,10 +37,16 @@ impl Response { /// and if `true` we will keep parsing until we get a pack as the client already signalled to the server that it's done. /// This way of doing things allows us to exploit knowledge about more recent versions of the protocol, which keeps code easier /// and more localized without having to support all the cruft that there is. - pub fn from_line_reader( + /// + /// `wants_to_negotiate` should be `false` for clones which is when we don't have sent any haves. The reason for this flag to exist + /// is to predict how to parse V1 output only, and neither `client_expects_pack` nor `wants_to_negotiate` are relevant for V2. + /// This ugliness is in place to avoid having to resort to an [an even more complex ugliness](https://github.com/git/git/blob/9e49351c3060e1fa6e0d2de64505b7becf157f28/fetch-pack.c#L583-L594) + /// that `git` has to use to predict how many acks are supposed to be read. We also genuinely hope that this covers it all…. + pub fn from_line_reader<'a>( version: Protocol, - reader: &mut impl client::ExtendedBufRead, + reader: &mut impl client::ExtendedBufRead<'a>, client_expects_pack: bool, + wants_to_negotiate: bool, ) -> Result { match version { Protocol::V0 | Protocol::V1 => { @@ -85,7 +91,10 @@ impl Response { assert_ne!(reader.readline_str(&mut line)?, 0, "consuming a peeked line works"); // When the server sends ready, we know there is going to be a pack so no need to stop early. saw_ready |= matches!(acks.last(), Some(Acknowledgement::Ready)); - if let Some(Acknowledgement::Nak) = acks.last().filter(|_| !client_expects_pack && !saw_ready) { + if let Some(Acknowledgement::Nak) = acks.last().filter(|_| !client_expects_pack || !saw_ready) { + if !wants_to_negotiate { + continue; + } break 'lines false; } }; diff --git a/vendor/gix-protocol/src/fetch/tests.rs b/vendor/gix-protocol/src/fetch/tests.rs index 93cf6e8db..e3c46f57e 100644 --- a/vendor/gix-protocol/src/fetch/tests.rs +++ b/vendor/gix-protocol/src/fetch/tests.rs @@ -6,11 +6,11 @@ mod arguments { use crate::fetch; fn arguments_v1(features: impl IntoIterator) -> fetch::Arguments { - fetch::Arguments::new(Protocol::V1, features.into_iter().map(|n| (n, None)).collect()) + fetch::Arguments::new(Protocol::V1, features.into_iter().map(|n| (n, None)).collect(), false) } fn arguments_v2(features: impl IntoIterator) -> fetch::Arguments { - fetch::Arguments::new(Protocol::V2, features.into_iter().map(|n| (n, None)).collect()) + fetch::Arguments::new(Protocol::V2, features.into_iter().map(|n| (n, None)).collect(), false) } struct Transport { @@ -40,8 +40,9 @@ mod arguments { &mut self, write_mode: WriteMode, on_into_read: MessageKind, + trace: bool, ) -> Result, Error> { - self.inner.request(write_mode, on_into_read) + self.inner.request(write_mode, on_into_read, trace) } fn to_url(&self) -> Cow<'_, BStr> { @@ -97,8 +98,9 @@ mod arguments { &mut self, write_mode: WriteMode, on_into_read: MessageKind, + trace: bool, ) -> Result, Error> { - self.inner.request(write_mode, on_into_read) + self.inner.request(write_mode, on_into_read, trace) } fn to_url(&self) -> Cow<'_, BStr> { @@ -145,6 +147,7 @@ mod arguments { b"does/not/matter".as_bstr().to_owned(), None::<(&str, _)>, gix_transport::client::git::ConnectMode::Process, // avoid header to be sent + false, ), stateful, } diff --git a/vendor/gix-protocol/src/fetch_fn.rs b/vendor/gix-protocol/src/fetch_fn.rs index b350976ef..73c9a753a 100644 --- a/vendor/gix-protocol/src/fetch_fn.rs +++ b/vendor/gix-protocol/src/fetch_fn.rs @@ -41,8 +41,13 @@ pub enum FetchConnection { /// if the server indicates 'permission denied'. Note that not all transport support authentication or authorization. /// * `progress` is used to emit progress messages. /// * `name` is the name of the git client to present as `agent`, like `"my-app (v2.0)"`". +/// * If `trace` is `true`, all packetlines received or sent will be passed to the facilities of the `gix-trace` crate. /// /// _Note_ that depending on the `delegate`, the actual action performed can be `ls-refs`, `clone` or `fetch`. +/// +/// # WARNING - Do not use! +/// +/// As it will hang when having multiple negotiation rounds. #[allow(clippy::result_large_err)] #[maybe_async] // TODO: remove this without losing test coverage - we have the same but better in `gix` and it's @@ -54,6 +59,7 @@ pub async fn fetch( mut progress: P, fetch_mode: FetchConnection, agent: impl Into, + trace: bool, ) -> Result<(), Error> where F: FnMut(credentials::helper::Action) -> credentials::protocol::Result, @@ -87,6 +93,7 @@ where res }, &mut progress, + trace, ) .await? } @@ -99,7 +106,7 @@ where return if matches!(protocol_version, gix_transport::Protocol::V1) || matches!(fetch_mode, FetchConnection::TerminateOnSuccessfulCompletion) { - indicate_end_of_interaction(transport).await.map_err(Into::into) + indicate_end_of_interaction(transport, trace).await.map_err(Into::into) } else { Ok(()) }; @@ -108,7 +115,7 @@ where fetch.validate_argument_prefixes_or_panic(protocol_version, &capabilities, &[], &fetch_features); } Err(err) => { - indicate_end_of_interaction(transport).await?; + indicate_end_of_interaction(transport, trace).await?; return Err(err.into()); } } @@ -116,7 +123,7 @@ where Response::check_required_features(protocol_version, &fetch_features)?; let sideband_all = fetch_features.iter().any(|(n, _)| *n == "sideband-all"); fetch_features.push(("agent", Some(Cow::Owned(agent)))); - let mut arguments = Arguments::new(protocol_version, fetch_features); + let mut arguments = Arguments::new(protocol_version, fetch_features, trace); let mut previous_response = None::; let mut round = 1; 'negotiation: loop { @@ -131,7 +138,8 @@ where let response = Response::from_line_reader( protocol_version, &mut reader, - true, /* hack, telling us we don't want this delegate approach anymore */ + true, /* hack, telling us we don't want this delegate approach anymore */ + false, /* just as much of a hack which causes us to expect a pack immediately */ ) .await?; previous_response = if response.has_pack() { @@ -152,13 +160,15 @@ where if matches!(protocol_version, gix_transport::Protocol::V2) && matches!(fetch_mode, FetchConnection::TerminateOnSuccessfulCompletion) { - indicate_end_of_interaction(transport).await?; + indicate_end_of_interaction(transport, trace).await?; } Ok(()) } -fn setup_remote_progress

(progress: &mut P, reader: &mut Box) -where +fn setup_remote_progress

( + progress: &mut P, + reader: &mut Box + Unpin + '_>, +) where P: NestedProgress, P::SubProgress: 'static, { @@ -168,5 +178,5 @@ where crate::RemoteProgress::translate_to_progress(is_err, data, &mut remote_progress); gix_transport::packetline::read::ProgressAction::Continue } - }) as gix_transport::client::HandleProgress)); + }) as gix_transport::client::HandleProgress<'_>)); } diff --git a/vendor/gix-protocol/src/handshake/function.rs b/vendor/gix-protocol/src/handshake/function.rs index 9e75c18d0..daf405e10 100644 --- a/vendor/gix-protocol/src/handshake/function.rs +++ b/vendor/gix-protocol/src/handshake/function.rs @@ -22,6 +22,7 @@ where AuthFn: FnMut(credentials::helper::Action) -> credentials::protocol::Result, T: client::Transport, { + let _span = gix_features::trace::detail!("gix_protocol::handshake()", service = ?service, extra_parameters = ?extra_parameters); let (server_protocol_version, refs, capabilities) = { progress.init(None, progress::steps()); progress.set_name("handshake".into()); diff --git a/vendor/gix-protocol/src/lib.rs b/vendor/gix-protocol/src/lib.rs index 7f7355711..6b4ee69d2 100644 --- a/vendor/gix-protocol/src/lib.rs +++ b/vendor/gix-protocol/src/lib.rs @@ -4,10 +4,10 @@ //! the actual client implementation. //! ## Feature Flags #![cfg_attr( - feature = "document-features", - cfg_attr(doc, doc = ::document_features::document_features!()) + all(doc, feature = "document-features"), + doc = ::document_features::document_features!() )] -#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(all(doc, feature = "document-features"), feature(doc_cfg, doc_auto_cfg))] #![deny(missing_docs, rust_2018_idioms, unsafe_code)] /// A selector for V2 commands to invoke on the server for purpose of pre-invocation validation. diff --git a/vendor/gix-protocol/src/ls_refs.rs b/vendor/gix-protocol/src/ls_refs.rs index c5b71da93..052576402 100644 --- a/vendor/gix-protocol/src/ls_refs.rs +++ b/vendor/gix-protocol/src/ls_refs.rs @@ -54,6 +54,7 @@ pub(crate) mod function { /// Invoke an ls-refs V2 command on `transport`, which requires a prior handshake that yielded /// server `capabilities`. `prepare_ls_refs(capabilities, arguments, features)` can be used to alter the _ls-refs_. `progress` is used to provide feedback. /// Note that `prepare_ls_refs()` is expected to add the `(agent, Some(name))` to the list of `features`. + /// If `trace` is `true`, all packetlines received or sent will be passed to the facilities of the `gix-trace` crate. #[maybe_async] pub async fn ls_refs( mut transport: impl Transport, @@ -64,7 +65,9 @@ pub(crate) mod function { &mut Vec<(&str, Option>)>, ) -> std::io::Result, progress: &mut impl Progress, + trace: bool, ) -> Result, Error> { + let _span = gix_features::trace::detail!("gix_protocol::ls_refs()", capabilities = ?capabilities); let ls_refs = Command::LsRefs; let mut ls_features = ls_refs.default_features(gix_transport::Protocol::V2, capabilities); let mut ls_args = ls_refs.initial_arguments(&ls_features); @@ -96,12 +99,13 @@ pub(crate) mod function { } else { Some(ls_args.into_iter()) }, + trace, ) .await?; from_v2_refs(&mut remote_refs).await? } Err(err) => { - indicate_end_of_interaction(transport).await?; + indicate_end_of_interaction(transport, trace).await?; return Err(err.into()); } }; diff --git a/vendor/gix-protocol/src/remote_progress.rs b/vendor/gix-protocol/src/remote_progress.rs index b516a06bf..af12cd35e 100644 --- a/vendor/gix-protocol/src/remote_progress.rs +++ b/vendor/gix-protocol/src/remote_progress.rs @@ -2,10 +2,9 @@ use std::convert::TryFrom; use bstr::ByteSlice; use winnow::{ - combinator::opt, - combinator::{preceded, terminated}, + combinator::{opt, preceded, terminated}, prelude::*, - token::{tag, take_till0, take_till1}, + token::{tag, take_till}, }; /// The information usually found in remote progress messages as sent by a git server during @@ -75,7 +74,7 @@ impl<'a> RemoteProgress<'a> { } fn parse_number(i: &mut &[u8]) -> PResult { - take_till0(|c: u8| !c.is_ascii_digit()) + take_till(0.., |c: u8| !c.is_ascii_digit()) .try_map(btoi::btoi) .parse_next(i) } @@ -83,7 +82,7 @@ fn parse_number(i: &mut &[u8]) -> PResult { fn next_optional_percentage(i: &mut &[u8]) -> PResult, ()> { opt(terminated( preceded( - take_till0(|c: u8| c.is_ascii_digit()), + take_till(0.., |c: u8| c.is_ascii_digit()), parse_number.try_map(u32::try_from), ), tag(b"%"), @@ -92,11 +91,11 @@ fn next_optional_percentage(i: &mut &[u8]) -> PResult, ()> { } fn next_optional_number(i: &mut &[u8]) -> PResult, ()> { - opt(preceded(take_till0(|c: u8| c.is_ascii_digit()), parse_number)).parse_next(i) + opt(preceded(take_till(0.., |c: u8| c.is_ascii_digit()), parse_number)).parse_next(i) } fn parse_progress<'i>(line: &mut &'i [u8]) -> PResult, ()> { - let action = take_till1(|c| c == b':').parse_next(line)?; + let action = take_till(1.., |c| c == b':').parse_next(line)?; let percent = next_optional_percentage.parse_next(line)?; let step = next_optional_number.parse_next(line)?; let max = next_optional_number.parse_next(line)?; diff --git a/vendor/gix-protocol/src/util.rs b/vendor/gix-protocol/src/util.rs index a790aebd5..09636d752 100644 --- a/vendor/gix-protocol/src/util.rs +++ b/vendor/gix-protocol/src/util.rs @@ -8,10 +8,12 @@ pub fn agent(name: impl Into) -> String { } /// Send a message to indicate the remote side that there is nothing more to expect from us, indicating a graceful shutdown. +/// If `trace` is `true`, all packetlines received or sent will be passed to the facilities of the `gix-trace` crate. #[cfg(any(feature = "blocking-client", feature = "async-client"))] #[maybe_async::maybe_async] pub async fn indicate_end_of_interaction( mut transport: impl gix_transport::client::Transport, + trace: bool, ) -> Result<(), gix_transport::client::Error> { // An empty request marks the (early) end of the interaction. Only relevant in stateful transports though. if transport.connection_persists_across_multiple_requests() { @@ -19,6 +21,7 @@ pub async fn indicate_end_of_interaction( .request( gix_transport::client::WriteMode::Binary, gix_transport::client::MessageKind::Flush, + trace, )? .into_read() .await?; -- cgit v1.2.3