unlimit transfer size when downloading files (for unknown mimetypes)

This commit is contained in:
Crispy 2024-07-10 21:51:01 +02:00
parent e90576cccd
commit 1d3b295f0d
2 changed files with 81 additions and 72 deletions

View file

@ -37,9 +37,9 @@ pub enum Status {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Content { pub struct Content {
mime_type: &'static str, pub mime_type: &'static str,
range: Option<(usize, usize, usize)>, pub range: Option<(usize, usize, usize)>,
bytes: Vec<u8>, pub bytes: Vec<u8>,
} }
impl Request { impl Request {
@ -139,64 +139,19 @@ impl Response {
impl Content { impl Content {
pub fn html(text: String) -> Self { pub fn html(text: String) -> Self {
Self::file("html", text.into_bytes())
}
pub fn text(text: String) -> Self {
Self::file("txt", text.into_bytes())
}
pub fn file(ext: &str, bytes: Vec<u8>) -> Self {
let content_type = match ext {
"txt" | "md" | "toml" => "text/plain",
"html" | "htm" => "text/html",
"css" => "text/css",
"apng" => "image/apng",
"bmp" => "image/bmp",
"gif" => "image/gif",
"jpeg" | "jpg" => "image/jpeg",
"png" => "image/png",
"svg" => "image/svg+xml",
"tif" | "tiff" => "image/tiff",
"webp" => "image/webp",
"aac" => "audio/aac",
"mp3" => "audio/mpeg",
"oga" | "ogg" => "audio/ogg",
"opus" => "audio/opus",
"wav" => "audio/wav",
"weba" => "audio/webm",
"3gp" => "video/3gpp",
"3gp2" => "video/3gpp2",
"avi" => "video/x-msvideo",
"mov" => "video/mov",
"mp4" => "video/mp4",
"mpeg" => "video/mpeg",
"ogv" => "video/ogv",
"webm" => "video/webm",
"json" => "application/json",
"gz" => "application/gzip",
_ => {
if bytes.is_ascii() {
"text/plain"
} else {
"application/octet-stream"
}
}
};
Self { Self {
mime_type: content_type, mime_type: "text/html",
range: None, range: None,
bytes, bytes: text.into_bytes(),
} }
} }
pub fn with_range(mut self, range: Option<(usize, usize, usize)>) -> Self { pub fn text(text: String) -> Self {
self.range = range; Self {
self mime_type: "text/plain",
range: None,
bytes: text.into_bytes(),
}
} }
} }

View file

@ -1,7 +1,7 @@
use std::{ use std::{
env, env,
fs::{self, File}, fs::{self, File},
io::{BufReader, Read, Seek, Write}, io::{Read, Seek, Write},
net::{TcpListener, TcpStream}, net::{TcpListener, TcpStream},
path::{Path, PathBuf}, path::{Path, PathBuf},
thread, thread,
@ -133,7 +133,7 @@ fn handle_request(request: &str, stream: &mut TcpStream) -> bool {
} }
fn get_file(request: &Request) -> Option<(Content, bool)> { fn get_file(request: &Request) -> Option<(Content, bool)> {
const MAX_SIZE: usize = 1024 * 1024 * 8; const MAX_PARTIAL_PACKET_SIZE: usize = 1024 * 1024 * 8;
let current_dir = env::current_dir().unwrap(); let current_dir = env::current_dir().unwrap();
@ -160,30 +160,47 @@ fn get_file(request: &Request) -> Option<(Content, bool)> {
} }
} else if path.is_file() { } else if path.is_file() {
let ext = path.extension().unwrap_or_default().to_str()?; let ext = path.extension().unwrap_or_default().to_str()?;
let file = File::open(&path).ok()?; let mut file = File::open(&path).ok()?;
let size = file.metadata().ok()?.len() as usize; let file_size = file.metadata().ok()?.len() as usize;
let mime_type = mime_type(ext);
let mut buf = vec![0; MAX_SIZE]; let buffer_size = if mime_type.is_some() {
let mut reader = BufReader::new(file); MAX_PARTIAL_PACKET_SIZE
} else {
file_size
};
let mut bytes = vec![0; buffer_size];
let start_pos = match request.range { let start_pos = match request.range {
Some(RequestRange::From(p)) => p, Some(RequestRange::From(p)) => p,
Some(RequestRange::Full(start, _end)) => start, Some(RequestRange::Full(start, _end)) => start,
_ => 0, _ => 0,
}; };
reader file.seek(std::io::SeekFrom::Start(start_pos as u64)).ok()?;
.seek(std::io::SeekFrom::Start(start_pos as u64))
.ok()?;
let size_read = reader.read(&mut buf).ok()?; let size_read = file.read(&mut bytes).ok()?;
buf.truncate(size_read); bytes.truncate(size_read);
let mut end_of_file = false; let mut end_of_file = false;
let range = if size_read < size { let range = if size_read < file_size {
end_of_file = start_pos + size_read == size; end_of_file = start_pos + size_read == file_size;
Some((start_pos, start_pos + size_read - 1, size)) Some((start_pos, start_pos + size_read - 1, file_size))
} else { } else {
None None
}; };
Some((Content::file(ext, buf).with_range(range), end_of_file)) let mime_type = mime_type.unwrap_or_else(|| {
if bytes.is_ascii() {
"text/plain"
} else {
"application/octet-stream"
}
});
Some((
Content {
mime_type,
range,
bytes,
},
end_of_file,
))
} else { } else {
None None
} }
@ -263,7 +280,7 @@ fn generate_index(relative_path: &str, path: &Path) -> Option<Content> {
<body> <body>
<h3>Index of {relative_path}</h3> <h3>Index of {relative_path}</h3>
<pre> <pre>
{parent}{list}</pre> {parent}{list} </pre>
</body> </body>
</html>"# </html>"#
); );
@ -325,3 +342,40 @@ fn formatted_time_now() -> String {
format!("{year}-{month:02}-{day:02}_{hour:02}:{minute:02}:{second:02}") format!("{year}-{month:02}-{day:02}_{hour:02}:{minute:02}:{second:02}")
} }
fn mime_type(ext: &str) -> Option<&'static str> {
let t = match ext {
"txt" | "md" | "toml" => "text/plain",
"html" | "htm" => "text/html",
"css" => "text/css",
"apng" => "image/apng",
"bmp" => "image/bmp",
"gif" => "image/gif",
"jpeg" | "jpg" => "image/jpeg",
"png" => "image/png",
"svg" => "image/svg+xml",
"tif" | "tiff" => "image/tiff",
"webp" => "image/webp",
"aac" => "audio/aac",
"mp3" => "audio/mpeg",
"oga" | "ogg" => "audio/ogg",
"opus" => "audio/opus",
"wav" => "audio/wav",
"weba" => "audio/webm",
"3gp" => "video/3gpp",
"3gp2" => "video/3gpp2",
"avi" => "video/x-msvideo",
"mov" => "video/mov",
"mp4" => "video/mp4",
"mpeg" => "video/mpeg",
"ogv" => "video/ogv",
"webm" => "video/webm",
"json" => "application/json",
_ => return None,
};
Some(t)
}