diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
commit | 10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch) | |
tree | bdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/gix-protocol/src/command | |
parent | Releasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff) | |
download | rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip |
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix-protocol/src/command')
-rw-r--r-- | vendor/gix-protocol/src/command/mod.rs | 214 | ||||
-rw-r--r-- | vendor/gix-protocol/src/command/tests.rs | 156 |
2 files changed, 370 insertions, 0 deletions
diff --git a/vendor/gix-protocol/src/command/mod.rs b/vendor/gix-protocol/src/command/mod.rs new file mode 100644 index 000000000..d560220d0 --- /dev/null +++ b/vendor/gix-protocol/src/command/mod.rs @@ -0,0 +1,214 @@ +//! V2 command abstraction to validate invocations and arguments, like a database of what we know about them. +use std::borrow::Cow; + +use super::Command; + +/// A key value pair of values known at compile time. +pub type Feature = (&'static str, Option<Cow<'static, str>>); + +impl Command { + /// Produce the name of the command as known by the server side. + pub fn as_str(&self) -> &'static str { + match self { + Command::LsRefs => "ls-refs", + Command::Fetch => "fetch", + } + } +} + +#[cfg(any(test, feature = "async-client", feature = "blocking-client"))] +mod with_io { + use bstr::{BString, ByteSlice}; + use gix_transport::client::Capabilities; + + use crate::{command::Feature, Command}; + + impl Command { + /// Only V2 + fn all_argument_prefixes(&self) -> &'static [&'static str] { + match self { + Command::LsRefs => &["symrefs", "peel", "ref-prefix ", "unborn"], + Command::Fetch => &[ + "want ", // hex oid + "have ", // hex oid + "done", + "thin-pack", + "no-progress", + "include-tag", + "ofs-delta", + // Shallow feature/capability + "shallow ", // hex oid + "deepen ", // commit depth + "deepen-relative", + "deepen-since ", // time-stamp + "deepen-not ", // rev + // filter feature/capability + "filter ", // filter-spec + // ref-in-want feature + "want-ref ", // ref path + // sideband-all feature + "sideband-all", + // packfile-uris feature + "packfile-uris ", // protocols + // wait-for-done feature + "wait-for-done", + ], + } + } + + fn all_features(&self, version: gix_transport::Protocol) -> &'static [&'static str] { + match self { + Command::LsRefs => &[], + Command::Fetch => match version { + gix_transport::Protocol::V1 => &[ + "multi_ack", + "thin-pack", + "side-band", + "side-band-64k", + "ofs-delta", + "shallow", + "deepen-since", + "deepen-not", + "deepen-relative", + "no-progress", + "include-tag", + "multi_ack_detailed", + "allow-tip-sha1-in-want", + "allow-reachable-sha1-in-want", + "no-done", + "filter", + ], + gix_transport::Protocol::V2 => &[ + "shallow", + "filter", + "ref-in-want", + "sideband-all", + "packfile-uris", + "wait-for-done", + ], + }, + } + } + + /// Compute initial arguments based on the given `features`. They are typically provided by the `default_features(…)` method. + /// Only useful for V2 + pub(crate) fn initial_arguments(&self, features: &[Feature]) -> Vec<BString> { + match self { + Command::Fetch => ["thin-pack", "ofs-delta"] + .iter() + .map(|s| s.as_bytes().as_bstr().to_owned()) + .chain( + [ + "sideband-all", + /* "packfile-uris" */ // packfile-uris must be configurable and can't just be used. Some servers advertise it and reject it later. + ] + .iter() + .filter(|f| features.iter().any(|(sf, _)| sf == *f)) + .map(|f| f.as_bytes().as_bstr().to_owned()), + ) + .collect(), + Command::LsRefs => vec![b"symrefs".as_bstr().to_owned(), b"peel".as_bstr().to_owned()], + } + } + + /// Turns on all modern features for V1 and all supported features for V2, returning them as a vector of features. + /// Note that this is the basis for any fetch operation as these features fulfil basic requirements and reasonably up-to-date servers. + pub fn default_features( + &self, + version: gix_transport::Protocol, + server_capabilities: &Capabilities, + ) -> Vec<Feature> { + match self { + Command::Fetch => match version { + gix_transport::Protocol::V1 => { + let has_multi_ack_detailed = server_capabilities.contains("multi_ack_detailed"); + let has_sideband_64k = server_capabilities.contains("side-band-64k"); + self.all_features(version) + .iter() + .copied() + .filter(|feature| match *feature { + "side-band" if has_sideband_64k => false, + "multi_ack" if has_multi_ack_detailed => false, + "no-progress" => false, + feature => server_capabilities.contains(feature), + }) + .map(|s| (s, None)) + .collect() + } + gix_transport::Protocol::V2 => { + let supported_features: Vec<_> = server_capabilities + .iter() + .find_map(|c| { + if c.name() == Command::Fetch.as_str() { + c.values().map(|v| v.map(|f| f.to_owned()).collect()) + } else { + None + } + }) + .unwrap_or_default(); + self.all_features(version) + .iter() + .copied() + .filter(|feature| supported_features.iter().any(|supported| supported == feature)) + .map(|s| (s, None)) + .collect() + } + }, + Command::LsRefs => vec![], + } + } + /// Panics if the given arguments and features don't match what's statically known. It's considered a bug in the delegate. + pub(crate) fn validate_argument_prefixes_or_panic( + &self, + version: gix_transport::Protocol, + server: &Capabilities, + arguments: &[BString], + features: &[Feature], + ) { + let allowed = self.all_argument_prefixes(); + for arg in arguments { + if allowed.iter().any(|allowed| arg.starts_with(allowed.as_bytes())) { + continue; + } + panic!("{}: argument {} is not known or allowed", self.as_str(), arg); + } + match version { + gix_transport::Protocol::V1 => { + for (feature, _) in features { + if server + .iter() + .any(|c| feature.starts_with(c.name().to_str_lossy().as_ref())) + { + continue; + } + panic!("{}: capability {} is not supported", self.as_str(), feature); + } + } + gix_transport::Protocol::V2 => { + let allowed = server + .iter() + .find_map(|c| { + if c.name() == self.as_str().as_bytes().as_bstr() { + c.values().map(|v| v.map(|f| f.to_string()).collect::<Vec<_>>()) + } else { + None + } + }) + .unwrap_or_default(); + for (feature, _) in features { + if allowed.iter().any(|allowed| feature == allowed) { + continue; + } + match *feature { + "agent" => {} + _ => panic!("{}: V2 feature/capability {} is not supported", self.as_str(), feature), + } + } + } + } + } + } +} + +#[cfg(test)] +mod tests; diff --git a/vendor/gix-protocol/src/command/tests.rs b/vendor/gix-protocol/src/command/tests.rs new file mode 100644 index 000000000..12c0eb6df --- /dev/null +++ b/vendor/gix-protocol/src/command/tests.rs @@ -0,0 +1,156 @@ +mod v1 { + fn capabilities(input: &str) -> gix_transport::client::Capabilities { + gix_transport::client::Capabilities::from_bytes(format!("\0{input}").as_bytes()) + .expect("valid input capabilities") + .0 + } + + const GITHUB_CAPABILITIES: &str = "multi_ack thin-pack side-band ofs-delta shallow deepen-since deepen-not deepen-relative no-progress include-tag allow-tip-sha1-in-want allow-reachable-sha1-in-want no-done symref=HEAD:refs/heads/main filter agent=git/github-gdf51a71f0236"; + mod fetch { + mod default_features { + use crate::{ + command::tests::v1::{capabilities, GITHUB_CAPABILITIES}, + Command, + }; + + #[test] + fn it_chooses_the_best_multi_ack_and_sideband() { + assert_eq!( + Command::Fetch.default_features( + gix_transport::Protocol::V1, + &capabilities("multi_ack side-band side-band-64k multi_ack_detailed") + ), + &[("side-band-64k", None), ("multi_ack_detailed", None),] + ); + } + + #[test] + fn it_chooses_all_supported_non_stacking_capabilities_and_leaves_no_progress() { + assert_eq!( + Command::Fetch.default_features(gix_transport::Protocol::V1, &capabilities(GITHUB_CAPABILITIES)), + &[ + ("multi_ack", None), + ("thin-pack", None), + ("side-band", None), + ("ofs-delta", None), + ("shallow", None), + ("deepen-since", None), + ("deepen-not", None), + ("deepen-relative", None), + ("include-tag", None), + ("allow-tip-sha1-in-want", None), + ("allow-reachable-sha1-in-want", None), + ("no-done", None), + ("filter", None), + ], + "we don't enforce no-progress" + ); + } + } + } +} + +mod v2 { + use gix_transport::client::Capabilities; + + fn capabilities(command: &str, input: &str) -> Capabilities { + Capabilities::from_lines(format!("version 2\n{command}={input}").into()) + .expect("valid input for V2 capabilities") + } + + mod fetch { + mod default_features { + use crate::{command::tests::v2::capabilities, Command}; + + #[test] + fn all_features() { + assert_eq!( + Command::Fetch.default_features( + gix_transport::Protocol::V2, + &capabilities("fetch", "shallow filter ref-in-want sideband-all packfile-uris") + ), + ["shallow", "filter", "ref-in-want", "sideband-all", "packfile-uris"] + .iter() + .map(|s| (*s, None)) + .collect::<Vec<_>>() + ) + } + } + + mod initial_arguments { + use bstr::ByteSlice; + + use crate::{command::tests::v2::capabilities, Command}; + + #[test] + fn for_all_features() { + assert_eq!( + Command::Fetch.initial_arguments(&Command::Fetch.default_features( + gix_transport::Protocol::V2, + &capabilities("fetch", "shallow filter sideband-all packfile-uris") + )), + ["thin-pack", "ofs-delta", "sideband-all"] + .iter() + .map(|s| s.as_bytes().as_bstr().to_owned()) + .collect::<Vec<_>>(), + "packfile-uris isn't really supported that well and we don't support it either yet" + ) + } + } + } + + mod ls_refs { + mod default_features { + use crate::{command::tests::v2::capabilities, Command}; + + #[test] + fn default_as_there_are_no_features() { + assert_eq!( + Command::LsRefs.default_features( + gix_transport::Protocol::V2, + &capabilities("something-else", "does not matter as there are none") + ), + &[] + ); + } + } + + mod validate { + use bstr::ByteSlice; + + use crate::{command::tests::v2::capabilities, Command}; + + #[test] + fn ref_prefixes_can_always_be_used() { + Command::LsRefs.validate_argument_prefixes_or_panic( + gix_transport::Protocol::V2, + &capabilities("something else", "do-not-matter"), + &[b"ref-prefix hello/".as_bstr().into()], + &[], + ); + } + + #[test] + #[should_panic] + fn unknown_argument() { + Command::LsRefs.validate_argument_prefixes_or_panic( + gix_transport::Protocol::V2, + &capabilities("other", "do-not-matter"), + &[b"definitely-nothing-we-know".as_bstr().into()], + &[], + ); + } + + #[test] + #[should_panic] + fn unknown_feature() { + Command::LsRefs.validate_argument_prefixes_or_panic( + gix_transport::Protocol::V2, + &capabilities("other", "do-not-matter"), + &[], + &[("some-feature-that-does-not-exist", None)], + ); + } + } + } +} |