breeze/src/view.rs

166 lines
4.5 KiB
Rust
Raw Normal View History

2022-12-28 22:05:28 -06:00
use std::{
ffi::OsStr,
path::{Component, PathBuf},
sync::Arc,
};
use axum::{
body::StreamBody,
extract::{Path, State},
2023-01-08 18:08:24 -06:00
http::HeaderValue,
response::{IntoResponse, Response},
2022-12-28 22:05:28 -06:00
};
2023-01-08 18:08:24 -06:00
use bytes::{Bytes, BytesMut};
2022-12-28 22:05:28 -06:00
use hyper::StatusCode;
2023-01-08 18:08:24 -06:00
use mime_guess::mime;
use tokio::{fs::File, io::AsyncReadExt};
2022-12-28 22:05:28 -06:00
use tokio_util::io::ReaderStream;
2023-01-08 18:08:24 -06:00
use crate::cache;
2022-12-28 22:05:28 -06:00
/* pub enum ViewResponse {
FromDisk(StreamBody<ReaderStream<File>>),
FromCache(Bytes)
}
impl IntoResponse for ViewResponse {
fn into_response(self) -> Response {
match self {
ViewResponse::FromDisk(stream) => stream.into_response(),
ViewResponse::FromCache(data) => data.into_response()
}
}
} */
#[axum::debug_handler]
2022-12-28 22:05:28 -06:00
pub async fn view(
State(state): State<Arc<crate::state::AppState>>,
Path(original_path): Path<PathBuf>,
) -> Response {
// (hopefully) prevent path traversal, just check for any non-file components
if original_path
.components()
.into_iter()
.any(|x| !matches!(x, Component::Normal(_)))
{
2023-01-08 18:08:24 -06:00
error!(target: "view", "a request attempted path traversal");
2022-12-28 22:05:28 -06:00
return StatusCode::NOT_FOUND.into_response();
}
let name = original_path
.file_name()
.and_then(OsStr::to_str)
.unwrap_or_default()
.to_string();
2023-01-08 18:08:24 -06:00
let mut cache = state.cache.lock().await;
2022-12-28 22:05:28 -06:00
2023-01-08 18:08:24 -06:00
let cache_item = cache.get(&name.clone());
2022-12-28 22:05:28 -06:00
if cache_item.is_none() {
2022-12-28 22:05:28 -06:00
let mut path = PathBuf::new();
path.push("uploads/");
2023-01-08 18:08:24 -06:00
path.push(name.clone());
2022-12-28 22:05:28 -06:00
if !path.exists() || !path.is_file() {
return StatusCode::NOT_FOUND.into_response();
}
2023-01-08 18:08:24 -06:00
let mut file = File::open(path).await.unwrap();
let file_len = file.metadata().await.unwrap().len() as usize;
2022-12-28 22:05:28 -06:00
2023-01-08 18:08:24 -06:00
if file_len < cache::MAX_LENGTH {
info!(target: "view", "recaching upload from disk");
2022-12-28 22:05:28 -06:00
2023-01-08 18:08:24 -06:00
let mut data = BytesMut::zeroed(file_len);
file.read_buf(&mut data.as_mut()).await.unwrap();
let data = data.freeze();
2022-12-28 22:05:28 -06:00
2023-01-08 18:08:24 -06:00
cache.insert(name.clone(), data.clone(), Some(cache::DURATION));
2022-12-28 22:05:28 -06:00
2023-01-08 18:08:24 -06:00
return cache::get_response(&mut cache, original_path);
} else {
let reader = ReaderStream::new(file);
let stream = StreamBody::new(reader);
2022-12-28 22:05:28 -06:00
2023-01-08 18:08:24 -06:00
info!(target: "view", "reading upload from disk");
2022-12-28 22:05:28 -06:00
2023-01-08 18:08:24 -06:00
return stream.into_response();
}
}
info!(target: "view", "reading upload from cache");
return cache::get_response(&mut cache, original_path);
2022-12-28 22:05:28 -06:00
}
2023-01-08 18:08:24 -06:00
/* #[axum::debug_handler]
pub async fn view(
State(state): State<Arc<crate::state::AppState>>,
2022-12-28 22:05:28 -06:00
Path(original_path): Path<PathBuf>,
) -> Response {
// (hopefully) prevent path traversal, just check for any non-file components
if original_path
.components()
.into_iter()
.any(|x| !matches!(x, Component::Normal(_)))
{
2023-01-08 18:08:24 -06:00
error!(target: "view", "a request attempted path traversal");
return StatusCode::NOT_FOUND.into_response();
2022-12-28 22:05:28 -06:00
}
let name = original_path
.file_name()
.and_then(OsStr::to_str)
.unwrap_or_default()
.to_string();
2023-01-08 18:08:24 -06:00
let cache = state.cache.lock().await;
2022-12-28 22:05:28 -06:00
let cache_item = cache.get(&name);
2023-01-08 18:08:24 -06:00
if cache_item.is_none() {
let mut path = PathBuf::new();
path.push("uploads/");
path.push(name);
2022-12-28 22:05:28 -06:00
2023-01-08 18:08:24 -06:00
if !path.exists() || !path.is_file() {
return StatusCode::NOT_FOUND.into_response();
}
2022-12-28 22:05:28 -06:00
2023-01-08 18:08:24 -06:00
let file = File::open(path).await.unwrap();
2022-12-28 22:05:28 -06:00
2023-01-08 18:08:24 -06:00
if file.metadata().await.unwrap().len() < (cache::MAX_LENGTH as u64) {
info!("file can be cached");
}
2022-12-28 22:05:28 -06:00
2023-01-08 18:08:24 -06:00
let reader = ReaderStream::new(file);
let stream = StreamBody::new(reader);
info!(target: "view", "reading upload from disk");
return stream.into_response();
2022-12-28 22:05:28 -06:00
}
2023-01-08 18:08:24 -06:00
info!(target: "view", "reading upload from cache");
2022-12-28 22:05:28 -06:00
2023-01-08 18:08:24 -06:00
let data = cache_item.unwrap().clone();
2022-12-28 22:05:28 -06:00
2023-01-08 18:08:24 -06:00
let content_type = mime_guess::from_path(original_path)
.first()
.unwrap_or(mime::APPLICATION_OCTET_STREAM)
.to_string();
let mut res = data.into_response();
let headers = res.headers_mut();
headers.clear();
headers.insert(
"content-type",
HeaderValue::from_str(content_type.as_str()).unwrap(),
);
return res;
} */