Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
node: Send special `done` packet to end protocol
Alexis Sellier committed 3 years ago
commit d59c422b9d05d48dd4f81d9f2c56d4f1587d7c66
parent f5f36191d9320e7d570b0c5ce29d791878db70e1
1 file changed +22 -7
modified radicle-node/src/worker.rs
@@ -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";
-

-
    pub fn flush<W: io::Write>(w: &mut W) -> io::Result<()> {
-
        write!(w, "0000")
+
    /// 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";
+

+
    /// 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