node: Send special `done` packet to end protocol

This commit is contained in:
Alexis Sellier 2023-02-07 10:13:41 +01:00
parent f5f36191d9
commit d59c422b9d
No known key found for this signature in database
1 changed files with 21 additions and 6 deletions

View File

@ -136,10 +136,7 @@ impl<G: Signer + EcSign + 'static> Worker<G> {
let result = self.fetch(fetch, &mut tunnel);
let mut session = tunnel.into_session();
// A flush after all commands have been sent is optional; we do it because we're not
// closing the connection and therefore there's no other way for the server to know
// we're done sending commands.
if let Err(err) = pktline::flush(&mut session) {
if let Err(err) = pktline::done(&mut session) {
log::error!(target: "worker", "Fetch error: {err}");
}
if let Err(err) = &result {
@ -284,6 +281,10 @@ impl<G: Signer + EcSign + 'static> Worker<G> {
return Err(e.into());
}
if let Err(e) = stream_r.read_pktlines(&mut daemon_w, &mut buffer) {
// Triggered by a [`pktline::DONE_PKT`] packet.
if e.kind() == io::ErrorKind::UnexpectedEof {
break;
}
log::error!(target: "worker", "Remote returned error for {}: {e}", fetch.rid);
return Err(e.into());
@ -355,9 +356,20 @@ mod pktline {
pub const FLUSH_PKT: &[u8; HEADER_LEN] = b"0000";
pub const DELIM_PKT: &[u8; HEADER_LEN] = b"0001";
pub const RESPONSE_END_PKT: &[u8; HEADER_LEN] = b"0002";
/// When the remote `fetch` exits, it sends a special `done` packet which triggers
/// an EOF. This `done` packet is not part of the git protocol, and so is
/// not sent to the deamon.
pub const DONE_PKT: &[u8; HEADER_LEN] = b"done";
pub fn flush<W: io::Write>(w: &mut W) -> io::Result<()> {
write!(w, "0000")
/// Send a special `done` packet. Since the git protocol is tunneled over an existing
/// connection, we can't signal the end of the protocol via the usual means, which is
/// to close the connection and trigger an EOF on the other side. Git also doesn't have
/// any special message we can send to signal the end of the protocol. Hence, we there's
/// no other way for the server to know that we're done sending commands than to send a
/// message that is not part of the git protocol. This message can then be processed by
/// the remote worker to end the protocol.
pub fn done<W: io::Write>(w: &mut W) -> io::Result<()> {
w.write_all(DONE_PKT)
}
pub struct Reader<'a, R> {
@ -387,6 +399,9 @@ mod pktline {
pub fn read_pktline(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.read_exact(&mut buf[..HEADER_LEN])?;
if &buf[..HEADER_LEN] == DONE_PKT {
return Err(io::ErrorKind::UnexpectedEof.into());
}
if &buf[..HEADER_LEN] == FLUSH_PKT
|| &buf[..HEADER_LEN] == DELIM_PKT
|| &buf[..HEADER_LEN] == RESPONSE_END_PKT