Skip to content

Commit

Permalink
Split out URI parsing code to improve testability and remove need for…
Browse files Browse the repository at this point in the history
… boxing
  • Loading branch information
w4 committed Jan 3, 2025
1 parent 72801bf commit 2b75eb7
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 56 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ timeago = { version = "0.4.2", default-features = false }
tokio = { version = "1.42", features = ["full", "tracing"] }
tokio-stream = "0.1"
tokio-util = { version = "0.7.10", features = ["io"] }
tower = "0.5"
tower-http = { version = "0.6", features = ["cors", "timeout"] }
tower-layer = "0.3"
tower-service = "0.3"
Expand Down
190 changes: 136 additions & 54 deletions src/methods/repo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@ use std::{

use axum::{
body::Body,
handler::HandlerWithoutStateExt,
handler::Handler,
http::{Request, StatusCode},
response::{IntoResponse, Response},
};
use path_clean::PathClean;
use tower::{util::BoxCloneService, Service};

use self::{
about::handle as handle_about,
Expand All @@ -38,10 +37,7 @@ use self::{
tree::handle as handle_tree,
};
use crate::database::schema::tag::YokedString;
use crate::{
database::schema::{commit::YokedCommit, tag::YokedTag},
layers::UnwrapInfallible,
};
use crate::database::schema::{commit::YokedCommit, tag::YokedTag};

pub const DEFAULT_BRANCHES: [&str; 2] = ["refs/heads/master", "refs/heads/main"];

Expand All @@ -53,15 +49,52 @@ pub async fn service(mut request: Request<Body>) -> Response {
.get::<Arc<PathBuf>>()
.expect("scan_path missing");

let mut child_path = None;
let ParsedUri {
uri,
child_path,
action,
} = parse_uri(request.uri().path().trim_matches('/'));

let uri = Path::new(uri).clean();
let path = scan_path.join(&uri);

macro_rules! h {
($handler:ident) => {
BoxCloneService::new($handler.into_service())
};
let db = request
.extensions()
.get::<Arc<rocksdb::DB>>()
.expect("db extension missing");
if path.as_os_str().is_empty()
|| !crate::database::schema::repository::Repository::exists(db, &uri).unwrap_or_default()
{
return RepositoryNotFound.into_response();
}

request.extensions_mut().insert(ChildPath(child_path));
request.extensions_mut().insert(Repository(uri));
request.extensions_mut().insert(RepositoryPath(path));

match action {
HandlerAction::About => handle_about.call(request, None::<()>).await,
HandlerAction::SmartGit => handle_smart_git.call(request, None::<()>).await,
HandlerAction::Refs => handle_refs.call(request, None::<()>).await,
HandlerAction::Log => handle_log.call(request, None::<()>).await,
HandlerAction::Tree => handle_tree.call(request, None::<()>).await,
HandlerAction::Commit => handle_commit.call(request, None::<()>).await,
HandlerAction::Diff => handle_diff.call(request, None::<()>).await,
HandlerAction::Patch => handle_patch.call(request, None::<()>).await,
HandlerAction::Tag => handle_tag.call(request, None::<()>).await,
HandlerAction::Snapshot => handle_snapshot.call(request, None::<()>).await,
HandlerAction::Summary => handle_summary.call(request, None::<()>).await,
}
}

#[derive(Debug, PartialEq, Eq)]
struct ParsedUri<'a> {
action: HandlerAction,
uri: &'a str,
child_path: Option<PathBuf>,
}

let uri = request.uri().path().trim_matches('/');
fn parse_uri(uri: &str) -> ParsedUri<'_> {
let mut uri_parts = memchr::memchr_iter(b'/', uri.as_bytes());

let original_uri = uri;
Expand All @@ -71,28 +104,75 @@ pub async fn service(mut request: Request<Body>) -> Response {
(None, uri)
};

let mut service = match action {
Some("about") => h!(handle_about),
Some("git-upload-pack") => h!(handle_smart_git),
match action {
Some("about") => ParsedUri {
action: HandlerAction::About,
uri,
child_path: None,
},
Some("git-upload-pack") => ParsedUri {
action: HandlerAction::SmartGit,
uri,
child_path: None,
},
Some("refs") => {
if let Some(idx) = uri_parts.next_back() {
if uri.get(idx + 1..) == Some("info") {
uri = &uri[..idx];
h!(handle_smart_git)
ParsedUri {
action: HandlerAction::SmartGit,
uri: &uri[..idx],
child_path: None,
}
} else {
h!(handle_refs)
ParsedUri {
action: HandlerAction::Refs,
uri,
child_path: None,
}
}
} else {
h!(handle_refs)
ParsedUri {
action: HandlerAction::Refs,
uri,
child_path: None,
}
}
}
Some("log") => h!(handle_log),
Some("tree") => h!(handle_tree),
Some("commit") => h!(handle_commit),
Some("diff") => h!(handle_diff),
Some("patch") => h!(handle_patch),
Some("tag") => h!(handle_tag),
Some("snapshot") => h!(handle_snapshot),
Some("log") => ParsedUri {
action: HandlerAction::Log,
uri,
child_path: None,
},
Some("tree") => ParsedUri {
action: HandlerAction::Tree,
uri,
child_path: None,
},
Some("commit") => ParsedUri {
action: HandlerAction::Commit,
uri,
child_path: None,
},
Some("diff") => ParsedUri {
action: HandlerAction::Diff,
uri,
child_path: None,
},
Some("patch") => ParsedUri {
action: HandlerAction::Patch,
uri,
child_path: None,
},
Some("tag") => ParsedUri {
action: HandlerAction::Tag,
uri,
child_path: None,
},
Some("snapshot") => ParsedUri {
action: HandlerAction::Snapshot,
uri,
child_path: None,
},
Some(_) => {
static TREE_FINDER: LazyLock<memchr::memmem::Finder> =
LazyLock::new(|| memchr::memmem::Finder::new(b"/tree/"));
Expand All @@ -101,39 +181,41 @@ pub async fn service(mut request: Request<Body>) -> Response {

// match tree children
if let Some(idx) = TREE_FINDER.find(uri.as_bytes()) {
// 6 is the length of /tree/
child_path = Some(Path::new(&uri[idx + 6..]).clean());
uri = &uri[..idx];
h!(handle_tree)
ParsedUri {
action: HandlerAction::Tree,
uri: &uri[..idx],
// 6 is the length of /tree/
child_path: Some(Path::new(&uri[idx + 6..]).clean()),
}
} else {
h!(handle_summary)
ParsedUri {
action: HandlerAction::Summary,
uri,
child_path: None,
}
}
}
None => h!(handle_summary),
};

let uri = Path::new(uri).clean();
let path = scan_path.join(&uri);

let db = request
.extensions()
.get::<Arc<rocksdb::DB>>()
.expect("db extension missing");
if path.as_os_str().is_empty()
|| !crate::database::schema::repository::Repository::exists(db, &uri).unwrap_or_default()
{
return RepositoryNotFound.into_response();
None => ParsedUri {
action: HandlerAction::Summary,
uri,
child_path: None,
},
}
}

request.extensions_mut().insert(ChildPath(child_path));
request.extensions_mut().insert(Repository(uri));
request.extensions_mut().insert(RepositoryPath(path));

service
.call(request)
.await
.unwrap_infallible()
.into_response()
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum HandlerAction {
About,
SmartGit,
Refs,
Log,
Tree,
Commit,
Diff,
Patch,
Tag,
Snapshot,
Summary,
}

#[derive(Clone)]
Expand Down

0 comments on commit 2b75eb7

Please sign in to comment.