Split out URI parsing code to improve testability and remove need for boxing
Diff
Cargo.lock | 3 +--
Cargo.toml | 1 -
src/methods/repo/mod.rs | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
3 files changed, 137 insertions(+), 57 deletions(-)
@@ -1,6 +1,6 @@
version = 4
version = 3
[[package]]
name = "addr2line"
@@ -2600,7 +2600,6 @@
"tokio",
"tokio-stream",
"tokio-util",
"tower",
"tower-http",
"tower-layer",
"tower-service",
@@ -68,7 +68,6 @@
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"
@@ -18,12 +18,11 @@
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,
@@ -38,10 +37,7 @@
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"];
@@ -52,16 +48,53 @@
.extensions()
.get::<Arc<PathBuf>>()
.expect("scan_path missing");
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);
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();
}
let mut child_path = None;
request.extensions_mut().insert(ChildPath(child_path));
request.extensions_mut().insert(Repository(uri));
request.extensions_mut().insert(RepositoryPath(path));
macro_rules! h {
($handler:ident) => {
BoxCloneService::new($handler.into_service())
};
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;
@@ -71,28 +104,75 @@
(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/"));
@@ -101,39 +181,41 @@
if let Some(idx) = TREE_FINDER.find(uri.as_bytes()) {
child_path = Some(Path::new(&uri[idx + 6..]).clean());
uri = &uri[..idx];
h!(handle_tree)
ParsedUri {
action: HandlerAction::Tree,
uri: &uri[..idx],
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)]