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 (11 of 12 files). Show all

Unified · Split

11 files
  1. git-protocol/src/fetch/blocking_io/arguments.rs +6 −2
  2. git-protocol/src/fetch/blocking_io/command.rs +0 −0
  3. git-protocol/src/fetch/blocking_io/delegate.rs +0 −0
  4. git-protocol/src/fetch/blocking_io/mod.rs +244 −0
  5. git-protocol/src/fetch/blocking_io/refs.rs +2 −0
  6. git-protocol/src/fetch/blocking_io/response.rs +1 −1
  7. git-protocol/src/fetch/blocking_io/tests/arguments.rs +2 −2
  8. git-protocol/src/fetch/blocking_io/tests/command.rs +12 −9
  9. git-protocol/src/fetch/blocking_io/tests/mod.rs +0 −0
  10. git-protocol/src/fetch/blocking_io/tests/refs.rs +4 −2
  11. git-protocol/src/fetch/mod.rs +12 −231
git-protocol/src/fetch/blocking_io/arguments.rsrenamed from git-protocol/src/fetch/arguments.rs+6−2
@@ -1,10 +1,14 @@
1use crate::fetch::{command::Feature, Command};1use std::{fmt, io::Write};
2
2use bstr::{BStr, BString, ByteSlice};3use bstr::{BStr, BString, ByteSlice};
4
3use git_transport::{5use git_transport::{
4 client::{self, TransportV2Ext},6 client::{self, TransportV2Ext},
5 Protocol,7 Protocol,
6};8};
7use std::{fmt, io::Write};9
10use crate::fetch::blocking_io::command::Feature;
11use crate::fetch::Command;
8 12
9/// The arguments passed to a server command.13/// The arguments passed to a server command.
10pub struct Arguments {14pub struct Arguments {
git-protocol/src/fetch/blocking_io/command.rsrenamed from git-protocol/src/fetch/command.rs+0−0

File renamed without changes.

git-protocol/src/fetch/blocking_io/delegate.rsrenamed from git-protocol/src/fetch/delegate.rs+0−0

File renamed without changes.

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

File renamed without changes.

git-protocol/src/fetch/blocking_io/tests/refs.rsrenamed from git-protocol/src/fetch/tests/refs.rs+4−2
@@ -1,7 +1,9 @@
1use super::super::{refs, InternalRef, Ref};
2use git_transport::client;
3use std::io;1use std::io;
4 2
3use git_transport::client;
4
5use crate::fetch::{blocking_io::refs::InternalRef, refs, Ref};
6
5fn oid(hex_sha: &str) -> git_hash::ObjectId {7fn oid(hex_sha: &str) -> git_hash::ObjectId {
6 git_hash::ObjectId::from_hex(hex_sha.as_bytes()).expect("valid input")8 git_hash::ObjectId::from_hex(hex_sha.as_bytes()).expect("valid input")
7}9}
git-protocol/src/fetch/mod.rs+12−231
@@ -1,237 +1,18 @@
1use crate::credentials;1#[cfg(feature = "blocking-client")]
2use git_features::{progress, progress::Progress};2mod blocking_io;
3use git_transport::{3#[cfg(feature = "blocking-client")]
4 client::{self, SetServiceResponse, TransportV2Ext},4pub use blocking_io::{
5 Service,5 arguments::Arguments,
6 command::Command,
7 delegate::{Action, Delegate},
8 fetch, refs,
9 refs::Ref,
10 response,
11 response::Response,
12 Error,
6};13};
7use quick_error::quick_error;
8use std::io;
9
10mod refs;
11use refs::InternalRef;
12pub use refs::Ref;
13
14mod command;
15pub use command::Command;
16
17mod arguments;
18pub use arguments::Arguments;
19
20///
21pub mod response;
22pub use response::Response;
23
24mod delegate;
25pub use delegate::{Action, Delegate};
26
27#[cfg(test)]
28mod tests;
29
30quick_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}
65 14
66/// Returns the name of the agent as key-value pair, commonly used in HTTP headers.15/// Returns the name of the agent as key-value pair, commonly used in HTTP headers.
67pub fn agent() -> (&'static str, Option<&'static str>) {16pub fn agent() -> (&'static str, Option<&'static str>) {
68 ("agent", Some(concat!("git/oxide-", env!("CARGO_PKG_VERSION"))))17 ("agent", Some(concat!("git/oxide-", env!("CARGO_PKG_VERSION"))))
69}18}
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`.
78pub 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>
84where
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
227fn 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}