🏡 index : ~doyle/chartered.git

author Jordan Doyle <jordan@doyle.la> 2021-10-08 3:13:17.0 +01:00:00
committer Jordan Doyle <jordan@doyle.la> 2021-10-08 3:13:49.0 +01:00:00
commit
0fe0925fe21384af33e930d11593a05375f20fe1 [patch]
tree
7e29956908ccd87f17e941c299ee79bccdabf829
parent
8a38228920b7edb4e161db96c68b4f84781caa26
download
0fe0925fe21384af33e930d11593a05375f20fe1.tar.gz

Modularise command handling



Diff

 chartered-git/src/main.rs                     | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
 chartered-git/src/command_handlers/fetch.rs   |  51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 chartered-git/src/command_handlers/ls_refs.rs |  31 +++++++++++++++++++++++++++++++
 chartered-git/src/command_handlers/mod.rs     |   2 ++
 4 files changed, 128 insertions(+), 80 deletions(-)

diff --git a/chartered-git/src/main.rs b/chartered-git/src/main.rs
index 3dc69f3..136412f 100644
--- a/chartered-git/src/main.rs
+++ a/chartered-git/src/main.rs
@@ -1,14 +1,16 @@
#![deny(clippy::pedantic)]
mod command_handlers;
mod generators;
mod tree;

#[allow(clippy::missing_errors_doc)]
pub mod git;
mod tree;

