wayver's git archive


a simple self-hosted git server
git clone https://git.wayver.dev/bile

src/handlers/git.rs@3aec1a1bb245ce255f59532eba2bdfe1d2879510

raw
Date Commit Message Author Files + -
2026-02-20 13:35 custom path extractor for better error handling wayverd 14 108 49
...

1use axum::{
2    extract::State,
3    http::{HeaderValue, StatusCode, Uri, header},
4    response::{IntoResponse as _, Response},
5};
6
7use crate::{
8    BileState,
9    error::Context as _,
10    git::Repository,
11    http::{
12        extractor::RepoName,
13        path::Path,
14        response::{ErrorPage, Result},
15    },
16};
17
18#[tracing::instrument(skip_all)]
19pub(crate) async fn get_1(
20    state: State<BileState>,
21    uri: Uri,
22    Path(repo_name): Path<RepoName>,
23) -> Response {
24    state
25        .spawn(move |state| inner(&state, &uri, &repo_name))
26        .await
27}
28
29#[tracing::instrument(skip_all)]
30pub(crate) async fn get_2(
31    state: State<BileState>,
32    uri: Uri,
33    Path((repo_name, _)): Path<(RepoName, String)>,
34) -> Response {
35    state
36        .spawn(move |state| inner(&state, &uri, &repo_name))
37        .await
38}
39
40fn inner(state: &BileState, uri: &Uri, repo_name: &RepoName) -> Result<Response> {
41    let Some(repo) = Repository::open(&state.config, repo_name).context("opening repository")?
42    else {
43        return Ok(ErrorPage::from(state)
44            .with_status(StatusCode::NOT_FOUND)
45            .into_response());
46    };
47
48    let path = uri
49        .path()
50        .strip_prefix(&format!("/{repo_name}/"))
51        .unwrap_or_default();
52
53    let path = repo.path().join(path);
54
55    // cant canonicalize if it doesnt exist
56    if !path.exists() {
57        return Ok(ErrorPage::from(state)
58            .with_status(StatusCode::NOT_FOUND)
59            .into_response());
60    }
61
62    let path = path.canonicalize().context("canonicalize new path")?;
63
64    // that path got us outside of the repository structure somehow
65    if !path.starts_with(repo.path()) {
66        tracing::warn!("Attempt to acces file outside of repo dir: {:?}", path);
67        return Ok(ErrorPage::from(state)
68            .with_status(StatusCode::FORBIDDEN)
69            .into_response());
70    }
71
72    // Either the requested resource does not exist or it is not
73    // a file, i.e. a directory.
74    if !path.is_file() {
75        return Ok(ErrorPage::from(state)
76            .with_status(StatusCode::NOT_FOUND)
77            .into_response());
78    }
79
80    let body = std::fs::read(&path).context("reading file")?;
81
82    Ok((
83        StatusCode::OK,
84        [(
85            header::CONTENT_TYPE,
86            HeaderValue::from_static(mime::APPLICATION_OCTET_STREAM.as_ref()),
87        )],
88        body,
89    )
90        .into_response())
91}
92