Handle requested path being a directory correctly

This commit is contained in:
Brian Picciano 2024-01-16 14:31:32 +01:00
parent 6a611c371c
commit 6dd53f96d8
4 changed files with 52 additions and 10 deletions

View File

@ -39,6 +39,9 @@ pub enum GetFileError {
#[error("file not found")] #[error("file not found")]
FileNotFound, FileNotFound,
#[error("path is directory")]
PathIsDirectory,
#[error(transparent)] #[error(transparent)]
Unexpected(#[from] unexpected::Error), Unexpected(#[from] unexpected::Error),
} }

View File

@ -330,11 +330,21 @@ impl super::Store for FSStore {
.object() .object()
.or_unexpected()?; .or_unexpected()?;
use gix::object::Kind;
match file_object.kind {
Kind::Tree => Err(origin::GetFileError::PathIsDirectory),
Kind::Blob => {
// TODO this is very not ideal, the whole file is first read totally into memory, and then // TODO this is very not ideal, the whole file is first read totally into memory, and then
// that is cloned. // that is cloned.
let data = bytes::Bytes::copy_from_slice(file_object.data.as_slice()); let data = bytes::Bytes::copy_from_slice(file_object.data.as_slice());
Ok(Box::pin(stream::once(async move { Ok(data) }))) Ok(Box::pin(stream::once(async move { Ok(data) })))
} }
Kind::Commit | Kind::Tag => Err(unexpected::Error::from(
format!("found object of kind {} in tree", file_object.kind).as_str(),
)
.into()),
}
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -113,6 +113,13 @@ impl Service {
) )
.into()) .into())
} }
Err(GetFileError::PathIsDirectory) => {
// redirect so that the path has '/' appended to it, which will cause the server to
// check index.gmi within the path on the new page load.
let mut path = path.into_owned();
path.push_str("/");
return Ok(self.respond_conn(w, "30", path.as_str(), None).await?);
}
Err(GetFileError::Unexpected(e)) => return Err(e.into()), Err(GetFileError::Unexpected(e)) => return Err(e.into()),
}; };

View File

@ -153,6 +153,19 @@ impl Service {
) )
} }
fn render_redirect(&self, status_code: u16, target_uri: &str) -> Response<Body> {
Response::builder()
.status(status_code)
.header("Location", target_uri.to_string())
.body(Body::empty())
.unwrap_or_else(|err| {
self.internal_error(
format!("failed to render {status_code} redirect to {target_uri}: {err}",)
.as_str(),
)
})
}
fn https_redirect(&self, domain: domain::Name, req: Request<Body>) -> Response<Body> { fn https_redirect(&self, domain: domain::Name, req: Request<Body>) -> Response<Body> {
let https_addr = self.config.http.https_addr.unwrap(); let https_addr = self.config.http.https_addr.unwrap();
@ -173,13 +186,12 @@ impl Service {
.try_into() .try_into()
.or_unexpected_while("constructing new URI")?; .or_unexpected_while("constructing new URI")?;
Response::builder() Ok(self.render_redirect(
.status(http::status::StatusCode::PERMANENT_REDIRECT) http::status::StatusCode::PERMANENT_REDIRECT.into(),
.header("Location", uri.to_string()) uri.to_string().as_str(),
.body(Body::empty()) ))
.or_unexpected_while("building redirect")
})() })()
.unwrap_or_else(|err| { .unwrap_or_else(|err: unexpected::Error| {
self.internal_error( self.internal_error(
format!("failed to redirect from {} to https: {}", req.uri(), err).as_str(), format!("failed to redirect from {} to https: {}", req.uri(), err).as_str(),
) )
@ -216,6 +228,16 @@ impl Service {
) )
.as_str(), .as_str(),
), ),
Err(GetFileError::PathIsDirectory) => {
// redirect so that the path has '/' appended to it, which will cause the server to
// check index.html within the path on the new page load.
let mut path = path.into_owned();
path.push_str("/");
self.render_redirect(
http::status::StatusCode::TEMPORARY_REDIRECT.into(),
path.as_str(),
)
}
Err(GetFileError::Unexpected(e)) => { Err(GetFileError::Unexpected(e)) => {
self.internal_error(format!("failed to fetch file {path}: {e}").as_str()) self.internal_error(format!("failed to fetch file {path}: {e}").as_str())
} }