gix_protocol/fetch/response/
blocking_io.rs

1use std::io;
2
3use gix_transport::{client, Protocol};
4
5use crate::fetch::{
6    response,
7    response::{shallow_update_from_line, Acknowledgement, ShallowUpdate, WantedRef},
8    Response,
9};
10
11fn parse_v2_section<'a, T>(
12    line: &mut String,
13    reader: &mut impl client::ExtendedBufRead<'a>,
14    res: &mut Vec<T>,
15    parse: impl Fn(&str) -> Result<T, response::Error>,
16) -> Result<bool, response::Error> {
17    line.clear();
18    while reader.readline_str(line)? != 0 {
19        res.push(parse(line)?);
20        line.clear();
21    }
22    // End of message, or end of section?
23    Ok(if reader.stopped_at() == Some(client::MessageKind::Delimiter) {
24        // try reading more sections
25        reader.reset(Protocol::V2);
26        false
27    } else {
28        // we are done, there is no pack
29        true
30    })
31}
32
33impl Response {
34    /// Parse a response of the given `version` of the protocol from `reader`.
35    ///
36    /// `client_expects_pack` is only relevant for V1 stateful connections, and if `false`, causes us to stop parsing when seeing `NAK`,
37    /// and if `true` we will keep parsing until we get a pack as the client already signalled to the server that it's done.
38    /// This way of doing things allows us to exploit knowledge about more recent versions of the protocol, which keeps code easier
39    /// and more localized without having to support all the cruft that there is.
40    ///
41    /// `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
42    /// is to predict how to parse V1 output only, and neither `client_expects_pack` nor `wants_to_negotiate` are relevant for V2.
43    /// 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)
44    /// that `git` has to use to predict how many acks are supposed to be read. We also genuinely hope that this covers it all….
45    pub fn from_line_reader<'a>(
46        version: Protocol,
47        reader: &mut impl client::ExtendedBufRead<'a>,
48        client_expects_pack: bool,
49        wants_to_negotiate: bool,
50    ) -> Result<Response, response::Error> {
51        match version {
52            Protocol::V0 | Protocol::V1 => {
53                let mut line = String::new();
54                let mut acks = Vec::<Acknowledgement>::new();
55                let mut shallows = Vec::<ShallowUpdate>::new();
56                let mut saw_ready = false;
57                let has_pack = 'lines: loop {
58                    line.clear();
59                    let peeked_line = match reader.peek_data_line() {
60                        Some(Ok(Ok(line))) => String::from_utf8_lossy(line),
61                        // This special case (hang/block forever) deals with a single NAK being a legitimate EOF sometimes
62                        // Note that this might block forever in stateful connections as there it's not really clear
63                        // if something will be following or not by just looking at the response. Instead you have to know
64                        // [a lot](https://github.com/git/git/blob/9e49351c3060e1fa6e0d2de64505b7becf157f28/fetch-pack.c#L583-L594)
65                        // to deal with this correctly.
66                        // For now this is acceptable, as V2 can be used as a workaround, which also is the default.
67                        Some(Err(err)) if err.kind() == io::ErrorKind::UnexpectedEof => break 'lines false,
68                        Some(Err(err)) => return Err(err.into()),
69                        Some(Ok(Err(err))) => return Err(err.into()),
70                        None => {
71                            // maybe we saw a shallow flush packet, let's reset and retry
72                            debug_assert_eq!(
73                                reader.stopped_at(),
74                                Some(client::MessageKind::Flush),
75                                "If this isn't a flush packet, we don't know what's going on"
76                            );
77                            reader.readline_str(&mut line)?;
78                            reader.reset(Protocol::V1);
79                            match reader.peek_data_line() {
80                                Some(Ok(Ok(line))) => String::from_utf8_lossy(line),
81                                Some(Err(err)) => return Err(err.into()),
82                                Some(Ok(Err(err))) => return Err(err.into()),
83                                None => break 'lines false, // EOF
84                            }
85                        }
86                    };
87
88                    if Response::parse_v1_ack_or_shallow_or_assume_pack(&mut acks, &mut shallows, &peeked_line) {
89                        break 'lines true;
90                    }
91                    assert_ne!(reader.readline_str(&mut line)?, 0, "consuming a peeked line works");
92                    // When the server sends ready, we know there is going to be a pack so no need to stop early.
93                    saw_ready |= matches!(acks.last(), Some(Acknowledgement::Ready));
94                    if let Some(Acknowledgement::Nak) = acks.last().filter(|_| !client_expects_pack || !saw_ready) {
95                        if !wants_to_negotiate {
96                            continue;
97                        }
98                        break 'lines false;
99                    }
100                };
101                Ok(Response {
102                    acks,
103                    shallows,
104                    wanted_refs: vec![],
105                    has_pack,
106                })
107            }
108            Protocol::V2 => {
109                // NOTE: We only read acknowledgements and scrub to the pack file, until we have use for the other features
110                let mut line = String::new();
111                reader.reset(Protocol::V2);
112                let mut acks = Vec::<Acknowledgement>::new();
113                let mut shallows = Vec::<ShallowUpdate>::new();
114                let mut wanted_refs = Vec::<WantedRef>::new();
115                let has_pack = 'section: loop {
116                    line.clear();
117                    if reader.readline_str(&mut line)? == 0 {
118                        return Err(response::Error::Io(io::Error::new(
119                            io::ErrorKind::UnexpectedEof,
120                            "Could not read message headline",
121                        )));
122                    }
123
124                    match line.trim_end() {
125                        "acknowledgments" => {
126                            if parse_v2_section(&mut line, reader, &mut acks, Acknowledgement::from_line)? {
127                                break 'section false;
128                            }
129                        }
130                        "shallow-info" => {
131                            if parse_v2_section(&mut line, reader, &mut shallows, shallow_update_from_line)? {
132                                break 'section false;
133                            }
134                        }
135                        "wanted-refs" => {
136                            if parse_v2_section(&mut line, reader, &mut wanted_refs, WantedRef::from_line)? {
137                                break 'section false;
138                            }
139                        }
140                        "packfile" => {
141                            // what follows is the packfile itself, which can be read with a sideband enabled reader
142                            break 'section true;
143                        }
144                        _ => return Err(response::Error::UnknownSectionHeader { header: line }),
145                    }
146                };
147                Ok(Response {
148                    acks,
149                    shallows,
150                    wanted_refs,
151                    has_pack,
152                })
153            }
154        }
155    }
156}