Primitive handling of info/refs & git-upload-pack
Diff
Cargo.lock | 2 ++
Cargo.toml | 2 ++
src/git_cgi.rs | 24 ++++++++++++++++++++++++
src/main.rs | 1 +
src/methods/repo.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
5 files changed, 93 insertions(+), 3 deletions(-)
@@ -1716,9 +1716,11 @@
"futures",
"git2",
"hex",
"httparse",
"humantime",
"md5",
"moka",
"nom",
"parking_lot 0.12.1",
"path-clean",
"rsass",
@@ -16,6 +16,7 @@
git2 = "0.14"
hex = "0.4"
humantime = "2.1"
nom = "7.1"
md5 = "0.7"
moka = { version = "0.9", features = ["future"] }
path-clean = "0.1"
@@ -33,6 +34,7 @@
tracing-subscriber = "0.3"
unix_mode = "0.1"
uuid = { version = "1.1", features = ["v4"] }
httparse = "1.7"
yoke = { version = "0.6", features = ["derive"] }
[build-dependencies]
@@ -1,0 +1,24 @@
use axum::body::{boxed, Body};
use axum::http::header::HeaderName;
use axum::http::HeaderValue;
use axum::response::Response;
use std::str::FromStr;
pub fn cgi_to_response(buffer: &[u8]) -> Response {
let mut headers = [httparse::EMPTY_HEADER; 10];
let (body_offset, headers) = httparse::parse_headers(buffer, &mut headers)
.unwrap()
.unwrap();
let mut response = Response::new(boxed(Body::from(buffer[body_offset..].to_vec())));
for header in headers {
response.headers_mut().insert(
HeaderName::from_str(header.name).unwrap(),
HeaderValue::from_bytes(header.value).unwrap(),
);
}
response
}
@@ -18,6 +18,7 @@
mod database;
mod git;
mod git_cgi;
mod layers;
mod methods;
@@ -1,20 +1,24 @@
use std::fmt::{Display, Formatter};
use std::{
fmt::{Debug, Display, Formatter},
io::Write,
ops::Deref,
path::{Path, PathBuf},
process::Stdio,
sync::Arc,
};
use askama::Template;
use axum::http::HeaderValue;
use axum::{
body::HttpBody,
extract::Query,
handler::Handler,
http,
http::HeaderValue,
http::Request,
response::{IntoResponse, Response},
Extension,
};
use bytes::Bytes;
use path_clean::PathClean;
use serde::Deserialize;
use tower::{util::BoxCloneService, Service};
@@ -50,7 +54,13 @@
}
pub async fn service<ReqBody: Send + 'static>(mut request: Request<ReqBody>) -> Response {
pub async fn service<ReqBody: HttpBody + Send + Debug + 'static>(
mut request: Request<ReqBody>,
) -> Response
where
<ReqBody as HttpBody>::Data: Send + Sync,
<ReqBody as HttpBody>::Error: std::error::Error + Send + Sync,
{
let mut uri_parts: Vec<&str> = request
.uri()
.path()
@@ -63,6 +73,13 @@
let mut service = match uri_parts.pop() {
Some("about") => BoxCloneService::new(handle_about.into_service()),
Some("refs") if uri_parts.last() == Some(&"info") => {
uri_parts.pop();
BoxCloneService::new(handle_info_refs.into_service())
}
Some("git-upload-pack") => BoxCloneService::new(handle_git_upload_pack.into_service()),
Some("refs") => BoxCloneService::new(handle_refs.into_service()),
Some("log") => BoxCloneService::new(handle_log.into_service()),
Some("tree") => BoxCloneService::new(handle_tree.into_service()),
@@ -210,6 +227,50 @@
next_offset,
branch: query.branch,
})
}
#[derive(Deserialize)]
pub struct SmartGitQuery {
service: String,
}
pub async fn handle_info_refs(
Extension(RepositoryPath(repository_path)): Extension<RepositoryPath>,
Query(query): Query<SmartGitQuery>,
) -> Response {
let out = std::process::Command::new("git")
.arg("http-backend")
.env("REQUEST_METHOD", "GET")
.env("PATH_INFO", "/info/refs")
.env("GIT_PROJECT_ROOT", repository_path)
.env("QUERY_STRING", format!("service={}", query.service))
.output()
.unwrap();
crate::git_cgi::cgi_to_response(&out.stdout)
}
pub async fn handle_git_upload_pack(
Extension(RepositoryPath(repository_path)): Extension<RepositoryPath>,
body: Bytes,
) -> Response {
let mut child = std::process::Command::new("git")
.arg("http-backend")
.env("REQUEST_METHOD", "POST")
.env("CONTENT_TYPE", "application/x-git-upload-pack-request")
.env("PATH_INFO", "/git-upload-pack")
.env("GIT_PROJECT_ROOT", repository_path)
.stdout(Stdio::piped())
.stdin(Stdio::piped())
.spawn()
.unwrap();
child.stdin.as_mut().unwrap().write_all(&body).unwrap();
let out = child.wait_with_output().unwrap();
crate::git_cgi::cgi_to_response(&out.stdout)
}
#[derive(Template)]