History

[git-protocol] move everything into `blocking_io` for later translation…

…into async-io, as usual.

Sebastian Thiel committed 5 years ago

Commit fa033741 parent 0d39b5d12 files · +295 −254Browse files

Showing the changes to git-protocol/src/fetch/blocking_io/command.rs (1 of 12 files). Show all

Unified · Split

git-protocol/src/fetch/blocking_io/command.rs+196−0
@@ -0,0 +1,196 @@
1+use crate::fetch::agent;
2+use bstr::{BString, ByteSlice};
3+use git_transport::client::Capabilities;
4+
5+/// The kind of command to invoke on the server side.
6+#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
7+pub enum Command {
8+ /// List references.
9+ LsRefs,
10+ /// Fetch a pack.
11+ Fetch,
12+}
13+
14+/// A key value pair of values known at compile time.
15+pub type Feature = (&'static str, Option<&'static str>);
16+
17+impl Command {
18+ /// Produce the name of the command as known by the server side.
19+ pub fn as_str(&self) -> &'static str {
20+ match self {
21+ Command::LsRefs => "ls-refs",
22+ Command::Fetch => "fetch",
23+ }
24+ }
25+
26+ /// Only V2
27+ fn all_argument_prefixes(&self) -> &'static [&'static str] {
28+ match self {
29+ Command::LsRefs => &["symrefs", "peel", "ref-prefix "],
30+ Command::Fetch => &[
31+ "want ", // hex oid
32+ "have ", // hex oid
33+ "done",
34+ "thin-pack",
35+ "no-progress",
36+ "include-tag",
37+ "ofs-delta",
38+ // Shallow feature/capability
39+ "shallow ", // hex oid
40+ "deepen ", // commit depth
41+ "deepen-relative",
42+ "deepen-since ", // time-stamp
43+ "deepen-not ", // rev
44+ // filter feature/capability
45+ "filter ", // filter-spec
46+ // ref-in-want feature
47+ "want-ref ", // ref path
48+ "sideband-all",
49+ // packfile-uris feature
50+ "packfile-uris ", // protocols
51+ ],
52+ }
53+ }
54+
55+ fn all_features(&self, version: git_transport::Protocol) -> &'static [&'static str] {
56+ match self {
57+ Command::LsRefs => &[],
58+ Command::Fetch => match version {
59+ git_transport::Protocol::V1 => &[
60+ "multi_ack",
61+ "thin-pack",
62+ "side-band",
63+ "side-band-64k",
64+ "ofs-delta",
65+ "shallow",
66+ "deepen-since",
67+ "deepen-not",
68+ "deepen-relative",
69+ "no-progress",
70+ "include-tag",
71+ "multi_ack_detailed",
72+ "allow-tip-sha1-in-want",
73+ "allow-reachable-sha1-in-want",
74+ "no-done",
75+ "filter",
76+ ],
77+ git_transport::Protocol::V2 => &["shallow", "filter", "ref-in-want", "sideband-all", "packfile-uris"],
78+ },
79+ }
80+ }
81+
82+ /// Compute initial arguments based on the given `features`. They are typically provided by the `default_features(…)` method.
83+ /// Only useful for V2
84+ pub(crate) fn initial_arguments(&self, features: &[Feature]) -> Vec<BString> {
85+ match self {
86+ Command::Fetch => ["thin-pack", "include-tag", "ofs-delta"]
87+ .iter()
88+ .map(|s| s.as_bytes().as_bstr().to_owned())
89+ .chain(
90+ ["sideband-all", "ref-in-want", "packfile-uris"]
91+ .iter()
92+ .filter(|f| features.iter().any(|(sf, _)| sf == *f))
93+ .map(|f| f.as_bytes().as_bstr().to_owned()),
94+ )
95+ .collect(),
96+ Command::LsRefs => vec![b"symrefs".as_bstr().to_owned(), b"peel".as_bstr().to_owned()],
97+ }
98+ }
99+
100+ pub(crate) fn default_features(
101+ &self,
102+ version: git_transport::Protocol,
103+ server_capabilities: &Capabilities,
104+ ) -> Vec<Feature> {
105+ match self {
106+ Command::Fetch => match version {
107+ git_transport::Protocol::V1 => {
108+ let has_multi_ack_detailed = server_capabilities.contains("multi_ack_detailed");
109+ let has_sideband_64k = server_capabilities.contains("side-band-64k");
110+ self.all_features(version)
111+ .iter()
112+ .copied()
113+ .filter(|feature| match *feature {
114+ "side-band" if has_sideband_64k => false,
115+ "multi_ack" if has_multi_ack_detailed => false,
116+ "include-tag" | "no-progress" => false,
117+ feature => server_capabilities.contains(feature),
118+ })
119+ .map(|s| (s, None))
120+ .chain(Some(agent()))
121+ .collect()
122+ }
123+ git_transport::Protocol::V2 => {
124+ let supported_features = server_capabilities
125+ .iter()
126+ .find_map(|c| {
127+ if c.name() == Command::Fetch.as_str().as_bytes().as_bstr() {
128+ c.values().map(|v| v.map(|f| f.to_owned()).collect())
129+ } else {
130+ None
131+ }
132+ })
133+ .unwrap_or_else(Vec::new);
134+ self.all_features(version)
135+ .iter()
136+ .copied()
137+ .filter(|feature| supported_features.iter().any(|supported| supported == feature))
138+ .map(|s| (s, None))
139+ .chain(Some(agent()))
140+ .collect()
141+ }
142+ },
143+ Command::LsRefs => vec![agent()],
144+ }
145+ }
146+ /// Panics if the given arguments and features don't match what's statically known. It's considered a bug in the delegate.
147+ pub(crate) fn validate_argument_prefixes_or_panic(
148+ &self,
149+ version: git_transport::Protocol,
150+ server: &Capabilities,
151+ arguments: &[BString],
152+ features: &[Feature],
153+ ) {
154+ let allowed = self.all_argument_prefixes();
155+ for arg in arguments {
156+ if allowed.iter().any(|allowed| arg.starts_with(allowed.as_bytes())) {
157+ continue;
158+ }
159+ panic!("{}: argument {} is not known or allowed", self.as_str(), arg);
160+ }
161+ match version {
162+ git_transport::Protocol::V1 => {
163+ for (feature, _) in features {
164+ if server
165+ .iter()
166+ .any(|c| feature.starts_with(c.name().to_str_lossy().as_ref()))
167+ {
168+ continue;
169+ }
170+ panic!("{}: capability {} is not supported", self.as_str(), feature);
171+ }
172+ }
173+ git_transport::Protocol::V2 => {
174+ let allowed = server
175+ .iter()
176+ .find_map(|c| {
177+ if c.name() == self.as_str().as_bytes().as_bstr() {
178+ c.values().map(|v| v.map(|f| f.to_string()).collect::<Vec<_>>())
179+ } else {
180+ None
181+ }
182+ })
183+ .unwrap_or_default();
184+ for (feature, _) in features {
185+ if allowed.iter().any(|allowed| feature == allowed) {
186+ continue;
187+ }
188+ match *feature {
189+ "agent" => {}
190+ _ => panic!("{}: V2 feature/capability {} is not supported", self.as_str(), feature),
191+ }
192+ }
193+ }
194+ }
195+ }
196+}