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/mod.rs (1 of 12 files). Show all

Unified · Split

git-protocol/src/fetch/mod.rs+12−231
@@ -1,237 +1,18 @@
1−use crate::credentials;
2−use git_features::{progress, progress::Progress};
3−use git_transport::{
4− client::{self, SetServiceResponse, TransportV2Ext},
5− Service,
1+#[cfg(feature = "blocking-client")]
2+mod blocking_io;
3+#[cfg(feature = "blocking-client")]
4+pub use blocking_io::{
5+ arguments::Arguments,
6+ command::Command,
7+ delegate::{Action, Delegate},
8+ fetch, refs,
9+ refs::Ref,
10+ response,
11+ response::Response,
12+ Error,
613 };
7−use quick_error::quick_error;
8−use std::io;
9−
10−mod refs;
11−use refs::InternalRef;
12−pub use refs::Ref;
13−
14−mod command;
15−pub use command::Command;
16−
17−mod arguments;
18−pub use arguments::Arguments;
19−
20−///
21−pub mod response;
22−pub use response::Response;
23−
24−mod delegate;
25−pub use delegate::{Action, Delegate};
26−
27−#[cfg(test)]
28−mod tests;
29−
30−quick_error! {
31− /// The error used in [`fetch()`].
32− #[derive(Debug)]
33− #[allow(missing_docs)]
34− pub enum Error {
35− PackIo(err: io::Error) {
36− display("Could not read streaming pack file")
37− from()
38− source(err)
39− }
40− Credentials(err: credentials::Error) {
41− display("Failed to obtain, approve or reject credentials")
42− from()
43− source(err)
44− }
45− Transport(err: client::Error) {
46− display("An error occurred on the transport layer while fetching data")
47− from()
48− source(err)
49− }
50− SymrefWithoutValue {
51− display("A symref 'capability' is expected to have a value")
52− }
53− Ref(err: refs::Error) {
54− display("A reference could not be parsed or invariants were not met")
55− from()
56− source(err)
57− }
58− Response(err: response::Error) {
59− display("The server response could not be parsed")
60− from()
61− source(err)
62− }
63− }
64−}
6514
6615 /// Returns the name of the agent as key-value pair, commonly used in HTTP headers.
6716 pub fn agent() -> (&'static str, Option<&'static str>) {
6817 ("agent", Some(concat!("git/oxide-", env!("CARGO_PKG_VERSION"))))
6918 }
70−
71−/// Perform a 'fetch' operation with the server using `transport`, with `delegate` handling all server interactions.
72−///
73−/// * `authenticate(operation_to_perform)` is used to receive credentials for the connection and potentially store it
74−/// if the server indicates 'permission denied'. Note that not all transport support authentication or authorization.
75−/// * `progress` is used to emit progress messages.
76−///
77−/// _Note_ that depending on the `delegate`, the actual action performed can be `ls-refs`, `clone` or `fetch`.
78−pub fn fetch<F>(
79− mut transport: impl client::Transport,
80− delegate: &mut impl Delegate,
81− mut authenticate: F,
82− mut progress: impl Progress,
83−) -> Result<(), Error>
84−where
85− F: FnMut(credentials::Action<'_>) -> credentials::Result,
86−{
87− let (protocol_version, mut parsed_refs, capabilities, call_ls_refs) = {
88− progress.init(None, progress::steps());
89− progress.set_name("handshake");
90− progress.step();
91− let result = transport.handshake(Service::UploadPack);
92− let SetServiceResponse {
93− actual_protocol,
94− capabilities,
95− refs,
96− } = match result {
97− Ok(v) => Ok(v),
98− Err(client::Error::Io { ref err }) if err.kind() == io::ErrorKind::PermissionDenied => {
99− drop(result); // needed to workaround this: https://github.com/rust-lang/rust/issues/76149
100− let url = transport.to_url();
101− progress.set_name("authentication");
102− let credentials::Outcome { identity, next } =
103− authenticate(credentials::Action::Fill(&url))?.expect("FILL provides an identity");
104− transport.set_identity(identity)?;
105− progress.step();
106− progress.set_name("handshake (authenticated)");
107− match transport.handshake(Service::UploadPack) {
108− Ok(v) => {
109− authenticate(next.approve())?;
110− Ok(v)
111− }
112− // Still no permission? Reject the credentials.
113− Err(client::Error::Io { err }) if err.kind() == io::ErrorKind::PermissionDenied => {
114− authenticate(next.reject())?;
115− Err(client::Error::Io { err })
116− }
117− // Otherwise, do nothing, as we don't know if it actually got to try the credentials.
118− // If they were previously stored, they remain. In the worst case, the user has to enter them again
119− // next time they try.
120− Err(err) => Err(err),
121− }
122− }
123− Err(err) => Err(err),
124− }?;
125−
126− let mut parsed_refs = Vec::<InternalRef>::new();
127− refs::from_capabilities(&mut parsed_refs, capabilities.iter())?;
128−
129− let call_ls_refs = match refs {
130− Some(mut refs) => {
131− assert_eq!(
132− actual_protocol,
133− git_transport::Protocol::V1,
134− "Only V1 auto-responds with refs"
135− );
136− refs::from_v1_refs_received_as_part_of_handshake(&mut parsed_refs, &mut refs)?;
137− false
138− }
139− None => true,
140− };
141− (
142− actual_protocol,
143− parsed_refs.into_iter().map(Into::into).collect::<Vec<Ref>>(),
144− capabilities,
145− call_ls_refs,
146− )
147− }; // this scope is needed, see https://github.com/rust-lang/rust/issues/76149
148−
149− if transport.desired_protocol_version() != protocol_version {
150− progress.info(format!(
151− "server did not support protocol {} and downgraded to {}",
152− transport.desired_protocol_version() as usize,
153− protocol_version as usize,
154− ));
155− }
156−
157− if call_ls_refs {
158− assert_eq!(
159− protocol_version,
160− git_transport::Protocol::V2,
161− "Only V2 needs a separate request to get specific refs"
162− );
163−
164− let ls_refs = Command::LsRefs;
165− let mut ls_features = ls_refs.default_features(protocol_version, &capabilities);
166− let mut ls_args = ls_refs.initial_arguments(&ls_features);
167− delegate.prepare_ls_refs(&capabilities, &mut ls_args, &mut ls_features);
168− ls_refs.validate_argument_prefixes_or_panic(protocol_version, &capabilities, &ls_args, &ls_features);
169−
170− progress.step();
171− progress.set_name("list refs");
172− let mut remote_refs = transport.invoke(
173− ls_refs.as_str(),
174− ls_features.into_iter(),
175− if ls_args.is_empty() {
176− None
177− } else {
178− Some(ls_args.into_iter())
179− },
180− )?;
181− refs::from_v2_refs(&mut parsed_refs, &mut remote_refs)?;
182− }
183−
184− let fetch = Command::Fetch;
185− let mut fetch_features = fetch.default_features(protocol_version, &capabilities);
186− let next = delegate.prepare_fetch(protocol_version, &capabilities, &mut fetch_features, &parsed_refs);
187− fetch.validate_argument_prefixes_or_panic(protocol_version, &capabilities, &[], &fetch_features);
188−
189− if next == Action::Close {
190− transport.close()?;
191− return Ok(());
192− }
193−
194− Response::check_required_features(protocol_version, &fetch_features)?;
195− let sideband_all = fetch_features.iter().any(|(n, _)| *n == "sideband-all");
196− let mut arguments = Arguments::new(protocol_version, fetch_features);
197− let mut previous_response = None::<Response>;
198− let mut round = 1;
199− 'negotiation: loop {
200− progress.step();
201− progress.set_name(format!("negotiate (round {})", round));
202− round += 1;
203− let action = delegate.negotiate(&parsed_refs, &mut arguments, previous_response.as_ref());
204− let mut reader = arguments.send(&mut transport, action == Action::Close)?;
205− if sideband_all {
206− setup_remote_progress(&mut progress, &mut reader);
207− }
208− let response = Response::from_line_reader(protocol_version, &mut reader)?;
209− previous_response = if response.has_pack() {
210− progress.step();
211− progress.set_name("receiving pack");
212− if !sideband_all {
213− setup_remote_progress(&mut progress, &mut reader);
214− }
215− delegate.receive_pack(reader, progress, &parsed_refs, &response)?;
216− break 'negotiation;
217− } else {
218− match action {
219− Action::Close => break 'negotiation,
220− Action::Continue => Some(response),
221− }
222− }
223− }
224− Ok(())
225−}
226−
227−fn setup_remote_progress(
228− progress: &mut impl Progress,
229− reader: &mut Box<dyn git_transport::client::ExtendedBufRead + '_>,
230−) {
231− reader.set_progress_handler(Some(Box::new({
232− let mut remote_progress = progress.add_child("remote");
233− move |is_err: bool, data: &[u8]| {
234− crate::RemoteProgress::translate_to_progress(is_err, data, &mut remote_progress)
235− }
236− }) as git_transport::client::HandleProgress));
237−}