2023-01-30 17:11:30 -06:00
|
|
|
use std::{
|
|
|
|
path::{Component, PathBuf},
|
|
|
|
sync::Arc,
|
|
|
|
};
|
|
|
|
|
|
|
|
use axum::{
|
|
|
|
body::StreamBody,
|
|
|
|
extract::{Path, State},
|
|
|
|
response::{IntoResponse, Response},
|
|
|
|
};
|
|
|
|
|
|
|
|
use bytes::Bytes;
|
|
|
|
use hyper::StatusCode;
|
|
|
|
use tokio::fs::File;
|
|
|
|
use tokio_util::io::ReaderStream;
|
|
|
|
|
2023-01-30 18:14:25 -06:00
|
|
|
pub enum ViewSuccess {
|
2023-01-30 17:11:30 -06:00
|
|
|
FromDisk(File),
|
|
|
|
FromCache(Bytes),
|
|
|
|
}
|
|
|
|
|
2023-01-30 18:14:25 -06:00
|
|
|
pub enum ViewError {
|
|
|
|
NotFound,
|
|
|
|
InternalServerError,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl IntoResponse for ViewSuccess {
|
2023-01-30 17:11:30 -06:00
|
|
|
fn into_response(self) -> Response {
|
|
|
|
match self {
|
2023-01-30 18:14:25 -06:00
|
|
|
ViewSuccess::FromDisk(file) => {
|
2023-01-30 17:11:30 -06:00
|
|
|
// create a streamed body response (we want to stream larger files)
|
|
|
|
let reader = ReaderStream::new(file);
|
|
|
|
let stream = StreamBody::new(reader);
|
|
|
|
|
|
|
|
stream.into_response()
|
|
|
|
}
|
2023-01-30 18:14:25 -06:00
|
|
|
ViewSuccess::FromCache(data) => {
|
2023-01-30 17:11:30 -06:00
|
|
|
// extract mutable headers from the response
|
|
|
|
let mut res = data.into_response();
|
|
|
|
let headers = res.headers_mut();
|
|
|
|
|
|
|
|
// clear the headers, let the browser imply it
|
|
|
|
headers.clear();
|
|
|
|
|
|
|
|
res
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-30 18:14:25 -06:00
|
|
|
impl IntoResponse for ViewError {
|
|
|
|
fn into_response(self) -> Response {
|
|
|
|
match self {
|
|
|
|
ViewError::NotFound => {
|
|
|
|
// convert string into response, change status code
|
|
|
|
let mut res = "not found!".into_response();
|
|
|
|
*res.status_mut() = StatusCode::NOT_FOUND;
|
|
|
|
|
|
|
|
res
|
|
|
|
}
|
|
|
|
ViewError::InternalServerError => {
|
|
|
|
// convert string into response, change status code
|
|
|
|
let mut res = "internal server error!".into_response();
|
|
|
|
*res.status_mut() = StatusCode::NOT_FOUND;
|
|
|
|
|
|
|
|
res
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-30 17:11:30 -06:00
|
|
|
#[axum::debug_handler]
|
|
|
|
pub async fn view(
|
|
|
|
State(engine): State<Arc<crate::engine::Engine>>,
|
|
|
|
Path(original_path): Path<PathBuf>,
|
2023-01-30 18:14:25 -06:00
|
|
|
) -> Result<ViewSuccess, ViewError> {
|
2023-01-30 17:11:30 -06:00
|
|
|
// (hopefully) prevent path traversal, just check for any non-file components
|
|
|
|
if original_path
|
|
|
|
.components()
|
|
|
|
.into_iter()
|
|
|
|
.any(|x| !matches!(x, Component::Normal(_)))
|
|
|
|
{
|
|
|
|
warn!(target: "view", "a request attempted path traversal");
|
2023-01-30 18:14:25 -06:00
|
|
|
return Err(ViewError::NotFound);
|
2023-01-30 17:11:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
engine.get_upload(&original_path).await
|
|
|
|
}
|