unlimit transfer size when downloading files (for unknown mimetypes)
This commit is contained in:
parent
e90576cccd
commit
1d3b295f0d
2 changed files with 81 additions and 72 deletions
67
src/http.rs
67
src/http.rs
|
@ -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(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
86
src/main.rs
86
src/main.rs
|
@ -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)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue