gunnar_sendpack/
driver.rs1use std::io::{Read, Write};
34
35use gix_hash::Kind;
36use gix_packetline::blocking_io::encode;
37use gix_packetline::decode::{self, Stream};
38use gix_packetline::{Channel, PacketLineRef};
39
40use crate::advertisement::{self, Advertisement};
41use crate::command::{self, PushCommand};
42use crate::error::{Error, Result};
43use crate::options::{self, SendPackOptions};
44use crate::report::{self, PushReport};
45use crate::transport::Transport;
46
47struct PktReader<'a> {
56 inner: &'a mut dyn Read,
57 buffer: Vec<u8>,
58 consumed: usize,
59 eof: bool,
60}
61
62impl<'a> PktReader<'a> {
63 fn new(inner: &'a mut dyn Read) -> Self {
64 PktReader {
65 inner,
66 buffer: Vec::new(),
67 consumed: 0,
68 eof: false,
69 }
70 }
71
72 fn next(&mut self) -> Result<Option<Line>> {
75 loop {
76 if self.consumed < self.buffer.len() {
77 match decode::streaming(&self.buffer[self.consumed..]) {
78 Ok(Stream::Complete {
79 line,
80 bytes_consumed,
81 }) => {
82 let out = Line::from(line);
83 self.consumed += bytes_consumed;
84 if self.consumed > 64 * 1024 {
89 self.buffer.drain(..self.consumed);
90 self.consumed = 0;
91 }
92 return Ok(Some(out));
93 }
94 Ok(Stream::Incomplete { .. }) => {}
95 Err(err) => return Err(Error::protocol(format!("malformed pkt-line: {err}"))),
96 }
97 }
98 if self.eof {
99 let pending = self.buffer.len() - self.consumed;
100 if pending > 0 {
101 return Err(Error::protocol(format!(
102 "stream ended mid-packet with {pending} bytes buffered"
103 )));
104 }
105 return Ok(None);
106 }
107 let mut chunk = [0u8; 8192];
108 let n = self.inner.read(&mut chunk)?;
109 if n == 0 {
110 self.eof = true;
111 } else {
112 self.buffer.extend_from_slice(&chunk[..n]);
113 }
114 }
115 }
116
117 fn payloads_until_flush(&mut self) -> Result<Vec<Vec<u8>>> {
123 let mut out = Vec::new();
124 while let Some(line) = self.next()? {
125 match line {
126 Line::Flush => return Ok(out),
127 Line::Data(d) => out.push(d),
128 other => {
131 return Err(Error::protocol(format!(
132 "unexpected control packet in a receive-pack stream: {other:?}"
133 )))
134 }
135 }
136 }
137 Ok(out)
138 }
139}
140
141#[derive(Debug)]
143enum Line {
144 Data(Vec<u8>),
145 Flush,
146 Delimiter,
147 ResponseEnd,
148}
149
150impl From<PacketLineRef<'_>> for Line {
151 fn from(line: PacketLineRef<'_>) -> Self {
152 match line {
153 PacketLineRef::Data(d) => Line::Data(d.to_vec()),
154 PacketLineRef::Flush => Line::Flush,
155 PacketLineRef::Delimiter => Line::Delimiter,
156 PacketLineRef::ResponseEnd => Line::ResponseEnd,
157 }
158 }
159}
160
161pub fn read_advertisement(reader: &mut dyn Read) -> Result<Advertisement> {
163 let payloads = PktReader::new(reader).payloads_until_flush()?;
164 advertisement::parse(&payloads)
165}
166
167pub fn frame_section(lines: &[Vec<u8>], out: &mut dyn Write) -> Result<()> {
172 for line in lines {
173 encode::text_to_write(line, &mut *out)?;
174 }
175 encode::flush_to_write(&mut *out)?;
176 Ok(())
177}
178
179pub fn encode_command_list(commands: &[PushCommand], caps: &[String]) -> Result<Vec<u8>> {
181 let mut out = Vec::new();
182 frame_section(&command::lines(commands, caps)?, &mut out)?;
183 Ok(out)
184}
185
186pub fn encode_push_options(options: &[String]) -> Result<Vec<u8>> {
188 let mut out = Vec::new();
189 frame_section(&command::push_option_lines(options)?, &mut out)?;
190 Ok(out)
191}
192
193pub fn read_report(reader: &mut dyn Read, side_band: bool) -> Result<PushReport> {
195 let mut pkt = PktReader::new(reader);
196 if !side_band {
197 let payloads = pkt.payloads_until_flush()?;
198 return report::parse(&payloads);
199 }
200
201 let mut band1: Vec<u8> = Vec::new();
206 let mut progress: Vec<String> = Vec::new();
207 let mut errors: Vec<String> = Vec::new();
208 while let Some(line) = pkt.next()? {
209 match line {
210 Line::Flush => break,
211 Line::Data(d) => {
212 let Some((&band, rest)) = d.split_first() else {
213 return Err(Error::protocol(
214 "an empty sideband packet carries no band number",
215 ));
216 };
217 match band {
218 b if b == Channel::Data as u8 => band1.extend_from_slice(rest),
219 b if b == Channel::Progress as u8 => {
220 progress.push(String::from_utf8_lossy(rest).into_owned())
221 }
222 b if b == Channel::Error as u8 => {
223 errors.push(String::from_utf8_lossy(rest).into_owned())
224 }
225 other => {
226 return Err(Error::protocol(format!(
227 "unknown sideband {other} in the push report"
228 )))
229 }
230 }
231 }
232 other => {
233 return Err(Error::protocol(format!(
234 "unexpected control packet in the push report: {other:?}"
235 )))
236 }
237 }
238 }
239
240 let mut cursor = std::io::Cursor::new(band1);
241 let mut inner = PktReader::new(&mut cursor);
242 let lines = inner.payloads_until_flush()?;
243 let mut out = report::parse(&lines)?;
244 out.progress = progress;
245 out.remote_errors = errors;
246 Ok(out)
247}
248
249pub fn send_pack<T, P>(
257 transport: &mut T,
258 advertisement: &Advertisement,
259 commands: &[PushCommand],
260 local_hash_kind: Kind,
261 write_pack: Option<P>,
262 opts: &SendPackOptions,
263) -> Result<PushReport>
264where
265 T: Transport,
266 P: FnOnce(&mut dyn Write) -> Result<()>,
267{
268 let capabilities = options::negotiate(advertisement, commands, local_hash_kind, opts)?;
269 let side_band = capabilities.iter().any(|c| c == "side-band-64k");
270 let expects_report = capabilities
271 .iter()
272 .any(|c| c == "report-status" || c == "report-status-v2");
273
274 let all_deletes = commands.iter().all(PushCommand::is_delete);
275 if all_deletes && write_pack.is_some() {
276 return Err(Error::invalid(
277 "an all-deletes push introduces no objects and must not carry a pack",
278 ));
279 }
280 if !all_deletes && write_pack.is_none() {
281 return Err(Error::invalid(
282 "a push that creates or updates a ref must carry a pack, even an empty one",
283 ));
284 }
285
286 let commands_bytes = encode_command_list(commands, &capabilities)?;
287 let options_bytes = if opts.push_options.is_empty() {
288 Vec::new()
289 } else {
290 encode_push_options(&opts.push_options)?
291 };
292
293 {
294 let (_, writer) = transport.io();
295 writer.write_all(&commands_bytes)?;
296 if !options_bytes.is_empty() {
297 writer.write_all(&options_bytes)?;
298 }
299 if let Some(write_pack) = write_pack {
301 write_pack(writer)?;
302 }
303 writer.flush()?;
304 }
305
306 if !expects_report {
307 return Ok(PushReport {
308 unpack: "ok".to_string(),
309 ..PushReport::default()
310 });
311 }
312 let (reader, _) = transport.io();
313 let mut report = read_report(reader, side_band)?;
314 let commanded: Vec<bstr::BString> = commands.iter().map(|c| c.name.clone()).collect();
320 report.reconcile(&commanded);
321 Ok(report)
322}