use crate::{
    generators::CargoConfig,
    git::{
        codec::{Encoder, GitCodec},
        packfile::{high_level::GitRepository, low_level::PackFile},
        packfile::high_level::GitRepository,
        PktLine,
    },
    tree::Tree,
@@ -249,10 +251,6 @@
        self.input_bytes.extend_from_slice(data);

        Box::pin(async move {
            let mut ls_refs = false;
            let mut fetch = false;
            let mut done = false;

            while let Some(frame) = self.codec.decode(&mut self.input_bytes)? {
                eprintln!("{:#?}", frame);

@@ -265,82 +263,48 @@
                    return Ok((self, session));
                }

                if frame.command.as_ref() == "command=ls-refs".as_bytes() {
                    ls_refs = true;
                } else if frame.command.as_ref() == "command=fetch".as_bytes() {
                    if frame.metadata.iter().any(|v| v.as_ref() == b"done") {
                        done = true;
                    } else {
                        fetch = true;
                let authed = self.authed()?;
                let org_name = self.org_name()?;

                let mut packfile = GitRepository::default();
                let config = CargoConfig::new(
                    Url::parse("http://127.0.0.1:8888/")?,
                    &authed.auth_key,
                    org_name,
                );
                let config = serde_json::to_vec(&config)?;
                packfile.insert(ArrayVec::<_, 0>::new(), "config.json", &config);
                // todo: the whole tree needs caching and then we can filter in code rather than at
                //  the database
                let tree = Tree::build(self.db.clone(), authed.user.id, org_name.to_string()).await;
                tree.write_to_packfile(&mut packfile);

                let (commit_hash, packfile_entries) =
                    packfile.commit("computer", "john@computer.no", "Update crates");

                match frame.command.as_ref() {
                    b"command=ls-refs" => {
                        command_handlers::ls_refs::handle(
                            &mut self,
                            &mut session,
                            channel,
                            frame.metadata,
                            &commit_hash,
                        )
                        .await?
                    }
                    b"command=fetch" => {
                        command_handlers::fetch::handle(
                            &mut self,
                            &mut session,
                            channel,
                            frame.metadata,
                            packfile_entries,
                        )
                        .await?
                    }
                    v => eprintln!("unknown command {:?}", v),
                }
            }

            let authed = self.authed()?;
            let org_name = self.org_name()?;

            if !ls_refs && !fetch && !done {
                return Ok((self, session));
            }

            let mut packfile = GitRepository::default();

            let config = CargoConfig::new(
                Url::parse("http://127.0.0.1:8888/")?,
                &authed.auth_key,
                org_name,
            );
            let config = serde_json::to_vec(&config)?;
            packfile.insert(ArrayVec::<_, 0>::new(), "config.json", &config);

            // todo: the whole tree needs caching and then we can filter in code rather than at
            //  the database
            let tree = Tree::build(self.db.clone(), authed.user.id, org_name.to_string()).await;
            tree.write_to_packfile(&mut packfile);

            let (commit_hash, packfile_entries) =
                packfile.commit("computer", "john@computer.no", "Update crates");

            eprintln!("commit hash: {}", hex::encode(&commit_hash));

            // echo -ne "0014command=ls-refs\n0014agent=git/2.321\n00010009peel\n000csymrefs\n000bunborn\n0014ref-prefix HEAD\n0019ref-prefix refs/HEAD\n001eref-prefix refs/tags/HEAD\n001fref-prefix refs/heads/HEAD\n0021ref-prefix refs/remotes/HEAD\n0026ref-prefix refs/remotes/HEAD/HEAD\n001aref-prefix refs/tags/\n0000"
            // GIT_PROTOCOL=version=2 ssh -o SendEnv=GIT_PROTOCOL git@github.com git-upload-pack '/w4/chartered.git'
            // ''.join([('{:04x}'.format(len(v) + 5)), v, "\n"])
            // echo -ne "0012command=fetch\n0001000ethin-pack\n0010no-progress\n0010include-tag\n000eofs-delta\n0032want f6046cf6372e0d8ab845f6dec1602c303a66ee91\n"
            // sends a 000dpackfile back
            // https://shafiul.github.io/gitbook/7_the_packfile.html
            if ls_refs {
                let commit_hash = hex::encode(&commit_hash);
                self.write(PktLine::Data(
                    format!("{} HEAD symref-target:refs/heads/master\n", commit_hash).as_bytes(),
                ))?;
                self.write(PktLine::Flush)?;
                self.flush(&mut session, channel);
            }

            if fetch {
                self.write(PktLine::Data(b"acknowledgments\n"))?;
                self.write(PktLine::Data(b"ready\n"))?;
                self.write(PktLine::Delimiter)?;
                // self.write(PktLine::Data(b"shallow-info\n"))?;
                // self.write(PktLine::Data(b"unshallow\n"))?;
                done = true;
            }

            if done {
                self.write(PktLine::Data(b"packfile\n"))?;

                self.write(PktLine::SidebandMsg(b"Hello from chartered!\n"))?;
                self.flush(&mut session, channel);

                let packfile = PackFile::new(packfile_entries);
                self.write(PktLine::SidebandData(packfile))?;
                self.write(PktLine::Flush)?;
                self.flush(&mut session, channel);

                session.exit_status_request(channel, 0);
                session.eof(channel);
                session.close(channel);
            }

            Ok((self, session))
diff --git a/chartered-git/src/command_handlers/fetch.rs b/chartered-git/src/command_handlers/fetch.rs
new file mode 100644
index 0000000..3fd0668 100644
--- /dev/null
+++ a/chartered-git/src/command_handlers/fetch.rs
@@ -1,0 +1,51 @@
use bytes::Bytes;
use thrussh::{server::Session, ChannelId};

use crate::{
    git::{
        packfile::low_level::{PackFile, PackFileEntry},
        PktLine,
    },
    Handler,
};

pub(crate) async fn handle(
    handle: &mut Handler,
    session: &mut Session,
    channel: ChannelId,
    metadata: Vec<Bytes>,
    packfile_entries: Vec<PackFileEntry<'_>>,
) -> Result<(), anyhow::Error> {
    // the client sending us `done` in the metadata means they know there's no negotiation
    // required for which commits we need to send, they just want us to send whatever we
    // have.
    let done = metadata.iter().any(|v| v.as_ref() == b"done");

    // the client thinks we can negotiate some commits with them, but we don't want to so
    // we'll just say we've got nothing in common and continue on as we were.
    if !done {
        handle.write(PktLine::Data(b"acknowledgments\n"))?;
        handle.write(PktLine::Data(b"ready\n"))?;
        handle.write(PktLine::Delimiter)?;
    }

    // magic header
    handle.write(PktLine::Data(b"packfile\n"))?;

    // send a welcome message
    handle.write(PktLine::SidebandMsg(b"Hello from chartered!\n"))?;
    handle.flush(session, channel);

    // send the complete packfile
    let packfile = PackFile::new(packfile_entries);
    handle.write(PktLine::SidebandData(packfile))?;
    handle.write(PktLine::Flush)?;
    handle.flush(session, channel);

    // tell the client we exited successfully and close the channel
    session.exit_status_request(channel, 0);
    session.eof(channel);
    session.close(channel);

    Ok(())
}
diff --git a/chartered-git/src/command_handlers/ls_refs.rs b/chartered-git/src/command_handlers/ls_refs.rs
new file mode 100644
index 0000000..9fe41a0 100644
--- /dev/null
+++ a/chartered-git/src/command_handlers/ls_refs.rs
@@ -1,0 +1,31 @@
//! [ls-refs][lsr] is sent from the client when they want to see what refs we have

//! on the server, we're generating our commits on the fly though so we'll just tell

//! them we have a master branch with whatever the generated commit hash is.

//!

//! [ls-ref]: https://git-scm.com/docs/protocol-v2/2.19.0#_ls_refs


use bytes::Bytes;
use thrussh::{server::Session, ChannelId};

use crate::{
    git::{packfile::low_level::HashOutput, PktLine},
    Handler,
};

pub(crate) async fn handle(
    handle: &mut Handler,
    session: &mut Session,
    channel: ChannelId,
    _metadata: Vec<Bytes>,
    commit_hash: &HashOutput,
) -> Result<(), anyhow::Error> {
    let commit_hash = hex::encode(&commit_hash);

    handle.write(PktLine::Data(
        format!("{} HEAD symref-target:refs/heads/master\n", commit_hash).as_bytes(),
    ))?;
    handle.write(PktLine::Flush)?;
    handle.flush(session, channel);

    Ok(())
}
diff --git a/chartered-git/src/command_handlers/mod.rs b/chartered-git/src/command_handlers/mod.rs
new file mode 100644
index 0000000..8406cdb 100644
--- /dev/null
+++ a/chartered-git/src/command_handlers/mod.rs
@@ -1,0 +1,2 @@
pub mod fetch;
pub mod ls_refs;